mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-12 21:50:47 +02:00
you've been waiting for this moment for so long
This commit is contained in:
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
158
app/build.gradle
Normal file
158
app/build.gradle
Normal file
@ -0,0 +1,158 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'io.fabric'
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
}
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
defaultConfig {
|
||||
applicationId 'pl.szczodrzynski.edziennik'
|
||||
minSdkVersion setup.minSdk
|
||||
targetSdkVersion setup.targetSdk
|
||||
versionCode release.versionCode
|
||||
versionName release.versionName
|
||||
multiDexEnabled true
|
||||
}
|
||||
buildTypes {
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.all {
|
||||
if (variant.buildType.name == "release") {
|
||||
outputFileName = "Edziennik_" + defaultConfig.versionName + ".apk"
|
||||
} else if (variant.buildType.name == "debugMinify") {
|
||||
outputFileName = "Edziennik_" + defaultConfig.versionName + "_debugMinify.apk"
|
||||
} else {
|
||||
outputFileName = "Edziennik_" + defaultConfig.versionName + "_debug.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt')
|
||||
proguardFiles fileTree('proguard').asList().toArray()
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "com.google.firebase:firebase-core:${versions.firebase}"
|
||||
}
|
||||
defaultConfig {
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility '1.8'
|
||||
targetCompatibility '1.8'
|
||||
}
|
||||
productFlavors {
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
packagingOptions {
|
||||
exclude 'META-INF/library-core_release.kotlin_module'
|
||||
}
|
||||
}
|
||||
|
||||
/*task finalizeBundleDebug(type: Copy) {
|
||||
from("debug/debug")
|
||||
include "app.aab"
|
||||
destinationDir file("debug/debug")
|
||||
rename "app.aab", "Edziennik_debug.aab"
|
||||
}
|
||||
|
||||
// it finalizes :bundleRelease
|
||||
task finalizeBundleRelease(type: Copy) {
|
||||
from("release/release")
|
||||
include "app.aab"
|
||||
destinationDir file("release/release")
|
||||
rename "app.aab", "Edziennik_${android.defaultConfig.versionCode}.aab"
|
||||
}*/
|
||||
/*
|
||||
// this adds the above two tasks
|
||||
tasks.whenTaskAdded { task ->
|
||||
if (task.name == "bundleDebug") {
|
||||
task.finalizedBy finalizeBundleDebug
|
||||
} else if (task.name == "bundleRelease") {
|
||||
task.finalizedBy finalizeBundleRelease
|
||||
}
|
||||
}*/
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
annotationProcessor "androidx.room:room-compiler:${versions.room}"
|
||||
debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
|
||||
|
||||
implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}"
|
||||
implementation "androidx.appcompat:appcompat:${versions.appcompat}"
|
||||
implementation "androidx.cardview:cardview:${versions.cardView}"
|
||||
implementation "androidx.constraintlayout:constraintlayout:${versions.constraintLayout}"
|
||||
implementation "androidx.core:core-ktx:${versions.ktx}"
|
||||
implementation "androidx.gridlayout:gridlayout:${versions.gridLayout}"
|
||||
implementation "androidx.legacy:legacy-support-v4:${versions.legacy}"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:${versions.lifecycle}"
|
||||
implementation "androidx.recyclerview:recyclerview:${versions.recyclerView}"
|
||||
implementation "androidx.room:room-runtime:${versions.room}"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
|
||||
|
||||
implementation "com.google.android.gms:play-services-wearable:${versions.play_services}"
|
||||
implementation "com.google.android.material:material:${versions.material}"
|
||||
implementation "com.google.firebase:firebase-messaging:${versions.firebasemessaging}"
|
||||
|
||||
//implementation "com.github.kuba2k2.MaterialDrawer:library:e603091449"
|
||||
implementation "com.mikepenz:crossfader:1.6.0" // do not update
|
||||
implementation "com.mikepenz:iconics-core:${versions.iconics}"
|
||||
implementation "com.mikepenz:iconics-views:${versions.iconics}"
|
||||
implementation "com.mikepenz:community-material-typeface:${versions.font_cmd}@aar"
|
||||
|
||||
implementation "com.github.kuba2k2:NavLib:${versions.navlib}"
|
||||
|
||||
implementation "com.afollestad.material-dialogs:commons:${versions.materialdialogs}"
|
||||
implementation "com.afollestad.material-dialogs:core:${versions.materialdialogs}"
|
||||
|
||||
implementation "cat.ereza:customactivityoncrash:2.2.0"
|
||||
implementation "com.applandeo:material-calendar-view:1.5.0"
|
||||
implementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
|
||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||
implementation "com.evernote:android-job:1.2.6"
|
||||
implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2"
|
||||
implementation "com.github.bassaer:chatmessageview:2.0.1"
|
||||
implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true}
|
||||
implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update
|
||||
implementation "com.jaredrummler:colorpicker:1.0.2"
|
||||
implementation "com.squareup.okhttp3:okhttp:3.12.0"
|
||||
implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update
|
||||
implementation "com.wdullaer:materialdatetimepicker:4.1.2"
|
||||
implementation "com.yuyh.json:jsonviewer:1.0.6"
|
||||
implementation "me.dm7.barcodescanner:zxing:1.9.8"
|
||||
implementation "me.grantland:autofittextview:0.2.1"
|
||||
implementation "me.leolin:ShortcutBadger:1.1.22@aar"
|
||||
implementation "org.greenrobot:eventbus:3.1.1"
|
||||
implementation "org.jsoup:jsoup:1.10.1"
|
||||
implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.15"
|
||||
//implementation "se.emilsjolander:stickylistheaders:2.7.0"
|
||||
implementation 'com.github.edisonw:StickyListHeaders:master-SNAPSHOT'
|
||||
implementation "uk.co.samuelwall:material-tap-target-prompt:2.14.0"
|
||||
|
||||
implementation project(":agendacalendarview")
|
||||
implementation project(":cafebar")
|
||||
implementation project(":material-about-library")
|
||||
implementation project(":mhttp")
|
||||
implementation project(":nachos")
|
||||
//implementation project(":Navigation")
|
||||
implementation project(":szkolny-font")
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
1
app/debug/output.json
Normal file
1
app/debug/output.json
Normal file
@ -0,0 +1 @@
|
||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":2600,"versionName":"2.6","enabled":true,"outputFile":"Edziennik_2600_debug.apk","fullName":"debug","baseName":"debug"},"path":"Edziennik_2600_debug.apk","properties":{}}]
|
42
app/google-services.json
Normal file
42
app/google-services.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "640759989760",
|
||||
"firebase_url": "https://edziennik-4d161.firebaseio.com",
|
||||
"project_id": "edziennik-4d161",
|
||||
"storage_bucket": "edziennik-4d161.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:640759989760:android:4aa71407b25cdc8d",
|
||||
"android_client_info": {
|
||||
"package_name": "pl.szczodrzynski.edziennik"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "640759989760-6f8q00u864lnuh3gh36e8g4cer9lv8pv.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyAvq9HMPxulz9ntdAHZ0eZuPf2YQs4nDSU"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 1
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
234
app/src/main/AndroidManifest.xml
Normal file
234
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,234 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="pl.szczodrzynski.edziennik">
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_descriptor"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/SplashTheme"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".messages.MessagesComposeActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/messages_compose_title"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
<activity
|
||||
android:name=".activities.FeedbackActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name=".login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/AppTheme.Light" />
|
||||
<activity
|
||||
android:name=".intro.ChangelogIntroActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Intro" />
|
||||
<!--
|
||||
______ _ _
|
||||
| ____(_) | |
|
||||
| |__ _ _ __ ___| |__ __ _ ___ ___
|
||||
| __| | | '__/ _ \ '_ \ / _` / __|/ _ \
|
||||
| | | | | | __/ |_) | (_| \__ \ __/
|
||||
|_| |_|_| \___|_.__/ \__,_|___/\___/
|
||||
-->
|
||||
<activity
|
||||
android:name=".activities.CrashActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:process=":error_activity"
|
||||
android:theme="@style/DeadTheme" />
|
||||
<!--
|
||||
_____ _ _ _ _ _
|
||||
/ ____| | | | | (_) (_) |
|
||||
| | _ __ __ _ ___| |__ __ _ ___| |_ ___ ___| |_ _ _
|
||||
| | | '__/ _` / __| '_ \ / _` |/ __| __| \ \ / / | __| | | |
|
||||
| |____| | | (_| \__ \ | | | | (_| | (__| |_| |\ V /| | |_| |_| |
|
||||
\_____|_| \__,_|___/_| |_| \__,_|\___|\__|_| \_/ |_|\__|\__, |
|
||||
__/ |
|
||||
|___/
|
||||
-->
|
||||
<activity
|
||||
android:name=".activities.CrashGtfoActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:theme="@style/DeadTheme" />
|
||||
<activity
|
||||
android:name=".widgets.WidgetConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!--
|
||||
__ ___ _ _
|
||||
\ \ / (_) | | | |
|
||||
\ \ /\ / / _ __| | __ _ ___| |_ ___
|
||||
\ \/ \/ / | |/ _` |/ _` |/ _ \ __|/ __|
|
||||
\ /\ / | | (_| | (_| | __/ |_ \__ \
|
||||
\/ \/ |_|\__,_|\__, |\___|\__||___/
|
||||
__/ |
|
||||
|_
|
||||
-->
|
||||
<activity
|
||||
android:name=".widgets.timetable.LessonDetailsActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.SettingsLicenseActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.WebPushConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme.Dark" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.CounterActivity"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
<activity android:name=".activities.QrScannerActivity" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
<!--
|
||||
_____ _ _
|
||||
| __ \ (_) | |
|
||||
| |__) | __ _____ ___ __| | ___ _ __ ___
|
||||
| ___/ '__/ _ \ \ / / |/ _` |/ _ \ '__/ __|
|
||||
| | | | | (_) \ V /| | (_| | __/ | \__ \
|
||||
|_| |_| \___/ \_/ |_|\__,_|\___|_| |___/
|
||||
-->
|
||||
<receiver
|
||||
android:name=".WidgetTimetable"
|
||||
android:label="@string/widget_timetable_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_timetable_info" />
|
||||
</receiver>
|
||||
<!--
|
||||
____ _ _
|
||||
| _ \ | | (_)
|
||||
| |_) | ___ ___ | |_ _ __ ___ ___ ___ ___ _____ _ __
|
||||
| _ < / _ \ / _ \| __| | '__/ _ \/ __/ _ \ \ \ / / _ \ '__|
|
||||
| |_) | (_) | (_) | |_ | | | __/ (_| __/ |\ V / __/ |
|
||||
|____/ \___/ \___/ \__| |_| \___|\___\___|_| \_/ \_____|
|
||||
-->
|
||||
<receiver
|
||||
android:name=".widgets.notifications.WidgetNotifications"
|
||||
android:label="@string/widget_notifications_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_notifications_info" />
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".widgets.luckynumber.WidgetLuckyNumber"
|
||||
android:label="@string/widget_lucky_number_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_lucky_number_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receivers.UserPresentReceiver"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receivers.BootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service
|
||||
android:name=".sync.MyFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name=".widgets.timetable.WidgetTimetableService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<service
|
||||
android:name=".widgets.notifications.WidgetNotificationsService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<service android:name=".receivers.BootReceiver$NotificationActionService" />
|
||||
|
||||
<service android:name=".Notifier$GetDataRetryService" />
|
||||
|
||||
<service
|
||||
android:name=".sync.SyncService"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/sync_service" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
</manifest>
|
27
app/src/main/assets/certificate.cer
Normal file
27
app/src/main/assets/certificate.cer
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
BIN
app/src/main/assets/fonts/RobotoMono-Regular.ttf
Normal file
BIN
app/src/main/assets/fonts/RobotoMono-Regular.ttf
Normal file
Binary file not shown.
60
app/src/main/assets/pl-changelog.html
Normal file
60
app/src/main/assets/pl-changelog.html
Normal file
@ -0,0 +1,60 @@
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
* {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #{bg-color}; color: #{text-color};
|
||||
}
|
||||
|
||||
a {
|
||||
color: #{link-color};
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #{link-color-active};
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-position: inside;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
li:not(:first-child) {
|
||||
padding-top: 8px;
|
||||
}
|
||||
</style>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>Wersja 3.0, 2019-09-13</h3>
|
||||
<ul>
|
||||
<li><b>Nowy wygląd i sposób nawigacji</b> w całej aplikacji.</li>
|
||||
<li>Menu nawigacji można teraz otworzyć przyciskiem na <b>dolnym pasku</b>. Pociągnięcie w górę tego paska wyświetla <b>menu kontekstowe</b> dotyczące danego widoku.</li>
|
||||
<li>Założyliśmy serwer Discord! <a href="https://discord.gg/n9e8pWr">https://discord.gg/n9e8pWr</a></li>
|
||||
<br>
|
||||
<li>Librus: poprawka powielonych ogłoszeń szkolnych.</li>
|
||||
<li>Naprawiłem błąd nieskończonej synchronizacji w Vulcanie.</li>
|
||||
<li>Naprawiłem crash launchera przy dodaniu widgetu.</li>
|
||||
<li>Naprawiłem częste crashe związane z widokiem kalendarza.</li>
|
||||
<li>Nowe, ładniejsze (choć trochę) motywy kolorów.</li>
|
||||
<li>Dużo drobnych poprawek UI i działania aplikacji.</li>
|
||||
</ul>
|
||||
|
||||
<!--<i>
|
||||
<h3>Plany na następne wersje:</h3>
|
||||
<ul>
|
||||
<li>Widget kalendarza ze sprawdzianami, ulepszenie widoku kalendarza w aplikacji</li>
|
||||
<li>Wsparcie dla systemu Synergia w jednostkach samorządu terytorialnego - aplikacja Nasze Szkoły</li>
|
||||
<li>Wsparcie dla Librusa w systemie Oświata w Radomiu</li>
|
||||
<li>EduDziennik</li>
|
||||
<li>Mobireg</li>
|
||||
<li>Możliwość edycji planu lekcji</li>
|
||||
</ul>
|
||||
</i>-->
|
||||
|
||||
</body>
|
717
app/src/main/java/pl/szczodrzynski/edziennik/App.java
Normal file
717
app/src/main/java/pl/szczodrzynski/edziennik/App.java
Normal file
@ -0,0 +1,717 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.evernote.android.job.JobManager;
|
||||
import com.google.android.gms.security.ProviderInstaller;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.FirebaseOptions;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.mikepenz.iconics.Iconics;
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.IIcon;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.KeyStore;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig;
|
||||
import im.wangchao.mhttp.MHttp;
|
||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar;
|
||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache;
|
||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor;
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.TlsVersion;
|
||||
import pl.szczodrzynski.edziennik.activities.CrashActivity;
|
||||
import pl.szczodrzynski.edziennik.api.Edziennik;
|
||||
import pl.szczodrzynski.edziennik.api.Iuczniowie;
|
||||
import pl.szczodrzynski.edziennik.api.Librus;
|
||||
import pl.szczodrzynski.edziennik.api.Mobidziennik;
|
||||
import pl.szczodrzynski.edziennik.api.Vulcan;
|
||||
import pl.szczodrzynski.edziennik.datamodels.AppDb;
|
||||
import pl.szczodrzynski.edziennik.datamodels.DebugLog;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile;
|
||||
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
|
||||
import pl.szczodrzynski.edziennik.models.AppConfig;
|
||||
import pl.szczodrzynski.edziennik.network.NetworkUtils;
|
||||
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
|
||||
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
|
||||
import pl.szczodrzynski.edziennik.receivers.JobsCreator;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncJob;
|
||||
import pl.szczodrzynski.edziennik.utils.PermissionChecker;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_VULCAN;
|
||||
|
||||
public class App extends androidx.multidex.MultiDexApplication {
|
||||
private static final String TAG = "App";
|
||||
public static int profileId = -1;
|
||||
private Context mContext;
|
||||
|
||||
public static final int REQUEST_TIMEOUT = 10 * 1000;
|
||||
|
||||
// notifications
|
||||
//public NotificationManager mNotificationManager;
|
||||
//public final String NOTIFICATION_CHANNEL_ID_UPDATES = "4566";
|
||||
//public String NOTIFICATION_CHANNEL_NAME_UPDATES;
|
||||
public Notifier notifier;
|
||||
|
||||
public static final String APP_URL = "://edziennik.szczodrzynski.pl/app/";
|
||||
|
||||
public ShortcutManager shortcutManager;
|
||||
|
||||
public PermissionChecker permissionChecker;
|
||||
|
||||
public String signature = "";
|
||||
public String deviceId = "";
|
||||
|
||||
public AppDb db;
|
||||
public void debugLog(String text) {
|
||||
if (!devMode)
|
||||
return;
|
||||
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
|
||||
}
|
||||
public void debugLogAsync(String text) {
|
||||
if (!devMode)
|
||||
return;
|
||||
AsyncTask.execute(() -> {
|
||||
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
|
||||
});
|
||||
}
|
||||
|
||||
// network & APIs
|
||||
public NetworkUtils networkUtils;
|
||||
public PersistentCookieJar cookieJar;
|
||||
public OkHttpClient http;
|
||||
public OkHttpClient httpLazy;
|
||||
//public Jakdojade apiJakdojade;
|
||||
public Edziennik apiEdziennik;
|
||||
public Mobidziennik apiMobidziennik;
|
||||
public Iuczniowie apiIuczniowie;
|
||||
public Librus apiLibrus;
|
||||
public Vulcan apiVulcan;
|
||||
|
||||
public SharedPreferences appSharedPrefs; // sharedPreferences for APPCONFIG + JOBS STORE
|
||||
public AppConfig appConfig; // APPCONFIG: common for all profiles
|
||||
//public AppProfile profile; // current profile
|
||||
public JsonObject loginStore = null;
|
||||
public SharedPreferences registerStore; // sharedPreferences for REGISTER
|
||||
//public Register register; // REGISTER for current profile, read from registerStore
|
||||
|
||||
public ProfileFull profile;
|
||||
|
||||
// other stuff
|
||||
public Gson gson;
|
||||
public String requestScheme = "https";
|
||||
public boolean unreadBadgesAvailable = true;
|
||||
|
||||
public static boolean devMode = false;
|
||||
|
||||
public static final boolean UPDATES_ON_PLAY_STORE = true;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public Icon getDesktopIconFromIconics(IIcon icon) {
|
||||
final IconicsDrawable drawable = new IconicsDrawable(mContext, icon)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(48))
|
||||
.padding(IconicsSize.dp(8))
|
||||
.backgroundColor(IconicsColor.colorRes(R.color.colorPrimaryDark))
|
||||
.roundedCorners(IconicsSize.dp(10));
|
||||
//drawable.setStyle(Paint.Style.FILL);
|
||||
final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return Icon.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
CaocConfig.Builder.create()
|
||||
.backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM
|
||||
.enabled(true) //default: true
|
||||
.showErrorDetails(true) //default: true
|
||||
.showRestartButton(true) //default: true
|
||||
.logErrorOnRestart(true) //default: true
|
||||
.trackActivities(true) //default: false
|
||||
.minTimeBetweenCrashesMs(2000) //default: 3000
|
||||
.errorDrawable(R.drawable.ic_rip) //default: bug image
|
||||
.restartActivity(MainActivity.class) //default: null (your app's launch activity)
|
||||
.errorActivity(CrashActivity.class) //default: null (default error activity)
|
||||
//.eventListener(new YourCustomEventListener()) //default: null
|
||||
.apply();
|
||||
mContext = this;
|
||||
db = AppDb.getDatabase(this);
|
||||
gson = new Gson();
|
||||
networkUtils = new NetworkUtils(this);
|
||||
apiEdziennik = new Edziennik(this);
|
||||
//apiJakdojade = new Jakdojade(this);
|
||||
apiMobidziennik = new Mobidziennik(this);
|
||||
apiIuczniowie = new Iuczniowie(this);
|
||||
apiLibrus = new Librus(this);
|
||||
apiVulcan = new Vulcan(this);
|
||||
|
||||
Iconics.init(getApplicationContext());
|
||||
Iconics.registerFont(SzkolnyFont.INSTANCE);
|
||||
|
||||
notifier = new Notifier(this);
|
||||
permissionChecker = new PermissionChecker(mContext);
|
||||
|
||||
deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
|
||||
cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this));
|
||||
|
||||
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
|
||||
.cache(null)
|
||||
.followRedirects(true)
|
||||
.followSslRedirects(true)
|
||||
.retryOnConnectionFailure(true)
|
||||
.cookieJar(cookieJar)
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(40, TimeUnit.SECONDS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
try {
|
||||
try {
|
||||
ProviderInstaller.installIfNeeded(this);
|
||||
} catch (Exception e) {
|
||||
Log.e("OkHttpTLSCompat", "Play Services not found or outdated");
|
||||
X509TrustManager x509TrustManager = null;
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init((KeyStore) null);
|
||||
for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {
|
||||
if (trustManager instanceof X509TrustManager)
|
||||
x509TrustManager = (X509TrustManager) trustManager;
|
||||
}
|
||||
|
||||
SSLContext sc = SSLContext.getInstance("TLSv1.2");
|
||||
sc.init(null, null, null);
|
||||
httpBuilder.sslSocketFactory(new TLSSocketFactory(sc.getSocketFactory()), x509TrustManager);
|
||||
|
||||
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_0)
|
||||
.tlsVersions(TlsVersion.TLS_1_1)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build();
|
||||
|
||||
List<ConnectionSpec> specs = new ArrayList<>();
|
||||
specs.add(cs);
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS);
|
||||
specs.add(ConnectionSpec.CLEARTEXT);
|
||||
|
||||
httpBuilder.connectionSpecs(specs);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception exc) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
|
||||
}
|
||||
}
|
||||
|
||||
http = httpBuilder.build();
|
||||
httpLazy = http.newBuilder().followRedirects(false).followSslRedirects(false).build();
|
||||
|
||||
MHttp.instance()
|
||||
.customOkHttpClient(http);
|
||||
|
||||
//register = new Register(mContext);
|
||||
|
||||
appSharedPrefs = getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE);
|
||||
|
||||
loadConfig();
|
||||
|
||||
Themes.INSTANCE.setThemeInt(appConfig.appTheme);
|
||||
|
||||
//profileLoadById(appSharedPrefs.getInt("current_profile_id", 1));
|
||||
|
||||
try {
|
||||
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
for (Signature signature: packageInfo.signatures) {
|
||||
byte[] signatureBytes = signature.toByteArray();
|
||||
MessageDigest md = MessageDigest.getInstance("SHA");
|
||||
md.update(signatureBytes);
|
||||
this.signature = Base64.encodeToString(md.digest(), Base64.DEFAULT);
|
||||
//Log.d(TAG, "Signature is "+this.signature);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if ("f054761fbdb6a238".equals(deviceId)) {
|
||||
devMode = true;
|
||||
}
|
||||
else if (appConfig.devModePassword != null) {
|
||||
checkDevModePassword();
|
||||
}
|
||||
|
||||
JobManager.create(this).addJobCreator(new JobsCreator());
|
||||
if (appConfig.registerSyncEnabled) {
|
||||
SyncJob.schedule(this);
|
||||
}
|
||||
else {
|
||||
SyncJob.clear();
|
||||
}
|
||||
|
||||
db.metadataDao().countUnseen().observeForever(count -> {
|
||||
Log.d("MainActivity", "Overall unseen count changed");
|
||||
assert count != null;
|
||||
if (unreadBadgesAvailable) {
|
||||
unreadBadgesAvailable = ShortcutBadger.applyCount(this, count);
|
||||
}
|
||||
});
|
||||
|
||||
//new IonCookieManager(mContext);
|
||||
|
||||
new Handler().post(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
shortcutManager = getSystemService(ShortcutManager.class);
|
||||
|
||||
ShortcutInfo shortcutTimetable = new ShortcutInfo.Builder(mContext, "item_timetable")
|
||||
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_timetable))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_timetable))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutAgenda = new ShortcutInfo.Builder(mContext, "item_agenda")
|
||||
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_agenda))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_calendar))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutGrades = new ShortcutInfo.Builder(mContext, "item_grades")
|
||||
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_grades))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_numeric_5_box))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutHomework = new ShortcutInfo.Builder(mContext, "item_homeworks")
|
||||
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_homework))
|
||||
//.setIcon(getDesktopIconFromIconics(SzkolnyFont.Icon.szf_file_document_edit))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORKS))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutMessages = new ShortcutInfo.Builder(mContext, "item_messages")
|
||||
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_messages))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_email))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES ))
|
||||
.build();
|
||||
|
||||
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutTimetable, shortcutAgenda, shortcutGrades, shortcutHomework, shortcutMessages));
|
||||
}
|
||||
|
||||
if (appConfig.appInstalledTime == 0) {
|
||||
try {
|
||||
appConfig.appInstalledTime = getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime;
|
||||
appConfig.appRateSnackbarTime = appConfig.appInstalledTime + 7 * 24 * 60 * 60 * 1000;
|
||||
saveConfig("appInstalledTime", "appRateSnackbarTime");
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*Task<CapabilityInfo> capabilityInfoTask =
|
||||
Wearable.getCapabilityClient(this)
|
||||
.getCapability("edziennik_wear_app", CapabilityClient.FILTER_REACHABLE);
|
||||
capabilityInfoTask.addOnCompleteListener((task) -> {
|
||||
if (task.isSuccessful()) {
|
||||
CapabilityInfo capabilityInfo = task.getResult();
|
||||
assert capabilityInfo != null;
|
||||
Set<Node> nodes;
|
||||
nodes = capabilityInfo.getNodes();
|
||||
Log.d(TAG, "Nodes "+nodes);
|
||||
|
||||
if (nodes.size() > 0) {
|
||||
Wearable.getMessageClient(this).sendMessage(
|
||||
nodes.toArray(new Node[]{})[0].getId(), "/ping", "Hello world".getBytes());
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Capability request failed to return any results.");
|
||||
}
|
||||
});
|
||||
|
||||
Wearable.getDataClient(this).addListener(dataEventBuffer -> {
|
||||
Log.d(TAG, "onDataChanged(): " + dataEventBuffer);
|
||||
|
||||
for (DataEvent event : dataEventBuffer) {
|
||||
if (event.getType() == DataEvent.TYPE_CHANGED) {
|
||||
String path = event.getDataItem().getUri().getPath();
|
||||
Log.d(TAG, "Data "+path+ " :: "+Arrays.toString(event.getDataItem().getData()));
|
||||
}
|
||||
}
|
||||
});*/
|
||||
|
||||
FirebaseApp pushMobidziennikApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApplicationId("1:1029629079999:android:58bb378dab031f42")
|
||||
.setGcmSenderId("1029629079999")
|
||||
.build(),
|
||||
"Mobidziennik"
|
||||
);
|
||||
|
||||
/*FirebaseApp pushLibrusApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
|
||||
.setApplicationId("1:513056078587:android:1e29083b760af544")
|
||||
.build(),
|
||||
"Librus"
|
||||
);*/
|
||||
|
||||
FirebaseApp pushVulcanApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
|
||||
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
|
||||
.build(),
|
||||
"Vulcan"
|
||||
);
|
||||
|
||||
try {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for App is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()+". Time is "+(System.currentTimeMillis() - startTime));
|
||||
appConfig.fcmToken = instanceIdResult.getToken();
|
||||
});
|
||||
FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
});
|
||||
/*FirebaseInstanceId.getInstance(pushLibrusApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Librus is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_LIBRUS, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
});*/
|
||||
FirebaseInstanceId.getInstance(pushVulcanApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Vulcan is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
Pair<String, List<Integer>> pair = appConfig.fcmTokens.get(LOGIN_TYPE_VULCAN);
|
||||
if (pair == null || pair.first == null || !pair.first.equals(instanceIdResult.getToken())) {
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(getPackageName());
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void loadConfig()
|
||||
{
|
||||
appConfig = new AppConfig(this);
|
||||
|
||||
|
||||
if (appSharedPrefs.contains("config")) {
|
||||
// remove old-format config, save the new one and empty the incorrectly-nulled config
|
||||
appConfig = gson.fromJson(appSharedPrefs.getString("config", ""), AppConfig.class);
|
||||
appSharedPrefs.edit().remove("config").apply();
|
||||
saveConfig();
|
||||
appConfig = new AppConfig(this);
|
||||
}
|
||||
|
||||
if (appSharedPrefs.contains("profiles")) {
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
/*List<Integer> appProfileIds = gson.fromJson(appSharedPrefs.getString("profiles", ""), new TypeToken<List<Integer>>(){}.getType());
|
||||
for (int id: appProfileIds) {
|
||||
AppProfile appProfile = gson.fromJson(appSharedPrefs.getString("profile"+id, ""), AppProfile.class);
|
||||
if (appProfile != null) {
|
||||
appConfig.profiles.add(appProfile);
|
||||
}
|
||||
appSharedPrefsEditor.remove("profile"+id);
|
||||
}*/
|
||||
appSharedPrefsEditor.remove("profiles");
|
||||
appSharedPrefsEditor.apply();
|
||||
//profilesSave();
|
||||
}
|
||||
|
||||
|
||||
Map<String,?> keys = appSharedPrefs.getAll();
|
||||
for (Map.Entry<String,?> entry : keys.entrySet()) {
|
||||
if (entry.getKey().startsWith("app.appConfig.")) {
|
||||
String fieldName = entry.getKey().replace("app.appConfig.", "");
|
||||
|
||||
try {
|
||||
Field field = AppConfig.class.getField(fieldName);
|
||||
Object object;
|
||||
try {
|
||||
object = gson.fromJson(entry.getValue().toString(), field.getGenericType());
|
||||
} catch (JsonSyntaxException e) {
|
||||
Log.d(TAG, "For field "+fieldName);
|
||||
e.printStackTrace();
|
||||
object = entry.getValue().toString();
|
||||
}
|
||||
if (object != null)
|
||||
field.set(appConfig, object);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appConfig.lastAppVersion > BuildConfig.VERSION_CODE) {
|
||||
BootReceiver br = new BootReceiver();
|
||||
Intent i = new Intent();
|
||||
//i.putExtra("UserChecked", true);
|
||||
br.onReceive(getContext(), i);
|
||||
Toast.makeText(mContext, R.string.warning_older_version_running, Toast.LENGTH_LONG).show();
|
||||
//Toast.makeText(mContext, "Zaktualizuj aplikację.", Toast.LENGTH_LONG).show();
|
||||
//System.exit(0);
|
||||
}
|
||||
|
||||
if (appConfig == null) {
|
||||
appConfig = new AppConfig(this);
|
||||
}
|
||||
}
|
||||
public void saveConfig()
|
||||
{
|
||||
try {
|
||||
appConfig.savePending = false;
|
||||
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
|
||||
JsonObject appConfigJson = gson.toJsonTree(appConfig).getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> entry : appConfigJson.entrySet()) {
|
||||
String jsonObj;
|
||||
jsonObj = entry.getValue().toString();
|
||||
/*if (entry.getValue().isJsonObject()) {
|
||||
jsonObj = entry.getValue().getAsJsonObject().toString();
|
||||
}
|
||||
else if (entry.getValue().isJsonArray()) {
|
||||
jsonObj = entry.getValue().getAsJsonArray().toString();
|
||||
}
|
||||
else {
|
||||
jsonObj = entry.getValue().toString();
|
||||
}*/
|
||||
appSharedPrefsEditor.putString("app.appConfig." + entry.getKey(), jsonObj);
|
||||
}
|
||||
|
||||
appSharedPrefsEditor.apply();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
|
||||
}
|
||||
public void saveConfig(String ... fieldNames)
|
||||
{
|
||||
appConfig.savePending = false;
|
||||
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
|
||||
for (String fieldName: fieldNames) {
|
||||
try {
|
||||
Object object = AppConfig.class.getField(fieldName).get(appConfig);
|
||||
appSharedPrefsEditor.putString("app.appConfig."+fieldName, gson.toJson(object));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
appSharedPrefsEditor.apply();
|
||||
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void profileSaveAsync() {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveAsync(Profile profile) {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveFullAsync(ProfileFull profile) {
|
||||
AsyncTask.execute(() -> {
|
||||
profileSaveFull(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveFull(ProfileFull profileFull) {
|
||||
db.profileDao().add(profileFull);
|
||||
db.loginStoreDao().add(profileFull);
|
||||
}
|
||||
public void profileSaveFull(Profile profile, LoginStore loginStore) {
|
||||
db.profileDao().add(profile);
|
||||
db.loginStoreDao().add(loginStore);
|
||||
}
|
||||
|
||||
public ProfileFull profileGetOrNull(int id) {
|
||||
return db.profileDao().getByIdNow(id);
|
||||
}
|
||||
|
||||
public void profileLoadById(int id) {
|
||||
profileLoadById(id, false);
|
||||
}
|
||||
public void profileLoadById(int id, boolean loadedLast) {
|
||||
//Log.d(TAG, "Loading ID "+id);
|
||||
/*if (profile == null) {
|
||||
profile = profileNew();
|
||||
AppDb.profileId = profile.id;
|
||||
appSharedPrefs.edit().putInt("current_profile_id", profile.id).apply();
|
||||
return;
|
||||
}*/
|
||||
if (profile == null || profile.getId() != id) {
|
||||
profile = db.profileDao().getByIdNow(id);
|
||||
/*if (profile == null) {
|
||||
profileLoadById(id);
|
||||
return;
|
||||
}*/
|
||||
if (profile != null) {
|
||||
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
|
||||
profileId = profile.getId();
|
||||
appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply();
|
||||
}
|
||||
else if (!loadedLast) {
|
||||
profileLoadById(profileLastId(), true);
|
||||
}
|
||||
else {
|
||||
profileId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void profileLoad(ProfileFull profile) {
|
||||
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
|
||||
this.profile = profile;
|
||||
profileId = profile.getId();
|
||||
}
|
||||
|
||||
/*public void profileRemove(int id)
|
||||
{
|
||||
Profile profile = db.profileDao().getByIdNow(id);
|
||||
|
||||
if (profile.id == profile.loginStoreId) {
|
||||
// this profile is the owner of the login store
|
||||
// we need to check if any other profile is using it
|
||||
List<Integer> transferProfileIds = db.profileDao().getIdsByLoginStoreIdNow(profile.loginStoreId);
|
||||
if (transferProfileIds.size() == 1) {
|
||||
// this login store is free of users, remove it along with the profile
|
||||
db.loginStoreDao().remove(profile.loginStoreId);
|
||||
// the current store is removed, we are ready to remove the profile
|
||||
}
|
||||
else if (transferProfileIds.size() > 1) {
|
||||
transferProfileIds.remove(transferProfileIds.indexOf(profile.id));
|
||||
// someone is using the store
|
||||
// we need to transfer it to the firstProfileId
|
||||
db.loginStoreDao().changeId(profile.loginStoreId, transferProfileIds.get(0));
|
||||
db.profileDao().changeStoreId(profile.loginStoreId, transferProfileIds.get(0));
|
||||
// the current store is removed, we are ready to remove the profile
|
||||
}
|
||||
}
|
||||
// else, the profile uses a store that it doesn't own
|
||||
// leave the store and go on with removing
|
||||
|
||||
Log.d(TAG, "Before removal: "+db.profileDao().getAllNow().toString());
|
||||
db.profileDao().remove(profile.id);
|
||||
Log.d(TAG, "After removal: "+db.profileDao().getAllNow().toString());
|
||||
|
||||
*//*int newId = 1;
|
||||
if (appConfig.profiles.size() > 0) {
|
||||
newId = appConfig.profiles.get(appConfig.profiles.size() - 1).id;
|
||||
}
|
||||
Log.d(TAG, "New ID: "+newId);
|
||||
//Toast.makeText(mContext, "selected new id "+newId, Toast.LENGTH_SHORT).show();
|
||||
profileLoadById(newId);*//*
|
||||
}*/
|
||||
|
||||
public int profileFirstId() {
|
||||
return db.profileDao().getFirstId();
|
||||
}
|
||||
|
||||
public int profileLastId() {
|
||||
return db.profileDao().getLastId();
|
||||
}
|
||||
|
||||
|
||||
public Context getContext()
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public void checkDevModePassword() {
|
||||
try {
|
||||
if (Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", appConfig.devModePassword).equals("ok here you go it's enabled now")) {
|
||||
devMode = true;
|
||||
}
|
||||
else {
|
||||
devMode = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
devMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
96
app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
Normal file
96
app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
Normal file
@ -0,0 +1,96 @@
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import im.wangchao.mhttp.internal.exception.ResponseFailException
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
import pl.szczodrzynski.edziennik.datamodels.Teacher
|
||||
import pl.szczodrzynski.navlib.R
|
||||
import pl.szczodrzynski.navlib.crc16
|
||||
import pl.szczodrzynski.navlib.getColorFromRes
|
||||
import kotlin.contracts.contract
|
||||
|
||||
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
|
||||
fun List<Teacher>.byNameFirstLast(nameFirstLast: String) = firstOrNull { it.name + " " + it.surname == nameFirstLast }
|
||||
fun List<Teacher>.byNameLastFirst(nameLastFirst: String) = firstOrNull { it.surname + " " + it.name == nameLastFirst }
|
||||
fun List<Teacher>.byNameFDotLast(nameFDotLast: String) = firstOrNull { it.name + "." + it.surname == nameFDotLast }
|
||||
fun List<Teacher>.byNameFDotSpaceLast(nameFDotSpaceLast: String) = firstOrNull { it.name + ". " + it.surname == nameFDotSpaceLast }
|
||||
|
||||
fun JsonObject.getString(key: String): String? = get(key).let { if (it.isJsonNull) null else it.asString }
|
||||
fun JsonObject.getInt(key: String): Int? = get(key).let { if (it.isJsonNull) null else it.asInt }
|
||||
fun JsonObject.getLong(key: String): Long? = get(key).let { if (it.isJsonNull) null else it.asLong }
|
||||
fun JsonObject.getJsonObject(key: String): JsonObject? = get(key).let { if (it.isJsonNull) null else it.asJsonObject }
|
||||
fun JsonObject.getJsonArray(key: String): JsonArray? = get(key).let { if (it.isJsonNull) null else it.asJsonArray }
|
||||
|
||||
fun CharSequence?.isNotNullNorEmpty(): Boolean {
|
||||
return this != null && this.isNotEmpty()
|
||||
}
|
||||
|
||||
fun currentTimeUnix() = System.currentTimeMillis() / 1000
|
||||
|
||||
fun Bundle?.getInt(key: String, defaultValue: Int): Int {
|
||||
return this?.getInt(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
fun Bundle?.getLong(key: String, defaultValue: Long): Long {
|
||||
return this?.getLong(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
fun Bundle?.getFloat(key: String, defaultValue: Float): Float {
|
||||
return this?.getFloat(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
fun Bundle?.getString(key: String, defaultValue: String): String {
|
||||
return this?.getString(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
fun colorFromName(context: Context, name: String?): Int {
|
||||
var crc = crc16(name ?: "")
|
||||
crc = (crc and 0xff) or (crc shr 8)
|
||||
crc %= 16
|
||||
val color = when (crc) {
|
||||
13 -> R.color.md_red_500
|
||||
4 -> R.color.md_pink_A400
|
||||
2 -> R.color.md_purple_A400
|
||||
9 -> R.color.md_deep_purple_A700
|
||||
5 -> R.color.md_indigo_500
|
||||
1 -> R.color.md_indigo_A700
|
||||
6 -> R.color.md_cyan_A200
|
||||
14 -> R.color.md_teal_400
|
||||
15 -> R.color.md_green_500
|
||||
7 -> R.color.md_yellow_A700
|
||||
3 -> R.color.md_deep_orange_A400
|
||||
8 -> R.color.md_deep_orange_A700
|
||||
10 -> R.color.md_brown_500
|
||||
12 -> R.color.md_grey_400
|
||||
11 -> R.color.md_blue_grey_400
|
||||
else -> R.color.md_light_green_A700
|
||||
}
|
||||
return context.getColorFromRes(color)
|
||||
}
|
||||
|
||||
fun MutableList<out Profile>.filterOutArchived() {
|
||||
this.removeAll { it.archived }
|
||||
}
|
||||
|
||||
fun Activity.isStoragePermissionGranted(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= 23) {
|
||||
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
true
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
public class GenericFileProvider extends FileProvider {}
|
968
app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt
Normal file
968
app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt
Normal file
@ -0,0 +1,968 @@
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
|
||||
import pl.szczodrzynski.edziennik.datamodels.Metadata.*
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.navlib.NavView
|
||||
import pl.szczodrzynski.navlib.SystemBarsUtil
|
||||
import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT
|
||||
import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet
|
||||
import pl.szczodrzynski.navlib.drawer.NavDrawer
|
||||
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
|
||||
import pl.szczodrzynski.navlib.drawer.items.withAppTitle
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.navigation.NavOptions
|
||||
import com.danimahardhika.cafebar.CafeBar
|
||||
import com.mikepenz.iconics.IconicsColor
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.IconicsSize
|
||||
import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.App.APP_URL
|
||||
import pl.szczodrzynski.edziennik.api.AppError
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.*
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
import pl.szczodrzynski.edziennik.datamodels.ProfileFull
|
||||
import pl.szczodrzynski.edziennik.dialogs.ChangelogDialog
|
||||
import pl.szczodrzynski.edziennik.fragments.*
|
||||
import pl.szczodrzynski.edziennik.login.LoginActivity
|
||||
import pl.szczodrzynski.edziennik.messages.MessagesDetailsFragment
|
||||
import pl.szczodrzynski.edziennik.messages.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.models.NavTarget
|
||||
import pl.szczodrzynski.edziennik.network.ServerRequest
|
||||
import pl.szczodrzynski.edziennik.sync.SyncJob
|
||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
|
||||
var useOldMessages = false
|
||||
|
||||
const val TAG = "MainActivity"
|
||||
|
||||
const val REQUEST_LOGIN_ACTIVITY = 20222
|
||||
|
||||
const val DRAWER_PROFILE_ADD_NEW = 200
|
||||
const val DRAWER_PROFILE_SYNC_ALL = 201
|
||||
const val DRAWER_PROFILE_EXPORT_DATA = 202
|
||||
const val DRAWER_PROFILE_MANAGE = 203
|
||||
const val DRAWER_ITEM_HOME = 1
|
||||
const val DRAWER_ITEM_TIMETABLE = 11
|
||||
const val DRAWER_ITEM_AGENDA = 12
|
||||
const val DRAWER_ITEM_GRADES = 13
|
||||
const val DRAWER_ITEM_MESSAGES = 17
|
||||
const val DRAWER_ITEM_HOMEWORKS = 14
|
||||
const val DRAWER_ITEM_NOTICES = 15
|
||||
const val DRAWER_ITEM_ATTENDANCES = 16
|
||||
const val DRAWER_ITEM_ANNOUNCEMENTS = 18
|
||||
const val DRAWER_ITEM_NOTIFICATIONS = 20
|
||||
const val DRAWER_ITEM_SETTINGS = 101
|
||||
const val DRAWER_ITEM_DEBUG = 102
|
||||
|
||||
const val TARGET_GRADES_EDITOR = 501
|
||||
const val TARGET_HELP = 502
|
||||
const val TARGET_FEEDBACK = 120
|
||||
const val TARGET_MESSAGES_DETAILS = 503
|
||||
|
||||
const val HOME_ID = DRAWER_ITEM_HOME
|
||||
|
||||
val navTargetList: List<NavTarget> by lazy {
|
||||
val list: MutableList<NavTarget> = mutableListOf()
|
||||
|
||||
// home item
|
||||
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragment::class)
|
||||
.withTitle(R.string.app_name)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_home)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.withPopToHome(false)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_TIMETABLE, R.string.menu_timetable, RegisterTimetableFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_timetable)
|
||||
.withBadgeTypeId(TYPE_LESSON_CHANGE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_AGENDA, R.string.menu_agenda, RegisterAgendaDefaultFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar)
|
||||
.withBadgeTypeId(TYPE_EVENT)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, RegisterGradesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box)
|
||||
.withBadgeTypeId(TYPE_GRADE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_MESSAGES, R.string.menu_messages, MessagesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_email)
|
||||
.withBadgeTypeId(TYPE_MESSAGE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_HOMEWORKS, R.string.menu_homework, RegisterHomeworksFragment::class)
|
||||
.withIcon(SzkolnyFont.Icon.szf_file_document_edit)
|
||||
.withBadgeTypeId(TYPE_HOMEWORK)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_NOTICES, R.string.menu_notices, RegisterNoticesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_message_alert)
|
||||
.withBadgeTypeId(TYPE_NOTICE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_ATTENDANCES, R.string.menu_attendances, RegisterAttendancesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove)
|
||||
.withBadgeTypeId(TYPE_ATTENDANCE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_ANNOUNCEMENTS, R.string.menu_announcements, RegisterAnnouncementsFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bulletin_board)
|
||||
.withBadgeTypeId(TYPE_ANNOUNCEMENT)
|
||||
.isInDrawer(true)
|
||||
|
||||
|
||||
// static drawer items
|
||||
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, RegisterNotificationsFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bell_ring)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.isBelowSeparator(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.isBelowSeparator(true)
|
||||
|
||||
|
||||
// profile settings items
|
||||
list += NavTarget(DRAWER_PROFILE_ADD_NEW, R.string.menu_add_new_profile, null)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_plus)
|
||||
.withDescription(R.string.drawer_add_new_profile_desc)
|
||||
.isInProfileList(true)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_MANAGE, R.string.menu_manage_profiles, ProfileManagerFragment::class)
|
||||
.withTitle(R.string.title_profile_manager)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_account_group)
|
||||
.withDescription(R.string.drawer_manage_profiles_desc)
|
||||
.isInProfileList(false)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.isInProfileList(true)
|
||||
|
||||
|
||||
// other target items, not directly navigated
|
||||
list += NavTarget(TARGET_GRADES_EDITOR, R.string.menu_grades_editor, GradesEditorFragment::class)
|
||||
list += NavTarget(TARGET_HELP, R.string.menu_help, HelpFragment::class)
|
||||
list += NavTarget(TARGET_FEEDBACK, R.string.menu_feedback, FeedbackFragment::class)
|
||||
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessagesDetailsFragment::class)
|
||||
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
|
||||
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
val b: ActivitySzkolnyBinding by lazy { ActivitySzkolnyBinding.inflate(layoutInflater) }
|
||||
val navView: NavView by lazy { b.navView }
|
||||
val drawer: NavDrawer by lazy { navView.drawer }
|
||||
val bottomSheet: NavBottomSheet by lazy { navView.bottomSheet }
|
||||
|
||||
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
|
||||
|
||||
val app: App by lazy {
|
||||
applicationContext as App
|
||||
}
|
||||
|
||||
private val fragmentManager by lazy { supportFragmentManager }
|
||||
private lateinit var navTarget: NavTarget
|
||||
private val navTargetId
|
||||
get() = navTarget.id
|
||||
|
||||
private val navBackStack = mutableListOf<NavTarget>()
|
||||
private var navLoading = true
|
||||
|
||||
/* ____ _____ _
|
||||
/ __ \ / ____| | |
|
||||
| | | |_ __ | | _ __ ___ __ _| |_ ___
|
||||
| | | | '_ \ | | | '__/ _ \/ _` | __/ _ \
|
||||
| |__| | | | | | |____| | | __/ (_| | || __/
|
||||
\____/|_| |_| \_____|_| \___|\__,_|\__\__*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setTheme(Themes.appTheme)
|
||||
|
||||
setContentView(b.root)
|
||||
|
||||
navLoading = true
|
||||
|
||||
b.navView.apply {
|
||||
drawer.init(this@MainActivity)
|
||||
|
||||
SystemBarsUtil(this@MainActivity).run {
|
||||
paddingByKeyboard = b.navView
|
||||
appFullscreen = true
|
||||
statusBarColor = getColorFromAttr(context, android.R.attr.colorBackground)
|
||||
statusBarDarker = false
|
||||
statusBarFallbackLight = COLOR_HALF_TRANSPARENT
|
||||
statusBarFallbackGradient = COLOR_HALF_TRANSPARENT
|
||||
navigationBarTransparent = false
|
||||
|
||||
b.navView.configSystemBarsUtil(this)
|
||||
|
||||
commit()
|
||||
}
|
||||
|
||||
toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread
|
||||
}
|
||||
|
||||
bottomBar.apply {
|
||||
fabEnable = false
|
||||
fabExtendable = true
|
||||
fabExtended = false
|
||||
fabGravity = Gravity.CENTER
|
||||
}
|
||||
|
||||
bottomSheet.apply {
|
||||
removeAllItems()
|
||||
toggleGroupEnabled = false
|
||||
textInputEnabled = false
|
||||
}
|
||||
|
||||
drawer.apply {
|
||||
setAccountHeaderBackground(app.appConfig.headerBackground)
|
||||
|
||||
drawerProfileListEmptyListener = {
|
||||
app.appConfig.loginFinished = false
|
||||
app.saveConfig("loginFinished")
|
||||
profileListEmptyListener()
|
||||
}
|
||||
drawerItemSelectedListener = { id, position, drawerItem ->
|
||||
loadTarget(id)
|
||||
true
|
||||
}
|
||||
drawerProfileSelectedListener = { id, profile, _, _ ->
|
||||
loadProfile(id)
|
||||
false
|
||||
}
|
||||
drawerProfileLongClickListener = { _, profile, _, view ->
|
||||
if (profile is ProfileDrawerItem) {
|
||||
showProfileContextMenu(profile, view)
|
||||
true
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
drawerProfileImageLongClickListener = drawerProfileLongClickListener
|
||||
drawerProfileSettingClickListener = this@MainActivity.profileSettingClickListener
|
||||
|
||||
miniDrawerVisibleLandscape = null
|
||||
miniDrawerVisiblePortrait = app.appConfig.miniDrawerVisible
|
||||
}
|
||||
}
|
||||
|
||||
navTarget = navTargetList[0]
|
||||
|
||||
var profileListEmpty = drawer.profileListEmpty
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
intent?.putExtras(savedInstanceState)
|
||||
savedInstanceState.clear()
|
||||
}
|
||||
|
||||
if (!profileListEmpty) {
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
app.db.profileDao().getAllFull().observe(this, Observer { profiles ->
|
||||
// TODO fix weird -1 profiles ???
|
||||
profiles.removeAll { it.id < 0 }
|
||||
drawer.setProfileList(profiles)
|
||||
if (profileListEmpty) {
|
||||
profileListEmpty = false
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
else if (app.profile != null) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
}
|
||||
})
|
||||
|
||||
// if null, getAllFull will load a profile and update drawerItems
|
||||
if (app.profile != null)
|
||||
setDrawerItems()
|
||||
|
||||
app.db.metadataDao().getUnreadCounts().observe(this, Observer { unreadCounters ->
|
||||
unreadCounters.map {
|
||||
it.type = it.thingType
|
||||
}
|
||||
drawer.setUnreadCounterList(unreadCounters)
|
||||
})
|
||||
|
||||
b.swipeRefreshLayout.isEnabled = true
|
||||
b.swipeRefreshLayout.setOnRefreshListener { this.syncCurrentFeature() }
|
||||
|
||||
isStoragePermissionGranted()
|
||||
|
||||
SyncJob.schedule(app)
|
||||
|
||||
// APP BACKGROUND
|
||||
if (app.appConfig.appBackground != null) {
|
||||
try {
|
||||
var bg = app.appConfig.appBackground
|
||||
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
|
||||
if (bgDir.exists()) {
|
||||
val files = bgDir.listFiles()
|
||||
val r = Random()
|
||||
val i = r.nextInt(files.size)
|
||||
bg = files[i].toString()
|
||||
}
|
||||
val linearLayout = b.root
|
||||
if (bg.endsWith(".gif")) {
|
||||
linearLayout.background = GifDrawable(bg)
|
||||
} else {
|
||||
linearLayout.background = BitmapDrawable.createFromPath(bg)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
// WHAT'S NEW DIALOG
|
||||
if (app.appConfig.lastAppVersion != BuildConfig.VERSION_CODE) {
|
||||
ServerRequest(app, app.requestScheme + APP_URL + "main.php?just_updated", "MainActivity/JU")
|
||||
.run { e, result ->
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
try {
|
||||
ChangelogDialog().show(supportFragmentManager, "whats_new")
|
||||
} catch (e2: Exception) {
|
||||
e2.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (app.appConfig.lastAppVersion < 170) {
|
||||
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
|
||||
//startActivity(intent);
|
||||
} else {
|
||||
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE
|
||||
app.saveConfig("lastAppVersion")
|
||||
}
|
||||
}
|
||||
|
||||
// RATE SNACKBAR
|
||||
if (app.appConfig.appRateSnackbarTime != 0L && app.appConfig.appRateSnackbarTime <= System.currentTimeMillis()) {
|
||||
navView.coordinator.postDelayed({
|
||||
CafeBar.builder(this)
|
||||
.content(R.string.rate_snackbar_text)
|
||||
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
|
||||
.positiveText(R.string.rate_snackbar_positive)
|
||||
.positiveColor(-0xb350b0)
|
||||
.negativeText(R.string.rate_snackbar_negative)
|
||||
.negativeColor(0xff666666.toInt())
|
||||
.neutralText(R.string.rate_snackbar_neutral)
|
||||
.neutralColor(0xff666666.toInt())
|
||||
.onPositive { cafeBar ->
|
||||
Utils.openGooglePlay(this)
|
||||
cafeBar.dismiss()
|
||||
app.appConfig.appRateSnackbarTime = 0
|
||||
app.saveConfig("appRateSnackbarTime")
|
||||
}
|
||||
.onNegative { cafeBar ->
|
||||
Toast.makeText(this, "Szkoda, opinie innych pomagają mi rozwijać aplikację.", Toast.LENGTH_LONG).show()
|
||||
cafeBar.dismiss()
|
||||
app.appConfig.appRateSnackbarTime = 0
|
||||
app.saveConfig("appRateSnackbarTime")
|
||||
}
|
||||
.onNeutral { cafeBar ->
|
||||
Toast.makeText(this, "OK", Toast.LENGTH_LONG).show()
|
||||
cafeBar.dismiss()
|
||||
app.appConfig.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000
|
||||
app.saveConfig("appRateSnackbarTime")
|
||||
}
|
||||
.autoDismiss(false)
|
||||
.swipeToDismiss(true)
|
||||
.floating(true)
|
||||
.show()
|
||||
}, 10000)
|
||||
}
|
||||
|
||||
// CONTEXT MENU ITEMS
|
||||
bottomSheet.removeAllItems()
|
||||
bottomSheet.appendItems(
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_sync)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
bottomSheet.close()
|
||||
app.apiEdziennik.guiSyncFeature(app, this, App.profileId, R.string.sync_dialog_title, R.string.sync_dialog_text, R.string.sync_done, fragmentToFeature(navTargetId))
|
||||
}),
|
||||
BottomSheetSeparatorItem(false),
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_SETTINGS) }),
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_feedback)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_help_circle)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) })
|
||||
)
|
||||
if (App.devMode) {
|
||||
bottomSheet += BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_debug)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
|
||||
}
|
||||
}
|
||||
|
||||
var profileListEmptyListener = {
|
||||
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
|
||||
}
|
||||
private var profileSettingClickListener = { id: Int, view: View? ->
|
||||
when (id) {
|
||||
DRAWER_PROFILE_ADD_NEW -> {
|
||||
LoginActivity.privacyPolicyAccepted = true
|
||||
// else it would try to navigateUp onBackPressed, which it can't do. There is no parent fragment
|
||||
LoginActivity.firstCompleted = false
|
||||
profileListEmptyListener()
|
||||
}
|
||||
DRAWER_PROFILE_SYNC_ALL -> {
|
||||
SyncJob.run(app)
|
||||
}
|
||||
else -> {
|
||||
loadTarget(id)
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/* _____
|
||||
/ ____|
|
||||
| (___ _ _ _ __ ___
|
||||
\___ \| | | | '_ \ / __|
|
||||
____) | |_| | | | | (__
|
||||
|_____/ \__, |_| |_|\___|
|
||||
__/ |
|
||||
|__*/
|
||||
fun syncCurrentFeature() {
|
||||
swipeRefreshLayout.isRefreshing = true
|
||||
Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show()
|
||||
val callback = object : SyncCallback {
|
||||
override fun onLoginFirst(profileList: List<Profile>, loginStore: LoginStore) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSuccess(activityContext: Context, profileFull: ProfileFull) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onError(activityContext: Context, error: AppError) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
app.apiEdziennik.guiShowErrorSnackbar(this@MainActivity, error)
|
||||
}
|
||||
|
||||
override fun onProgress(progressStep: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onActionStarted(stringResId: Int) {
|
||||
|
||||
}
|
||||
}
|
||||
val feature = fragmentToFeature(navTargetId)
|
||||
if (feature == FEATURE_ALL) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
app.apiEdziennik.guiSync(app, this, App.profileId, R.string.sync_dialog_title, R.string.sync_dialog_text, R.string.sync_done)
|
||||
} else {
|
||||
app.apiEdziennik.guiSyncSilent(app, this, App.profileId, callback, feature)
|
||||
}
|
||||
}
|
||||
private fun fragmentToFeature(currentFragment: Int): Int {
|
||||
return when (currentFragment) {
|
||||
DRAWER_ITEM_TIMETABLE -> FEATURE_TIMETABLE
|
||||
DRAWER_ITEM_AGENDA -> FEATURE_AGENDA
|
||||
DRAWER_ITEM_GRADES -> FEATURE_GRADES
|
||||
DRAWER_ITEM_HOMEWORKS -> FEATURE_HOMEWORKS
|
||||
DRAWER_ITEM_NOTICES -> FEATURE_NOTICES
|
||||
DRAWER_ITEM_ATTENDANCES -> FEATURE_ATTENDANCES
|
||||
DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) {
|
||||
1 -> FEATURE_MESSAGES_OUTBOX
|
||||
else -> FEATURE_MESSAGES_INBOX
|
||||
}
|
||||
DRAWER_ITEM_ANNOUNCEMENTS -> FEATURE_ANNOUNCEMENTS
|
||||
else -> FEATURE_ALL
|
||||
}
|
||||
}
|
||||
private fun fragmentToSyncName(currentFragment: Int): Int {
|
||||
return when (currentFragment) {
|
||||
DRAWER_ITEM_TIMETABLE -> R.string.sync_feature_timetable
|
||||
DRAWER_ITEM_AGENDA -> R.string.sync_feature_agenda
|
||||
DRAWER_ITEM_GRADES -> R.string.sync_feature_grades
|
||||
DRAWER_ITEM_HOMEWORKS -> R.string.sync_feature_homeworks
|
||||
DRAWER_ITEM_NOTICES -> R.string.sync_feature_notices
|
||||
DRAWER_ITEM_ATTENDANCES -> R.string.sync_feature_attendances
|
||||
DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) {
|
||||
1 -> R.string.sync_feature_messages_outbox
|
||||
else -> R.string.sync_feature_messages_inbox
|
||||
}
|
||||
DRAWER_ITEM_ANNOUNCEMENTS -> R.string.sync_feature_announcements
|
||||
else -> R.string.sync_feature_syncing_all
|
||||
}
|
||||
}
|
||||
|
||||
/* _____ _ _
|
||||
|_ _| | | | |
|
||||
| | _ __ | |_ ___ _ __ | |_ ___
|
||||
| | | '_ \| __/ _ \ '_ \| __/ __|
|
||||
_| |_| | | | || __/ | | | |_\__ \
|
||||
|_____|_| |_|\__\___|_| |_|\__|__*/
|
||||
private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
}
|
||||
private fun handleIntent(extras: Bundle?) {
|
||||
|
||||
Log.d(TAG, "handleIntent() {")
|
||||
extras?.keySet()?.forEach { key ->
|
||||
Log.d(TAG, " \"$key\": "+extras.get(key))
|
||||
}
|
||||
Log.d(TAG, "}")
|
||||
|
||||
if (extras?.containsKey("reloadProfileId") == true) {
|
||||
val reloadProfileId = extras.getInt("reloadProfileId", -1)
|
||||
extras.remove("reloadProfileId")
|
||||
if (reloadProfileId == -1 || (app.profile != null && app.profile.id == reloadProfileId)) {
|
||||
reloadTarget()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var intentProfileId = -1
|
||||
var intentTargetId = -1
|
||||
|
||||
if (extras?.containsKey("profileId") == true) {
|
||||
intentProfileId = extras.getInt("profileId", -1)
|
||||
extras.remove("profileId")
|
||||
}
|
||||
|
||||
if (extras?.containsKey("fragmentId") == true) {
|
||||
intentTargetId = extras.getInt("fragmentId", -1)
|
||||
extras.remove("fragmentId")
|
||||
}
|
||||
|
||||
/*if (intentTargetId == -1 && navController.currentDestination?.id == R.id.loadingFragment) {
|
||||
intentTargetId = navTarget.id
|
||||
}*/
|
||||
|
||||
if (navLoading) {
|
||||
navLoading = false
|
||||
b.fragment.removeAllViews()
|
||||
if (intentTargetId == -1)
|
||||
intentTargetId = HOME_ID
|
||||
}
|
||||
|
||||
when {
|
||||
app.profile == null -> {
|
||||
if (intentProfileId == -1)
|
||||
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
|
||||
loadProfile(intentProfileId, intentTargetId)
|
||||
}
|
||||
intentProfileId != -1 -> {
|
||||
loadProfile(intentProfileId, intentTargetId)
|
||||
}
|
||||
intentTargetId != -1 -> {
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(intentTargetId, extras)
|
||||
}
|
||||
else -> {
|
||||
drawer.currentProfile = app.profile.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun recreate() {
|
||||
recreate(navTargetId)
|
||||
}
|
||||
fun recreate(targetId: Int) {
|
||||
recreate(targetId, null)
|
||||
}
|
||||
fun recreate(targetId: Int? = null, arguments: Bundle? = null) {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
if (arguments != null)
|
||||
intent.putExtras(arguments)
|
||||
if (targetId != null) {
|
||||
intent.putExtra("fragmentId", targetId)
|
||||
}
|
||||
finish()
|
||||
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(Intent.ACTION_MAIN)
|
||||
registerReceiver(intentReceiver, filter)
|
||||
super.onResume()
|
||||
}
|
||||
override fun onPause() {
|
||||
unregisterReceiver(intentReceiver)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt("fragmentId", navTargetId)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQUEST_LOGIN_ACTIVITY) {
|
||||
if (resultCode == Activity.RESULT_CANCELED && false) {
|
||||
finish()
|
||||
}
|
||||
else {
|
||||
if (!app.appConfig.loginFinished)
|
||||
finish()
|
||||
else {
|
||||
handleIntent(data?.extras)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _ _
|
||||
| | | | | | | | | |
|
||||
| | ___ __ _ __| | _ __ ___ ___| |_| |__ ___ __| |___
|
||||
| | / _ \ / _` |/ _` | | '_ ` _ \ / _ \ __| '_ \ / _ \ / _` / __|
|
||||
| |___| (_) | (_| | (_| | | | | | | | __/ |_| | | | (_) | (_| \__ \
|
||||
|______\___/ \__,_|\__,_| |_| |_| |_|\___|\__|_| |_|\___/ \__,_|__*/
|
||||
val navOptions = NavOptions.Builder()
|
||||
.setEnterAnim(R.anim.task_open_enter) // new fragment enter
|
||||
.setExitAnim(R.anim.task_open_exit) // old fragment exit
|
||||
.setPopEnterAnim(R.anim.task_close_enter) // old fragment enter back
|
||||
.setPopExitAnim(R.anim.task_close_exit) // new fragment exit
|
||||
.build()
|
||||
|
||||
fun loadProfile(id: Int) = loadProfile(id, navTargetId)
|
||||
fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments)
|
||||
fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
|
||||
Log.d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)")
|
||||
if (app.profile != null && App.profileId == id) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
return
|
||||
}
|
||||
AsyncTask.execute {
|
||||
app.profileLoadById(id)
|
||||
|
||||
this.runOnUiThread {
|
||||
if (app.profile == null) {
|
||||
LoginActivity.firstCompleted = false
|
||||
if (app.appConfig.loginFinished) {
|
||||
// this shouldn't run
|
||||
profileListEmptyListener()
|
||||
}
|
||||
} else {
|
||||
setDrawerItems()
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fun loadTarget(id: Int, arguments: Bundle? = null) {
|
||||
var loadId = id
|
||||
if (loadId == -1) {
|
||||
loadId = DRAWER_ITEM_HOME
|
||||
}
|
||||
val target = navTargetList
|
||||
.singleOrNull { it.id == loadId }
|
||||
if (target == null) {
|
||||
Toast.makeText(this, getString(R.string.error_invalid_fragment, id), Toast.LENGTH_LONG).show()
|
||||
loadTarget(navTargetList.first(), arguments)
|
||||
}
|
||||
else {
|
||||
loadTarget(target, arguments)
|
||||
}
|
||||
}
|
||||
private fun loadTarget(target: NavTarget, arguments: Bundle? = null) {
|
||||
Log.d("NavDebug", "loadItem(id = ${target.id})")
|
||||
|
||||
bottomSheet.close()
|
||||
bottomSheet.removeAllContextual()
|
||||
bottomSheet.toggleGroupEnabled = false
|
||||
bottomSheet.onCloseListener = null
|
||||
drawer.close()
|
||||
drawer.setSelection(target.id, fireOnClick = false)
|
||||
navView.toolbar.setTitle(target.title ?: target.name)
|
||||
|
||||
Log.d("NavDebug", "Navigating from ${navTarget.fragmentClass?.java?.simpleName} to ${target.fragmentClass?.java?.simpleName}")
|
||||
|
||||
val fragment = target.fragmentClass?.java?.newInstance() ?: return
|
||||
fragment.arguments = arguments
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
|
||||
if (navTarget == target) {
|
||||
// just reload the current target
|
||||
transaction.setCustomAnimations(
|
||||
R.anim.fade_in,
|
||||
R.anim.fade_out
|
||||
)
|
||||
}
|
||||
else {
|
||||
navBackStack.lastIndexOf(target).let {
|
||||
if (it == -1)
|
||||
return@let target
|
||||
// pop the back stack up until that target
|
||||
transaction.setCustomAnimations(
|
||||
R.anim.task_close_enter,
|
||||
R.anim.task_close_exit
|
||||
)
|
||||
|
||||
// navigating grades_add -> grades
|
||||
// navTarget == grades_add
|
||||
// navBackStack = [home, grades, grades_editor]
|
||||
// it == 1
|
||||
//
|
||||
// navTarget = target
|
||||
// remove 1
|
||||
// remove 2
|
||||
val popCount = navBackStack.size - it
|
||||
for (i in 0 until popCount) {
|
||||
navBackStack.removeAt(navBackStack.lastIndex)
|
||||
}
|
||||
navTarget = target
|
||||
|
||||
return@let null
|
||||
}?.let {
|
||||
// target is neither current nor in the back stack
|
||||
// so navigate to it
|
||||
transaction.setCustomAnimations(
|
||||
R.anim.task_open_enter,
|
||||
R.anim.task_open_exit
|
||||
)
|
||||
navBackStack.add(navTarget)
|
||||
navTarget = target
|
||||
}
|
||||
}
|
||||
|
||||
if (navTarget.popToHome) {
|
||||
// if the current has popToHome, let only home be in the back stack
|
||||
// probably `if (navTarget.popToHome)` in popBackStack() is not needed now
|
||||
val popCount = navBackStack.size - 1
|
||||
for (i in 0 until popCount) {
|
||||
navBackStack.removeAt(navBackStack.lastIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("NavDebug", "Current fragment ${navTarget.fragmentClass?.java?.simpleName}, pop to home ${navTarget.popToHome}, back stack:")
|
||||
navBackStack.forEachIndexed { index, target2 ->
|
||||
Log.d("NavDebug", " - $index: ${target2.fragmentClass?.java?.simpleName}")
|
||||
}
|
||||
|
||||
transaction.replace(R.id.fragment, fragment)
|
||||
transaction.commitAllowingStateLoss()
|
||||
|
||||
// TASK DESCRIPTION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val bm = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
|
||||
val taskDesc = ActivityManager.TaskDescription(
|
||||
if (target.id == HOME_ID) getString(R.string.app_name) else getString(R.string.app_task_format, getString(target.name)),
|
||||
bm,
|
||||
getColorFromAttr(this, R.attr.colorSurface)
|
||||
)
|
||||
setTaskDescription(taskDesc)
|
||||
}
|
||||
|
||||
}
|
||||
fun reloadTarget() = loadTarget(navTarget)
|
||||
|
||||
private fun popBackStack(): Boolean {
|
||||
if (navBackStack.size == 0) {
|
||||
return false
|
||||
}
|
||||
// TODO back stack argument support
|
||||
if (navTarget.popToHome) {
|
||||
loadTarget(HOME_ID)
|
||||
}
|
||||
else {
|
||||
loadTarget(navBackStack.last())
|
||||
}
|
||||
return true
|
||||
}
|
||||
fun navigateUp() {
|
||||
if (!popBackStack()) {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the NavLib's menu button ripple to gain user attention
|
||||
* that something has changed in the bottom sheet.
|
||||
*/
|
||||
fun gainAttention() {
|
||||
b.navView.postDelayed({
|
||||
navView.gainAttentionOnBottomBar()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/* _____ _ _
|
||||
| __ \ (_) |
|
||||
| | | |_ __ __ ___ _____ _ __ _| |_ ___ _ __ ___ ___
|
||||
| | | | '__/ _` \ \ /\ / / _ \ '__| | | __/ _ \ '_ ` _ \/ __|
|
||||
| |__| | | | (_| |\ V V / __/ | | | || __/ | | | | \__ \
|
||||
|_____/|_| \__,_| \_/\_/ \___|_| |_|\__\___|_| |_| |_|__*/
|
||||
private fun createDrawerItem(target: NavTarget, level: Int = 1): IDrawerItem<*> {
|
||||
val item = DrawerPrimaryItem()
|
||||
.withIdentifier(target.id.toLong())
|
||||
.withName(target.name)
|
||||
.withHiddenInMiniDrawer(!app.appConfig.miniDrawerButtonIds.contains(target.id))
|
||||
.also { if (target.description != null) it.withDescription(target.description!!) }
|
||||
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
||||
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
|
||||
.also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)}
|
||||
|
||||
if (target.badgeTypeId != null)
|
||||
drawer.addUnreadCounterType(target.badgeTypeId!!, target.id)
|
||||
// TODO sub items
|
||||
/*
|
||||
if (target.subItems != null) {
|
||||
for (subItem in target.subItems!!) {
|
||||
item.subItems += createDrawerItem(subItem, level+1)
|
||||
}
|
||||
}*/
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
fun setDrawerItems() {
|
||||
Log.d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}")
|
||||
val drawerItems = arrayListOf<IDrawerItem<*>>()
|
||||
val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>()
|
||||
|
||||
val supportedFragments = if (app.profile == null) arrayListOf<Int>()
|
||||
else app.profile.supportedFragments
|
||||
|
||||
targetPopToHomeList.clear()
|
||||
|
||||
var separatorAdded = false
|
||||
|
||||
for (target in navTargetList) {
|
||||
if (target.isInDrawer && target.isBelowSeparator && !separatorAdded) {
|
||||
separatorAdded = true
|
||||
drawerItems += DividerDrawerItem()
|
||||
}
|
||||
|
||||
if (target.popToHome)
|
||||
targetPopToHomeList += target.id
|
||||
|
||||
if (target.isInDrawer && (target.isStatic || supportedFragments.isEmpty() || supportedFragments.contains(target.id))) {
|
||||
drawerItems += createDrawerItem(target)
|
||||
if (target.id == 1) {
|
||||
targetHomeId = target.id
|
||||
}
|
||||
}
|
||||
|
||||
if (target.isInProfileList) {
|
||||
drawerProfiles += ProfileSettingDrawerItem()
|
||||
.withIdentifier(target.id.toLong())
|
||||
.withName(target.name)
|
||||
.also { if (target.description != null) it.withDescription(target.description!!) }
|
||||
.also { if (target.icon != null) it.withIcon(target.icon!!) }
|
||||
}
|
||||
}
|
||||
|
||||
// seems that this cannot be open, because the itemAdapter has Profile items
|
||||
// instead of normal Drawer items...
|
||||
drawer.profileSelectionClose()
|
||||
|
||||
drawer.setItems(*drawerItems.toTypedArray())
|
||||
drawer.removeAllProfileSettings()
|
||||
drawer.addProfileSettings(*drawerProfiles.toTypedArray())
|
||||
}
|
||||
|
||||
private fun showProfileContextMenu(profile: IProfile<*>, view: View) {
|
||||
val profileId = profile.identifier.toInt()
|
||||
val popupMenu = PopupMenu(this, view)
|
||||
popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings)
|
||||
popupMenu.menu.add(0, 2, 2, R.string.profile_menu_remove)
|
||||
popupMenu.setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == 1) {
|
||||
if (profileId != app.profile.id) {
|
||||
loadProfile(profileId, DRAWER_ITEM_SETTINGS)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
loadTarget(DRAWER_ITEM_SETTINGS, null)
|
||||
} else if (item.itemId == 2) {
|
||||
app.apiEdziennik.guiRemoveProfile(this@MainActivity, profileId, profile.name?.getText(this).toString())
|
||||
}
|
||||
true
|
||||
}
|
||||
popupMenu.show()
|
||||
}
|
||||
|
||||
private val targetPopToHomeList = arrayListOf<Int>()
|
||||
private var targetHomeId: Int = -1
|
||||
override fun onBackPressed() {
|
||||
if (!b.navView.onBackPressed()) {
|
||||
|
||||
navigateUp()
|
||||
|
||||
/*val currentDestinationId = navController.currentDestination?.id
|
||||
|
||||
if (if (targetHomeId != -1 && targetPopToHomeList.contains(navController.currentDestination?.id)) {
|
||||
if (!navController.popBackStack(targetHomeId, false)) {
|
||||
navController.navigateUp()
|
||||
}
|
||||
true
|
||||
} else {
|
||||
navController.navigateUp()
|
||||
}) {
|
||||
val currentId = navController.currentDestination?.id ?: -1
|
||||
val drawerSelection = navTargetList
|
||||
.singleOrNull {
|
||||
it.navGraphId == currentId
|
||||
}?.also {
|
||||
navView.toolbar.setTitle(it.title ?: it.name)
|
||||
}?.id ?: -1
|
||||
drawer.setSelection(drawerSelection, false)
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
356
app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java
Normal file
356
app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java
Normal file
@ -0,0 +1,356 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncJob;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncService;
|
||||
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
|
||||
import static pl.szczodrzynski.edziennik.sync.SyncService.ACTION_CANCEL;
|
||||
|
||||
public class Notifier {
|
||||
|
||||
private static final String TAG = "Notifier";
|
||||
public static final int ID_GET_DATA = 1337000;
|
||||
public static final int ID_GET_DATA_ERROR = 1337001;
|
||||
private static String CHANNEL_GET_DATA_NAME;
|
||||
private static String CHANNEL_GET_DATA_DESC;
|
||||
private static final String GROUP_KEY_GET_DATA = "pl.szczodrzynski.edziennik.GET_DATA";
|
||||
|
||||
private static final int ID_NOTIFICATIONS = 1337002;
|
||||
private static String CHANNEL_NOTIFICATIONS_NAME;
|
||||
private static String CHANNEL_NOTIFICATIONS_DESC;
|
||||
public static final String GROUP_KEY_NOTIFICATIONS = "pl.szczodrzynski.edziennik.NOTIFICATIONS";
|
||||
|
||||
private static final int ID_NOTIFICATIONS_QUIET = 1337002;
|
||||
private static String CHANNEL_NOTIFICATIONS_QUIET_NAME;
|
||||
private static String CHANNEL_NOTIFICATIONS_QUIET_DESC;
|
||||
public static final String GROUP_KEY_NOTIFICATIONS_QUIET = "pl.szczodrzynski.edziennik.NOTIFICATIONS_QUIET";
|
||||
|
||||
private static final int ID_UPDATES = 1337003;
|
||||
private static String CHANNEL_UPDATES_NAME;
|
||||
private static String CHANNEL_UPDATES_DESC;
|
||||
private static final String GROUP_KEY_UPDATES = "pl.szczodrzynski.edziennik.UPDATES";
|
||||
|
||||
private App app;
|
||||
private NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder getDataNotificationBuilder;
|
||||
private int notificationColor;
|
||||
|
||||
Notifier(App _app) {
|
||||
this.app = _app;
|
||||
|
||||
CHANNEL_GET_DATA_NAME = app.getString(R.string.notification_channel_get_data_name);
|
||||
CHANNEL_GET_DATA_DESC = app.getString(R.string.notification_channel_get_data_desc);
|
||||
CHANNEL_NOTIFICATIONS_NAME = app.getString(R.string.notification_channel_notifications_name);
|
||||
CHANNEL_NOTIFICATIONS_DESC = app.getString(R.string.notification_channel_notifications_desc);
|
||||
CHANNEL_NOTIFICATIONS_QUIET_NAME = app.getString(R.string.notification_channel_notifications_quiet_name);
|
||||
CHANNEL_NOTIFICATIONS_QUIET_DESC = app.getString(R.string.notification_channel_notifications_quiet_desc);
|
||||
CHANNEL_UPDATES_NAME = app.getString(R.string.notification_channel_updates_name);
|
||||
CHANNEL_UPDATES_DESC = app.getString(R.string.notification_channel_updates_desc);
|
||||
|
||||
notificationColor = ContextCompat.getColor(app.getContext(), R.color.colorPrimary);
|
||||
notificationManager = (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channelGetData = new NotificationChannel(GROUP_KEY_GET_DATA, CHANNEL_GET_DATA_NAME, NotificationManager.IMPORTANCE_LOW);
|
||||
channelGetData.setDescription(CHANNEL_GET_DATA_DESC);
|
||||
notificationManager.createNotificationChannel(channelGetData);
|
||||
|
||||
NotificationChannel channelNotifications = new NotificationChannel(GROUP_KEY_NOTIFICATIONS, CHANNEL_NOTIFICATIONS_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channelNotifications.setDescription(CHANNEL_NOTIFICATIONS_DESC);
|
||||
channelNotifications.enableLights(true);
|
||||
channelNotifications.setLightColor(notificationColor);
|
||||
notificationManager.createNotificationChannel(channelNotifications);
|
||||
|
||||
NotificationChannel channelNotificationsQuiet = new NotificationChannel(GROUP_KEY_NOTIFICATIONS_QUIET, CHANNEL_NOTIFICATIONS_QUIET_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channelNotificationsQuiet.setDescription(CHANNEL_NOTIFICATIONS_QUIET_DESC);
|
||||
channelNotificationsQuiet.setSound(null, null);
|
||||
channelNotificationsQuiet.enableVibration(false);
|
||||
notificationManager.createNotificationChannel(channelNotificationsQuiet);
|
||||
|
||||
NotificationChannel channelUpdates = new NotificationChannel(GROUP_KEY_UPDATES, CHANNEL_UPDATES_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channelUpdates.setDescription(CHANNEL_UPDATES_DESC);
|
||||
notificationManager.createNotificationChannel(channelUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldBeQuiet() {
|
||||
long now = Time.getNow().getInMillis();
|
||||
long start = app.appConfig.quietHoursStart;
|
||||
long end = app.appConfig.quietHoursEnd;
|
||||
if (start > end) {
|
||||
end += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Night passing");
|
||||
}
|
||||
if (start > now) {
|
||||
now += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Now is smaller");
|
||||
}
|
||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
||||
return app.appConfig.quietHoursStart > 0 && now >= start && now <= end;
|
||||
}
|
||||
|
||||
private int getNotificationDefaults() {
|
||||
return (shouldBeQuiet() ? 0 : Notification.DEFAULT_ALL);
|
||||
}
|
||||
private String getNotificationGroup() {
|
||||
return shouldBeQuiet() ? GROUP_KEY_NOTIFICATIONS_QUIET : GROUP_KEY_NOTIFICATIONS;
|
||||
}
|
||||
private int getNotificationPriority() {
|
||||
return shouldBeQuiet() ? PRIORITY_DEFAULT : PRIORITY_MAX;
|
||||
}
|
||||
|
||||
/* _____ _ _____ _
|
||||
| __ \ | | / ____| | |
|
||||
| | | | __ _| |_ __ _ | | __ ___| |_
|
||||
| | | |/ _` | __/ _` | | | |_ |/ _ \ __|
|
||||
| |__| | (_| | || (_| | | |__| | __/ |_
|
||||
|_____/ \__,_|\__\__,_| \_____|\___|\_*/
|
||||
public Notification notificationGetDataShow(int maxProgress) {
|
||||
Intent notificationIntent = new Intent(app.getContext(), SyncService.class);
|
||||
notificationIntent.setAction(ACTION_CANCEL);
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
getDataNotificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_GET_DATA)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_get_data_title))
|
||||
.setContentText(app.getString(R.string.notification_get_data_text))
|
||||
.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_cancel), pendingIntent)
|
||||
//.setGroup(GROUP_KEY_GET_DATA)
|
||||
.setOngoing(true)
|
||||
.setProgress(maxProgress, 0, false)
|
||||
.setTicker(app.getString(R.string.notification_get_data_summary))
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataProgress(int progress, int maxProgress) {
|
||||
getDataNotificationBuilder.setProgress(maxProgress, progress, false);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataAction(int stringResId) {
|
||||
getDataNotificationBuilder.setContentTitle(app.getString(R.string.sync_action_format, app.getString(stringResId)));
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataProfile(String profileName) {
|
||||
getDataNotificationBuilder.setContentText(profileName);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataError(String profileName, String error, int failedProfileId) {
|
||||
Intent notificationIntent = new Intent(app.getContext(), Notifier.GetDataRetryService.class);
|
||||
notificationIntent.putExtra("failedProfileId", failedProfileId);
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
getDataNotificationBuilder.mActions.clear();
|
||||
/*try {
|
||||
//Use reflection clean up old actions
|
||||
Field f = getDataNotificationBuilder.getClass().getDeclaredField("mActions");
|
||||
f.setAccessible(true);
|
||||
f.set(getDataNotificationBuilder, new ArrayList<NotificationCompat.Action>());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
|
||||
getDataNotificationBuilder.setProgress(0, 0, false)
|
||||
.setTicker(app.getString(R.string.notification_get_data_error_summary))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_warning)
|
||||
.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_once_again), pendingIntent)
|
||||
.setContentTitle(app.getString(R.string.notification_get_data_error_title, profileName))
|
||||
.setContentText(error)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(error))
|
||||
.setOngoing(false);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public void notificationPost(int id, Notification notification) {
|
||||
notificationManager.notify(id, notification);
|
||||
}
|
||||
|
||||
public void notificationCancel(int id) {
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
|
||||
//public void notificationGetDataHide() {
|
||||
// notificationManager.cancel(ID_GET_DATA);
|
||||
// }
|
||||
|
||||
public static class GetDataRetryService extends IntentService {
|
||||
private static final String TAG = "Notifier/GetDataRetry";
|
||||
|
||||
public GetDataRetryService() {
|
||||
super(Notifier.GetDataRetryService.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
SyncJob.run((App) getApplication(), intent.getExtras().getInt("failedProfileId", -1), -1);
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
assert notificationManager != null;
|
||||
notificationManager.cancel(ID_GET_DATA_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _ __ _ _ _
|
||||
| \ | | | | (_)/ _(_) | | (_)
|
||||
| \| | ___ | |_ _| |_ _ ___ __ _| |_ _ ___ _ __
|
||||
| . ` |/ _ \| __| | _| |/ __/ _` | __| |/ _ \| '_ \
|
||||
| |\ | (_) | |_| | | | | (_| (_| | |_| | (_) | | | |
|
||||
|_| \_|\___/ \__|_|_| |_|\___\__,_|\__|_|\___/|_| |*/
|
||||
public void add(pl.szczodrzynski.edziennik.models.Notification notification) {
|
||||
app.appConfig.notifications.add(notification);
|
||||
}
|
||||
|
||||
public void postAll(ProfileFull profile) {
|
||||
Collections.sort(app.appConfig.notifications, (o1, o2) -> (o2.addedDate - o1.addedDate > 0) ? 1 : (o2.addedDate - o1.addedDate < 0) ? -1 : 0);
|
||||
if (profile != null && !profile.getSyncNotifications())
|
||||
return;
|
||||
|
||||
if (app.appConfig.notifications.size() > 40) {
|
||||
app.appConfig.notifications.subList(40, app.appConfig.notifications.size() - 1).clear();
|
||||
}
|
||||
|
||||
int unreadCount = 0;
|
||||
List<pl.szczodrzynski.edziennik.models.Notification> notificationList = new ArrayList<>();
|
||||
for (pl.szczodrzynski.edziennik.models.Notification notification: app.appConfig.notifications) {
|
||||
if (!notification.notified) {
|
||||
notification.seen = false;
|
||||
notification.notified = true;
|
||||
unreadCount++;
|
||||
if (notificationList.size() < 10) {
|
||||
notificationList.add(notification);
|
||||
}
|
||||
}
|
||||
else {
|
||||
notification.seen = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (pl.szczodrzynski.edziennik.models.Notification notification: notificationList) {
|
||||
Intent intent = new Intent(app, MainActivity.class);
|
||||
notification.fillIntent(intent);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0);
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, getNotificationGroup())
|
||||
// title, text, type, date
|
||||
.setContentTitle(notification.title)
|
||||
.setContentText(notification.text)
|
||||
.setSubText(pl.szczodrzynski.edziennik.models.Notification.stringType(app, notification.type))
|
||||
.setWhen(notification.addedDate)
|
||||
.setTicker(app.getString(R.string.notification_ticker_format, pl.szczodrzynski.edziennik.models.Notification.stringType(app, notification.type)))
|
||||
// icon, color, lights, priority
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(notificationColor)
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setPriority(getNotificationPriority())
|
||||
// channel, group, style
|
||||
.setChannelId(getNotificationGroup())
|
||||
.setGroup(getNotificationGroup())
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(notification.text))
|
||||
// intent, auto cancel
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
if (!shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(notification.id, notificationBuilder.build());
|
||||
}
|
||||
|
||||
if (notificationList.size() > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Intent intent = new Intent(app, MainActivity.class);
|
||||
intent.setAction("android.intent.action.MAIN");
|
||||
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS,
|
||||
intent, 0);
|
||||
|
||||
NotificationCompat.Builder groupBuilder =
|
||||
new NotificationCompat.Builder(app, getNotificationGroup())
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount))
|
||||
.setGroupSummary(true)
|
||||
.setAutoCancel(true)
|
||||
.setChannelId(getNotificationGroup())
|
||||
.setGroup(getNotificationGroup())
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setPriority(getNotificationPriority())
|
||||
.setContentIntent(pendingIntent)
|
||||
.setStyle(new NotificationCompat.BigTextStyle());
|
||||
if (!shouldBeQuiet()) {
|
||||
groupBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _
|
||||
| | | | | | | |
|
||||
| | | |_ __ __| | __ _| |_ ___ ___
|
||||
| | | | '_ \ / _` |/ _` | __/ _ \/ __|
|
||||
| |__| | |_) | (_| | (_| | || __/\__ \
|
||||
\____/| .__/ \__,_|\__,_|\__\___||___/
|
||||
| |
|
||||
|*/
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename) {
|
||||
if (!app.appConfig.notifyAboutUpdates)
|
||||
return;
|
||||
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
|
||||
.putExtra("update_version", updateVersion)
|
||||
.putExtra("update_url", updateUrl)
|
||||
.putExtra("update_filename", updateFilename);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_UPDATES)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_updates_title))
|
||||
.setContentText(app.getString(R.string.notification_updates_text, updateVersion))
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setTicker(app.getString(R.string.notification_updates_summary))
|
||||
.setPriority(PRIORITY_MAX)
|
||||
.setAutoCancel(true);
|
||||
if (!shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(ID_UPDATES, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public void notificationUpdatesHide() {
|
||||
if (!app.appConfig.notifyAboutUpdates)
|
||||
return;
|
||||
notificationManager.cancel(ID_UPDATES);
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
for (pl.szczodrzynski.edziennik.models.Notification notification: app.appConfig.notifications) {
|
||||
Log.d(TAG, "Profile"+notification.profileId+" Notification from "+ Date.fromMillis(notification.addedDate).getFormattedString()+" "+ Time.fromMillis(notification.addedDate).getStringHMS()+" - "+notification.text);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,450 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.EventFull;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LessonChange;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile;
|
||||
import pl.szczodrzynski.edziennik.fragments.HomeFragment;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.ItemWidgetTimetableModel;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
import pl.szczodrzynski.edziennik.models.Week;
|
||||
import pl.szczodrzynski.edziennik.widgets.WidgetConfig;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncJob;
|
||||
import pl.szczodrzynski.edziennik.widgets.timetable.LessonDetailsActivity;
|
||||
import pl.szczodrzynski.edziennik.widgets.timetable.WidgetTimetableService;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.ExtensionsKt.filterOutArchived;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
|
||||
|
||||
public class WidgetTimetable extends AppWidgetProvider {
|
||||
|
||||
|
||||
public static final String ACTION_SYNC_DATA = "ACTION_SYNC_DATA";
|
||||
private static final String TAG = "WidgetTimetable";
|
||||
private static int modeInt = 0;
|
||||
|
||||
public WidgetTimetable() {
|
||||
// Start the worker thread
|
||||
//HandlerThread sWorkerThread = new HandlerThread("WidgetTimetable-worker");
|
||||
//sWorkerThread.start();
|
||||
//Handler sWorkerQueue = new Handler(sWorkerThread.getLooper());
|
||||
}
|
||||
|
||||
public static SparseArray<List<ItemWidgetTimetableModel>> timetables = null;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_SYNC_DATA.equals(intent.getAction())){
|
||||
SyncJob.run((App) context.getApplicationContext());
|
||||
}
|
||||
super.onReceive(context, intent);
|
||||
}
|
||||
|
||||
public static PendingIntent getPendingSelfIntent(Context context, String action) {
|
||||
Intent intent = new Intent(context, WidgetTimetable.class);
|
||||
intent.setAction(action);
|
||||
return getPendingSelfIntent(context, intent);
|
||||
}
|
||||
public static PendingIntent getPendingSelfIntent(Context context, Intent intent) {
|
||||
return PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||
}
|
||||
|
||||
public static Bitmap drawableToBitmap (Drawable drawable) {
|
||||
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable)drawable).getBitmap();
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
ComponentName thisWidget = new ComponentName(context, WidgetTimetable.class);
|
||||
|
||||
timetables = new SparseArray<>();
|
||||
//timetables.clear();
|
||||
|
||||
App app = (App)context.getApplicationContext();
|
||||
|
||||
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (int appWidgetId : allWidgetIds) {
|
||||
|
||||
//d(TAG, "thr "+Thread.currentThread().getName());
|
||||
|
||||
WidgetConfig widgetConfig = app.appConfig.widgetTimetableConfigs.get(appWidgetId);
|
||||
if (widgetConfig == null) {
|
||||
widgetConfig = new WidgetConfig(app.profileFirstId());
|
||||
app.appConfig.widgetTimetableConfigs.put(appWidgetId, widgetConfig);
|
||||
app.appConfig.savePending = true;
|
||||
}
|
||||
|
||||
RemoteViews views;
|
||||
if (widgetConfig.bigStyle) {
|
||||
views = new RemoteViews(context.getPackageName(), widgetConfig.darkTheme ? R.layout.widget_timetable_dark_big : R.layout.widget_timetable_big);
|
||||
}
|
||||
else {
|
||||
views = new RemoteViews(context.getPackageName(), widgetConfig.darkTheme ? R.layout.widget_timetable_dark : R.layout.widget_timetable);
|
||||
}
|
||||
|
||||
PorterDuff.Mode mode = PorterDuff.Mode.DST_IN;
|
||||
/*if (widgetConfig.darkTheme) {
|
||||
switch (modeInt) {
|
||||
case 0:
|
||||
mode = PorterDuff.Mode.ADD;
|
||||
d(TAG, "ADD");
|
||||
break;
|
||||
case 1:
|
||||
mode = PorterDuff.Mode.DST_ATOP;
|
||||
d(TAG, "DST_ATOP");
|
||||
break;
|
||||
case 2:
|
||||
mode = PorterDuff.Mode.DST_IN;
|
||||
d(TAG, "DST_IN");
|
||||
break;
|
||||
case 3:
|
||||
mode = PorterDuff.Mode.DST_OUT;
|
||||
d(TAG, "DST_OUT");
|
||||
break;
|
||||
case 4:
|
||||
mode = PorterDuff.Mode.DST_OVER;
|
||||
d(TAG, "DST_OVER");
|
||||
break;
|
||||
case 5:
|
||||
mode = PorterDuff.Mode.LIGHTEN;
|
||||
d(TAG, "LIGHTEN");
|
||||
break;
|
||||
case 6:
|
||||
mode = PorterDuff.Mode.MULTIPLY;
|
||||
d(TAG, "MULTIPLY");
|
||||
break;
|
||||
case 7:
|
||||
mode = PorterDuff.Mode.OVERLAY;
|
||||
d(TAG, "OVERLAY");
|
||||
break;
|
||||
case 8:
|
||||
mode = PorterDuff.Mode.SCREEN;
|
||||
d(TAG, "SCREEN");
|
||||
break;
|
||||
case 9:
|
||||
mode = PorterDuff.Mode.SRC_ATOP;
|
||||
d(TAG, "SRC_ATOP");
|
||||
break;
|
||||
case 10:
|
||||
mode = PorterDuff.Mode.SRC_IN;
|
||||
d(TAG, "SRC_IN");
|
||||
break;
|
||||
case 11:
|
||||
mode = PorterDuff.Mode.SRC_OUT;
|
||||
d(TAG, "SRC_OUT");
|
||||
break;
|
||||
case 12:
|
||||
mode = PorterDuff.Mode.SRC_OVER;
|
||||
d(TAG, "SRC_OVER");
|
||||
break;
|
||||
case 13:
|
||||
mode = PorterDuff.Mode.XOR;
|
||||
d(TAG, "XOR");
|
||||
break;
|
||||
default:
|
||||
modeInt = 0;
|
||||
mode = PorterDuff.Mode.ADD;
|
||||
d(TAG, "ADD");
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
// this code seems to crash the launcher on >= P
|
||||
float transparency = widgetConfig.opacity; //0...1
|
||||
long colorFilter = 0x01000000L * (long) (255f * transparency);
|
||||
try {
|
||||
final Method[] declaredMethods = Class.forName("android.widget.RemoteViews").getDeclaredMethods();
|
||||
final int len = declaredMethods.length;
|
||||
if (len > 0) {
|
||||
for (int m = 0; m < len; m++) {
|
||||
final Method method = declaredMethods[m];
|
||||
if (method.getName().equals("setDrawableParameters")) {
|
||||
method.setAccessible(true);
|
||||
method.invoke(views, R.id.widgetTimetableListView, true, -1, (int) colorFilter, mode, -1);
|
||||
method.invoke(views, R.id.widgetTimetableHeader, true, -1, (int) colorFilter, mode, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Intent refreshIntent = new Intent(context, WidgetTimetable.class);
|
||||
refreshIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
||||
refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
|
||||
PendingIntent pendingRefreshIntent = PendingIntent.getBroadcast(context,
|
||||
0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableRefresh, pendingRefreshIntent);
|
||||
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableSync, WidgetTimetable.getPendingSelfIntent(context, ACTION_SYNC_DATA));
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableRefresh, new IconicsDrawable(context, CommunityMaterial.Icon2.cmd_refresh)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(widgetConfig.bigStyle ? 24 : 16)).toBitmap());
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableSync, new IconicsDrawable(context, CommunityMaterial.Icon2.cmd_sync)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(widgetConfig.bigStyle ? 24 : 16)).toBitmap());
|
||||
|
||||
boolean unified = widgetConfig.profileId == -1;
|
||||
|
||||
List<Profile> profileList = new ArrayList<>();
|
||||
if (unified) {
|
||||
profileList = app.db.profileDao().getAllNow();
|
||||
filterOutArchived(profileList);
|
||||
}
|
||||
else {
|
||||
Profile profile = app.db.profileDao().getByIdNow(widgetConfig.profileId);
|
||||
if (profile != null) {
|
||||
profileList.add(profile);
|
||||
}
|
||||
}
|
||||
|
||||
//d(TAG, "Profiles: "+ Arrays.toString(profileList.toArray()));
|
||||
|
||||
if (profileList == null || profileList.size() == 0) {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.VISIBLE);
|
||||
views.setTextViewText(R.id.widgetTimetableLoading, app.getString(R.string.widget_timetable_profile_doesnt_exist));
|
||||
}
|
||||
else {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.GONE);
|
||||
//Register profile;
|
||||
|
||||
long bellSyncDiffMillis = 0;
|
||||
if (app.appConfig.bellSyncDiff != null) {
|
||||
bellSyncDiffMillis = app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000;
|
||||
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier;
|
||||
bellSyncDiffMillis *= -1;
|
||||
}
|
||||
|
||||
List<ItemWidgetTimetableModel> lessonList = new ArrayList<>();
|
||||
|
||||
Time syncedNow = Time.fromMillis(Time.getNow().getInMillis() + bellSyncDiffMillis);
|
||||
|
||||
Date today = Date.getToday();
|
||||
|
||||
int openProfileId = -1;
|
||||
Date displayingDate = null;
|
||||
int displayingWeekDay = 0;
|
||||
if (unified) {
|
||||
views.setTextViewText(R.id.widgetTimetableSubtitle, app.getString(R.string.widget_timetable_title_unified));
|
||||
}
|
||||
else {
|
||||
views.setTextViewText(R.id.widgetTimetableSubtitle, profileList.get(0).getName());
|
||||
openProfileId = profileList.get(0).getId();
|
||||
}
|
||||
|
||||
List<LessonFull> lessons = app.db.lessonDao().getAllWeekNow(unified ? -1 : openProfileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today);
|
||||
|
||||
int scrollPos = 0;
|
||||
|
||||
for (Profile profile: profileList) {
|
||||
Date profileDisplayingDate = HomeFragment.findDateWithLessons(profile.getId(), lessons, syncedNow, 1);
|
||||
int profileDisplayingWeekDay = profileDisplayingDate.getWeekDay();
|
||||
int dayDiff = Date.diffDays(profileDisplayingDate, Date.getToday());
|
||||
|
||||
//d(TAG, "For profile "+profile.name+" displayingDate is "+profileDisplayingDate.getStringY_m_d());
|
||||
if (displayingDate == null || profileDisplayingDate.getValue() < displayingDate.getValue()) {
|
||||
displayingDate = profileDisplayingDate;
|
||||
displayingWeekDay = profileDisplayingWeekDay;
|
||||
//d(TAG, "Setting as global dd");
|
||||
if (dayDiff == 0) {
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, app.getString(R.string.day_today_format, Week.getFullDayName(displayingWeekDay)));
|
||||
} else if (dayDiff == 1) {
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, app.getString(R.string.day_tomorrow_format, Week.getFullDayName(displayingWeekDay)));
|
||||
} else {
|
||||
views.setTextViewText(R.id.widgetTimetableTitle, Week.getFullDayName(displayingWeekDay) + " " + profileDisplayingDate.getStringDm());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Profile profile: profileList) {
|
||||
int pos = 0;
|
||||
|
||||
List<EventFull> events = app.db.eventDao().getAllByDateNow(profile.getId(), displayingDate);
|
||||
if (events == null)
|
||||
events = new ArrayList<>();
|
||||
|
||||
if (unified) {
|
||||
ItemWidgetTimetableModel separator = new ItemWidgetTimetableModel();
|
||||
separator.profileId = profile.getId();
|
||||
separator.bigStyle = widgetConfig.bigStyle;
|
||||
separator.darkTheme = widgetConfig.darkTheme;
|
||||
separator.separatorProfileName = profile.getName();
|
||||
lessonList.add(separator);
|
||||
}
|
||||
|
||||
for (LessonFull lesson : lessons) {
|
||||
//d(TAG, "Profile "+profile.id+" Lesson profileId "+lesson.profileId+" weekDay "+lesson.weekDay+", "+lesson);
|
||||
if (profile.getId() != lesson.profileId || displayingWeekDay != lesson.weekDay)
|
||||
continue;
|
||||
//d(TAG, "Not skipped");
|
||||
ItemWidgetTimetableModel model = new ItemWidgetTimetableModel();
|
||||
|
||||
model.bigStyle = widgetConfig.bigStyle;
|
||||
model.darkTheme = widgetConfig.darkTheme;
|
||||
|
||||
model.profileId = profile.getId();
|
||||
|
||||
model.lessonDate = displayingDate;
|
||||
model.startTime = lesson.startTime;
|
||||
model.endTime = lesson.endTime;
|
||||
|
||||
model.lessonPassed = (syncedNow.getValue() > lesson.endTime.getValue()) && displayingWeekDay == Week.getTodayWeekDay();
|
||||
model.lessonCurrent = (Time.inRange(lesson.startTime, lesson.endTime, syncedNow)) && displayingWeekDay == Week.getTodayWeekDay();
|
||||
|
||||
if (model.lessonCurrent) {
|
||||
scrollPos = pos;
|
||||
} else if (model.lessonPassed) {
|
||||
scrollPos = pos + 1;
|
||||
}
|
||||
pos++;
|
||||
|
||||
model.subjectName = bs(lesson.subjectLongName);
|
||||
model.classroomName = lesson.classroomName;
|
||||
|
||||
model.bellSyncDiffMillis = bellSyncDiffMillis;
|
||||
|
||||
if (lesson.changeId != 0) {
|
||||
if (lesson.changeType == LessonChange.TYPE_CHANGE) {
|
||||
model.lessonChange = true;
|
||||
if (lesson.changedClassroomName()) {
|
||||
model.newClassroomName = lesson.changeClassroomName;
|
||||
}
|
||||
|
||||
if (lesson.changedSubjectLongName()) {
|
||||
model.newSubjectName = lesson.changeSubjectLongName;
|
||||
}
|
||||
}
|
||||
if (lesson.changeType == LessonChange.TYPE_CANCELLED) {
|
||||
model.lessonCancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (EventFull event : events) {
|
||||
if (event.startTime == null)
|
||||
continue;
|
||||
if (event.eventDate.getValue() == displayingDate.getValue()
|
||||
&& event.startTime.getValue() == lesson.startTime.getValue()) {
|
||||
model.eventColors.add(event.type == TYPE_HOMEWORK ? ItemWidgetTimetableModel.EVENT_COLOR_HOMEWORK : event.getColor());
|
||||
}
|
||||
}
|
||||
|
||||
lessonList.add(model);
|
||||
}
|
||||
}
|
||||
|
||||
if (lessonList.size() == 0) {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.VISIBLE);
|
||||
views.setRemoteAdapter(R.id.widgetTimetableListView, new Intent());
|
||||
views.setTextViewText(R.id.widgetTimetableLoading, app.getString(R.string.widget_timetable_no_lessons));
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
else {
|
||||
views.setViewVisibility(R.id.widgetTimetableLoading, View.GONE);
|
||||
|
||||
timetables.put(appWidgetId, lessonList);
|
||||
//WidgetTimetableListProvider.widgetsLessons.put(appWidgetId, lessons);
|
||||
//views.setRemoteAdapter(R.id.widgetTimetableListView, new Intent());
|
||||
Intent listIntent = new Intent(context, WidgetTimetableService.class);
|
||||
listIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
||||
listIntent.setData(Uri.parse(listIntent.toUri(Intent.URI_INTENT_SCHEME)));
|
||||
views.setRemoteAdapter(R.id.widgetTimetableListView, listIntent);
|
||||
|
||||
// template to handle the click listener for each item
|
||||
Intent intentTemplate = new Intent(context, LessonDetailsActivity.class);
|
||||
// Old activities shouldn't be in the history stack
|
||||
intentTemplate.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntentTimetable = PendingIntent.getActivity(context,
|
||||
0,
|
||||
intentTemplate,
|
||||
0);
|
||||
views.setPendingIntentTemplate(R.id.widgetTimetableListView, pendingIntentTimetable);
|
||||
|
||||
Intent openIntent = new Intent(context, MainActivity.class);
|
||||
openIntent.setAction("android.intent.action.MAIN");
|
||||
if (!unified) {
|
||||
openIntent.putExtra("profileId", openProfileId);
|
||||
openIntent.putExtra("timetableDate", displayingDate.getValue());
|
||||
}
|
||||
openIntent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE);
|
||||
PendingIntent pendingOpenIntent = PendingIntent.getActivity(context,
|
||||
appWidgetId, openIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.widgetTimetableHeader, pendingOpenIntent);
|
||||
|
||||
if (!unified)
|
||||
views.setScrollPosition(R.id.widgetTimetableListView, scrollPos);
|
||||
}
|
||||
}
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetTimetableListView);
|
||||
}
|
||||
//modeInt++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
for (int appWidgetId: appWidgetIds) {
|
||||
app.appConfig.widgetTimetableConfigs.remove(appWidgetId);
|
||||
}
|
||||
app.saveConfig("widgetTimetableConfigs");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,240 @@
|
||||
package pl.szczodrzynski.edziennik.activities;
|
||||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityCounterBinding;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.fragments.HomeFragment;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.fragments.HomeFragment.updateInterval;
|
||||
|
||||
public class CounterActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "CounterActivity";
|
||||
private App app;
|
||||
private ActivityCounterBinding b;
|
||||
|
||||
Timer timetableTimer;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
app = (App) getApplication();
|
||||
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_counter, null, false);
|
||||
setContentView(b.getRoot());
|
||||
|
||||
timetableTimer = new Timer();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
private List<LessonFull> lessons = new ArrayList<>();
|
||||
|
||||
private void update() {
|
||||
// BELL SYNCING
|
||||
Time now = Time.getNow();
|
||||
Time syncedNow = now;
|
||||
//Time updateDiff = null;
|
||||
if (app.appConfig.bellSyncDiff != null) {
|
||||
if (app.appConfig.bellSyncMultiplier < 0) {
|
||||
// the bell is too fast, need to step further to go with it
|
||||
// add some time
|
||||
syncedNow = Time.sum(now, app.appConfig.bellSyncDiff);
|
||||
//Toast.makeText(c, "Bell sync diff is "+app.appConfig.bellSyncDiff.getStringHMS()+"\n\n Synced now is "+syncedNow.getStringHMS(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (app.appConfig.bellSyncMultiplier > 0) {
|
||||
// the bell is delayed, need to roll the "now" time back
|
||||
// subtract some time
|
||||
syncedNow = Time.diff(now, app.appConfig.bellSyncDiff);
|
||||
}
|
||||
}
|
||||
|
||||
assert counterTarget != null;
|
||||
if (lessons.size() == 0 || syncedNow.getValue() > counterTarget.getValue()) {
|
||||
findLessons(syncedNow);
|
||||
}
|
||||
else {
|
||||
scheduleUpdate(updateCounter(syncedNow));
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleUpdate(long newRefreshInterval) {
|
||||
try {
|
||||
timetableTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
runOnUiThread(() -> update());
|
||||
}
|
||||
}, newRefreshInterval);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void findLessons(Time syncedNow) {
|
||||
AsyncTask.execute(() -> {
|
||||
Date today = Date.getToday();
|
||||
lessons = app.db.lessonDao().getAllNearestNow(App.profileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today, syncedNow);
|
||||
|
||||
if (lessons != null && lessons.size() != 0) {
|
||||
Date displayingDate = lessons.get(0).lessonDate;
|
||||
if (displayingDate == null) {
|
||||
runOnUiThread(() -> scheduleUpdate(updateViews(null, syncedNow, 0, 0)));
|
||||
return;
|
||||
}
|
||||
int displayingWeekDay = displayingDate.getWeekDay();
|
||||
|
||||
Log.d(TAG, "Displaying date is "+displayingDate.getStringY_m_d()+", weekDay is "+displayingWeekDay);
|
||||
|
||||
int notPassedIndex = -1;
|
||||
int notPassedWeekDay = -1;
|
||||
//int firstIndex = -1;
|
||||
int lastIndex = -1;
|
||||
int index = 0;
|
||||
for (LessonFull lesson: lessons) {
|
||||
if (notPassedIndex == -1 && !lesson.lessonPassed) {
|
||||
if (lesson.lessonDate != null)
|
||||
displayingDate = lesson.lessonDate;
|
||||
displayingWeekDay = lesson.weekDay;
|
||||
notPassedIndex = index;
|
||||
notPassedWeekDay = lesson.weekDay;
|
||||
}
|
||||
if (lesson.weekDay == notPassedWeekDay) {
|
||||
/*if (firstIndex == -1)
|
||||
firstIndex = index;*/
|
||||
lastIndex = index;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// for safety
|
||||
/*if (firstIndex == -1)
|
||||
firstIndex++;*/
|
||||
if (notPassedIndex == -1)
|
||||
notPassedIndex++;
|
||||
if (lastIndex == -1)
|
||||
lastIndex++;
|
||||
|
||||
Log.d(TAG, "Not passed index is "+notPassedIndex);
|
||||
Log.d(TAG, "Last index is "+lastIndex);
|
||||
Log.d(TAG, "New Displaying date is "+displayingDate.getStringY_m_d()+", weekDay is "+displayingWeekDay);
|
||||
|
||||
Date finalDisplayingDate = displayingDate;
|
||||
int finalNotPassedIndex = notPassedIndex;
|
||||
int finalLastIndex = lastIndex;
|
||||
runOnUiThread(() -> scheduleUpdate(updateViews(finalDisplayingDate, syncedNow, finalNotPassedIndex, finalLastIndex)));
|
||||
}
|
||||
else {
|
||||
runOnUiThread(() -> scheduleUpdate(updateViews(null, syncedNow, 0, 0)));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private Time counterTarget = new Time(0, 0, 0);
|
||||
private static final short TIME_TILL = 0;
|
||||
private static final short TIME_LEFT = 1;
|
||||
private short counterType = TIME_LEFT;
|
||||
private long updateCounter(Time syncedNow) {
|
||||
Time diff = Time.diff(counterTarget, syncedNow);
|
||||
b.timeLeft.setText(counterType == TIME_TILL ? HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds, "\n") : HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds, "\n"));
|
||||
return updateInterval(app, diff);
|
||||
}
|
||||
|
||||
private long updateViews(Date displayingDate, Time syncedNow, int notPassedIndex, int lastIndex) {
|
||||
long newRefreshInterval = 1000*5;
|
||||
|
||||
if (displayingDate == null) {
|
||||
return newRefreshInterval;
|
||||
}
|
||||
|
||||
int dayDiff = Date.diffDays(displayingDate, Date.getToday());
|
||||
if (displayingDate.getValue() != Date.getToday().getValue() && dayDiff == 0) {
|
||||
dayDiff++;
|
||||
}
|
||||
|
||||
LessonFull lessonFirst = lessons.get(dayDiff == 0 ? 0 : notPassedIndex);
|
||||
// should never be out of range
|
||||
LessonFull lessonLast = lessons.get(lastIndex);
|
||||
|
||||
boolean duringLessons = Time.inRange(lessonFirst.startTime, lessonLast.endTime, syncedNow) && dayDiff == 0;
|
||||
if (duringLessons) {
|
||||
LessonFull lessonCurrent = null;
|
||||
LessonFull lessonNext = null;
|
||||
|
||||
if (lessons.get(notPassedIndex).lessonCurrent) {
|
||||
lessonCurrent = lessons.get(notPassedIndex);
|
||||
if (lessons.size() > notPassedIndex+1 && lessons.get(notPassedIndex+1).weekDay == displayingDate.getWeekDay())
|
||||
lessonNext = lessons.get(notPassedIndex+1);
|
||||
}
|
||||
else {
|
||||
lessonNext = lessons.get(notPassedIndex);
|
||||
}
|
||||
|
||||
if (lessonCurrent != null) { // show time to the end of this lesson
|
||||
b.lessonName.setText(lessonCurrent.subjectLongName);
|
||||
|
||||
counterType = TIME_LEFT;
|
||||
counterTarget = lessonCurrent.endTime;
|
||||
newRefreshInterval = updateCounter(syncedNow);
|
||||
}
|
||||
else if (lessonNext != null) { // it's break time, show time to the start of next lesson
|
||||
b.lessonName.setText(R.string.lesson_break);
|
||||
|
||||
counterType = TIME_LEFT;
|
||||
counterTarget = lessonNext.startTime;
|
||||
newRefreshInterval = updateCounter(syncedNow);
|
||||
}
|
||||
else { // idk what it is now (during lessons, but not during lesson or a break)
|
||||
b.lessonName.setText(R.string.card_timetable_wtf);
|
||||
b.timeLeft.setText(R.string.card_timetable_wtf_report);
|
||||
newRefreshInterval = 1000*60*2;
|
||||
finish();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (syncedNow.getValue() < lessonFirst.startTime.getValue()) {
|
||||
// before lessons
|
||||
b.lessonName.setText(R.string.lesson_break);
|
||||
|
||||
counterType = TIME_LEFT;
|
||||
counterTarget = lessonFirst.startTime;
|
||||
newRefreshInterval = updateCounter(syncedNow);
|
||||
}
|
||||
else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
return newRefreshInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
//Log.d(TAG, "OnDestroy");
|
||||
try {
|
||||
timetableTimer.cancel();
|
||||
timetableTimer.purge();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package pl.szczodrzynski.edziennik.activities;
|
||||
|
||||
/*
|
||||
* Copyright 2014-2017 Eduard Ereza Martínez
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.text.Html;
|
||||
import android.util.Base64;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import cat.ereza.customactivityoncrash.CustomActivityOnCrash;
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig;
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.BuildConfig;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.network.ServerRequest;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.App.APP_URL;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
|
||||
|
||||
public final class CrashActivity extends AppCompatActivity {
|
||||
|
||||
private App app;
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
this.app = (App)getApplication();
|
||||
setTheme(Themes.INSTANCE.getAppTheme());
|
||||
|
||||
setContentView(R.layout.activity_crash);
|
||||
|
||||
final CaocConfig config = CustomActivityOnCrash.getConfigFromIntent(getIntent());
|
||||
|
||||
if (config == null) {
|
||||
//This should never happen - Just finish the activity to avoid a recursive crash.
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
//Close/restart button logic:
|
||||
//If a class if set, use restart.
|
||||
//Else, use close and just finish the app.
|
||||
//It is recommended that you follow this logic if implementing a custom error activity.
|
||||
Button restartButton = findViewById(R.id.crash_restart_btn);
|
||||
restartButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CustomActivityOnCrash.restartApplication(CrashActivity.this, config);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Button devMessageButton = findViewById(R.id.crash_dev_message_btn);
|
||||
devMessageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(CrashActivity.this, CrashGtfoActivity.class);
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
final Button reportButton = findViewById(R.id.crash_report_btn);
|
||||
reportButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!app.networkUtils.isOnline())
|
||||
{
|
||||
new MaterialDialog.Builder(CrashActivity.this)
|
||||
.title(R.string.network_you_are_offline_title)
|
||||
.content(R.string.network_you_are_offline_text)
|
||||
.positiveText(R.string.ok)
|
||||
.show();
|
||||
}
|
||||
else
|
||||
{
|
||||
//app.networkUtils.setSelfSignedSSL(CrashActivity.this, null);
|
||||
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?report", "CrashActivity")
|
||||
.setBodyParameter("base64_encoded", Base64.encodeToString(getErrorString(getIntent(), true).getBytes(), Base64.DEFAULT))
|
||||
.run((e, result) -> {
|
||||
if (result != null)
|
||||
{
|
||||
if (result.get("success").getAsBoolean()) {
|
||||
Toast.makeText(CrashActivity.this, getString(R.string.crash_report_sent), Toast.LENGTH_SHORT).show();
|
||||
reportButton.setEnabled(false);
|
||||
reportButton.setTextColor(getResources().getColor(android.R.color.darker_gray));
|
||||
}
|
||||
else {
|
||||
Toast.makeText(CrashActivity.this, getString(R.string.crash_report_cannot_send) + ": " + result.get("reason").getAsString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.makeText(CrashActivity.this, getString(R.string.crash_report_cannot_send)+" JsonObject equals null", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Button moreInfoButton = findViewById(R.id.crash_details_btn);
|
||||
moreInfoButton.setOnClickListener(v -> new MaterialDialog.Builder(CrashActivity.this)
|
||||
.title(R.string.crash_details)
|
||||
.content(Html.fromHtml(getErrorString(getIntent(), false)))
|
||||
.typeface(null, "RobotoMono-Regular.ttf")
|
||||
.positiveText(R.string.close)
|
||||
.neutralText(R.string.copy_to_clipboard)
|
||||
.onNeutral((dialog, which) -> copyErrorToClipboard())
|
||||
.show());
|
||||
|
||||
String errorInformation = CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent());
|
||||
if (errorInformation.contains("MANUAL CRASH"))
|
||||
{
|
||||
findViewById(R.id.crash_notice).setVisibility(View.GONE);
|
||||
findViewById(R.id.crash_report_btn).setVisibility(View.GONE);
|
||||
findViewById(R.id.crash_feature).setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
findViewById(R.id.crash_notice).setVisibility(View.VISIBLE);
|
||||
findViewById(R.id.crash_report_btn).setVisibility(View.VISIBLE);
|
||||
findViewById(R.id.crash_feature).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private String getErrorString(Intent intent, boolean plain) {
|
||||
// build a string containing the stack trace and the device name + user's registration data
|
||||
String contentPlain = "Crash report:\n\n"+CustomActivityOnCrash.getStackTraceFromIntent(intent);
|
||||
String content = "<small>"+contentPlain+"</small>";
|
||||
content = content.replaceAll(getPackageName(), "<font color='#4caf50'>"+getPackageName()+"</font>");
|
||||
content = content.replaceAll("\n", "<br>");
|
||||
|
||||
contentPlain += "\n"+Build.MANUFACTURER+"\n"+Build.BRAND+"\n"+Build.MODEL+"\n"+Build.DEVICE+"\n";
|
||||
if (app.profile != null && app.profile.getRegistration() == REGISTRATION_ENABLED) {
|
||||
contentPlain += "U: "+app.profile.getUsernameId()+"\nS: "+ app.profile.getStudentNameLong() +"\n";
|
||||
}
|
||||
contentPlain += BuildConfig.VERSION_NAME+" "+BuildConfig.BUILD_TYPE;
|
||||
|
||||
return plain ? contentPlain : content;
|
||||
}
|
||||
|
||||
private void copyErrorToClipboard() {
|
||||
String errorInformation = CustomActivityOnCrash.getAllErrorDetailsFromIntent(CrashActivity.this, getIntent());
|
||||
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
|
||||
//Are there any devices without clipboard...?
|
||||
if (clipboard != null) {
|
||||
ClipData clip = ClipData.newPlainText(getString(R.string.customactivityoncrash_error_activity_error_details_clipboard_label), errorInformation);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(CrashActivity.this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package pl.szczodrzynski.edziennik.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
|
||||
public class CrashGtfoActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTheme((((App)getApplication()).getContext()
|
||||
.getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE)
|
||||
.getBoolean("dark_theme", false) ? R.style.AppTheme_Dark : R.style.AppTheme));
|
||||
setContentView(R.layout.activity_gtfo);
|
||||
}
|
||||
}
|
@ -0,0 +1,363 @@
|
||||
package pl.szczodrzynski.edziennik.activities;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityFeedbackBinding;
|
||||
import pl.szczodrzynski.edziennik.datamodels.FeedbackMessage;
|
||||
import pl.szczodrzynski.edziennik.datamodels.FeedbackMessageWithCount;
|
||||
import pl.szczodrzynski.edziennik.network.ServerRequest;
|
||||
import pl.szczodrzynski.edziennik.utils.Anim;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.github.bassaer.chatmessageview.model.IChatUser;
|
||||
import com.github.bassaer.chatmessageview.model.Message;
|
||||
import com.github.bassaer.chatmessageview.view.ChatView;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.App.APP_URL;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.crc16;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.openUrl;
|
||||
|
||||
public class FeedbackActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "FeedbackActivity";
|
||||
private App app;
|
||||
private ActivityFeedbackBinding b;
|
||||
private boolean firstSend = true;
|
||||
private String deviceToSend = null;
|
||||
private String nameToSend = null;
|
||||
|
||||
private BroadcastReceiver receiver;
|
||||
|
||||
private class User implements IChatUser {
|
||||
Integer id;
|
||||
String name;
|
||||
Bitmap icon;
|
||||
|
||||
public User(int id, String name, Bitmap icon) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getIcon() {
|
||||
return this.icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIcon(Bitmap icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
private User dev;
|
||||
private User user;
|
||||
private ChatView mChatView;
|
||||
|
||||
private void send(String text){
|
||||
/*if ("enable dev mode pls".equals(text)) {
|
||||
try {
|
||||
Log.d(TAG, Utils.AESCrypt.encrypt("ok here you go it's enabled now", "8iryqZUfIUiLmJGi"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}*/
|
||||
MaterialDialog progressDialog = new MaterialDialog.Builder(this)
|
||||
.title(R.string.loading)
|
||||
.content(R.string.sending_message)
|
||||
.negativeText(R.string.cancel)
|
||||
.show();
|
||||
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?feedback_message", "FeedbackSend")
|
||||
.setBodyParameter("message_text", text)
|
||||
.setBodyParameter("target_device", deviceToSend == null ? "null" : deviceToSend)
|
||||
.run(((e, result) -> {
|
||||
progressDialog.dismiss();
|
||||
if (result != null && result.get("success") != null && result.get("success").getAsBoolean()) {
|
||||
FeedbackMessage feedbackMessage = new FeedbackMessage(false, text);
|
||||
if (deviceToSend != null) {
|
||||
feedbackMessage.fromUser = deviceToSend;
|
||||
feedbackMessage.fromUserName = nameToSend;
|
||||
}
|
||||
AsyncTask.execute(() -> app.db.feedbackMessageDao().add(feedbackMessage));
|
||||
Message message = new Message.Builder()
|
||||
.setUser(user)
|
||||
.setRight(true)
|
||||
.setText(feedbackMessage.text)
|
||||
.hideIcon(true)
|
||||
.build();
|
||||
mChatView.send(message);
|
||||
mChatView.setInputText("");
|
||||
b.textInput.setText("");
|
||||
if (firstSend) {
|
||||
Anim.fadeOut(b.inputLayout, 500, new Animation.AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
b.inputLayout.setVisibility(View.GONE);
|
||||
Anim.fadeIn(b.chatLayout, 500, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
});
|
||||
if (deviceToSend == null) {
|
||||
// we are not the developer
|
||||
FeedbackMessage feedbackMessage2 = new FeedbackMessage(true, "Postaram się jak najszybciej Tobie odpowiedzieć. Dostaniesz powiadomienie o odpowiedzi, która pokaże się w tym miejscu.");
|
||||
AsyncTask.execute(() -> app.db.feedbackMessageDao().add(feedbackMessage2));
|
||||
message = new Message.Builder()
|
||||
.setUser(dev)
|
||||
.setRight(false)
|
||||
.setText(feedbackMessage2.text)
|
||||
.hideIcon(false)
|
||||
.build();
|
||||
mChatView.receive(message);
|
||||
}
|
||||
firstSend = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Toast.makeText(app, "Nie udało się wysłać wiadomości.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void openFaq() {
|
||||
openUrl(this, "http://szkolny.eu/pomoc/");
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.faq_back_title)
|
||||
.content(R.string.faq_back_text)
|
||||
.positiveText(R.string.yes)
|
||||
.negativeText(R.string.no)
|
||||
.onPositive(((dialog, which) -> {
|
||||
|
||||
}))
|
||||
.onNegative(((dialog, which) -> {
|
||||
|
||||
}))
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setTheme(Themes.INSTANCE.getAppTheme());
|
||||
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_feedback, null, false);
|
||||
setContentView(b.getRoot());
|
||||
app = (App) getApplication();
|
||||
|
||||
setSupportActionBar(b.toolbar);
|
||||
if (getSupportActionBar() != null)
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
b.faqText.setOnClickListener((v -> {
|
||||
openFaq();
|
||||
}));
|
||||
b.faqButton.setOnClickListener((v -> {
|
||||
openFaq();
|
||||
}));
|
||||
|
||||
receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
FeedbackMessage message = app.gson.fromJson(intent.getStringExtra("message"), FeedbackMessage.class);
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(message.sentTime);
|
||||
Message chatMessage = new Message.Builder()
|
||||
.setUser(intent.getStringExtra("type").equals("dev_chat") ? new User(crc16(message.fromUser.getBytes()), message.fromUserName, BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_circle)) : dev)
|
||||
.setRight(!message.received)
|
||||
.setText(message.text)
|
||||
.setSendTime(c)
|
||||
.hideIcon(!message.received)
|
||||
.build();
|
||||
if (message.received)
|
||||
mChatView.receive(chatMessage);
|
||||
else
|
||||
mChatView.send(chatMessage);
|
||||
}
|
||||
};
|
||||
|
||||
mChatView = b.chatView;
|
||||
|
||||
dev = new User(0, "Szkolny.eu", BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
|
||||
user = new User(1, "Ja", BitmapFactory.decodeResource(getResources(), R.drawable.profile));
|
||||
|
||||
//Set UI parameters if you need
|
||||
mChatView.setLeftBubbleColor(Utils.getAttr(this, R.attr.colorSurface));
|
||||
mChatView.setLeftMessageTextColor(Utils.getAttr(this, android.R.attr.textColorPrimary));
|
||||
mChatView.setRightBubbleColor(Utils.getAttr(this, R.attr.colorPrimary));
|
||||
mChatView.setRightMessageTextColor(Color.WHITE);
|
||||
|
||||
//mChatView.setBackgroundColor(ContextCompat.getColor(this, R.color.blueGray500));
|
||||
mChatView.setSendButtonColor(Utils.getAttr(this, R.attr.colorAccent));
|
||||
mChatView.setSendIcon(R.drawable.ic_action_send);
|
||||
//mChatView.setUsernameTextColor(Color.WHITE);
|
||||
//mChatView.setSendTimeTextColor(Color.WHITE);
|
||||
//mChatView.setDateSeparatorColor(Color.WHITE);
|
||||
mChatView.setInputTextHint("Napisz...");
|
||||
//mChatView.setInputTextColor(Color.BLACK);
|
||||
mChatView.setMessageMarginTop(5);
|
||||
mChatView.setMessageMarginBottom(5);
|
||||
|
||||
if (App.devMode && app.deviceId.equals("f054761fbdb6a238")) {
|
||||
b.targetDeviceLayout.setVisibility(View.VISIBLE);
|
||||
b.targetDeviceDropDown.setOnClickListener((v -> {
|
||||
AsyncTask.execute(() -> {
|
||||
List<FeedbackMessageWithCount> messageList = app.db.feedbackMessageDao().getAllWithCountNow();
|
||||
runOnUiThread(() -> {
|
||||
PopupMenu popupMenu = new PopupMenu(this, b.targetDeviceDropDown);
|
||||
int index = 0;
|
||||
for (FeedbackMessageWithCount message: messageList) {
|
||||
popupMenu.getMenu().add(0, index, index, message.fromUserName+" - "+message.fromUser+" ("+message.messageCount+")");
|
||||
index++;
|
||||
}
|
||||
popupMenu.setOnMenuItemClickListener(item -> {
|
||||
b.targetDeviceDropDown.setText(item.getTitle());
|
||||
mChatView.getMessageView().removeAll();
|
||||
FeedbackMessageWithCount message = messageList.get(item.getItemId());
|
||||
deviceToSend = message.fromUser;
|
||||
nameToSend = message.fromUserName;
|
||||
AsyncTask.execute(() -> {
|
||||
List<FeedbackMessage> messageList2 = app.db.feedbackMessageDao().getAllByUserNow(deviceToSend);
|
||||
runOnUiThread(() -> {
|
||||
b.chatLayout.setVisibility(View.VISIBLE);
|
||||
b.inputLayout.setVisibility(View.GONE);
|
||||
for (FeedbackMessage message2 : messageList2) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(message2.sentTime);
|
||||
Message chatMessage = new Message.Builder()
|
||||
.setUser(message2.received ? new User(crc16(message2.fromUser.getBytes()), message2.fromUserName, BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_circle)) : user)
|
||||
.setRight(!message2.received)
|
||||
.setText(message2.text)
|
||||
.setSendTime(c)
|
||||
.hideIcon(!message2.received)
|
||||
.build();
|
||||
if (message2.received)
|
||||
mChatView.receive(chatMessage);
|
||||
else
|
||||
mChatView.send(chatMessage);
|
||||
}
|
||||
});
|
||||
});
|
||||
return false;
|
||||
});
|
||||
popupMenu.show();
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
else {
|
||||
AsyncTask.execute(() -> {
|
||||
List<FeedbackMessage> messageList = app.db.feedbackMessageDao().getAllNow();
|
||||
firstSend = messageList.size() == 0;
|
||||
runOnUiThread(() -> {
|
||||
if (firstSend) {
|
||||
openFaq();
|
||||
b.chatLayout.setVisibility(View.GONE);
|
||||
b.inputLayout.setVisibility(View.VISIBLE);
|
||||
b.sendButton.setOnClickListener((v -> {
|
||||
if (b.textInput.getText() == null || b.textInput.getText().length() == 0) {
|
||||
Toast.makeText(app, "Podaj treść wiadomości.", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
send(b.textInput.getText().toString());
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
/*new MaterialDialog.Builder(this)
|
||||
.title(R.string.faq)
|
||||
.content(R.string.faq_text)
|
||||
.positiveText(R.string.yes)
|
||||
.negativeText(R.string.no)
|
||||
.onPositive(((dialog, which) -> {
|
||||
openFaq();
|
||||
}))
|
||||
.show();*/
|
||||
b.chatLayout.setVisibility(View.VISIBLE);
|
||||
b.inputLayout.setVisibility(View.GONE);
|
||||
}
|
||||
for (FeedbackMessage message : messageList) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(message.sentTime);
|
||||
Message chatMessage = new Message.Builder()
|
||||
.setUser(message.fromUser != null ? new User(crc16(message.fromUser.getBytes()), message.fromUserName, BitmapFactory.decodeResource(getResources(), R.drawable.ic_account_circle)) : message.received ? dev : user)
|
||||
.setRight(!message.received)
|
||||
.setText(message.text)
|
||||
.setSendTime(c)
|
||||
.hideIcon(!message.received)
|
||||
.build();
|
||||
if (message.received)
|
||||
mChatView.receive(chatMessage);
|
||||
else
|
||||
mChatView.send(chatMessage);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//Click Send Button
|
||||
mChatView.setOnClickSendButtonListener(view -> {
|
||||
send(mChatView.getInputText());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) // Press Back Icon
|
||||
{
|
||||
finish();
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
registerReceiver(receiver, new IntentFilter("pl.szczodrzynski.edziennik.activities.FeedbackActivity"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
unregisterReceiver(receiver);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package pl.szczodrzynski.edziennik.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
|
||||
public class QrScannerActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
|
||||
private ZXingScannerView mScannerView;
|
||||
public static ZXingScannerView.ResultHandler resultHandler;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view
|
||||
setContentView(mScannerView);
|
||||
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
|
||||
mScannerView.startCamera(); // Start camera on resume
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
|
||||
mScannerView.startCamera(); // Start camera on resume
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mScannerView.stopCamera(); // Stop camera on pause
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case 1: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
mScannerView.startCamera();
|
||||
} else {
|
||||
// permission denied, boo! Disable the
|
||||
// functionality that depends on this permission.
|
||||
Toast.makeText(this, R.string.no_permissions, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
// other 'case' lines to check for other
|
||||
// permissions this app might request
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(Result rawResult) {
|
||||
if (resultHandler != null) {
|
||||
resultHandler.handleResult(rawResult);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
package pl.szczodrzynski.edziennik.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
|
||||
import com.danielstone.materialaboutlibrary.ConvenienceBuilder
|
||||
import com.danielstone.materialaboutlibrary.MaterialAboutActivity
|
||||
import com.danielstone.materialaboutlibrary.items.MaterialAboutActionItem
|
||||
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
|
||||
import com.danielstone.materialaboutlibrary.model.MaterialAboutList
|
||||
import com.danielstone.materialaboutlibrary.util.OpenSourceLicense
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
|
||||
class SettingsLicenseActivity : MaterialAboutActivity() {
|
||||
|
||||
var foregroundColor: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val app = application as App
|
||||
setTheme(Themes.appTheme)
|
||||
foregroundColor = Themes.getPrimaryTextColor(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
private fun createLicenseCard(
|
||||
context: Context,
|
||||
libraryTitle: CharSequence,
|
||||
copyrightYear: CharSequence,
|
||||
copyrightName: CharSequence,
|
||||
license: OpenSourceLicense,
|
||||
libraryUrl: String): MaterialAboutCard {
|
||||
val licenseItem = MaterialAboutActionItem.Builder()
|
||||
.icon(IconicsDrawable(this)
|
||||
.icon(CommunityMaterial.Icon.cmd_book)
|
||||
.colorInt(foregroundColor)
|
||||
.sizeDp(18))
|
||||
.setIconGravity(MaterialAboutActionItem.GRAVITY_TOP)
|
||||
.text(libraryTitle)
|
||||
.subText(String.format(getString(license.resourceId), copyrightYear, copyrightName))
|
||||
.setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(context, Uri.parse(libraryUrl)))
|
||||
.build()
|
||||
|
||||
return MaterialAboutCard.Builder().addItem(licenseItem).build()
|
||||
}
|
||||
|
||||
override fun getMaterialAboutList(context: Context): MaterialAboutList {
|
||||
|
||||
return MaterialAboutList(
|
||||
createLicenseCard(this,
|
||||
"OkHttp",
|
||||
"",
|
||||
"square",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/square/okhttp/"),
|
||||
createLicenseCard(this,
|
||||
"MHttp",
|
||||
"2018",
|
||||
"Mot.",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/motcwang/MHttp/"),
|
||||
createLicenseCard(this,
|
||||
"AgendaCalendarView",
|
||||
"2015",
|
||||
"Thibault Guégan",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/Tibolte/AgendaCalendarView/"),
|
||||
createLicenseCard(this,
|
||||
"Material Calendar View",
|
||||
"2017",
|
||||
"Applandeo sp. z o.o.",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/Applandeo/Material-Calendar-View/"),
|
||||
createLicenseCard(this,
|
||||
"Android-Job",
|
||||
"2007-2017",
|
||||
"Evernote Corporation",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/evernote/android-job/"),
|
||||
createLicenseCard(this,
|
||||
"Custom Activity On Crash",
|
||||
"",
|
||||
"Eduard Ereza MartĂnez (Ereza)",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/Ereza/CustomActivityOnCrash/"),
|
||||
createLicenseCard(this,
|
||||
"Android-Iconics",
|
||||
"2018",
|
||||
"Mike Penz",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/mikepenz/Android-Iconics/"),
|
||||
createLicenseCard(this,
|
||||
"MaterialDrawer",
|
||||
"2016",
|
||||
"Mike Penz",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/mikepenz/MaterialDrawer/"),
|
||||
createLicenseCard(this,
|
||||
"Material Dialogs",
|
||||
"2014-2016",
|
||||
"Aidan Michael Follestad",
|
||||
OpenSourceLicense.MIT,
|
||||
"https://github.com/afollestad/material-dialogs/"),
|
||||
createLicenseCard(this,
|
||||
"MaterialDateTimePicker",
|
||||
"2014",
|
||||
"Wouter Dullaert",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/wdullaer/MaterialDateTimePicker/"),
|
||||
createLicenseCard(this,
|
||||
"ColorPicker",
|
||||
"2016",
|
||||
"Jared Rummler, 2015 Daniel Nilsson",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/jaredrummler/ColorPicker/"),
|
||||
createLicenseCard(this,
|
||||
"material-about-library",
|
||||
"2016-2018",
|
||||
"Daniel Stone",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/daniel-stoneuk/material-about-library/"),
|
||||
createLicenseCard(this,
|
||||
"material-intro",
|
||||
"2017",
|
||||
"Jan Heinrich Reimer",
|
||||
OpenSourceLicense.MIT,
|
||||
"https://github.com/heinrichreimer/material-intro/"),
|
||||
createLicenseCard(this,
|
||||
"JsonViewer",
|
||||
"2017",
|
||||
"smuyyh",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/smuyyh/JsonViewer/"),
|
||||
createLicenseCard(this,
|
||||
"ShortcutBadger",
|
||||
"2014",
|
||||
"Leo Lin",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/leolin310148/ShortcutBadger/"),
|
||||
createLicenseCard(this,
|
||||
"Android Image Cropper",
|
||||
"2016",
|
||||
"Arthur Teplitzki, 2013 Edmodo, Inc.",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/ArthurHub/Android-Image-Cropper/"),
|
||||
createLicenseCard(this,
|
||||
"Material Tap Target Prompt",
|
||||
"2016-2018",
|
||||
"Samuel Wall",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/sjwall/MaterialTapTargetPrompt/"),
|
||||
createLicenseCard(this,
|
||||
"Android Swipe Layout",
|
||||
"2014",
|
||||
"代码家 (daimajia)",
|
||||
OpenSourceLicense.MIT,
|
||||
"https://github.com/daimajia/AndroidSwipeLayout/"),
|
||||
createLicenseCard(this,
|
||||
"barcodescanner (ZXing)",
|
||||
"2014",
|
||||
"Dushyanth Maguluru",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/dm77/barcodescanner/"),
|
||||
createLicenseCard(this,
|
||||
"CircularProgressIndicator",
|
||||
"2018",
|
||||
"Anton Kozyriatskyi",
|
||||
OpenSourceLicense.APACHE_2,
|
||||
"https://github.com/antonKozyriatskyi/CircularProgressIndicator/")
|
||||
|
||||
|
||||
/*createLicenseCard(this,
|
||||
"NoNonsense-FilePicker",
|
||||
"",
|
||||
"Jonas Kalderstam (spacecowboy)",
|
||||
OpenSourceLicense.GNU_GPL_3,
|
||||
"https://github.com/spacecowboy/NoNonsense-FilePicker/")*/
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
override fun getActivityTitle(): CharSequence? {
|
||||
return getString(R.string.settings_about_licenses_text)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,299 @@
|
||||
package pl.szczodrzynski.edziennik.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Gravity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView;
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityWebPushConfigBinding;
|
||||
import pl.szczodrzynski.edziennik.network.ServerRequest;
|
||||
import pl.szczodrzynski.edziennik.utils.Anim;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.App.APP_URL;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.REGISTRATION_ENABLED;
|
||||
|
||||
public class WebPushConfigActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler {
|
||||
private static final String TAG = "WebPushConfigActivity";
|
||||
private ZXingScannerView mScannerView;
|
||||
|
||||
ActivityWebPushConfigBinding b;
|
||||
|
||||
boolean cameraRunning = false;
|
||||
|
||||
private void showCamera() {
|
||||
cameraRunning = true;
|
||||
Anim.fadeIn(b.qrCodeScanner, 500, null);
|
||||
b.webPushConfig.setVisibility(View.GONE);
|
||||
b.qrCodeScanner.startCamera();
|
||||
b.qrCodeScanner.setResultHandler(this);
|
||||
}
|
||||
|
||||
private App app;
|
||||
|
||||
private void hideCamera() {
|
||||
cameraRunning = false;
|
||||
Anim.fadeOut(b.qrCodeScanner, 500, null);
|
||||
b.webPushConfig.setVisibility(View.VISIBLE);
|
||||
b.qrCodeScanner.stopCamera();
|
||||
}
|
||||
|
||||
private void getPairedBrowsers(@NonNull String newFcm, int removeId) {
|
||||
Anim.fadeIn(b.browserListProgressBar, 500, null);
|
||||
Anim.fadeOut(b.browserList, 500, null);
|
||||
Anim.fadeOut(b.browserListErrorText, 500, null);
|
||||
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?web_push_list"+(!newFcm.equals("") ? "&web_push_pair" : "") + (removeId != -1 ? "&web_push_unpair" : ""), "WebPushConfigActivity", app.profile)
|
||||
.setBodyParameter((removeId != -1 ? "id" : "browser_fcm"), (removeId != -1 ? Integer.toString(removeId) : newFcm))
|
||||
.run(((e, result) -> {
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
Anim.fadeOut(b.browserListProgressBar, 500, null);
|
||||
if (result == null || result.get("browser_count") == null) {
|
||||
b.browserListErrorText.setText(R.string.web_push_connection_error);
|
||||
Anim.fadeIn(b.browserListErrorText, 500, null);
|
||||
return;
|
||||
}
|
||||
if (result.get("browser_count").getAsInt() == 0) {
|
||||
b.browserListErrorText.setText(R.string.web_push_no_browsers);
|
||||
Anim.fadeIn(b.browserListErrorText, 500, null);
|
||||
if (app.appConfig.webPushEnabled) {
|
||||
app.appConfig.webPushEnabled = false;
|
||||
app.appConfig.savePending = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
b.browserList.removeAllViews();
|
||||
|
||||
LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
textViewParams.setMargins(0, 0, Utils.dpToPx(8), 0);
|
||||
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
LinearLayout.LayoutParams tableRowParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
JsonArray browsers = result.get("browsers").getAsJsonArray();
|
||||
for (JsonElement browserEl: browsers) {
|
||||
JsonObject browser = browserEl.getAsJsonObject();
|
||||
if (browser != null) {
|
||||
//Log.d(TAG, browser.toString());
|
||||
String browserDescription = "(error)";
|
||||
if (browser.get("description") != null) {
|
||||
browserDescription = browser.get("description").getAsString();
|
||||
}
|
||||
int browserId = -1;
|
||||
if (browser.get("id") != null) {
|
||||
browserId = browser.get("id").getAsInt();
|
||||
}
|
||||
|
||||
TableRow browserRow = new TableRow(this);
|
||||
browserRow.setLayoutParams(tableRowParams);
|
||||
|
||||
TextView browserDescriptionText = new TextView(this);
|
||||
//browserDescriptionText.setLayoutParams(textViewParams);
|
||||
browserDescriptionText.setText(browserDescription);
|
||||
browserDescriptionText.setGravity(Gravity.CENTER_VERTICAL);
|
||||
browserRow.addView(browserDescriptionText);
|
||||
|
||||
Button browserRemoveButton = new Button(this, null, android.R.attr.buttonStyleSmall);
|
||||
browserRemoveButton.setMinHeight(0);
|
||||
browserRemoveButton.setText(R.string.remove);
|
||||
int finalBrowserId = browserId;
|
||||
browserRemoveButton.setOnClickListener((v -> {
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.are_you_sure)
|
||||
.content(R.string.web_push_really_remove)
|
||||
.positiveText(R.string.yes)
|
||||
.negativeText(R.string.no)
|
||||
.onPositive(((dialog, which) -> getPairedBrowsers("", finalBrowserId)))
|
||||
.show();
|
||||
}));
|
||||
browserRow.addView(browserRemoveButton/*, buttonParams*/);
|
||||
|
||||
b.browserList.addView(browserRow);
|
||||
}
|
||||
}
|
||||
if (!app.appConfig.webPushEnabled) {
|
||||
app.appConfig.webPushEnabled = true;
|
||||
app.appConfig.savePending = true;
|
||||
}
|
||||
Anim.fadeIn(b.browserList, 500, null);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case 1: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
showCamera();
|
||||
} else {
|
||||
// permission denied, boo! Disable the
|
||||
// functionality that depends on this permission.
|
||||
Toast.makeText(this, R.string.no_permissions, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
// other 'case' lines to check for other
|
||||
// permissions this app might request
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
|
||||
app = (App) getApplicationContext();
|
||||
|
||||
getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
|
||||
|
||||
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_web_push_config, null, false);
|
||||
setContentView(b.getRoot());
|
||||
|
||||
Toolbar toolbar = b.toolbar;
|
||||
toolbar.setTitle(R.string.settings_notification_web_push);
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionbar = getSupportActionBar();
|
||||
actionbar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
mScannerView = b.qrCodeScanner;
|
||||
List<BarcodeFormat> formats = new ArrayList<>();
|
||||
formats.add(BarcodeFormat.QR_CODE);
|
||||
mScannerView.setFormats(formats);
|
||||
mScannerView.setAspectTolerance(0.5f);
|
||||
|
||||
b.webPushScanNewButton.setOnClickListener((v -> {
|
||||
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
showCamera();
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1);
|
||||
}
|
||||
}));
|
||||
|
||||
if (app.profile.getRegistration() != REGISTRATION_ENABLED) {
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.web_push_unavailable)
|
||||
.content(R.string.web_push_you_need_to_register)
|
||||
.positiveText(R.string.ok)
|
||||
.negativeText(R.string.what_is_this)
|
||||
.onPositive(((dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
finish();
|
||||
}))
|
||||
.onNegative(((dialog, which) -> {
|
||||
new MaterialDialog.Builder(this)
|
||||
.title(R.string.help)
|
||||
.content(R.string.help_notification_web_push)
|
||||
.positiveText(R.string.ok)
|
||||
.show();
|
||||
}))
|
||||
.dismissListener((dialog -> finish()))
|
||||
.autoDismiss(false)
|
||||
.canceledOnTouchOutside(false)
|
||||
.show();
|
||||
b.webPushScanNewButton.setEnabled(false);
|
||||
}
|
||||
else {
|
||||
getPairedBrowsers("", -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// Register ourselves as a handler for scan results.
|
||||
//mScannerView.startCamera(); // Start camera on resume
|
||||
//showCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
hideCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(Result rawResult) {
|
||||
// Do something with the result here
|
||||
//Log.v(TAG, rawResult.getText()); // Prints scan results
|
||||
//Log.v(TAG, rawResult.getBarcodeFormat().toString()); // Prints the scan format (qrcode, pdf417 etc.)
|
||||
|
||||
//Toast.makeText(this, rawResult.getText(), Toast.LENGTH_SHORT).show();
|
||||
|
||||
getPairedBrowsers(rawResult.getText(), -1);
|
||||
|
||||
/*Ion.with(app.getContext())
|
||||
.load(app.requestScheme + APP_URL + "main.php?web_push_pair")
|
||||
.setBodyParameter("username", (app.profile.autoRegistrationAllowed ? app.profile.registrationUsername : app.appConfig.deviceId))
|
||||
.setBodyParameter("app_version_build_type", BuildConfig.BUILD_TYPE)
|
||||
.setBodyParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE))
|
||||
.setBodyParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")")
|
||||
.setBodyParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
|
||||
.setBodyParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
|
||||
.setBodyParameter("device_os_version", Build.VERSION.RELEASE)
|
||||
.setBodyParameter("fcm_token", app.appConfig.fcmToken)
|
||||
.setBodyParameter("browser_fcm", rawResult.getText())
|
||||
.asJsonObject()
|
||||
.setCallback((e, result) -> {
|
||||
getPairedBrowsers(-1);
|
||||
});*/
|
||||
|
||||
hideCamera();
|
||||
|
||||
// If you would like to resume scanning, call this method below:
|
||||
//mScannerView.resumeCameraPreview(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
if (cameraRunning) {
|
||||
hideCamera();
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (cameraRunning) {
|
||||
hideCamera();
|
||||
return;
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.databinding.RowAnnouncementsItemBinding;
|
||||
import pl.szczodrzynski.edziennik.datamodels.AnnouncementFull;
|
||||
|
||||
public class AnnouncementsAdapter extends RecyclerView.Adapter<AnnouncementsAdapter.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
public List<AnnouncementFull> announcementList;
|
||||
public OnAnnouncementClickListener onClick;
|
||||
|
||||
public interface OnAnnouncementClickListener {
|
||||
void onClick(View v, AnnouncementFull announcement);
|
||||
}
|
||||
|
||||
public AnnouncementsAdapter(Context context, List<AnnouncementFull> announcementList, OnAnnouncementClickListener onClick) {
|
||||
//setHasStableIds(true);
|
||||
|
||||
this.context = context;
|
||||
this.announcementList = announcementList;
|
||||
this.onClick = onClick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return announcementList.size();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.row_announcements_item, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int groupPosition) {
|
||||
AnnouncementFull item = announcementList.get(groupPosition);
|
||||
RowAnnouncementsItemBinding b = holder.b;
|
||||
|
||||
b.announcementsItemCard.setOnClickListener((v -> {
|
||||
if (onClick != null) {
|
||||
onClick.onClick(v, item);
|
||||
}
|
||||
}));
|
||||
b.announcementsItemSender.setText(item.teacherFullName);
|
||||
b.announcementsItemTitle.setText(item.subject);
|
||||
b.announcementsItemText.setText(item.text);
|
||||
b.announcementsItemDate.setText(item.startDate.getFormattedString()+(item.endDate == null ? "" : " - "+item.endDate.getFormattedString()));
|
||||
if (!item.seen) {
|
||||
b.announcementsItemTitle.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
|
||||
b.announcementsItemTitle.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
b.announcementsItemSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||
b.announcementsItemTitle.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
|
||||
} else {
|
||||
b.announcementsItemSender.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
|
||||
b.announcementsItemTitle.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
|
||||
b.announcementsItemTitle.setBackground(null);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
RowAnnouncementsItemBinding b;
|
||||
|
||||
ViewHolder(RowAnnouncementsItemBinding b) {
|
||||
super(b.getRoot());
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.datamodels.AttendanceFull;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_ABSENT;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_ABSENT_EXCUSED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_BELATED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_BELATED_EXCUSED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_PRESENT;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_RELEASED;
|
||||
|
||||
public class AttendancesAdapter extends RecyclerView.Adapter<AttendancesAdapter.ViewHolder> {
|
||||
private Context context;
|
||||
public List<AttendanceFull> attendanceList;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public AttendancesAdapter(Context mCtx, List<AttendanceFull> noticeList) {
|
||||
this.context = mCtx;
|
||||
this.attendanceList = noticeList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
View view = inflater.inflate(R.layout.row_attendance_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
AttendanceFull attendance = attendanceList.get(position);
|
||||
|
||||
holder.attendanceLessonTopic.setText(attendance.lessonTopic);
|
||||
holder.attendanceTeacher.setText(attendance.teacherFullName);
|
||||
holder.attendanceSubject.setText(attendance.subjectLongName);
|
||||
holder.attendanceDate.setText(attendance.lessonDate.getStringDmy());
|
||||
holder.attendanceTime.setText(attendance.startTime.getStringHM());
|
||||
|
||||
switch (attendance.type) {
|
||||
case TYPE_ABSENT:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xfff44336, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText(R.string.attendance_absent);
|
||||
break;
|
||||
case TYPE_ABSENT_EXCUSED:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffaeea00, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText(R.string.attendance_absent_excused);
|
||||
break;
|
||||
case TYPE_BELATED:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffca28, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText(R.string.attendance_belated);
|
||||
break;
|
||||
case TYPE_BELATED_EXCUSED:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff4bb733, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText(R.string.attendance_belated_excused);
|
||||
break;
|
||||
case TYPE_RELEASED:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff9e9e9e, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText(R.string.attendance_released);
|
||||
break;
|
||||
case TYPE_PRESENT:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffae00, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText(R.string.attendance_present);
|
||||
break;
|
||||
default:
|
||||
holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff03a9f4, PorterDuff.Mode.MULTIPLY));
|
||||
holder.attendanceType.setText("?");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!attendance.seen) {
|
||||
holder.attendanceLessonTopic.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
|
||||
holder.attendanceLessonTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
attendance.seen = true;
|
||||
AsyncTask.execute(() -> {
|
||||
app.db.metadataDao().setSeen(App.profileId, attendance, true);
|
||||
//Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure");
|
||||
//context.sendBroadcast(i);
|
||||
});
|
||||
}
|
||||
else {
|
||||
holder.attendanceLessonTopic.setBackground(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return attendanceList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView attendanceType;
|
||||
TextView attendanceLessonTopic;
|
||||
TextView attendanceSubject;
|
||||
TextView attendanceTeacher;
|
||||
TextView attendanceDate;
|
||||
TextView attendanceTime;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
attendanceType = itemView.findViewById(R.id.attendanceType);
|
||||
attendanceLessonTopic = itemView.findViewById(R.id.attendanceLessonTopic);
|
||||
attendanceSubject = itemView.findViewById(R.id.attendanceSubject);
|
||||
attendanceTeacher = itemView.findViewById(R.id.attendanceTeacher);
|
||||
attendanceDate = itemView.findViewById(R.id.attendanceDate);
|
||||
attendanceTime = itemView.findViewById(R.id.attendanceTime);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mikepenz.iconics.view.IconicsImageView;
|
||||
import com.mikepenz.iconics.view.IconicsTextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Event;
|
||||
import pl.szczodrzynski.edziennik.datamodels.EventFull;
|
||||
import pl.szczodrzynski.edziennik.dialogs.EventListDialog;
|
||||
import pl.szczodrzynski.edziennik.dialogs.EventManualDialog;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
||||
|
||||
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
|
||||
private static final String TAG = "EventListAdapter";
|
||||
private Context context;
|
||||
private List<EventFull> examList;
|
||||
private EventListDialog parentDialog;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public EventListAdapter(Context mCtx, List<EventFull> examList, EventListDialog parentDialog) {
|
||||
this.context = mCtx;
|
||||
this.examList = examList;
|
||||
this.parentDialog = parentDialog;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public EventListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
View view = inflater.inflate(R.layout.row_dialog_event_list_item, parent, false);
|
||||
return new EventListAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull EventListAdapter.ViewHolder holder, int position) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
EventFull event = examList.get(position);
|
||||
|
||||
if (event.type == TYPE_HOMEWORK) {
|
||||
holder.eventListItemRoot.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.CLEAR));
|
||||
}
|
||||
else {
|
||||
holder.eventListItemRoot.getBackground().setColorFilter(new PorterDuffColorFilter(event.getColor(), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
|
||||
holder.eventListItemStartTime.setText(event.startTime == null ? app.getString(R.string.event_all_day) : event.startTime.getStringHM());
|
||||
//holder.examListItemEndTime.setText(event.endTime.getStringHM());
|
||||
holder.eventListItemTeamName.setText(Utils.bs(event.teamName));
|
||||
holder.eventListItemTeacherName.setText(bs(null, event.teacherFullName, "\n") + bs(event.subjectLongName));
|
||||
holder.eventListItemAddedDate.setText(Date.fromMillis(event.addedDate).getFormattedStringShort());
|
||||
holder.eventListItemType.setText(event.typeName);
|
||||
holder.eventListItemTopic.setText(event.topic);
|
||||
holder.eventListItemEdit.setVisibility((event.addedManually ? View.VISIBLE : View.GONE));
|
||||
holder.eventListItemHomework.setVisibility((event.type == Event.TYPE_HOMEWORK ? View.VISIBLE : View.GONE));
|
||||
holder.eventListItemEdit.setOnClickListener(v -> {
|
||||
if (parentDialog != null) {
|
||||
parentDialog.callDismissListener = false;
|
||||
parentDialog.dialog.dismiss();
|
||||
}
|
||||
d(TAG, "Event "+event);
|
||||
if (event.type == Event.TYPE_HOMEWORK) {
|
||||
new EventManualDialog(context, event.profileId)
|
||||
.withDismissListener(parentDialog::performDismiss)
|
||||
.show(app, event, null, null, EventManualDialog.DIALOG_HOMEWORK);
|
||||
}
|
||||
else {
|
||||
new EventManualDialog(context, event.profileId)
|
||||
.withDismissListener(parentDialog::performDismiss)
|
||||
.show(app, event, null, null, EventManualDialog.DIALOG_EVENT);
|
||||
}
|
||||
});
|
||||
|
||||
if (event.sharedBy == null) {
|
||||
holder.eventListItemSharedBy.setVisibility(View.GONE);
|
||||
}
|
||||
else if (event.sharedByName != null) {
|
||||
holder.eventListItemSharedBy.setText(app.getString(R.string.event_shared_by_format, (event.sharedBy.equals("self") ? app.getString(R.string.event_shared_by_self) : event.sharedByName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return examList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
ConstraintLayout eventListItemRoot;
|
||||
TextView eventListItemStartTime;
|
||||
TextView eventListItemTeacherName;
|
||||
TextView eventListItemType;
|
||||
TextView eventListItemTopic;
|
||||
TextView eventListItemAddedDate;
|
||||
TextView eventListItemTeamName;
|
||||
IconicsImageView eventListItemEdit;
|
||||
IconicsImageView eventListItemHomework;
|
||||
IconicsTextView eventListItemSharedBy;
|
||||
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
eventListItemRoot = itemView.findViewById(R.id.eventListItemRoot);
|
||||
eventListItemStartTime = itemView.findViewById(R.id.eventListItemStartTime);
|
||||
eventListItemTeacherName = itemView.findViewById(R.id.eventListItemTeacherName);
|
||||
eventListItemType = itemView.findViewById(R.id.eventListItemType);
|
||||
eventListItemTopic = itemView.findViewById(R.id.eventListItemTopic);
|
||||
eventListItemAddedDate = itemView.findViewById(R.id.eventListItemAddedDate);
|
||||
eventListItemTeamName = itemView.findViewById(R.id.eventListItemTeamName);
|
||||
eventListItemEdit = itemView.findViewById(R.id.eventListItemEdit);
|
||||
eventListItemHomework = itemView.findViewById(R.id.eventListItemHomework);
|
||||
eventListItemSharedBy = itemView.findViewById(R.id.eventListItemSharedBy);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package pl.szczodrzynski.edziennik.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Typeface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.daimajia.swipe.SwipeLayout
|
||||
import com.mikepenz.iconics.view.IconicsImageView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.fragments.GradesEditorFragment
|
||||
import pl.szczodrzynski.edziennik.fragments.GradesEditorFragment.Companion.modifyGradeChooser
|
||||
import pl.szczodrzynski.edziennik.utils.Colors.gradeNameToColor
|
||||
import java.text.DecimalFormat
|
||||
|
||||
class GradesEditorAdapter(
|
||||
private val mContext: Context,
|
||||
private val gradeList: List<GradesEditorFragment.EditorGrade>,
|
||||
private val listener: OnGradeActionListener
|
||||
) : RecyclerView.Adapter<GradesEditorAdapter.ViewHolder>() {
|
||||
|
||||
interface OnGradeActionListener {
|
||||
fun onClickRemove(gradeId: Long)
|
||||
fun onClickEdit(gradeId: Long)
|
||||
fun onClickAdd()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
//inflating and returning our view holder
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view = inflater.inflate(R.layout.row_grades_editor_item, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val app = mContext.applicationContext as App
|
||||
|
||||
val editorGrade = gradeList[position]
|
||||
|
||||
holder.gradesListRoot.setOnClickListener { holder.swipeLayout.toggle() }
|
||||
|
||||
val gradeColor = gradeNameToColor(editorGrade.name)
|
||||
|
||||
holder.gradesListName.text = editorGrade.name
|
||||
holder.gradesListName.isSelected = true
|
||||
holder.gradesListName.setTypeface(null, Typeface.BOLD)
|
||||
holder.gradesListName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.25) -0x1000000 else -0x1)
|
||||
holder.gradesListName.background.colorFilter = PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)
|
||||
holder.gradesListCategory.text = editorGrade.category
|
||||
if (editorGrade.weight < 0) {
|
||||
editorGrade.weight *= -1f
|
||||
}
|
||||
|
||||
if (editorGrade.weight == 0f) {
|
||||
holder.gradesListWeight.text = app.getString(R.string.grades_weight_not_counted)
|
||||
} else {
|
||||
holder.gradesListWeight.text = app.getString(R.string.grades_weight_format, DecimalFormat("0.##").format(editorGrade.weight.toDouble()))
|
||||
}
|
||||
|
||||
holder.gradesListValue.text = mContext.getString(R.string.grades_value_format, DecimalFormat("0.00").format(editorGrade.value.toDouble()))
|
||||
|
||||
|
||||
holder.swipeLayout.showMode = SwipeLayout.ShowMode.LayDown
|
||||
holder.swipeLayout.addDrag(SwipeLayout.DragEdge.Right, holder.bottomWrapper)
|
||||
holder.swipeLayout.addSwipeListener(object : SwipeLayout.SwipeListener {
|
||||
override fun onClose(layout: SwipeLayout) {
|
||||
//when the SurfaceView totally cover the BottomView.
|
||||
}
|
||||
|
||||
override fun onUpdate(layout: SwipeLayout, leftOffset: Int, topOffset: Int) {
|
||||
//you are swiping.
|
||||
}
|
||||
|
||||
override fun onStartOpen(layout: SwipeLayout) {
|
||||
|
||||
}
|
||||
|
||||
override fun onOpen(layout: SwipeLayout) {
|
||||
//when the BottomView totally show.
|
||||
}
|
||||
|
||||
override fun onStartClose(layout: SwipeLayout) {
|
||||
|
||||
}
|
||||
|
||||
override fun onHandRelease(layout: SwipeLayout, xvel: Float, yvel: Float) {
|
||||
//when user's hand released.
|
||||
}
|
||||
})
|
||||
|
||||
holder.buttonRemove.setOnClickListener { listener.onClickRemove(editorGrade.id) }
|
||||
|
||||
holder.buttonEdit.setOnClickListener { v -> modifyGradeChooser(v, editorGrade) { listener.onClickEdit(editorGrade.id) } }
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return gradeList.size
|
||||
}
|
||||
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
var gradesListRoot: ConstraintLayout = itemView.findViewById(R.id.gradesListRoot)
|
||||
var gradesListName: TextView = itemView.findViewById(R.id.gradesListName)
|
||||
var gradesListWeight: TextView = itemView.findViewById(R.id.gradesListWeight)
|
||||
var gradesListValue: TextView = itemView.findViewById(R.id.gradesListValue)
|
||||
var gradesListCategory: TextView = itemView.findViewById(R.id.gradesListCategory)
|
||||
var swipeLayout: SwipeLayout = itemView.findViewById(R.id.swipeLayout)
|
||||
var bottomWrapper: View = itemView.findViewById(R.id.bottom_wrapper)
|
||||
var buttonRemove: IconicsImageView = itemView.findViewById(R.id.buttonRemove)
|
||||
var buttonEdit: IconicsImageView = itemView.findViewById(R.id.buttonEdit)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,144 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Grade;
|
||||
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
|
||||
import pl.szczodrzynski.edziennik.dialogs.GradeDetailsDialog;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.COLOR_MODE_DEFAULT;
|
||||
|
||||
public class GradesListAdapter extends RecyclerView.Adapter<GradesListAdapter.ViewHolder> {
|
||||
private Context mContext;
|
||||
private List<GradeFull> gradeList;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public GradesListAdapter(Context mCtx, List<GradeFull> gradeList) {
|
||||
this.mContext = mCtx;
|
||||
this.gradeList = gradeList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GradesListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
View view = inflater.inflate(R.layout.row_grades_list_item, parent, false);
|
||||
return new GradesListAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull GradesListAdapter.ViewHolder holder, int position) {
|
||||
App app = (App) mContext.getApplicationContext();
|
||||
|
||||
GradeFull grade = gradeList.get(position);
|
||||
|
||||
holder.root.setOnClickListener((v -> {
|
||||
new GradeDetailsDialog(v.getContext(), App.profileId).show(app, grade);
|
||||
}));
|
||||
|
||||
int gradeColor;
|
||||
if (app.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
|
||||
gradeColor = grade.color;
|
||||
}
|
||||
else {
|
||||
gradeColor = Colors.gradeToColor(grade);
|
||||
}
|
||||
|
||||
holder.gradesListName.setText(grade.name);
|
||||
holder.gradesListName.setSelected(true);
|
||||
holder.gradesListName.setTypeface(null, Typeface.BOLD);
|
||||
holder.gradesListName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
|
||||
holder.gradesListName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
|
||||
|
||||
if (grade.description.trim().isEmpty()) {
|
||||
holder.gradesListDescription.setText(grade.category);
|
||||
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, "") : "");
|
||||
}
|
||||
else {
|
||||
holder.gradesListDescription.setText(grade.description);
|
||||
holder.gradesListCategory.setText(grade.isImprovement ? app.getString(R.string.grades_improvement_category_format, grade.category) : grade.category);
|
||||
}
|
||||
|
||||
DecimalFormat format = new DecimalFormat("#.##");
|
||||
DecimalFormat formatWithZeroes = new DecimalFormat("#.00");
|
||||
|
||||
if (grade.weight < 0) {
|
||||
grade.weight *= -1;
|
||||
}
|
||||
if (grade.type == Grade.TYPE_DESCRIPTIVE || grade.type == Grade.TYPE_TEXT || grade.type == Grade.TYPE_BEHAVIOUR) {
|
||||
holder.gradesListWeight.setVisibility(View.GONE);
|
||||
grade.weight = 0;
|
||||
}
|
||||
else {
|
||||
holder.gradesListWeight.setVisibility(View.VISIBLE);
|
||||
if (grade.type == Grade.TYPE_POINT) {
|
||||
holder.gradesListWeight.setText(app.getString(R.string.grades_max_points_format, format.format(grade.valueMax)));
|
||||
}
|
||||
else if (grade.weight == 0) {
|
||||
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_not_counted));
|
||||
}
|
||||
else {
|
||||
holder.gradesListWeight.setText(app.getString(R.string.grades_weight_format, format.format(grade.weight) + (grade.classAverage != -1 ? ", " + formatWithZeroes.format(grade.classAverage) : "")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
holder.gradesListTeacher.setText(grade.teacherFullName);
|
||||
holder.gradesListAddedDate.setText(Date.fromMillis(grade.addedDate).getFormattedStringShort());
|
||||
|
||||
if (!grade.seen) {
|
||||
holder.gradesListDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesListDescription.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesListDescription.setBackground(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return gradeList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
View root;
|
||||
TextView gradesListName;
|
||||
TextView gradesListDescription;
|
||||
TextView gradesListCategory;
|
||||
TextView gradesListWeight;
|
||||
TextView gradesListTeacher;
|
||||
TextView gradesListAddedDate;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
root = itemView.getRootView();
|
||||
gradesListName = itemView.findViewById(R.id.gradesListName);
|
||||
gradesListDescription = itemView.findViewById(R.id.gradesListCategoryColumn);
|
||||
gradesListCategory = itemView.findViewById(R.id.gradesListCategoryDescription);
|
||||
gradesListWeight = itemView.findViewById(R.id.gradesListWeight);
|
||||
gradesListTeacher = itemView.findViewById(R.id.gradesListTeacher);
|
||||
gradesListAddedDate = itemView.findViewById(R.id.gradesListAddedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,784 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.mikepenz.iconics.view.IconicsImageView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.datamodels.AppDb;
|
||||
import pl.szczodrzynski.edziennik.datamodels.GradeFull;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Subject;
|
||||
import pl.szczodrzynski.edziennik.models.ItemGradesSubjectModel;
|
||||
import pl.szczodrzynski.edziennik.utils.Anim;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import static pl.szczodrzynski.edziennik.MainActivity.TARGET_GRADES_EDITOR;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.COLOR_MODE_DEFAULT;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_AVG_2_SEM;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_SEM_2_AVG;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Profile.YEAR_1_SEM_2_SEM;
|
||||
|
||||
public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> implements View.OnClickListener {
|
||||
private static final String TAG = "GradesSubjectAdapter";
|
||||
private MainActivity activity;
|
||||
public List<ItemGradesSubjectModel> subjectList;
|
||||
|
||||
private void updateBadges(Context context, ItemGradesSubjectModel model) {
|
||||
// do not need this since we have an Observer for unread counters..
|
||||
//((App)getContext().getApplicationContext()).saveRegister(); // I don't like this.
|
||||
}
|
||||
|
||||
private boolean invalidAvg(float avg) {
|
||||
return avg == 0.0f || Float.isNaN(avg);
|
||||
}
|
||||
|
||||
private void updateSubjectSemesterBadges(ViewHolder holder, ItemGradesSubjectModel model) {
|
||||
if (model.semester1Unread > 0 || model.semester2Unread > 0) {
|
||||
holder.gradesSubjectTitle.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesSubjectTitle.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectTitle.setBackground(null);
|
||||
}
|
||||
if (model.semester1Unread > 0) {
|
||||
holder.gradesSubjectSemester1Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesSubjectSemester1Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Title.setBackground(null);
|
||||
}
|
||||
if (model.semester2Unread > 0) {
|
||||
holder.gradesSubjectSemester2Title.setBackground(getContext().getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.gradesSubjectSemester2Title.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Title.setBackground(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean gradesSetAsRead(ViewHolder holder, ItemGradesSubjectModel model, int semester) {
|
||||
boolean somethingChanged = false;
|
||||
AppDb db = AppDb.getDatabase(null);
|
||||
if (semester == 1) {
|
||||
model.semester1Unread = 0;
|
||||
for (GradeFull grade : model.grades1) {
|
||||
if (!grade.seen) {
|
||||
db.metadataDao().setSeen(App.profileId, grade, somethingChanged = true);
|
||||
}
|
||||
}
|
||||
if (model.semester1Proposed != null && !model.semester1Proposed.seen)
|
||||
db.metadataDao().setSeen(App.profileId, model.semester1Proposed, somethingChanged = true);
|
||||
if (model.semester1Final != null && !model.semester1Final.seen)
|
||||
db.metadataDao().setSeen(App.profileId, model.semester1Final, somethingChanged = true);
|
||||
}
|
||||
else if (semester == 2) {
|
||||
model.semester2Unread = 0;
|
||||
for (GradeFull grade : model.grades2) {
|
||||
if (!grade.seen) {
|
||||
db.metadataDao().setSeen(App.profileId, grade, somethingChanged = true);
|
||||
}
|
||||
}
|
||||
if (model.semester2Proposed != null && !model.semester2Proposed.seen)
|
||||
db.metadataDao().setSeen(App.profileId, model.semester2Proposed, somethingChanged = true);
|
||||
if (model.semester2Final != null && !model.semester2Final.seen)
|
||||
db.metadataDao().setSeen(App.profileId, model.semester2Final, somethingChanged = true);
|
||||
if (model.yearProposed != null && !model.yearProposed.seen)
|
||||
db.metadataDao().setSeen(App.profileId, model.yearProposed, somethingChanged = true);
|
||||
if (model.yearFinal != null && !model.yearFinal.seen)
|
||||
db.metadataDao().setSeen(App.profileId, model.yearFinal, somethingChanged = true);
|
||||
}
|
||||
if (somethingChanged) updateSubjectSemesterBadges(holder, model);
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
private void expandSubject(ViewHolder holder, ItemGradesSubjectModel model) {
|
||||
Anim.fadeOut(holder.gradesSubjectPreviewContainer, 200, new Animation.AnimationListener() {
|
||||
@Override public void onAnimationStart(Animation animation) { }
|
||||
@Override public void onAnimationRepeat(Animation animation) { }
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
boolean somethingChanged = false;
|
||||
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
|
||||
somethingChanged = gradesSetAsRead(holder, model, 1);
|
||||
}
|
||||
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
|
||||
somethingChanged = gradesSetAsRead(holder, model, 2);
|
||||
}
|
||||
if (somethingChanged) updateBadges(getContext(), model);
|
||||
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
|
||||
Anim.expand(holder.gradesSubjectContent, 500, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void collapseSubject(ViewHolder holder, ItemGradesSubjectModel model) {
|
||||
Anim.collapse(holder.gradesSubjectContent, 500, new Animation.AnimationListener() {
|
||||
@Override public void onAnimationStart(Animation animation) { }
|
||||
@Override public void onAnimationRepeat(Animation animation) { }
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
//holder.gradesSubjectPreviewContent.setVisibility(View.VISIBLE);
|
||||
Anim.fadeIn(holder.gradesSubjectPreviewContainer, 200, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class BuildGradeViews extends AsyncTask<Void, Void, Void> {
|
||||
boolean findViews;
|
||||
ItemGradesSubjectModel model;
|
||||
//ViewGroup parent;
|
||||
//int position;
|
||||
ViewHolder holder;
|
||||
|
||||
BuildGradeViews(ItemGradesSubjectModel model, ViewHolder holder, ViewGroup parent, int position, boolean findViews) {
|
||||
this.model = model;
|
||||
this.holder = holder;
|
||||
this.findViews = findViews;
|
||||
//this.parent = parent;
|
||||
//this.position = position;
|
||||
}
|
||||
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (this.findViews) {
|
||||
findViews(holder, holder.gradesSubjectRoot);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
DecimalFormat df = new DecimalFormat("#.00");
|
||||
if (this.findViews) {
|
||||
// TODO NIE WIEM CO TO ROBI XD
|
||||
//this.viewHolder.semestrTitle1.setText(C0193R.string.semestr1);
|
||||
//this.viewHolder.semestrTitle2.setText(C0193R.string.semestr2);
|
||||
/*if (GradesSubjectAdapter.this.columns == 0) {
|
||||
DisplayMetrics metrics = GradeListAdapterBySubject.this.context.getResources().getDisplayMetrics();
|
||||
if (GradeListAdapterBySubject.this.context.getResources().getBoolean(C0193R.bool.tablet)) {
|
||||
GradeListAdapterBySubject.this.columns = ((int) ((((float) (this.parent.getWidth() / 2)) / metrics.density) - 30.0f)) / 58;
|
||||
} else {
|
||||
GradeListAdapterBySubject.this.columns = ((int) ((((float) this.parent.getWidth()) / metrics.density) - 30.0f)) / 58;
|
||||
}
|
||||
}
|
||||
this.viewHolder.semestrGridView1.setColumnCount(GradeListAdapterBySubject.this.columns);
|
||||
this.viewHolder.semestrGridView2.setColumnCount(GradeListAdapterBySubject.this.columns);*/
|
||||
}
|
||||
if (model != null && model.subject != null && model.subject.id != holder.lastSubject) {
|
||||
holder.gradesSubjectRoot.setBackground(Colors.getAdaptiveBackgroundDrawable(model.subject.color, model.subject.color));
|
||||
updateSubjectSemesterBadges(holder, model);
|
||||
holder.gradesSubjectTitle.setText(model.subject.longName);
|
||||
holder.gradesSubjectPreviewContent.removeAllViews();
|
||||
|
||||
if (model.expandView) {
|
||||
// commented is without animations, do not use now (with unread badges)
|
||||
//holder.gradesSubjectContent.setVisibility(View.VISIBLE);
|
||||
//holder.gradesSubjectPreviewContent.setVisibility(View.INVISIBLE);
|
||||
expandSubject(holder, model);
|
||||
model.expandView = false;
|
||||
}
|
||||
int showSemester = model.profile.getCurrentSemester();
|
||||
|
||||
List<GradeFull> gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
|
||||
if (gradeList.size() == 0) {
|
||||
showSemester = (showSemester == 1 ? 2 : 1);
|
||||
gradeList = (showSemester == 1 ? model.grades1 : model.grades2);
|
||||
}
|
||||
|
||||
App app = (App) getContext().getApplicationContext();
|
||||
|
||||
float scale = getContext().getResources().getDisplayMetrics().density;
|
||||
int _5dp = (int) (5 * scale + 0.5f);
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
layoutParams.setMargins(0, 0, _5dp, 0);
|
||||
|
||||
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
|
||||
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.appConfig.miniDrawerVisible ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
|
||||
int totalWidthPx = 0;
|
||||
boolean ellipsized = false;
|
||||
|
||||
if (showSemester != model.profile.getCurrentSemester()) {
|
||||
// showing different semester, because of no grades in the selected one
|
||||
holder.gradesSubjectPreviewSemester.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewSemester.setText(getContext().getString(R.string.grades_semester_header_format, showSemester));
|
||||
// decrease the max preview width. DONE below
|
||||
/*holder.gradesSubjectPreviewSemester.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewSemester.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;*/
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewSemester.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
|
||||
if (model.grades1.size() > 0) {
|
||||
holder.gradesSubjectSemester1Nest.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester1Content.setHasFixedSize(false);
|
||||
holder.gradesSubjectSemester1Content.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester1Content.setLayoutManager(new LinearLayoutManager(activity));
|
||||
holder.gradesSubjectSemester1Content.setAdapter(new GradesListAdapter(activity, model.grades1));
|
||||
holder.gradesSubjectSemester1Header.setVisibility(View.VISIBLE);
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectSemester1Container.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester1Average.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1Average.setVisibility(View.VISIBLE);
|
||||
int formatSingle = (model.isPointSubject ? R.string.grades_average_single_percent_format : model.isBehaviourSubject ? R.string.grades_average_single_point_format : R.string.grades_average_single_format);
|
||||
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
|
||||
// PREVIEW AVERAGE
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewAverage.setText(
|
||||
getContext().getString(
|
||||
formatSingle,
|
||||
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
|
||||
)
|
||||
);
|
||||
}
|
||||
// AVERAGE value
|
||||
holder.gradesSubjectSemester1Average.setText(
|
||||
getContext().getString(
|
||||
format,
|
||||
1,
|
||||
invalidAvg(model.semester1Average) ? "-" : df.format(model.semester1Average)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PROPOSED grade
|
||||
if (model.semester1Proposed != null) {
|
||||
holder.gradesSubjectSemester1Proposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1Proposed.setText(model.semester1Proposed.name);
|
||||
holder.gradesSubjectSemester1Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectSemester1Proposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewProposed.setText(model.semester1Proposed.name);
|
||||
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester1Proposed.name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Proposed.setVisibility(View.GONE);
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// FINAL grade
|
||||
if (model.semester1Final != null) {
|
||||
holder.gradesSubjectSemester1Final.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1Final.setText(model.semester1Final.name);
|
||||
holder.gradesSubjectSemester1Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewFinal.setText(model.semester1Final.name);
|
||||
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester1Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Final.setVisibility(View.GONE);
|
||||
if (showSemester == 1) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1Header.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester1Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (model.grades2.size() > 0) {
|
||||
holder.gradesSubjectSemester2Nest.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester2Content.setHasFixedSize(false);
|
||||
holder.gradesSubjectSemester2Content.setNestedScrollingEnabled(false);
|
||||
holder.gradesSubjectSemester2Content.setLayoutManager(new LinearLayoutManager(activity));
|
||||
holder.gradesSubjectSemester2Content.setAdapter(new GradesListAdapter(activity, model.grades2));
|
||||
holder.gradesSubjectSemester2Header.setVisibility(View.VISIBLE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectSemester2Container.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester2Average.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewAverage.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2Average.setVisibility(View.VISIBLE);
|
||||
// PREVIEW AVERAGE
|
||||
int formatDouble = (model.isPointSubject ? R.string.grades_average_double_percent_format : model.isBehaviourSubject ? R.string.grades_average_double_point_format : R.string.grades_average_double_format);
|
||||
int format = (model.isPointSubject ? R.string.grades_semester_average_percent_format : model.isBehaviourSubject ? R.string.grades_semester_average_point_format : R.string.grades_semester_average_format);
|
||||
if (showSemester == 2) {
|
||||
if (model.semester2Proposed != null || model.semester2Final != null) {
|
||||
holder.gradesSubjectPreviewAverage.setText(invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average));
|
||||
holder.gradesSubjectPreviewYearAverage.setText(invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage));
|
||||
holder.gradesSubjectPreviewYearAverage.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewAverage.setText(
|
||||
getContext().getString(
|
||||
formatDouble,
|
||||
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average),
|
||||
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
|
||||
)
|
||||
);
|
||||
holder.gradesSubjectPreviewYearAverage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// AVERAGE value
|
||||
holder.gradesSubjectSemester2Average.setText(
|
||||
getContext().getString(
|
||||
format,
|
||||
2,
|
||||
invalidAvg(model.semester2Average) ? "-" : df.format(model.semester2Average)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PROPOSED grade
|
||||
if (model.semester2Proposed != null) {
|
||||
holder.gradesSubjectSemester2Proposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2Proposed.setText(model.semester2Proposed.name);
|
||||
holder.gradesSubjectSemester2Proposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectSemester2Proposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewProposed.setText(model.semester2Proposed.name);
|
||||
holder.gradesSubjectPreviewProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Proposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectPreviewProposed.setTextColor(Colors.gradeNameToColor(model.semester2Proposed.name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Proposed.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewProposed.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// FINAL grade
|
||||
if (model.semester2Final != null) {
|
||||
holder.gradesSubjectSemester2Final.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2Final.setText(model.semester2Final.name);
|
||||
holder.gradesSubjectSemester2Final.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewFinal.setText(model.semester2Final.name);
|
||||
holder.gradesSubjectPreviewFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.semester2Final.name), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Final.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewFinal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (model.isDescriptiveSubject && !model.isNormalSubject && !model.isPointSubject && !model.isBehaviourSubject) {
|
||||
holder.gradesSubjectYearAverage.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectYearAverage.setVisibility(View.VISIBLE);
|
||||
// AVERAGE value
|
||||
int format = (model.isPointSubject ? R.string.grades_year_average_percent_format : model.isBehaviourSubject ? R.string.grades_year_average_point_format : R.string.grades_year_average_format);
|
||||
holder.gradesSubjectYearAverage.setText(
|
||||
getContext().getString(
|
||||
format,
|
||||
invalidAvg(model.yearAverage) ? "-" : df.format(model.yearAverage)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// PROPOSED grade
|
||||
if (model.yearProposed != null) {
|
||||
holder.gradesSubjectYearProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectYearProposed.setText(model.yearProposed.name);
|
||||
holder.gradesSubjectYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearProposed.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewYearProposed.setText(model.yearProposed.name);
|
||||
holder.gradesSubjectPreviewYearProposed.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearProposed.name), PorterDuff.Mode.MULTIPLY));
|
||||
holder.gradesSubjectPreviewYearProposed.setTextColor(Colors.gradeNameToColor(model.yearProposed.name));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectYearProposed.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearProposed.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
// FINAL grade
|
||||
if (model.yearFinal != null) {
|
||||
holder.gradesSubjectYearFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectYearFinal.setText(model.yearFinal.name);
|
||||
holder.gradesSubjectYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearFinal.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectPreviewYearFinal.setText(model.yearFinal.name);
|
||||
holder.gradesSubjectPreviewYearFinal.getBackground().setColorFilter(new PorterDuffColorFilter(Colors.gradeNameToColor(model.yearFinal.name), PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectYearFinal.setVisibility(View.GONE);
|
||||
if (showSemester == 2) {
|
||||
holder.gradesSubjectPreviewYearFinal.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester2Header.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester2Container.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// decrease the width by average, proposed, final and semester TextViews
|
||||
holder.gradesSubjectPreviewContainer.measure(WRAP_CONTENT, MATCH_PARENT);
|
||||
//Log.d(TAG, "gradesSubjectPreviewContainer "+holder.gradesSubjectPreviewContainer.getMeasuredWidth());
|
||||
|
||||
/*holder.gradesSubjectPreviewAverage.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewAverage.getMeasuredWidth();
|
||||
maxWidthPx -= 2*_5dp;
|
||||
if (holder.gradesSubjectPreviewProposed.getVisibility() == View.VISIBLE) {
|
||||
holder.gradesSubjectPreviewProposed.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewProposed.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;
|
||||
}
|
||||
if (holder.gradesSubjectPreviewFinal.getVisibility() == View.VISIBLE) {
|
||||
holder.gradesSubjectPreviewFinal.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
maxWidthPx -= holder.gradesSubjectPreviewFinal.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;
|
||||
}*/
|
||||
maxWidthPx -= holder.gradesSubjectPreviewContainer.getMeasuredWidth();
|
||||
maxWidthPx -= _5dp;
|
||||
|
||||
for (GradeFull grade: gradeList) {
|
||||
if (grade.semester != showSemester)
|
||||
continue;
|
||||
|
||||
int gradeColor;
|
||||
if (model.profile.getGradeColorMode() == COLOR_MODE_DEFAULT) {
|
||||
gradeColor = grade.color;
|
||||
} else {
|
||||
gradeColor = Colors.gradeToColor(grade);
|
||||
}
|
||||
|
||||
TextView gradeName = new TextView(activity);
|
||||
gradeName.setText(grade.name);
|
||||
gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
|
||||
gradeName.setPadding(_5dp, 0, _5dp, 0);
|
||||
gradeName.setBackgroundResource(R.drawable.bg_rounded_4dp);
|
||||
gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));
|
||||
gradeName.setTypeface(null, Typeface.BOLD);
|
||||
|
||||
gradeName.measure(WRAP_CONTENT, WRAP_CONTENT);
|
||||
totalWidthPx += gradeName.getMeasuredWidth() + _5dp;
|
||||
//Log.d(TAG, "totalWidthPx " + totalWidthPx);
|
||||
if (totalWidthPx >= maxWidthPx) {
|
||||
if (ellipsized)
|
||||
continue;
|
||||
ellipsized = true;
|
||||
TextView ellipsisText = new TextView(activity);
|
||||
ellipsisText.setText(R.string.ellipsis);
|
||||
ellipsisText.setTextAppearance(activity, R.style.NavView_TextView);
|
||||
ellipsisText.setTypeface(null, Typeface.BOLD);
|
||||
ellipsisText.setPadding(0, 0, 0, 0);
|
||||
holder.gradesSubjectPreviewContent.addView(ellipsisText, layoutParams);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectPreviewContent.addView(gradeName, layoutParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (findViews) {
|
||||
//Log.d("GradesSubjectAdapter", "runOnUiThread");
|
||||
//this.viewHolder.gradesSubjectContent.setTag(View.VISIBLE);
|
||||
//this.viewHolder.gradesSubjectPreviewContainer.setTag(View.VISIBLE);
|
||||
activity.runOnUiThread(() -> {
|
||||
|
||||
holder.gradesSubjectRoot.setOnClickListener(v -> {
|
||||
if (holder.gradesSubjectContent.getVisibility() == View.GONE) {
|
||||
expandSubject(holder, model);
|
||||
}
|
||||
else {
|
||||
collapseSubject(holder, model);
|
||||
}
|
||||
});
|
||||
|
||||
holder.gradesSubjectSemester1Header.setOnClickListener(v -> {
|
||||
if (holder.gradesSubjectSemester1Container.getVisibility() == View.GONE) {
|
||||
if (gradesSetAsRead(holder, model, 1)) updateBadges(getContext(), model);
|
||||
|
||||
Anim.expand(holder.gradesSubjectSemester1Container, 500, null);
|
||||
if (holder.gradesSubjectSemester2Container.getVisibility() != View.GONE) {
|
||||
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
|
||||
}
|
||||
});
|
||||
holder.gradesSubjectSemester2Header.setOnClickListener(v -> {
|
||||
if (holder.gradesSubjectSemester2Container.getVisibility() == View.GONE) {
|
||||
if (gradesSetAsRead(holder, model, 2)) updateBadges(getContext(), model);
|
||||
|
||||
Anim.expand(holder.gradesSubjectSemester2Container, 500, null);
|
||||
if (holder.gradesSubjectSemester1Container.getVisibility() != View.GONE) {
|
||||
Anim.collapse(holder.gradesSubjectSemester1Container, 500, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Anim.collapse(holder.gradesSubjectSemester2Container, 500, null);
|
||||
}
|
||||
});
|
||||
|
||||
// hide the grade simulator when there are point, behaviour or descriptive grades
|
||||
if (model.isPointSubject || model.isBehaviourSubject || (model.isDescriptiveSubject && !model.isNormalSubject)) {
|
||||
holder.gradesSubjectSemester1EditButton.setVisibility(View.GONE);
|
||||
holder.gradesSubjectSemester2EditButton.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
holder.gradesSubjectSemester1EditButton.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester1EditButton.setOnClickListener(v -> {
|
||||
Bundle arguments = new Bundle();
|
||||
|
||||
if (model.subject != null) {
|
||||
arguments.putLong("subjectId", model.subject.id);
|
||||
}
|
||||
arguments.putInt("semester", 1);
|
||||
//d(TAG, "Model is " + model);
|
||||
switch (model.profile.getYearAverageMode()) {
|
||||
case YEAR_1_SEM_2_AVG:
|
||||
case YEAR_1_SEM_2_SEM:
|
||||
arguments.putInt("averageMode", -1);
|
||||
break;
|
||||
default:
|
||||
arguments.putInt("averageMode", model.semester2Final == null && model.profile.getYearAverageMode() == YEAR_1_AVG_2_SEM ? -1 : model.profile.getYearAverageMode());
|
||||
arguments.putFloat("yearAverageBefore", model.yearAverage);
|
||||
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester2);
|
||||
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester2);
|
||||
arguments.putFloat("averageOtherSemester", model.semester2Average);
|
||||
arguments.putFloat("finalOtherSemester", model.semester2Final == null ? -1 : model.semester2Final.value);
|
||||
break;
|
||||
}
|
||||
|
||||
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
|
||||
});
|
||||
holder.gradesSubjectSemester2EditButton.setVisibility(View.VISIBLE);
|
||||
holder.gradesSubjectSemester2EditButton.setOnClickListener(v -> {
|
||||
Bundle arguments = new Bundle();
|
||||
|
||||
if (model.subject != null) {
|
||||
arguments.putLong("subjectId", model.subject.id);
|
||||
}
|
||||
arguments.putInt("semester", 2);
|
||||
//d(TAG, "Model is " + model);
|
||||
switch (model.profile.getYearAverageMode()) {
|
||||
case YEAR_1_AVG_2_SEM:
|
||||
case YEAR_1_SEM_2_SEM:
|
||||
arguments.putInt("averageMode", -1);
|
||||
break;
|
||||
default:
|
||||
arguments.putInt("averageMode", model.semester1Final == null && model.profile.getYearAverageMode() == YEAR_1_SEM_2_AVG ? -1 : model.profile.getYearAverageMode());
|
||||
arguments.putFloat("yearAverageBefore", model.yearAverage);
|
||||
arguments.putFloat("gradeSumOtherSemester", model.gradeSumSemester1);
|
||||
arguments.putFloat("gradeCountOtherSemester", model.gradeCountSemester1);
|
||||
arguments.putFloat("averageOtherSemester", model.semester1Average);
|
||||
arguments.putFloat("finalOtherSemester", model.semester1Final == null ? -1 : model.semester1Final.value);
|
||||
break;
|
||||
}
|
||||
|
||||
activity.loadTarget(TARGET_GRADES_EDITOR, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
if (model != null && model.subject != null) {
|
||||
holder.lastSubject = model.subject.id;
|
||||
}
|
||||
super.onPostExecute(aVoid);
|
||||
}
|
||||
}
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public GradesSubjectAdapter(List<ItemGradesSubjectModel> data, MainActivity context) {
|
||||
super(context, R.layout.row_grades_subject_item, data);
|
||||
this.activity = context;
|
||||
this.subjectList = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int position = (Integer) v.getTag();
|
||||
Object object = getItem(position);
|
||||
ItemGradesSubjectModel dataModel = (ItemGradesSubjectModel)object;
|
||||
|
||||
}
|
||||
|
||||
private void findViews(ViewHolder holder, View root) {
|
||||
//holder.gradesSubjectRoot = root.findViewById(R.id.gradesSubjectRoot);
|
||||
holder.gradesSubjectTitle = root.findViewById(R.id.gradesSubjectTitle);
|
||||
holder.gradesSubjectExpandIndicator = root.findViewById(R.id.gradesSubjectExpandIndicator);
|
||||
|
||||
holder.gradesSubjectPreviewContainer = root.findViewById(R.id.gradesSubjectPreviewContainer);
|
||||
holder.gradesSubjectPreviewSemester = root.findViewById(R.id.gradesSubjectPreviewSemester);
|
||||
holder.gradesSubjectPreviewContent = root.findViewById(R.id.gradesSubjectPreviewContent);
|
||||
holder.gradesSubjectPreviewAverage = root.findViewById(R.id.gradesSubjectPreviewAverage);
|
||||
holder.gradesSubjectPreviewProposed = root.findViewById(R.id.gradesSubjectPreviewProposed);
|
||||
holder.gradesSubjectPreviewFinal = root.findViewById(R.id.gradesSubjectPreviewFinal);
|
||||
holder.gradesSubjectPreviewYearAverage = root.findViewById(R.id.gradesSubjectPreviewYearAverage);
|
||||
holder.gradesSubjectPreviewYearProposed = root.findViewById(R.id.gradesSubjectPreviewYearProposed);
|
||||
holder.gradesSubjectPreviewYearFinal = root.findViewById(R.id.gradesSubjectPreviewYearFinal);
|
||||
|
||||
holder.gradesSubjectContent = root.findViewById(R.id.gradesSubjectContent);
|
||||
|
||||
holder.gradesSubjectSemester1Header = root.findViewById(R.id.gradesSubjectSemester1Header);
|
||||
holder.gradesSubjectSemester1Title = root.findViewById(R.id.gradesSubjectSemester1Title);
|
||||
holder.gradesSubjectSemester1ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester1ExpandIndicator);
|
||||
holder.gradesSubjectSemester1Average = root.findViewById(R.id.gradesSubjectSemester1Average);
|
||||
holder.gradesSubjectSemester1Proposed = root.findViewById(R.id.gradesSubjectSemester1Proposed);
|
||||
holder.gradesSubjectSemester1Final = root.findViewById(R.id.gradesSubjectSemester1Final);
|
||||
holder.gradesSubjectSemester1EditButton = root.findViewById(R.id.gradesSubjectSemester1EditButton);
|
||||
holder.gradesSubjectSemester1Container = root.findViewById(R.id.gradesSubjectSemester1Container);
|
||||
holder.gradesSubjectSemester1Nest = root.findViewById(R.id.gradesSubjectSemester1Nest);
|
||||
holder.gradesSubjectSemester1Content = root.findViewById(R.id.gradesSubjectSemester1Content);
|
||||
|
||||
holder.gradesSubjectSemester2Header = root.findViewById(R.id.gradesSubjectSemester2Header);
|
||||
holder.gradesSubjectSemester2Title = root.findViewById(R.id.gradesSubjectSemester2Title);
|
||||
holder.gradesSubjectSemester2ExpandIndicator = root.findViewById(R.id.gradesSubjectSemester2ExpandIndicator);
|
||||
holder.gradesSubjectSemester2Average = root.findViewById(R.id.gradesSubjectSemester2Average);
|
||||
holder.gradesSubjectSemester2Proposed = root.findViewById(R.id.gradesSubjectSemester2Proposed);
|
||||
holder.gradesSubjectSemester2Final = root.findViewById(R.id.gradesSubjectSemester2Final);
|
||||
holder.gradesSubjectSemester2EditButton = root.findViewById(R.id.gradesSubjectSemester2EditButton);
|
||||
holder.gradesSubjectSemester2Container = root.findViewById(R.id.gradesSubjectSemester2Container);
|
||||
holder.gradesSubjectSemester2Nest = root.findViewById(R.id.gradesSubjectSemester2Nest);
|
||||
holder.gradesSubjectSemester2Content = root.findViewById(R.id.gradesSubjectSemester2Content);
|
||||
|
||||
holder.gradesSubjectYearAverage = root.findViewById(R.id.gradesSubjectYearAverage);
|
||||
holder.gradesSubjectYearProposed = root.findViewById(R.id.gradesSubjectYearProposed);
|
||||
holder.gradesSubjectYearFinal = root.findViewById(R.id.gradesSubjectYearFinal);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
ItemGradesSubjectModel model = subjectList.get(position);
|
||||
if (model == null) {
|
||||
//Toast.makeText(activity, "return convertView;", Toast.LENGTH_SHORT).show();
|
||||
return convertView;
|
||||
}
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
try {
|
||||
convertView = LayoutInflater.from(activity).inflate(R.layout.row_grades_subject_item, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.gradesSubjectRoot = convertView.findViewById(R.id.gradesSubjectRoot);
|
||||
convertView.setTag(holder);
|
||||
new BuildGradeViews(model, holder, parent, position, true).execute();
|
||||
return convertView;
|
||||
} catch (Exception e) {
|
||||
return new View(getContext());
|
||||
}
|
||||
}
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
Subject subject = model.subject;
|
||||
holder.gradesSubjectTitle.setText(subject != null ? subject.longName : "");
|
||||
/*if (model.getNotSeen() > 0) {
|
||||
viewHolder.notseen.setVisibility(0);
|
||||
viewHolder.notseen.setText(model.getNotSeen() + "");
|
||||
} else {
|
||||
viewHolder.notseen.setVisibility(8);
|
||||
}*/
|
||||
new BuildGradeViews(model, holder, parent, position, false).execute();
|
||||
return convertView;
|
||||
}
|
||||
|
||||
public int getViewTypeCount() {
|
||||
return getCount();
|
||||
}
|
||||
|
||||
public int getItemViewType(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
long lastSubject;
|
||||
|
||||
TextView gradesSubjectTitle;
|
||||
ConstraintLayout gradesSubjectRoot;
|
||||
IconicsImageView gradesSubjectExpandIndicator;
|
||||
|
||||
LinearLayout gradesSubjectPreviewContainer;
|
||||
TextView gradesSubjectPreviewSemester;
|
||||
LinearLayout gradesSubjectPreviewContent;
|
||||
TextView gradesSubjectPreviewAverage;
|
||||
TextView gradesSubjectPreviewProposed;
|
||||
TextView gradesSubjectPreviewFinal;
|
||||
TextView gradesSubjectPreviewYearAverage;
|
||||
TextView gradesSubjectPreviewYearProposed;
|
||||
TextView gradesSubjectPreviewYearFinal;
|
||||
|
||||
LinearLayout gradesSubjectContent;
|
||||
|
||||
LinearLayout gradesSubjectSemester1Header;
|
||||
TextView gradesSubjectSemester1Title;
|
||||
IconicsImageView gradesSubjectSemester1ExpandIndicator;
|
||||
TextView gradesSubjectSemester1Average;
|
||||
TextView gradesSubjectSemester1Proposed;
|
||||
TextView gradesSubjectSemester1Final;
|
||||
IconicsImageView gradesSubjectSemester1EditButton;
|
||||
LinearLayout gradesSubjectSemester1Container;
|
||||
NestedScrollView gradesSubjectSemester1Nest;
|
||||
RecyclerView gradesSubjectSemester1Content;
|
||||
|
||||
LinearLayout gradesSubjectSemester2Header;
|
||||
TextView gradesSubjectSemester2Title;
|
||||
IconicsImageView gradesSubjectSemester2ExpandIndicator;
|
||||
TextView gradesSubjectSemester2Average;
|
||||
TextView gradesSubjectSemester2Proposed;
|
||||
TextView gradesSubjectSemester2Final;
|
||||
IconicsImageView gradesSubjectSemester2EditButton;
|
||||
LinearLayout gradesSubjectSemester2Container;
|
||||
NestedScrollView gradesSubjectSemester2Nest;
|
||||
RecyclerView gradesSubjectSemester2Content;
|
||||
|
||||
TextView gradesSubjectYearAverage;
|
||||
TextView gradesSubjectYearProposed;
|
||||
TextView gradesSubjectYearFinal;
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.datamodels.EventFull;
|
||||
import pl.szczodrzynski.edziennik.dialogs.EventManualDialog;
|
||||
import pl.szczodrzynski.edziennik.fragments.HomeFragment;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
|
||||
public class HomeworksAdapter extends RecyclerView.Adapter<HomeworksAdapter.ViewHolder> {
|
||||
private Context context;
|
||||
private List<EventFull> homeworkList;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public HomeworksAdapter(Context mCtx, List<EventFull> homeworkList) {
|
||||
this.context = mCtx;
|
||||
this.homeworkList = homeworkList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
View view = inflater.inflate(R.layout.row_homeworks_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
public static String dayDiffString(Context context, int dayDiff) {
|
||||
if (dayDiff > 0) {
|
||||
if (dayDiff == 1) {
|
||||
return context.getString(R.string.tomorrow);
|
||||
}
|
||||
else if (dayDiff == 2) {
|
||||
return context.getString(R.string.the_day_after);
|
||||
}
|
||||
return HomeFragment.plural(context, R.plurals.time_till_days, Math.abs(dayDiff));
|
||||
}
|
||||
else if (dayDiff < 0) {
|
||||
if (dayDiff == -1) {
|
||||
return context.getString(R.string.yesterday);
|
||||
}
|
||||
else if (dayDiff == -2) {
|
||||
return context.getString(R.string.the_day_before);
|
||||
}
|
||||
return context.getString(R.string.ago_format, HomeFragment.plural(context, R.plurals.time_till_days, Math.abs(dayDiff)));
|
||||
}
|
||||
return context.getString(R.string.today);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
EventFull homework = homeworkList.get(position);
|
||||
|
||||
int diffDays = Date.diffDays(homework.eventDate, Date.getToday());
|
||||
|
||||
holder.homeworksItemHomeworkDate.setText(app.getString(R.string.date_relative_format, homework.eventDate.getFormattedString(), dayDiffString(context, diffDays)));
|
||||
holder.homeworksItemTopic.setText(homework.topic);
|
||||
holder.homeworksItemSubjectTeacher.setText(context.getString(R.string.homeworks_subject_teacher_format, bs(homework.subjectLongName), bs(homework.teacherFullName)));
|
||||
holder.homeworksItemTeamDate.setText(context.getString(R.string.homeworks_team_date_format, bs(homework.teamName), Date.fromMillis(homework.addedDate).getFormattedStringShort()));
|
||||
|
||||
if (!homework.seen) {
|
||||
holder.homeworksItemTopic.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
|
||||
holder.homeworksItemTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
homework.seen = true;
|
||||
AsyncTask.execute(() -> {
|
||||
app.db.metadataDao().setSeen(App.profileId, homework, true);
|
||||
});
|
||||
}
|
||||
else {
|
||||
holder.homeworksItemTopic.setBackground(null);
|
||||
}
|
||||
|
||||
holder.homeworksItemEdit.setVisibility((homework.addedManually ? View.VISIBLE : View.GONE));
|
||||
holder.homeworksItemEdit.setOnClickListener(v -> {
|
||||
new EventManualDialog(context).show(app, homework, null, null, EventManualDialog.DIALOG_HOMEWORK);
|
||||
});
|
||||
|
||||
if (homework.sharedBy == null) {
|
||||
holder.homeworksItemSharedBy.setVisibility(View.GONE);
|
||||
}
|
||||
else if (homework.sharedByName != null) {
|
||||
holder.homeworksItemSharedBy.setText(app.getString(R.string.event_shared_by_format, (homework.sharedBy.equals("self") ? app.getString(R.string.event_shared_by_self) : homework.sharedByName)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return homeworkList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView homeworksItemCard;
|
||||
TextView homeworksItemTopic;
|
||||
TextView homeworksItemHomeworkDate;
|
||||
TextView homeworksItemSharedBy;
|
||||
TextView homeworksItemSubjectTeacher;
|
||||
TextView homeworksItemTeamDate;
|
||||
Button homeworksItemEdit;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
homeworksItemCard = itemView.findViewById(R.id.homeworksItemCard);
|
||||
homeworksItemTopic = itemView.findViewById(R.id.homeworksItemTopic);
|
||||
homeworksItemHomeworkDate = itemView.findViewById(R.id.homeworksItemHomeworkDate);
|
||||
homeworksItemSharedBy = itemView.findViewById(R.id.homeworksItemSharedBy);
|
||||
homeworksItemSubjectTeacher = itemView.findViewById(R.id.homeworksItemSubjectTeacher);
|
||||
homeworksItemTeamDate = itemView.findViewById(R.id.homeworksItemTeamDate);
|
||||
homeworksItemEdit = itemView.findViewById(R.id.homeworksItemEdit);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package pl.szczodrzynski.edziennik.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.os.AsyncTask
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.colorRes
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.datamodels.Notice
|
||||
import pl.szczodrzynski.edziennik.datamodels.NoticeFull
|
||||
import pl.szczodrzynski.edziennik.models.Date
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.bs
|
||||
|
||||
class NoticesAdapter//getting the context and product list with constructor
|
||||
(private val context: Context, var noticeList: List<NoticeFull>) : RecyclerView.Adapter<NoticesAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
//inflating and returning our view holder
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.row_notices_item, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val app = context.applicationContext as App
|
||||
|
||||
val notice = noticeList[position]
|
||||
|
||||
if (app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK) {
|
||||
holder.noticesItemReason.text = bs(null, notice.category, "\n") + notice.text
|
||||
holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherFullName, if (notice.points > 0) "+" + notice.points else notice.points)
|
||||
} else {
|
||||
holder.noticesItemReason.text = notice.text
|
||||
holder.noticesItemTeacherName.text = notice.teacherFullName
|
||||
}
|
||||
holder.noticesItemAddedDate.text = Date.fromMillis(notice.addedDate).formattedString
|
||||
|
||||
if (notice.type == Notice.TYPE_POSITIVE) {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_plus_circle)
|
||||
.colorRes(R.color.md_green_600)
|
||||
.sizeDp(36))
|
||||
} else if (notice.type == Notice.TYPE_NEGATIVE) {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon.cmd_alert_decagram)
|
||||
.colorRes(R.color.md_red_600)
|
||||
.sizeDp(36))
|
||||
} else {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_message_outline)
|
||||
.colorRes(R.color.md_blue_500)
|
||||
.sizeDp(36))
|
||||
}
|
||||
|
||||
if (!notice.seen) {
|
||||
holder.noticesItemReason.background = context.resources.getDrawable(R.drawable.bg_rounded_8dp)
|
||||
when {
|
||||
notice.type == Notice.TYPE_POSITIVE -> holder.noticesItemReason.background.colorFilter = PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)
|
||||
notice.type == Notice.TYPE_NEGATIVE -> holder.noticesItemReason.background.colorFilter = PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)
|
||||
else -> holder.noticesItemReason.background.colorFilter = PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)
|
||||
}
|
||||
notice.seen = true
|
||||
AsyncTask.execute { app.db.metadataDao().setSeen(App.profileId, notice, true) }
|
||||
} else {
|
||||
holder.noticesItemReason.background = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return noticeList.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
var noticesItemCard: CardView = itemView.findViewById(R.id.noticesItemCard)
|
||||
var noticesItemType: ImageView = itemView.findViewById(R.id.noticesItemType)
|
||||
var noticesItemReason: TextView = itemView.findViewById(R.id.noticesItemReason)
|
||||
var noticesItemTeacherName: TextView = itemView.findViewById(R.id.noticesItemTeacherName)
|
||||
var noticesItemAddedDate: TextView = itemView.findViewById(R.id.noticesItemAddedDate)
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Notification;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
||||
|
||||
public class NotificationsAdapter extends RecyclerView.Adapter<NotificationsAdapter.ViewHolder> {
|
||||
private static final String TAG = "NotificationsAdapter";
|
||||
private Context context;
|
||||
private List<Notification> notificationList;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public NotificationsAdapter(Context mCtx, List<Notification> notificationList) {
|
||||
this.context = mCtx;
|
||||
this.notificationList = notificationList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
View view = inflater.inflate(R.layout.row_notifications_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
Notification notification = notificationList.get(position);
|
||||
|
||||
holder.notificationsItemDate.setText(Date.fromMillis(notification.addedDate).getFormattedString());
|
||||
holder.notificationsItemText.setText(notification.text);
|
||||
holder.notificationsItemTitle.setText(notification.title);
|
||||
holder.notificationsItemType.setText(Notification.stringType(context, notification.type));
|
||||
|
||||
holder.notificationsItemCard.setOnClickListener((v -> {
|
||||
Intent intent = new Intent("android.intent.action.MAIN");
|
||||
notification.fillIntent(intent);
|
||||
|
||||
d(TAG, "notification with item "+notification.redirectFragmentId+" extras "+(intent.getExtras() == null ? "null" : intent.getExtras().toString()));
|
||||
|
||||
//Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0));
|
||||
|
||||
if (notification.profileId != -1 && notification.profileId != app.profile.getId() && context instanceof Activity) {
|
||||
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
app.sendBroadcast(intent);
|
||||
}));
|
||||
|
||||
if (!notification.seen) {
|
||||
holder.notificationsItemText.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
|
||||
holder.notificationsItemText.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.notificationsItemText.setBackground(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return notificationList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView notificationsItemCard;
|
||||
TextView notificationsItemDate;
|
||||
TextView notificationsItemText;
|
||||
TextView notificationsItemTitle;
|
||||
TextView notificationsItemType;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
notificationsItemCard = itemView.findViewById(R.id.notificationsItemCard);
|
||||
notificationsItemDate = itemView.findViewById(R.id.notificationsItemDate);
|
||||
notificationsItemText = itemView.findViewById(R.id.notificationsItemText);
|
||||
notificationsItemTitle = itemView.findViewById(R.id.notificationsItemTitle);
|
||||
notificationsItemType = itemView.findViewById(R.id.notificationsItemType);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
package pl.szczodrzynski.edziennik.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.datamodels.EventFull;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LessonChange;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.dialogs.EventListDialog;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.SpannableHtmlTagHandler;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK;
|
||||
|
||||
public class TimetableAdapter extends RecyclerView.Adapter<TimetableAdapter.ViewHolder> {
|
||||
private static final String TAG = "TimetableAdapter";
|
||||
private Context context;
|
||||
private Date lessonDate;
|
||||
private List<LessonFull> lessonList;
|
||||
private List<EventFull> eventList;
|
||||
public boolean setAsRead = false;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public TimetableAdapter(Context mCtx, Date lessonDate, List<LessonFull> lessonList, List<EventFull> eventList) {
|
||||
this.context = mCtx;
|
||||
this.lessonDate = lessonDate;
|
||||
this.lessonList = lessonList;
|
||||
this.eventList = eventList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
View view = inflater.inflate(R.layout.row_timetable_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
LessonFull lesson = lessonList.get(position);
|
||||
holder.timetableItemStartTime.setText(lesson.startTime.getStringHM());
|
||||
holder.timetableItemEndTime.setText(lesson.endTime.getStringHM());
|
||||
holder.timetableItemSubjectName.setText(lesson.subjectLongName);
|
||||
holder.timetableItemClassroomName.setText(lesson.getClassroomName());
|
||||
holder.timetableItemTeamName.setText(lesson.getTeamName());
|
||||
holder.timetableItemTeacherName.setText(lesson.getTeacherFullName());
|
||||
|
||||
holder.timetableItemCard.setCardBackgroundColor(Utils.getAttr(context, R.attr.colorSurface));
|
||||
|
||||
if (lesson.changeId != 0)
|
||||
{
|
||||
if (!lesson.seen) {
|
||||
holder.timetableItemSubjectName.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_4dp));
|
||||
holder.timetableItemSubjectName.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setSeen(App.profileId, lesson, true));
|
||||
}
|
||||
if (lesson.changeType == LessonChange.TYPE_CANCELLED)
|
||||
{
|
||||
holder.timetableItemSubjectName.setTypeface(null, Typeface.NORMAL);
|
||||
holder.timetableItemSubjectChange.setVisibility(View.GONE);
|
||||
|
||||
holder.timetableItemSubjectName.setText(Html.fromHtml("<del>"+lesson.subjectLongName+"</del>", null, new SpannableHtmlTagHandler()));
|
||||
}
|
||||
else if (lesson.changeType == LessonChange.TYPE_CHANGE)
|
||||
{
|
||||
holder.timetableItemSubjectName.setTypeface(null, Typeface.BOLD_ITALIC);
|
||||
if (lesson.changedSubjectLongName()) {
|
||||
holder.timetableItemSubjectChange.setText(lesson.subjectLongName);
|
||||
holder.timetableItemSubjectName.setText(lesson.changeSubjectLongName);
|
||||
holder.timetableItemSubjectChange.setVisibility(View.VISIBLE);
|
||||
|
||||
holder.timetableItemSubjectChange.setText(Html.fromHtml("<del>"+lesson.subjectLongName+"</del>", null, new SpannableHtmlTagHandler()));
|
||||
}
|
||||
else {
|
||||
holder.timetableItemSubjectChange.setVisibility(View.GONE);
|
||||
}
|
||||
holder.timetableItemTeacherName.setText(lesson.getTeacherFullName(true));
|
||||
holder.timetableItemTeamName.setText(lesson.getTeamName(true));
|
||||
holder.timetableItemClassroomName.setText(lesson.getClassroomName(true));
|
||||
}
|
||||
else if (lesson.changeType == LessonChange.TYPE_ADDED)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
holder.timetableItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.CLEAR));
|
||||
|
||||
if (lesson.changeType == LessonChange.TYPE_CANCELLED) {
|
||||
holder.timetableItemCard.setCardBackgroundColor(Themes.INSTANCE.isDark() ? 0x60000000 : 0xffeeeeee);
|
||||
}
|
||||
else if (lesson.changeType == LessonChange.TYPE_CHANGE) {
|
||||
holder.timetableItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(Utils.getAttr(context, R.attr.colorPrimary), PorterDuff.Mode.SRC_ATOP));//.setBackgroundColor(App.getAttr(context, R.attr.cardBackgroundHighlight));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
holder.timetableItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.CLEAR));
|
||||
holder.timetableItemSubjectName.setBackground(null);
|
||||
holder.timetableItemSubjectName.setTypeface(null, Typeface.NORMAL);
|
||||
holder.timetableItemSubjectChange.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
int eventCount = 0;
|
||||
|
||||
for (EventFull event: eventList) {
|
||||
if (event.eventDate.getValue() == lessonDate.getValue()
|
||||
&& event.startTime != null
|
||||
&& event.startTime.getValue() == lesson.startTime.getValue()) {
|
||||
eventCount++;
|
||||
if (eventCount == 1) {
|
||||
holder.timetableItemEvent1.setVisibility(View.VISIBLE);
|
||||
if (event.type == TYPE_HOMEWORK)
|
||||
holder.timetableItemEvent1.setBackground(new IconicsDrawable(context).color(IconicsColor.colorRes(R.color.md_red_500)).size(IconicsSize.dp(10)).icon(CommunityMaterial.Icon2.cmd_home));
|
||||
else
|
||||
holder.timetableItemEvent1.setBackgroundColor(event.getColor());
|
||||
}
|
||||
else if (eventCount == 2) {
|
||||
holder.timetableItemEvent2.setVisibility(View.VISIBLE);
|
||||
if (event.type == TYPE_HOMEWORK)
|
||||
holder.timetableItemEvent2.setBackground(new IconicsDrawable(context).color(IconicsColor.colorRes(R.color.md_red_500)).size(IconicsSize.dp(10)).icon(CommunityMaterial.Icon2.cmd_home));
|
||||
else
|
||||
holder.timetableItemEvent2.setBackgroundColor(event.getColor());
|
||||
}
|
||||
else if (eventCount == 3) {
|
||||
holder.timetableItemEvent3.setVisibility(View.VISIBLE);
|
||||
if (event.type == TYPE_HOMEWORK)
|
||||
holder.timetableItemEvent3.setBackground(new IconicsDrawable(context).color(IconicsColor.colorRes(R.color.md_red_500)).size(IconicsSize.dp(10)).icon(CommunityMaterial.Icon2.cmd_home));
|
||||
else
|
||||
holder.timetableItemEvent3.setBackgroundColor(event.getColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
holder.timetableItemCard.setOnClickListener(v -> new EventListDialog(context).show(app, lessonDate, lesson.startTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return lessonList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView timetableItemCard;
|
||||
ConstraintLayout timetableItemLayout;
|
||||
TextView timetableItemStartTime;
|
||||
TextView timetableItemClassroomName;
|
||||
TextView timetableItemTeamName;
|
||||
TextView timetableItemSubjectChange;
|
||||
TextView timetableItemSubjectName;
|
||||
TextView timetableItemTeacherName;
|
||||
TextView timetableItemEndTime;
|
||||
View timetableItemEvent1;
|
||||
View timetableItemEvent2;
|
||||
View timetableItemEvent3;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
timetableItemCard = itemView.findViewById(R.id.timetableItemCard);
|
||||
timetableItemLayout = itemView.findViewById(R.id.timetableItemLayout);
|
||||
timetableItemStartTime = itemView.findViewById(R.id.timetableItemStartTime);
|
||||
timetableItemClassroomName = itemView.findViewById(R.id.timetableItemClassroomName);
|
||||
timetableItemTeamName = itemView.findViewById(R.id.timetableItemTeamName);
|
||||
timetableItemSubjectChange = itemView.findViewById(R.id.timetableItemSubjectChange);
|
||||
timetableItemSubjectName = itemView.findViewById(R.id.timetableItemSubjectName);
|
||||
timetableItemTeacherName = itemView.findViewById(R.id.noticesItemTeacherName);
|
||||
timetableItemEndTime = itemView.findViewById(R.id.timetableItemEndTime);
|
||||
timetableItemEvent1 = itemView.findViewById(R.id.timetableItemEvent1);
|
||||
timetableItemEvent2 = itemView.findViewById(R.id.timetableItemEvent2);
|
||||
timetableItemEvent3 = itemView.findViewById(R.id.timetableItemEvent3);
|
||||
}
|
||||
}
|
||||
}
|
321
app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java
Normal file
321
app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java
Normal file
@ -0,0 +1,321 @@
|
||||
package pl.szczodrzynski.edziennik.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import im.wangchao.mhttp.Request;
|
||||
import im.wangchao.mhttp.Response;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
|
||||
|
||||
public class AppError {
|
||||
public static final int CODE_OTHER = 0;
|
||||
public static final int CODE_OK = 1;
|
||||
public static final int CODE_NO_INTERNET = 10;
|
||||
public static final int CODE_SSL_ERROR = 13;
|
||||
public static final int CODE_ARCHIVED = 5;
|
||||
public static final int CODE_MAINTENANCE = 6;
|
||||
public static final int CODE_LOGIN_ERROR = 7;
|
||||
public static final int CODE_ACCOUNT_MISMATCH = 8;
|
||||
public static final int CODE_APP_SERVER_ERROR = 9;
|
||||
public static final int CODE_MULTIACCOUNT_SETUP = 12;
|
||||
public static final int CODE_TIMEOUT = 11;
|
||||
public static final int CODE_PROFILE_NOT_FOUND = 14;
|
||||
public static final int CODE_ATTACHMENT_NOT_AVAILABLE = 28;
|
||||
// user's fault
|
||||
public static final int CODE_INVALID_LOGIN = 2;
|
||||
public static final int CODE_INVALID_SERVER_ADDRESS = 21;
|
||||
public static final int CODE_INVALID_SCHOOL_NAME = 22;
|
||||
public static final int CODE_INVALID_DEVICE = 23;
|
||||
public static final int CODE_OLD_PASSWORD = 4;
|
||||
public static final int CODE_INVALID_TOKEN = 24;
|
||||
public static final int CODE_EXPIRED_TOKEN = 27;
|
||||
public static final int CODE_INVALID_SYMBOL = 25;
|
||||
public static final int CODE_INVALID_PIN = 26;
|
||||
public static final int CODE_LIBRUS_NOT_ACTIVATED = 29;
|
||||
public static final int CODE_SYNERGIA_NOT_ACTIVATED = 32;
|
||||
public static final int CODE_LIBRUS_DISCONNECTED = 31;
|
||||
public static final int CODE_PROFILE_ARCHIVED = 30;
|
||||
|
||||
public static final int CODE_INTERNAL_MISSING_DATA = 100;
|
||||
// internal errors - not for user's information.
|
||||
// these error codes are processed in API main classes
|
||||
public static final int CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120;
|
||||
public static final int CODE_INTERNAL_LIBRUS_ACCOUNT_410_ = 120;
|
||||
|
||||
public String TAG;
|
||||
public int line;
|
||||
public int errorCode;
|
||||
public String errorText;
|
||||
public Response response;
|
||||
public Request request;
|
||||
public Throwable throwable;
|
||||
public String apiResponse;
|
||||
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, Response response, Request request, Throwable throwable, String apiResponse) {
|
||||
this.TAG = TAG;
|
||||
this.line = line;
|
||||
this.errorCode = errorCode;
|
||||
this.errorText = errorText;
|
||||
this.response = response;
|
||||
this.request = request;
|
||||
this.throwable = throwable;
|
||||
this.apiResponse = apiResponse;
|
||||
}
|
||||
|
||||
public AppError(String TAG, int line, int errorCode) {
|
||||
this(TAG, line, errorCode, null, null, null, null, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Throwable throwable, String apiResponse) {
|
||||
this(TAG, line, errorCode, null, null, null, throwable, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Throwable throwable, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, null, null, throwable, apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, Response response, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, Response response, String apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, String apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, null, null, null, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, null, null, null, apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText) {
|
||||
this(TAG, line, errorCode, errorText, null, null, null, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, null, null, null, apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable, String apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, String apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse.toString());
|
||||
}
|
||||
|
||||
public String getDetails(Context context) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(stringErrorCode(context, errorCode, errorText)).append("\n");
|
||||
sb.append("(").append(stringErrorType(errorCode)).append("#").append(errorCode).append(")\n");
|
||||
sb.append("at ").append(TAG).append(":").append(line).append("\n");
|
||||
sb.append("\n");
|
||||
if (throwable == null)
|
||||
sb.append("Throwable is null");
|
||||
else
|
||||
sb.append(Log.getStackTraceString(throwable));
|
||||
sb.append("\n");
|
||||
sb.append(Build.MANUFACTURER).append(" ").append(Build.BRAND).append(" ").append(Build.MODEL).append(" ").append(Build.DEVICE).append("\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public interface GetApiResponseCallback {
|
||||
void onSuccess(String apiResponse);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param context a Context
|
||||
* @param apiResponseCallback a callback executed on a worker thread
|
||||
*/
|
||||
public void getApiResponse(Context context, GetApiResponseCallback apiResponseCallback) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Request:\n");
|
||||
if (request != null) {
|
||||
sb.append(request.method()).append(" ").append(request.url().toString()).append("\n");
|
||||
sb.append(request.headers().toString()).append("\n");
|
||||
sb.append("\n");
|
||||
sb.append(request.bodyToString()).append("\n\n");
|
||||
}
|
||||
else
|
||||
sb.append("null\n\n");
|
||||
|
||||
if (apiResponse == null && response != null)
|
||||
apiResponse = response.parserErrorBody;
|
||||
|
||||
sb.append("Response:\n");
|
||||
if (response != null) {
|
||||
sb.append(response.code()).append(" ").append(response.message()).append("\n");
|
||||
sb.append(response.headers().toString()).append("\n");
|
||||
sb.append("\n");
|
||||
if (apiResponse == null) {
|
||||
if (Thread.currentThread().getName().equals("main")) {
|
||||
AsyncTask.execute(() -> {
|
||||
if (response.raw().body() != null) {
|
||||
try {
|
||||
sb.append(response.raw().body().string());
|
||||
} catch (Exception e) {
|
||||
sb.append("Exception while getting response body:\n").append(Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append("null");
|
||||
}
|
||||
apiResponseCallback.onSuccess(sb.toString());
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (response.raw().body() != null) {
|
||||
try {
|
||||
sb.append(response.raw().body().string());
|
||||
} catch (Exception e) {
|
||||
sb.append("Exception while getting response body:\n").append(Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append("null");
|
||||
}
|
||||
apiResponseCallback.onSuccess(sb.toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
sb.append("null\n\n");
|
||||
|
||||
sb.append("API Response:\n");
|
||||
if (apiResponse != null) {
|
||||
sb.append(apiResponse).append("\n\n");
|
||||
}
|
||||
else {
|
||||
sb.append("null\n\n");
|
||||
}
|
||||
|
||||
apiResponseCallback.onSuccess(sb.toString());
|
||||
}
|
||||
|
||||
public AppError changeIfCodeOther() {
|
||||
if (errorCode != CODE_OTHER && errorCode != CODE_MAINTENANCE)
|
||||
return this;
|
||||
if (throwable instanceof UnknownHostException)
|
||||
errorCode = CODE_NO_INTERNET;
|
||||
else if (throwable instanceof SSLException)
|
||||
errorCode = CODE_SSL_ERROR;
|
||||
else if (throwable instanceof SocketTimeoutException)
|
||||
errorCode = CODE_TIMEOUT;
|
||||
else if (throwable instanceof InterruptedIOException)
|
||||
errorCode = CODE_NO_INTERNET;
|
||||
else if (response != null &&
|
||||
(response.code() == 424
|
||||
|| response.code() == 400
|
||||
|| response.code() == 401
|
||||
|| response.code() == 500
|
||||
|| response.code() == 503
|
||||
|| response.code() == 404))
|
||||
errorCode = CODE_MAINTENANCE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String asReadableString(Context context) {
|
||||
return stringErrorCode(context, errorCode, errorText) + (errorCode == CODE_MAINTENANCE && errorText != null && !errorText.isEmpty() ? " ("+errorText+")" : "");
|
||||
}
|
||||
|
||||
public static String stringErrorCode(Context context, int errorCode, String errorText)
|
||||
{
|
||||
switch (errorCode) {
|
||||
case CODE_OK:
|
||||
return context.getString(R.string.sync_error_ok);
|
||||
case CODE_INVALID_LOGIN:
|
||||
return context.getString(R.string.sync_error_invalid_login);
|
||||
case CODE_LOGIN_ERROR:
|
||||
return context.getString(R.string.sync_error_login_error);
|
||||
case CODE_INVALID_DEVICE:
|
||||
return context.getString(R.string.sync_error_invalid_device);
|
||||
case CODE_OLD_PASSWORD:
|
||||
return context.getString(R.string.sync_error_old_password);
|
||||
case CODE_ARCHIVED:
|
||||
return context.getString(R.string.sync_error_archived);
|
||||
case CODE_MAINTENANCE:
|
||||
return context.getString(R.string.sync_error_maintenance);
|
||||
case CODE_NO_INTERNET:
|
||||
return context.getString(R.string.sync_error_no_internet);
|
||||
case CODE_ACCOUNT_MISMATCH:
|
||||
return context.getString(R.string.sync_error_account_mismatch);
|
||||
case CODE_APP_SERVER_ERROR:
|
||||
return context.getString(R.string.sync_error_app_server);
|
||||
case CODE_TIMEOUT:
|
||||
return context.getString(R.string.sync_error_timeout);
|
||||
case CODE_SSL_ERROR:
|
||||
return context.getString(R.string.sync_error_ssl);
|
||||
case CODE_INVALID_SERVER_ADDRESS:
|
||||
return context.getString(R.string.sync_error_invalid_server_address);
|
||||
case CODE_INVALID_SCHOOL_NAME:
|
||||
return context.getString(R.string.sync_error_invalid_school_name);
|
||||
case CODE_PROFILE_NOT_FOUND:
|
||||
return context.getString(R.string.sync_error_profile_not_found);
|
||||
case CODE_INVALID_TOKEN:
|
||||
return context.getString(R.string.sync_error_invalid_token);
|
||||
case CODE_ATTACHMENT_NOT_AVAILABLE:
|
||||
return context.getString(R.string.sync_error_attachment_not_available);
|
||||
case CODE_LIBRUS_NOT_ACTIVATED:
|
||||
return context.getString(R.string.sync_error_librus_not_activated);
|
||||
case CODE_PROFILE_ARCHIVED:
|
||||
return context.getString(R.string.sync_error_profile_archived);
|
||||
case CODE_LIBRUS_DISCONNECTED:
|
||||
return context.getString(R.string.sync_error_librus_disconnected);
|
||||
case CODE_SYNERGIA_NOT_ACTIVATED:
|
||||
return context.getString(R.string.sync_error_synergia_not_activated);
|
||||
default:
|
||||
case CODE_MULTIACCOUNT_SETUP:
|
||||
case CODE_OTHER:
|
||||
return errorText != null ? errorText : context.getString(R.string.sync_error_unknown);
|
||||
}
|
||||
}
|
||||
public static String stringErrorType(int errorCode)
|
||||
{
|
||||
switch (errorCode) {
|
||||
default:
|
||||
case CODE_OTHER: return "CODE_OTHER";
|
||||
case CODE_OK: return "CODE_OK";
|
||||
case CODE_NO_INTERNET: return "CODE_NO_INTERNET";
|
||||
case CODE_SSL_ERROR: return "CODE_SSL_ERROR";
|
||||
case CODE_ARCHIVED: return "CODE_ARCHIVED";
|
||||
case CODE_MAINTENANCE: return "CODE_MAINTENANCE";
|
||||
case CODE_LOGIN_ERROR: return "CODE_LOGIN_ERROR";
|
||||
case CODE_ACCOUNT_MISMATCH: return "CODE_ACCOUNT_MISMATCH";
|
||||
case CODE_APP_SERVER_ERROR: return "CODE_APP_SERVER_ERROR";
|
||||
case CODE_MULTIACCOUNT_SETUP: return "CODE_MULTIACCOUNT_SETUP";
|
||||
case CODE_TIMEOUT: return "CODE_TIMEOUT";
|
||||
case CODE_PROFILE_NOT_FOUND: return "CODE_PROFILE_NOT_FOUND";
|
||||
case CODE_INVALID_LOGIN: return "CODE_INVALID_LOGIN";
|
||||
case CODE_INVALID_SERVER_ADDRESS: return "CODE_INVALID_SERVER_ADDRESS";
|
||||
case CODE_INVALID_SCHOOL_NAME: return "CODE_INVALID_SCHOOL_NAME";
|
||||
case CODE_INVALID_DEVICE: return "CODE_INVALID_DEVICE";
|
||||
case CODE_OLD_PASSWORD: return "CODE_OLD_PASSWORD";
|
||||
case CODE_INVALID_TOKEN: return "CODE_INVALID_TOKEN";
|
||||
case CODE_EXPIRED_TOKEN: return "CODE_EXPIRED_TOKEN";
|
||||
case CODE_INVALID_SYMBOL: return "CODE_INVALID_SYMBOL";
|
||||
case CODE_INVALID_PIN: return "CODE_INVALID_PIN";
|
||||
case CODE_ATTACHMENT_NOT_AVAILABLE: return "CODE_ATTACHMENT_NOT_AVAILABLE";
|
||||
case CODE_LIBRUS_NOT_ACTIVATED: return "CODE_LIBRUS_NOT_ACTIVATED";
|
||||
case CODE_PROFILE_ARCHIVED: return "CODE_PROFILE_ARCHIVED";
|
||||
case CODE_LIBRUS_DISCONNECTED: return "CODE_LIBRUS_DISCONNECTED";
|
||||
case CODE_SYNERGIA_NOT_ACTIVATED: return "CODE_SYNERGIA_NOT_ACTIVATED";
|
||||
}
|
||||
}
|
||||
}
|
1171
app/src/main/java/pl/szczodrzynski/edziennik/api/Edziennik.java
Normal file
1171
app/src/main/java/pl/szczodrzynski/edziennik/api/Edziennik.java
Normal file
File diff suppressed because it is too large
Load Diff
1704
app/src/main/java/pl/szczodrzynski/edziennik/api/Iuczniowie.java
Normal file
1704
app/src/main/java/pl/szczodrzynski/edziennik/api/Iuczniowie.java
Normal file
File diff suppressed because it is too large
Load Diff
3667
app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java
Normal file
3667
app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java
Normal file
File diff suppressed because it is too large
Load Diff
2426
app/src/main/java/pl/szczodrzynski/edziennik/api/Mobidziennik.java
Normal file
2426
app/src/main/java/pl/szczodrzynski/edziennik/api/Mobidziennik.java
Normal file
File diff suppressed because it is too large
Load Diff
1676
app/src/main/java/pl/szczodrzynski/edziennik/api/Vulcan.java
Normal file
1676
app/src/main/java/pl/szczodrzynski/edziennik/api/Vulcan.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import im.wangchao.mhttp.Request;
|
||||
|
||||
/**
|
||||
* Callback containing a {@link Request.Builder} which has correct headers and body to download a corresponding message attachment when ran.
|
||||
* {@code onSuccess} has to be ran on the UI thread.
|
||||
*/
|
||||
public interface AttachmentGetCallback {
|
||||
void onSuccess(Request.Builder builder);
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Message;
|
||||
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile;
|
||||
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
|
||||
import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo;
|
||||
import pl.szczodrzynski.edziennik.models.Endpoint;
|
||||
|
||||
public interface EdziennikInterface {
|
||||
|
||||
/**
|
||||
* Sync all Edziennik data.
|
||||
* Ran always on worker thread.
|
||||
*
|
||||
* @param activityContext a {@link Context}, used for resource extractions, passed back to {@link SyncCallback}
|
||||
* @param callback ran on worker thread.
|
||||
* @param profileId
|
||||
* @param profile
|
||||
* @param loginStore
|
||||
*/
|
||||
void sync(@NonNull Context activityContext, @NonNull SyncCallback callback, int profileId, @Nullable Profile profile, @NonNull LoginStore loginStore);
|
||||
void syncMessages(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile);
|
||||
void syncFeature(@NonNull Context activityContext, @NonNull SyncCallback callback, @NonNull ProfileFull profile, int ... featureList);
|
||||
|
||||
int FEATURE_ALL = 0;
|
||||
int FEATURE_TIMETABLE = 1;
|
||||
int FEATURE_AGENDA = 2;
|
||||
int FEATURE_GRADES = 3;
|
||||
int FEATURE_HOMEWORKS = 4;
|
||||
int FEATURE_NOTICES = 5;
|
||||
int FEATURE_ATTENDANCES = 6;
|
||||
int FEATURE_MESSAGES_INBOX = 7;
|
||||
int FEATURE_MESSAGES_OUTBOX = 8;
|
||||
int FEATURE_ANNOUNCEMENTS = 9;
|
||||
|
||||
/**
|
||||
* Download a single message or get its recipient list if it's already downloaded.
|
||||
*
|
||||
* May be executed on any thread.
|
||||
*
|
||||
* @param activityContext
|
||||
* @param errorCallback used for error reporting. Ran on a background thread.
|
||||
* @param profile
|
||||
* @param message a message of which body and recipient list should be downloaded.
|
||||
* @param messageCallback always executed on UI thread.
|
||||
*/
|
||||
void getMessage(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, @NonNull MessageFull message, @NonNull MessageGetCallback messageCallback);
|
||||
void getAttachment(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, @NonNull MessageFull message, long attachmentId, @NonNull AttachmentGetCallback attachmentCallback);
|
||||
//void getMessageList(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, int type, @NonNull MessageListCallback messageCallback);
|
||||
/**
|
||||
* Download a list of available message recipients.
|
||||
*
|
||||
* Updates a database-saved {@code teacherList} with {@code loginId}s.
|
||||
*
|
||||
* A {@link pl.szczodrzynski.edziennik.datamodels.Teacher} is considered as a recipient when its {@code loginId} is not null.
|
||||
*
|
||||
* May be executed on any thread.
|
||||
*
|
||||
* @param activityContext
|
||||
* @param errorCallback used for error reporting. Ran on a background thread.
|
||||
* @param profile
|
||||
* @param recipientListGetCallback always executed on UI thread.
|
||||
*/
|
||||
void getRecipientList(@NonNull Context activityContext, @NonNull SyncCallback errorCallback, @NonNull ProfileFull profile, @NonNull RecipientListGetCallback recipientListGetCallback);
|
||||
MessagesComposeInfo getComposeInfo(@NonNull ProfileFull profile);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param profile a {@link Profile} containing already changed endpoints
|
||||
* @return a map of configurable {@link Endpoint}s along with their names, {@code null} when unsupported
|
||||
*/
|
||||
Map<String, Endpoint> getConfigurableEndpoints(Profile profile);
|
||||
|
||||
/**
|
||||
* Check if the specified endpoint is enabled for the current profile.
|
||||
*
|
||||
* @param profile a {@link Profile} containing already changed endpoints
|
||||
* @param defaultActive if the endpoint is enabled by default.
|
||||
* @param name the endpoint's name
|
||||
* @return {@code true} if the endpoint is enabled, {@code false} when it's not. Return {@code defaultActive} if unsupported.
|
||||
*/
|
||||
boolean isEndpointEnabled(Profile profile, boolean defaultActive, String name);
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.AppError;
|
||||
|
||||
public interface ErrorCallback {
|
||||
void onError(Context activityContext, @NonNull AppError error);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
public interface LoginCallback {
|
||||
void onSuccess();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.Message;
|
||||
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
|
||||
|
||||
/**
|
||||
* Callback containing a {@link MessageFull} which already has its {@code body} and {@code recipients}.
|
||||
* {@code onSuccess} is always ran on the UI thread.
|
||||
*/
|
||||
public interface MessageGetCallback {
|
||||
void onSuccess(MessageFull message);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.MessageFull;
|
||||
|
||||
public interface MessageListCallback {
|
||||
void onSuccess(List<MessageFull> messageList);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
public interface ProgressCallback extends ErrorCallback {
|
||||
void onProgress(int progressStep);
|
||||
void onActionStarted(@StringRes int stringResId);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.datamodels.Teacher;
|
||||
|
||||
public interface RecipientListGetCallback {
|
||||
void onSuccess(List<Teacher> teacherList);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package pl.szczodrzynski.edziennik.api.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.AppError;
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore;
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile;
|
||||
import pl.szczodrzynski.edziennik.datamodels.ProfileFull;
|
||||
|
||||
/**
|
||||
* A callback used for error reporting, progress information.
|
||||
* All the methods are always ran on a worker thread.
|
||||
*/
|
||||
public interface SyncCallback extends ProgressCallback {
|
||||
void onLoginFirst(List<Profile> profileList, LoginStore loginStore);
|
||||
void onSuccess(Context activityContext, ProfileFull profileFull);
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.AppError
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
|
||||
data class ApiLoginResult(val loginStore: LoginStore, val error: AppError?)
|
@ -0,0 +1,32 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
|
||||
const val FEATURE_ALL = 0
|
||||
const val FEATURE_TIMETABLE = 1
|
||||
const val FEATURE_AGENDA = 2
|
||||
const val FEATURE_GRADES = 3
|
||||
const val FEATURE_HOMEWORKS = 4
|
||||
const val FEATURE_NOTICES = 5
|
||||
const val FEATURE_ATTENDANCES = 6
|
||||
const val FEATURE_MESSAGES_INBOX = 7
|
||||
const val FEATURE_MESSAGES_OUTBOX = 8
|
||||
const val FEATURE_ANNOUNCEMENTS = 9
|
||||
|
||||
const val LOGIN_TYPE_MOBIDZIENNIK = 1
|
||||
const val LOGIN_TYPE_LIBRUS = 2
|
||||
const val LOGIN_TYPE_IUCZNIOWIE = 3
|
||||
const val LOGIN_TYPE_VULCAN = 4
|
||||
const val LOGIN_TYPE_DEMO = 20
|
||||
|
||||
const val LOGIN_MODE_LIBRUS_EMAIL = 0
|
||||
const val LOGIN_MODE_LIBRUS_SYNERGIA = 1
|
||||
const val LOGIN_MODE_LIBRUS_JST = 2
|
||||
|
||||
const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp"
|
||||
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv"
|
||||
const val LIBRUS_REDIRECT_URL = "http://localhost/bar"
|
||||
const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?client_id=$LIBRUS_CLIENT_ID&redirect_uri=$LIBRUS_REDIRECT_URL&response_type=code"
|
||||
const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action"
|
||||
const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token"
|
||||
|
||||
const val LIBRUS_ACCOUNT_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts/fresh/" // + login
|
||||
const val LIBRUS_ACCOUNTS_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts"
|
@ -0,0 +1,6 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.AppError
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
|
||||
data class FirstLoginResult(val profileList: ArrayList<Profile>, val error: AppError?)
|
@ -0,0 +1,201 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus
|
||||
|
||||
import android.content.Context
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.AppError
|
||||
import pl.szczodrzynski.edziennik.api.AppError.*
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_JST
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_SYNERGIA
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginJst
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.MessageFull
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
import pl.szczodrzynski.edziennik.datamodels.ProfileFull
|
||||
import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo
|
||||
import pl.szczodrzynski.edziennik.models.Endpoint
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.lang.Exception
|
||||
|
||||
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface {
|
||||
private val TAG = "librus.Librus"
|
||||
|
||||
lateinit var syncCallback: SyncCallback
|
||||
lateinit var featureList: ArrayList<Int>
|
||||
lateinit var dataStore: DataStore
|
||||
var onLogin: (() -> Unit)? = null
|
||||
val internalErrorList = ArrayList<Int>()
|
||||
|
||||
fun isError(error: AppError?): Boolean {
|
||||
if (error == null)
|
||||
return false
|
||||
syncCallback.onError(null, error)
|
||||
return true
|
||||
}
|
||||
|
||||
/* _ _ _
|
||||
| | (_) |
|
||||
| | _| |__ _ __ _ _ ___
|
||||
| | | | '_ \| '__| | | / __|
|
||||
| |____| | |_) | | | |_| \__ \
|
||||
|______|_|_.__/|_| \__,_|__*/
|
||||
private fun loginLibrus() {
|
||||
LoginLibrus(app, loginStore, syncCallback) {
|
||||
if (profile == null) {
|
||||
firstLoginLibrus()
|
||||
return@LoginLibrus
|
||||
}
|
||||
synergiaTokenExtractor()
|
||||
}
|
||||
}
|
||||
private fun firstLoginLibrus() {
|
||||
FirstLoginLibrus(app, loginStore, syncCallback) { profileList ->
|
||||
syncCallback.onLoginFirst(profileList, loginStore)
|
||||
}
|
||||
}
|
||||
private fun synergiaTokenExtractor() {
|
||||
if (profile == null) {
|
||||
throw Exception("Profile may not be null")
|
||||
}
|
||||
SynergiaTokenExtractor(app, profile, loginStore, syncCallback) {
|
||||
d(TAG, "Profile $profile")
|
||||
d(TAG, "LoginStore $loginStore")
|
||||
onLogin?.invoke()
|
||||
}
|
||||
}
|
||||
/* _____ _
|
||||
/ ____| (_)
|
||||
| (___ _ _ _ __ ___ _ __ __ _ _ __ _
|
||||
\___ \| | | | '_ \ / _ \ '__/ _` | |/ _` |
|
||||
____) | |_| | | | | __/ | | (_| | | (_| |
|
||||
|_____/ \__, |_| |_|\___|_| \__, |_|\__,_|
|
||||
__/ | __/ |
|
||||
|___/ |__*/
|
||||
private fun loginSynergia() {
|
||||
LoginSynergia(app, loginStore, syncCallback) {
|
||||
if (profile == null) {
|
||||
firstLoginSynergia()
|
||||
return@LoginSynergia
|
||||
}
|
||||
onLogin?.invoke()
|
||||
}
|
||||
}
|
||||
private fun firstLoginSynergia() {
|
||||
FirstLoginSynergia(app, loginStore, syncCallback) { profileList ->
|
||||
syncCallback.onLoginFirst(profileList, loginStore)
|
||||
}
|
||||
}
|
||||
/* _ _____ _______
|
||||
| |/ ____|__ __|
|
||||
| | (___ | |
|
||||
_ | |\___ \ | |
|
||||
| |__| |____) | | |
|
||||
\____/|_____/ |*/
|
||||
private fun loginJst() {
|
||||
LoginJst(app, loginStore, syncCallback) {
|
||||
if (profile == null) {
|
||||
firstLoginSynergia()
|
||||
return@LoginJst
|
||||
}
|
||||
onLogin?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapCallback(callback: SyncCallback): SyncCallback {
|
||||
return object : SyncCallback {
|
||||
override fun onSuccess(activityContext: Context?, profileFull: ProfileFull?) {
|
||||
callback.onSuccess(activityContext, profileFull)
|
||||
}
|
||||
|
||||
override fun onProgress(progressStep: Int) {
|
||||
callback.onProgress(progressStep)
|
||||
}
|
||||
|
||||
override fun onActionStarted(stringResId: Int) {
|
||||
callback.onActionStarted(stringResId)
|
||||
}
|
||||
|
||||
override fun onLoginFirst(profileList: MutableList<Profile>?, loginStore: LoginStore?) {
|
||||
callback.onLoginFirst(profileList, loginStore)
|
||||
}
|
||||
|
||||
override fun onError(activityContext: Context?, error: AppError) {
|
||||
when (error.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(activityContext, error)
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(error.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
loginLibrus()
|
||||
}
|
||||
else -> callback.onError(activityContext, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun login(callback: SyncCallback) {
|
||||
this.internalErrorList.clear()
|
||||
this.syncCallback = wrapCallback(callback)
|
||||
when (loginStore.mode) {
|
||||
LOGIN_MODE_LIBRUS_EMAIL -> {
|
||||
loginLibrus()
|
||||
}
|
||||
LOGIN_MODE_LIBRUS_SYNERGIA -> {
|
||||
|
||||
}
|
||||
LOGIN_MODE_LIBRUS_JST -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getData() {
|
||||
|
||||
}
|
||||
|
||||
override fun sync(activityContext: Context, callback: SyncCallback, profileId: Int, profile: Profile?, loginStore: LoginStore) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun syncMessages(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun syncFeature(activityContext: Context, callback: SyncCallback, profile: ProfileFull, vararg featureList: Int) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getMessage(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, messageCallback: MessageGetCallback) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getAttachment(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, attachmentId: Long, attachmentCallback: AttachmentGetCallback) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getRecipientList(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, recipientListGetCallback: RecipientListGetCallback) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getComposeInfo(profile: ProfileFull): MessagesComposeInfo {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getConfigurableEndpoints(profile: Profile?): MutableMap<String, Endpoint> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun isEndpointEnabled(profile: Profile?, defaultActive: Boolean, name: String?): Boolean {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
|
||||
class DataLibrus(val app: App, val profile: Profile, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) {
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
|
||||
class FirstLoginLibrus(val app: App, val loginStore: LoginStore, val progressCallback: ProgressCallback, val onSuccess: (profileList: List<Profile>) -> Unit) {
|
||||
init {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
|
||||
class FirstLoginSynergia(val app: App, val loginStore: LoginStore, val progressCallback: ProgressCallback, val onSuccess: (profileList: List<Profile>) -> Unit) {
|
||||
init {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.ApiLoginResult
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
|
||||
class LoginJst(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "librus.LoginJst"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
|
||||
import android.util.Pair
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.AppError
|
||||
import pl.szczodrzynski.edziennik.api.AppError.*
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.getInt
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.c
|
||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||
import java.util.ArrayList
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class LoginLibrus(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "librus.LoginLibrus"
|
||||
}
|
||||
|
||||
init {
|
||||
// ustawiamy tokeny, generujemy itp
|
||||
// nic nie robimy z dostępem do api.librus.pl
|
||||
// to będzie później
|
||||
val accessToken = loginStore.getLoginData("accessToken", null)
|
||||
val refreshToken = loginStore.getLoginData("refreshToken", null)
|
||||
val tokenExpiryTime = loginStore.getLoginData("tokenExpiryTime", 0L)
|
||||
|
||||
// succeed having a non-expired access token and a refresh token
|
||||
if (tokenExpiryTime-30 > System.currentTimeMillis() / 1000 && refreshToken != null && accessToken != null) {
|
||||
onSuccess()
|
||||
}
|
||||
else if (refreshToken != null) {
|
||||
app.cookieJar.clearForDomain("portal.librus.pl")
|
||||
accessToken(null, refreshToken)
|
||||
}
|
||||
else {
|
||||
app.cookieJar.clearForDomain("portal.librus.pl")
|
||||
authorize(LIBRUS_AUTHORIZE_URL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun authorize(url: String?) {
|
||||
callback.onActionStarted(R.string.sync_action_authorizing)
|
||||
Request.builder()
|
||||
.url(url)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.withClient(app.httpLazy)
|
||||
.callback(object : TextCallbackHandler() {
|
||||
override fun onSuccess(data: String, response: Response) {
|
||||
//d("headers "+response.headers().toString());
|
||||
val location = response.headers().get("Location")
|
||||
if (location != null) {
|
||||
val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location)
|
||||
if (authMatcher.find()) {
|
||||
accessToken(authMatcher.group(1), null)
|
||||
} else {
|
||||
//callback.onError(activityContext, Edziennik.CODE_OTHER, "Auth code not found: "+location);
|
||||
authorize(location)
|
||||
}
|
||||
} else {
|
||||
val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(data)
|
||||
if (csrfMatcher.find()) {
|
||||
login(csrfMatcher.group(1))
|
||||
} else {
|
||||
callback.onError(null, AppError(TAG, 463, CODE_OTHER, "CSRF token not found.", response, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
callback.onError(null, AppError(TAG, 207, CODE_OTHER, response, throwable))
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
private fun login(csrfToken: String) {
|
||||
callback.onActionStarted(R.string.sync_action_logging_in)
|
||||
val email = loginStore.getLoginData("email", "")
|
||||
val password = loginStore.getLoginData("password", "")
|
||||
Request.builder()
|
||||
.url(LIBRUS_LOGIN_URL)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addParameter("email", email)
|
||||
.addParameter("password", password)
|
||||
.addHeader("X-CSRF-TOKEN", csrfToken)
|
||||
.contentType(MediaTypeUtils.APPLICATION_JSON)
|
||||
.post()
|
||||
.callback(object : JsonCallbackHandler() {
|
||||
override fun onSuccess(data: JsonObject?, response: Response) {
|
||||
if (data == null) {
|
||||
if (response.parserErrorBody != null && response.parserErrorBody.contains("wciąż nieaktywne")) {
|
||||
callback.onError(null, AppError(TAG, 487, CODE_LIBRUS_NOT_ACTIVATED, response))
|
||||
}
|
||||
callback.onError(null, AppError(TAG, 489, CODE_MAINTENANCE, response))
|
||||
return
|
||||
}
|
||||
if (data.get("errors") != null) {
|
||||
callback.onError(null, AppError(TAG, 490, CODE_OTHER, data.get("errors").asJsonArray.get(0).asString, response, data))
|
||||
return
|
||||
}
|
||||
authorize(data.getString("redirect") ?: LIBRUS_AUTHORIZE_URL)
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
if (response.code() == 403 || response.code() == 401) {
|
||||
callback.onError(null, AppError(TAG, 248, CODE_INVALID_LOGIN, response, throwable))
|
||||
return
|
||||
}
|
||||
callback.onError(null, AppError(TAG, 251, CODE_OTHER, response, throwable))
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
private var refreshTokenFailed = false
|
||||
private fun accessToken(code: String?, refreshToken: String?) {
|
||||
callback.onActionStarted(R.string.sync_action_getting_token)
|
||||
val params = ArrayList<Pair<String, Any>>()
|
||||
params.add(Pair("client_id", LIBRUS_CLIENT_ID))
|
||||
if (code != null) {
|
||||
params.add(Pair("grant_type", "authorization_code"))
|
||||
params.add(Pair("code", code))
|
||||
params.add(Pair("redirect_uri", LIBRUS_REDIRECT_URL))
|
||||
} else if (refreshToken != null) {
|
||||
params.add(Pair("grant_type", "refresh_token"))
|
||||
params.add(Pair("refresh_token", refreshToken))
|
||||
}
|
||||
Request.builder()
|
||||
.url(LIBRUS_TOKEN_URL)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addParams(params)
|
||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||
.post()
|
||||
.callback(object : JsonCallbackHandler() {
|
||||
override fun onSuccess(data: JsonObject?, response: Response) {
|
||||
if (data == null) {
|
||||
callback.onError(null, AppError(TAG, 539, CODE_MAINTENANCE, response))
|
||||
return
|
||||
}
|
||||
if (data.get("error") != null) {
|
||||
val hint = data.getString("hint")
|
||||
if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) {
|
||||
c(TAG, "refreshing the token failed. Trying to log in again.")
|
||||
refreshTokenFailed = true
|
||||
authorize(LIBRUS_AUTHORIZE_URL)
|
||||
return
|
||||
}
|
||||
val errorText = data.getString("error") + " " + (data.getString("message") ?: "") + " " + (hint ?: "")
|
||||
callback.onError(null, AppError(TAG, 552, CODE_OTHER, errorText, response, data))
|
||||
return
|
||||
}
|
||||
try {
|
||||
loginStore.putLoginData("tokenType", data.getString("token_type"))
|
||||
loginStore.putLoginData("accessToken", data.getString("access_token"))
|
||||
loginStore.putLoginData("refreshToken", data.getString("refresh_token"))
|
||||
loginStore.putLoginData("tokenExpiryTime", System.currentTimeMillis() / 1000 + (data.getInt("expires_in") ?: 86400))
|
||||
onSuccess()
|
||||
} catch (e: NullPointerException) {
|
||||
callback.onError(null, AppError(TAG, 311, CODE_OTHER, response, e, data))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
callback.onError(null, AppError(TAG, 317, CODE_OTHER, response, throwable))
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
|
||||
class LoginSynergia(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "librus.LoginSynergia"
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.login
|
||||
|
||||
import com.google.gson.JsonNull
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.AppError
|
||||
import pl.szczodrzynski.edziennik.api.AppError.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT
|
||||
import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNT_URL
|
||||
import pl.szczodrzynski.edziennik.datamodels.LoginStore
|
||||
import pl.szczodrzynski.edziennik.datamodels.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.net.HttpURLConnection.*
|
||||
|
||||
class SynergiaTokenExtractor(val app: App, val profile: Profile, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "librus.SynergiaToken"
|
||||
}
|
||||
|
||||
init {
|
||||
val accountToken = profile.getStudentData("accountToken", null)
|
||||
val accountTokenTime = profile.getStudentData("accountTokenTime", 0L)
|
||||
if (accountToken.isNotNullNorEmpty() && currentTimeUnix() - accountTokenTime < 3 * 60 * 60) {
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
if (!synergiaAccount())
|
||||
callback.onError(null, AppError(TAG, 33, CODE_INTERNAL_MISSING_DATA))
|
||||
}
|
||||
}
|
||||
|
||||
private fun synergiaAccount(): Boolean {
|
||||
val accountLogin = profile.getStudentData("accountLogin", null) ?: return false
|
||||
val tokenType = loginStore.getLoginData("tokenType", null) ?: return false
|
||||
val accessToken = loginStore.getLoginData("accessToken", null) ?: return false
|
||||
callback.onActionStarted(R.string.sync_action_getting_account)
|
||||
d(TAG, "Requesting " + (LIBRUS_ACCOUNT_URL + accountLogin))
|
||||
Request.builder()
|
||||
.url(LIBRUS_ACCOUNT_URL + accountLogin)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addHeader("Authorization", "$tokenType $accessToken")
|
||||
.get()
|
||||
.allowErrorCode(HTTP_NOT_FOUND)
|
||||
.allowErrorCode(HTTP_FORBIDDEN)
|
||||
.allowErrorCode(HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HTTP_GONE)
|
||||
.callback(object : JsonCallbackHandler() {
|
||||
override fun onSuccess(data: JsonObject?, response: Response) {
|
||||
if (data == null) {
|
||||
callback.onError(null, AppError(TAG, 641, CODE_MAINTENANCE, response))
|
||||
return
|
||||
}
|
||||
if (response.code() == 410) {
|
||||
val reason = data.get("reason")
|
||||
if (reason != null && reason !is JsonNull && reason.asString == "requires_an_action") {
|
||||
callback.onError(null, AppError(TAG, 1078, CODE_LIBRUS_DISCONNECTED, response, data))
|
||||
return
|
||||
}
|
||||
callback.onError(null, AppError(TAG, 70, CODE_INTERNAL_LIBRUS_ACCOUNT_410))
|
||||
return
|
||||
}
|
||||
if (data.get("message") != null) {
|
||||
val message = data.get("message").asString
|
||||
if (message == "Account not found") {
|
||||
callback.onError(null, AppError(TAG, 651, CODE_OTHER, app.getString(R.string.sync_error_register_student_not_associated_format, profile.studentNameLong, accountLogin), response, data))
|
||||
return
|
||||
}
|
||||
callback.onError(null, AppError(TAG, 654, CODE_OTHER, message + "\n\n" + accountLogin, response, data))
|
||||
return
|
||||
}
|
||||
if (response.code() == HTTP_OK) {
|
||||
try {
|
||||
// synergiaAccount is executed when a synergia token needs a refresh
|
||||
val accountId = data.getInt("id")
|
||||
val accountToken = data.getString("accessToken")
|
||||
if (accountId == null || accountToken == null) {
|
||||
callback.onError(null, AppError(TAG, 1284, CODE_OTHER, data))
|
||||
return
|
||||
}
|
||||
profile.putStudentData("accountId", accountId)
|
||||
profile.putStudentData("accountToken", accountToken)
|
||||
profile.putStudentData("accountTokenTime", System.currentTimeMillis() / 1000)
|
||||
profile.studentNameLong = data.getString("studentName")
|
||||
val nameParts = data.getString("studentName")?.split(" ")?.toTypedArray()
|
||||
profile.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0)
|
||||
onSuccess()
|
||||
} catch (e: NullPointerException) {
|
||||
e.printStackTrace()
|
||||
callback.onError(null, AppError(TAG, 662, CODE_OTHER, response, e, data))
|
||||
}
|
||||
|
||||
} else {
|
||||
callback.onError(null, AppError(TAG, 425, CODE_OTHER, response, data))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
callback.onError(null, AppError(TAG, 432, CODE_OTHER, response, throwable))
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.enqueue()
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
import android.util.LongSparseArray
|
||||
import androidx.core.util.forEach
|
||||
import androidx.core.util.isNotEmpty
|
||||
import pl.szczodrzynski.edziennik.datamodels.*
|
||||
import pl.szczodrzynski.edziennik.models.Date
|
||||
|
||||
data class DataStore(private val appDb: AppDb, val profileId: Int) {
|
||||
val teacherList: LongSparseArray<Teacher> = LongSparseArray()
|
||||
val subjectList: LongSparseArray<Subject> = LongSparseArray()
|
||||
val teamList = mutableListOf<Team>()
|
||||
val lessonList = mutableListOf<Lesson>()
|
||||
val lessonChangeList = mutableListOf<LessonChange>()
|
||||
val gradeCategoryList = mutableListOf<GradeCategory>()
|
||||
val gradeList = mutableListOf<Grade>()
|
||||
val eventList = mutableListOf<Event>()
|
||||
val eventTypeList = mutableListOf<EventType>()
|
||||
val noticeList = mutableListOf<Notice>()
|
||||
val attendanceList = mutableListOf<Attendance>()
|
||||
val announcementList = mutableListOf<Announcement>()
|
||||
val messageList = mutableListOf<Message>()
|
||||
val messageRecipientList = mutableListOf<MessageRecipient>()
|
||||
val messageRecipientIgnoreList = mutableListOf<MessageRecipient>()
|
||||
val metadataList = mutableListOf<Metadata>()
|
||||
val messageMetadataList = mutableListOf<Metadata>()
|
||||
|
||||
init {
|
||||
|
||||
clear()
|
||||
|
||||
appDb.teacherDao().getAllNow(profileId).forEach { teacher ->
|
||||
teacherList.put(teacher.id, teacher)
|
||||
}
|
||||
appDb.subjectDao().getAllNow(profileId).forEach { subject ->
|
||||
subjectList.put(subject.id, subject)
|
||||
}
|
||||
|
||||
/*val teacher = teachers.byNameFirstLast("Jan Kowalski") ?: Teacher(1, 1, "", "").let {
|
||||
teachers.add(it)
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
teacherList.clear()
|
||||
subjectList.clear()
|
||||
teamList.clear()
|
||||
lessonList.clear()
|
||||
lessonChangeList.clear()
|
||||
gradeCategoryList.clear()
|
||||
gradeList.clear()
|
||||
eventTypeList.clear()
|
||||
noticeList.clear()
|
||||
attendanceList.clear()
|
||||
announcementList.clear()
|
||||
messageList.clear()
|
||||
messageRecipientList.clear()
|
||||
messageRecipientIgnoreList.clear()
|
||||
metadataList.clear()
|
||||
messageMetadataList.clear()
|
||||
}
|
||||
|
||||
fun saveData() {
|
||||
if (teacherList.isNotEmpty()) {
|
||||
val tempList: ArrayList<Teacher> = ArrayList()
|
||||
teacherList.forEach { _, teacher ->
|
||||
tempList.add(teacher)
|
||||
}
|
||||
appDb.teacherDao().addAll(tempList)
|
||||
}
|
||||
if (subjectList.isNotEmpty()) {
|
||||
val tempList: ArrayList<Subject> = ArrayList()
|
||||
subjectList.forEach { _, subject ->
|
||||
tempList.add(subject)
|
||||
}
|
||||
appDb.subjectDao().addAll(tempList)
|
||||
}
|
||||
if (teamList.isNotEmpty())
|
||||
appDb.teamDao().addAll(teamList)
|
||||
if (lessonList.isNotEmpty()) {
|
||||
appDb.lessonDao().clear(profileId)
|
||||
appDb.lessonDao().addAll(lessonList)
|
||||
}
|
||||
if (lessonChangeList.isNotEmpty())
|
||||
appDb.lessonChangeDao().addAll(lessonChangeList)
|
||||
if (gradeCategoryList.isNotEmpty())
|
||||
appDb.gradeCategoryDao().addAll(gradeCategoryList)
|
||||
if (gradeList.isNotEmpty()) {
|
||||
appDb.gradeDao().clear(profileId)
|
||||
appDb.gradeDao().addAll(gradeList)
|
||||
}
|
||||
if (eventList.isNotEmpty()) {
|
||||
appDb.eventDao().removeFuture(profileId, Date.getToday())
|
||||
appDb.eventDao().addAll(eventList)
|
||||
}
|
||||
if (eventTypeList.isNotEmpty())
|
||||
appDb.eventTypeDao().addAll(eventTypeList)
|
||||
if (noticeList.isNotEmpty()) {
|
||||
appDb.noticeDao().clear(profileId)
|
||||
appDb.noticeDao().addAll(noticeList)
|
||||
}
|
||||
if (attendanceList.isNotEmpty())
|
||||
appDb.attendanceDao().addAll(attendanceList)
|
||||
if (announcementList.isNotEmpty())
|
||||
appDb.announcementDao().addAll(announcementList)
|
||||
if (messageList.isNotEmpty())
|
||||
appDb.messageDao().addAllIgnore(messageList)
|
||||
if (messageRecipientList.isNotEmpty())
|
||||
appDb.messageRecipientDao().addAll(messageRecipientList)
|
||||
if (messageRecipientIgnoreList.isNotEmpty())
|
||||
appDb.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList)
|
||||
if (metadataList.isNotEmpty())
|
||||
appDb.metadataDao().addAllIgnore(metadataList)
|
||||
if (messageMetadataList.isNotEmpty())
|
||||
appDb.metadataDao().setSeen(messageMetadataList)
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
|
||||
data class Feature(val featureId: Int, val loginOptions: Map<Int, List<Int>>) {
|
||||
|
||||
init {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
|
||||
val Features = listOf(
|
||||
Feature(FEATURE_TIMETABLE, mapOf(
|
||||
LOGIN_TYPE_LIBRUS to listOf(
|
||||
LOGIN_MODE_LIBRUS_EMAIL,
|
||||
LOGIN_MODE_LIBRUS_SYNERGIA,
|
||||
LOGIN_MODE_LIBRUS_JST
|
||||
)
|
||||
))
|
||||
)
|
@ -0,0 +1,47 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
@Entity(tableName = "announcements",
|
||||
primaryKeys = {"profileId", "announcementId"},
|
||||
indices = {@Index(value = {"profileId"})})
|
||||
public class Announcement {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "announcementId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "announcementSubject")
|
||||
public String subject;
|
||||
@ColumnInfo(name = "announcementText")
|
||||
public String text;
|
||||
@Nullable
|
||||
@ColumnInfo(name = "announcementStartDate")
|
||||
public Date startDate;
|
||||
@Nullable
|
||||
@ColumnInfo(name = "announcementEndDate")
|
||||
public Date endDate;
|
||||
|
||||
public long teacherId;
|
||||
|
||||
@Ignore
|
||||
public Announcement() {}
|
||||
|
||||
public Announcement(int profileId, long id, String subject, String text, @Nullable Date startDate, @Nullable Date endDate, long teacherId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.subject = subject;
|
||||
this.text = text;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.teacherId = teacherId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,61 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_ANNOUNCEMENT;
|
||||
|
||||
@Dao
|
||||
public abstract class AnnouncementDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Announcement announcement);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Announcement> announcementList);
|
||||
|
||||
@Query("DELETE FROM announcements WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@RawQuery(observedEntities = {Announcement.class})
|
||||
abstract LiveData<List<AnnouncementFull>> getAll(SupportSQLiteQuery query);
|
||||
public LiveData<List<AnnouncementFull>> getAll(int profileId, String filter) {
|
||||
return getAll(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM announcements \n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE announcements.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
public LiveData<List<AnnouncementFull>> getAll(int profileId) {
|
||||
return getAll(profileId, "1");
|
||||
}
|
||||
public LiveData<List<AnnouncementFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter);
|
||||
}
|
||||
|
||||
@RawQuery(observedEntities = {Announcement.class, Metadata.class})
|
||||
abstract List<AnnouncementFull> getAllNow(SupportSQLiteQuery query);
|
||||
public List<AnnouncementFull> getAllNow(int profileId, String filter) {
|
||||
return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM announcements \n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE announcements.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
public List<AnnouncementFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
public class AnnouncementFull extends Announcement {
|
||||
public String teacherFullName = "";
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
}
|
@ -0,0 +1,547 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.room.TypeConverters;
|
||||
import androidx.room.migration.Migration;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@Database(entities = {
|
||||
Grade.class,
|
||||
//GradeCategory.class,
|
||||
Teacher.class,
|
||||
Subject.class,
|
||||
Notice.class,
|
||||
Lesson.class,
|
||||
LessonChange.class,
|
||||
Team.class,
|
||||
Attendance.class,
|
||||
Event.class,
|
||||
EventType.class,
|
||||
LoginStore.class,
|
||||
Profile.class,
|
||||
LuckyNumber.class,
|
||||
Announcement.class,
|
||||
GradeCategory.class,
|
||||
FeedbackMessage.class,
|
||||
Message.class,
|
||||
MessageRecipient.class,
|
||||
DebugLog.class,
|
||||
Metadata.class}, version = 52)
|
||||
@TypeConverters({
|
||||
ConverterTime.class,
|
||||
ConverterDate.class,
|
||||
ConverterJsonObject.class,
|
||||
ConverterListLong.class,
|
||||
ConverterListString.class})
|
||||
public abstract class AppDb extends RoomDatabase {
|
||||
|
||||
public abstract GradeDao gradeDao();
|
||||
//public abstract GradeCategoryDao gradeCategoryDao();
|
||||
public abstract TeacherDao teacherDao();
|
||||
public abstract SubjectDao subjectDao();
|
||||
public abstract NoticeDao noticeDao();
|
||||
public abstract LessonDao lessonDao();
|
||||
public abstract LessonChangeDao lessonChangeDao();
|
||||
public abstract TeamDao teamDao();
|
||||
public abstract AttendanceDao attendanceDao();
|
||||
public abstract EventDao eventDao();
|
||||
public abstract EventTypeDao eventTypeDao();
|
||||
public abstract LoginStoreDao loginStoreDao();
|
||||
public abstract ProfileDao profileDao();
|
||||
public abstract LuckyNumberDao luckyNumberDao();
|
||||
public abstract AnnouncementDao announcementDao();
|
||||
public abstract GradeCategoryDao gradeCategoryDao();
|
||||
public abstract FeedbackMessageDao feedbackMessageDao();
|
||||
public abstract MessageDao messageDao();
|
||||
public abstract MessageRecipientDao messageRecipientDao();
|
||||
public abstract DebugLogDao debugLogDao();
|
||||
public abstract MetadataDao metadataDao();
|
||||
|
||||
private static volatile AppDb INSTANCE;
|
||||
|
||||
private static final Migration MIGRATION_11_12 = new Migration(11, 12) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("PRAGMA foreign_keys=off;");
|
||||
database.execSQL("ALTER TABLE teams RENAME TO _teams_old;");
|
||||
database.execSQL("CREATE TABLE teams (profileId INTEGER NOT NULL, teamId INTEGER NOT NULL, teamType INTEGER NOT NULL, teamName TEXT, teamTeacherId INTEGER NOT NULL, PRIMARY KEY(profileId, teamId));");
|
||||
database.execSQL("INSERT INTO teams (profileId, teamId, teamType, teamName, teamTeacherId) SELECT profileId, teamId, teamType, teamName, teacherId FROM _teams_old;");
|
||||
database.execSQL("DROP TABLE _teams_old;");
|
||||
database.execSQL("PRAGMA foreign_keys=on;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_12_13 = new Migration(12, 13) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE lessonChanges ADD lessonChangeWeekDay INTEGER NOT NULL DEFAULT -1;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_13_14 = new Migration(13, 14) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE loginStores (loginStoreId INTEGER NOT NULL, loginStoreType INTEGER NOT NULL, loginStoreData TEXT, PRIMARY KEY(loginStoreId));");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_14_15 = new Migration(14, 15) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades RENAME TO _grades_old;");
|
||||
database.execSQL("CREATE TABLE `grades` (\n" +
|
||||
"\t`profileId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeDescription`\tTEXT,\n" +
|
||||
"\t`gradeName`\tTEXT,\n" +
|
||||
"\t`gradeValue`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeWeight`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeSemester`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeType`\tINTEGER NOT NULL,\n" +
|
||||
"\t`teacherId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`categoryId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`subjectId`\tINTEGER NOT NULL,\n" +
|
||||
"\tPRIMARY KEY(`profileId`,`gradeId`)\n" +
|
||||
");");
|
||||
database.execSQL("INSERT INTO grades\n" +
|
||||
" SELECT *\n" +
|
||||
" FROM _grades_old;");
|
||||
database.execSQL("DROP TABLE _grades_old;");
|
||||
database.execSQL("CREATE INDEX index_grades_profileId ON grades (profileId);");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_15_16 = new Migration(15, 16) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE profiles (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"name TEXT, " +
|
||||
"subname TEXT, " +
|
||||
"image TEXT, " +
|
||||
"syncEnabled INTEGER NOT NULL, " +
|
||||
"syncNotifications INTEGER NOT NULL, " +
|
||||
"enableSharedEvents INTEGER NOT NULL, " +
|
||||
"countInSeconds INTEGER NOT NULL, " +
|
||||
"loggedIn INTEGER NOT NULL, " +
|
||||
"empty INTEGER NOT NULL, " +
|
||||
"studentNameLong TEXT, " +
|
||||
"studentNameShort TEXT, " +
|
||||
"studentNumber INTEGER NOT NULL, " +
|
||||
"studentData TEXT, " +
|
||||
"registration INTEGER NOT NULL, " +
|
||||
"gradeColorMode INTEGER NOT NULL, " +
|
||||
"agendaViewType INTEGER NOT NULL, " +
|
||||
"currentSemester INTEGER NOT NULL, " +
|
||||
"attendancePercentage REAL NOT NULL, " +
|
||||
"dateSemester1Start TEXT, " +
|
||||
"dateSemester2Start TEXT, " +
|
||||
"dateYearEnd TEXT, " +
|
||||
"luckyNumberEnabled INTEGER NOT NULL, " +
|
||||
"luckyNumber INTEGER NOT NULL, " +
|
||||
"luckyNumberDate TEXT, " +
|
||||
"loginStoreId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId));");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_16_17 = new Migration(16, 17) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE profiles ADD archived INTEGER NOT NULL DEFAULT 0;");
|
||||
database.execSQL("ALTER TABLE teams ADD teamCode TEXT;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_17_18 = new Migration(17, 18) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE events ADD eventBlacklisted INTEGER NOT NULL DEFAULT 0;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_18_19 = new Migration(18, 19) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeClassAverage REAL NOT NULL DEFAULT -1;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_19_20 = new Migration(19, 20) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE luckyNumbers (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"luckyNumberDate TEXT NOT NULL, " +
|
||||
"luckyNumber INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, luckyNumberDate));");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_20_21 = new Migration(20, 21) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeCategory TEXT");
|
||||
database.execSQL("ALTER TABLE grades ADD gradeColor INTEGER NOT NULL DEFAULT -1");
|
||||
database.execSQL("DROP TABLE gradeCategories");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_21_22 = new Migration(21, 22) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE eventTypes (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"eventType INTEGER NOT NULL, " +
|
||||
"eventTypeName TEXT, " +
|
||||
"eventTypeColor INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, eventType));");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_22_23 = new Migration(22, 23) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades RENAME TO _grades_old;");
|
||||
database.execSQL("CREATE TABLE `grades` (\n" +
|
||||
"\t`profileId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeCategory`\tTEXT,\n" +
|
||||
"\t`gradeColor`\tINTEGER NOT NULL DEFAULT -1,\n" +
|
||||
"\t`gradeDescription`\tTEXT,\n" +
|
||||
"\t`gradeName`\tTEXT,\n" +
|
||||
"\t`gradeValue`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeWeight`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeSemester`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeClassAverage`\tREAL NOT NULL DEFAULT -1,\n" +
|
||||
"\t`gradeType`\tINTEGER NOT NULL,\n" +
|
||||
"\t`teacherId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`subjectId`\tINTEGER NOT NULL,\n" +
|
||||
"\tPRIMARY KEY(`profileId`,`gradeId`)\n" +
|
||||
");");
|
||||
database.execSQL("DROP INDEX index_grades_profileId");
|
||||
database.execSQL("CREATE INDEX `index_grades_profileId` ON `grades` (\n" +
|
||||
"\t`profileId`\n" +
|
||||
");");
|
||||
database.execSQL("INSERT INTO grades (profileId, gradeId, gradeDescription, gradeName, gradeValue, gradeWeight, gradeSemester, gradeType, teacherId, subjectId, gradeClassAverage, gradeCategory, gradeColor) SELECT profileId, gradeId, gradeDescription, gradeName, gradeValue, gradeWeight, gradeSemester, gradeType, teacherId, subjectId, gradeClassAverage, gradeCategory, gradeColor FROM _grades_old;");
|
||||
database.execSQL("DROP TABLE _grades_old;");
|
||||
|
||||
database.execSQL("ALTER TABLE attendances RENAME TO _attendances_old;");
|
||||
database.execSQL("CREATE TABLE `attendances` (\n" +
|
||||
"\t`profileId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`attendanceId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`attendanceLessonDate`\tTEXT NOT NULL,\n" +
|
||||
"\t`attendanceStartTime`\tTEXT NOT NULL,\n" +
|
||||
"\t`attendanceLessonTopic`\tTEXT,\n" +
|
||||
"\t`attendanceSemester`\tINTEGER NOT NULL,\n" +
|
||||
"\t`attendanceType`\tINTEGER NOT NULL,\n" +
|
||||
"\t`teacherId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`subjectId`\tINTEGER NOT NULL,\n" +
|
||||
"\tPRIMARY KEY(`profileId`,`attendanceId`,`attendanceLessonDate`,`attendanceStartTime`)\n" +
|
||||
");");
|
||||
database.execSQL("DROP INDEX index_attendances_profileId");
|
||||
database.execSQL("CREATE INDEX `index_attendances_profileId` ON `attendances` (\n" +
|
||||
"\t`profileId`\n" +
|
||||
");");
|
||||
database.execSQL("INSERT INTO attendances SELECT * FROM _attendances_old;");
|
||||
database.execSQL("DROP TABLE _attendances_old;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_23_24 = new Migration(23, 24) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE profiles ADD yearAverageMode INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_24_25 = new Migration(24, 25) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE announcements (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"announcementId INTEGER NOT NULL, " +
|
||||
"announcementSubject TEXT, " +
|
||||
"announcementText TEXT, " +
|
||||
"announcementStartDate TEXT, " +
|
||||
"announcementEndDate TEXT, " +
|
||||
"teacherId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, announcementId));");
|
||||
//database.execSQL("DROP INDEX index_announcements_profileId");
|
||||
database.execSQL("CREATE INDEX index_announcements_profileId ON announcements (" +
|
||||
"profileId" +
|
||||
");");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_25_26 = new Migration(25, 26) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradePointGrade INTEGER NOT NULL DEFAULT 0;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_26_27 = new Migration(26, 27) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeValueMax REAL NOT NULL DEFAULT 0;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_27_28 = new Migration(27, 28) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE gradeCategories (profileId INTEGER NOT NULL, categoryId INTEGER NOT NULL, weight REAL NOT NULL, color INTEGER NOT NULL, `text` TEXT, columns TEXT, valueFrom REAL NOT NULL, valueTo REAL NOT NULL, PRIMARY KEY(profileId, categoryId));");
|
||||
database.execSQL("ALTER TABLE grades ADD gradeComment TEXT;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_28_29 = new Migration(28, 29) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE feedbackMessages (messageId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, received INTEGER NOT NULL DEFAULT 0, sentTime INTEGER NOT NULL, `text` TEXT)");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_29_30 = new Migration(29, 30) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE feedbackMessages ADD fromUser TEXT DEFAULT NULL");
|
||||
database.execSQL("ALTER TABLE feedbackMessages ADD fromUserName TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_30_31 = new Migration(30, 31) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE messages (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"messageSubject TEXT, " +
|
||||
"messageBody TEXT DEFAULT NULL, " +
|
||||
"messageType INTEGER NOT NULL DEFAULT 0, " +
|
||||
"senderId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"senderReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"recipientIds TEXT DEFAULT NULL, " +
|
||||
"recipientReplyIds TEXT DEFAULT NULL, " +
|
||||
"readByRecipientDates TEXT DEFAULT NULL, " +
|
||||
"overrideHasAttachments INTEGER NOT NULL DEFAULT 0, " +
|
||||
"attachmentIds TEXT DEFAULT NULL, " +
|
||||
"attachmentNames TEXT DEFAULT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageId));");
|
||||
//database.execSQL("DROP INDEX index_announcements_profileId");
|
||||
database.execSQL("CREATE INDEX index_messages_profileId ON messages (" +
|
||||
"profileId" +
|
||||
");");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_31_32 = new Migration(31, 32) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE messages ADD attachmentSizes TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_32_33 = new Migration(32, 33) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("DROP TABLE messages");
|
||||
database.execSQL("CREATE TABLE messages (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"messageSubject TEXT, " +
|
||||
"messageBody TEXT DEFAULT NULL, " +
|
||||
"messageType INTEGER NOT NULL DEFAULT 0, " +
|
||||
"senderId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"senderReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"overrideHasAttachments INTEGER NOT NULL DEFAULT 0, " +
|
||||
"attachmentIds TEXT DEFAULT NULL, " +
|
||||
"attachmentNames TEXT DEFAULT NULL, " +
|
||||
"attachmentSizes TEXT DEFAULT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageId));");
|
||||
//database.execSQL("DROP INDEX index_announcements_profileId");
|
||||
database.execSQL("CREATE INDEX index_messages_profileId ON messages (" +
|
||||
"profileId" +
|
||||
");");
|
||||
database.execSQL("CREATE TABLE messageRecipients (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageRecipientId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReadDate INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageRecipientId));");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_33_34 = new Migration(33, 34) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("DROP TABLE messageRecipients");
|
||||
database.execSQL("CREATE TABLE messageRecipients (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageRecipientId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReadDate INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageRecipientId, messageId));");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_34_35 = new Migration(34, 35) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE teachers ADD teacherLoginId TEXT DEFAULT NULL;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_35_36 = new Migration(35, 36) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET yearAverageMode = 4 WHERE yearAverageMode = 0");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_36_37 = new Migration(36, 37) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_37_38 = new Migration(37, 38) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
Date today = Date.getToday();
|
||||
int schoolYearStart = today.month < 9 ? today.year-1 : today.year;
|
||||
database.execSQL("UPDATE profiles SET dateSemester1Start = '"+schoolYearStart+"-09-01' WHERE dateSemester1Start IS NULL;");
|
||||
database.execSQL("UPDATE profiles SET dateSemester2Start = '"+(schoolYearStart+1)+"-02-01' WHERE dateSemester2Start IS NULL;");
|
||||
database.execSQL("UPDATE profiles SET dateYearEnd = '"+(schoolYearStart+1)+"-06-30' WHERE dateYearEnd IS NULL;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_38_39 = new Migration(38, 39) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE debugLogs (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `text` TEXT);");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_39_40 = new Migration(39, 40) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE profiles ADD changedEndpoints TEXT DEFAULT NULL;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_40_41 = new Migration(40, 41) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_41_42 = new Migration(41, 42) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_42_43 = new Migration(42, 43) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE profiles ADD lastFullSync INTEGER NOT NULL DEFAULT 0;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_43_44 = new Migration(43, 44) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;");
|
||||
database.execSQL("UPDATE profiles SET currentSemester = 1;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_44_45 = new Migration(44, 45) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_45_46 = new Migration(45, 46) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET lastFullSync = 0");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_46_47 = new Migration(46, 47) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("UPDATE profiles SET lastFullSync = 0");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_47_48 = new Migration(47, 48) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE notices ADD points REAL NOT NULL DEFAULT 0");
|
||||
database.execSQL("ALTER TABLE notices ADD category TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_48_49 = new Migration(48, 49) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeParentId INTEGER NOT NULL DEFAULT -1");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_49_50 = new Migration(49, 50) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeIsImprovement INTEGER NOT NULL DEFAULT 0");
|
||||
database.execSQL("DELETE FROM attendances WHERE attendances.profileId IN (SELECT profiles.profileId FROM profiles JOIN loginStores USING(loginStoreId) WHERE loginStores.loginStoreType = 1)");
|
||||
database.execSQL("UPDATE profiles SET empty = 1 WHERE profileId IN (SELECT profiles.profileId FROM profiles JOIN loginStores USING(loginStoreId) WHERE loginStores.loginStoreType = 4)");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_50_51 = new Migration(50, 51) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE profiles ADD lastReceiversSync INTEGER NOT NULL DEFAULT 0");
|
||||
database.execSQL("ALTER TABLE teachers ADD teacherType INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_51_52 = new Migration(51, 52) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE teachers ADD teacherTypeDescription TEXT DEFAULT NULL");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static AppDb getDatabase(final Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (AppDb.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
|
||||
AppDb.class, "edziennik.db")
|
||||
.addMigrations(
|
||||
MIGRATION_11_12,
|
||||
MIGRATION_12_13,
|
||||
MIGRATION_13_14,
|
||||
MIGRATION_14_15,
|
||||
MIGRATION_15_16,
|
||||
MIGRATION_16_17,
|
||||
MIGRATION_17_18,
|
||||
MIGRATION_18_19,
|
||||
MIGRATION_19_20,
|
||||
MIGRATION_20_21,
|
||||
MIGRATION_21_22,
|
||||
MIGRATION_22_23,
|
||||
MIGRATION_23_24,
|
||||
MIGRATION_24_25,
|
||||
MIGRATION_25_26,
|
||||
MIGRATION_26_27,
|
||||
MIGRATION_27_28,
|
||||
MIGRATION_28_29,
|
||||
MIGRATION_29_30,
|
||||
MIGRATION_30_31,
|
||||
MIGRATION_31_32,
|
||||
MIGRATION_32_33,
|
||||
MIGRATION_33_34,
|
||||
MIGRATION_34_35,
|
||||
MIGRATION_35_36,
|
||||
MIGRATION_36_37,
|
||||
MIGRATION_37_38,
|
||||
MIGRATION_38_39,
|
||||
MIGRATION_39_40,
|
||||
MIGRATION_40_41,
|
||||
MIGRATION_41_42,
|
||||
MIGRATION_42_43,
|
||||
MIGRATION_43_44,
|
||||
MIGRATION_44_45,
|
||||
MIGRATION_45_46,
|
||||
MIGRATION_46_47,
|
||||
MIGRATION_47_48,
|
||||
MIGRATION_48_49,
|
||||
MIGRATION_49_50,
|
||||
MIGRATION_50_51,
|
||||
MIGRATION_51_52)
|
||||
.allowMainThreadQueries()
|
||||
//.fallbackToDestructiveMigration()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
@Entity(tableName = "attendances",
|
||||
primaryKeys = {"profileId", "attendanceId", "attendanceLessonDate", "attendanceStartTime"},
|
||||
indices = {@Index(value = {"profileId"})})
|
||||
public class Attendance {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "attendanceId")
|
||||
public long id;
|
||||
|
||||
@NonNull
|
||||
@ColumnInfo(name = "attendanceLessonDate")
|
||||
public Date lessonDate;
|
||||
@NonNull
|
||||
@ColumnInfo(name = "attendanceStartTime")
|
||||
public Time startTime;
|
||||
@ColumnInfo(name = "attendanceLessonTopic")
|
||||
public String lessonTopic;
|
||||
@ColumnInfo(name = "attendanceSemester")
|
||||
public int semester;
|
||||
public static final int TYPE_PRESENT = 0;
|
||||
public static final int TYPE_ABSENT = 1;
|
||||
public static final int TYPE_ABSENT_EXCUSED = 2;
|
||||
public static final int TYPE_RELEASED = 3;
|
||||
public static final int TYPE_BELATED = 4;
|
||||
public static final int TYPE_BELATED_EXCUSED = 5;
|
||||
public static final int TYPE_CUSTOM = 6;
|
||||
@ColumnInfo(name = "attendanceType")
|
||||
public int type = TYPE_PRESENT;
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
|
||||
@Ignore
|
||||
public Attendance() {
|
||||
this(-1, -1, -1, -1, 0, "", Date.getToday(), Time.getNow(), TYPE_PRESENT);
|
||||
}
|
||||
|
||||
public Attendance(int profileId, long id, long teacherId, long subjectId, int semester, String lessonTopic, Date lessonDate, Time startTime, int type) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.teacherId = teacherId;
|
||||
this.subjectId = subjectId;
|
||||
this.semester = semester;
|
||||
this.lessonTopic = lessonTopic;
|
||||
this.lessonDate = lessonDate;
|
||||
this.startTime = startTime;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Attendance.TYPE_PRESENT;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_ATTENDANCE;
|
||||
|
||||
@Dao
|
||||
public abstract class AttendanceDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Attendance attendance);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Attendance> attendanceList);
|
||||
|
||||
@Query("DELETE FROM attendances WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@Query("DELETE FROM attendances WHERE profileId = :profileId AND attendanceLessonDate > :date")
|
||||
public abstract void clearAfterDate(int profileId, Date date);
|
||||
|
||||
@RawQuery(observedEntities = {Attendance.class})
|
||||
abstract LiveData<List<AttendanceFull>> getAll(SupportSQLiteQuery query);
|
||||
public LiveData<List<AttendanceFull>> getAll(int profileId, String filter) {
|
||||
return getAll(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM attendances \n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE attendances.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC"));
|
||||
}
|
||||
public LiveData<List<AttendanceFull>> getAll(int profileId) {
|
||||
return getAll(profileId, "1");
|
||||
}
|
||||
public LiveData<List<AttendanceFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter);
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
abstract List<AttendanceFull> getAllNow(SupportSQLiteQuery query);
|
||||
public List<AttendanceFull> getAllNow(int profileId, String filter) {
|
||||
return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM attendances \n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE attendances.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC"));
|
||||
}
|
||||
public List<AttendanceFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
// only absent and absent_excused count as absences
|
||||
// all the other types are counted as being present
|
||||
@Query("SELECT \n" +
|
||||
"CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" +
|
||||
" / \n" +
|
||||
"CAST(count() AS float)*100 \n" +
|
||||
"FROM attendances \n" +
|
||||
"WHERE profileId = :profileId")
|
||||
public abstract LiveData<Float> getAttendancePercentage(int profileId);
|
||||
|
||||
@Query("SELECT \n" +
|
||||
"CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" +
|
||||
" / \n" +
|
||||
"CAST(count() AS float)*100 \n" +
|
||||
"FROM attendances \n" +
|
||||
"WHERE profileId = :profileId AND attendanceSemester = :semester")
|
||||
public abstract float getAttendancePercentageNow(int profileId, int semester);
|
||||
|
||||
@Query("SELECT \n" +
|
||||
"CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" +
|
||||
" / \n" +
|
||||
"CAST(count() AS float)*100 \n" +
|
||||
"FROM attendances \n" +
|
||||
"WHERE profileId = :profileId")
|
||||
public abstract float getAttendancePercentageNow(int profileId);
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
public class AttendanceFull extends Attendance {
|
||||
public String teacherFullName = "";
|
||||
|
||||
public String subjectLongName = "";
|
||||
public String subjectShortName = "";
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
public class ConverterDate {
|
||||
|
||||
@TypeConverter
|
||||
public static Date toDate(String value) {
|
||||
return value == null ? null : Date.fromY_m_d(value);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String toString(Date value) {
|
||||
return value == null ? null : value.getStringY_m_d();
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class ConverterJsonObject {
|
||||
|
||||
@TypeConverter
|
||||
public static JsonObject toJsonObject(String value) {
|
||||
return value == null ? null : new JsonParser().parse(value).getAsJsonObject();
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String toString(JsonObject value) {
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
public class ConverterListLong {
|
||||
|
||||
@TypeConverter
|
||||
public static List<Long> toListLong(String value) {
|
||||
return value == null ? null : new Gson().fromJson(value, new TypeToken<List<Long>>(){}.getType());
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String toString(List<Long> value) {
|
||||
return value == null ? null : new Gson().toJson(value);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
public class ConverterListString {
|
||||
|
||||
@TypeConverter
|
||||
public static List<String> toListString(String value) {
|
||||
return value == null ? null : new Gson().fromJson(value, new TypeToken<List<String>>(){}.getType());
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String toString(List<String> value) {
|
||||
return value == null ? null : new Gson().toJson(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
public class ConverterTime {
|
||||
|
||||
@TypeConverter
|
||||
public static Time toTime(String value) {
|
||||
return value == null ? null : value.equals("null") ? null : Time.fromHms(value);
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static String toString(Time value) {
|
||||
return value == null ? null : value.getStringValue();
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
||||
|
||||
@Entity(tableName = "debugLogs")
|
||||
public class DebugLog {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public int id;
|
||||
|
||||
public String text;
|
||||
|
||||
public DebugLog(String text) {
|
||||
d("DebugLog", text);
|
||||
this.text = text;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
|
||||
@Dao
|
||||
public interface DebugLogDao {
|
||||
@Insert
|
||||
void add(DebugLog debugLog);
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
@Entity(tableName = "events",
|
||||
primaryKeys = {"profileId", "eventId"},
|
||||
indices = {@Index(value = {"profileId", "eventDate", "eventStartTime"}), @Index(value = {"profileId", "eventType"})})
|
||||
public class Event {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "eventId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "eventDate")
|
||||
public Date eventDate;
|
||||
@ColumnInfo(name = "eventStartTime")
|
||||
public Time startTime; // null for allDay
|
||||
@ColumnInfo(name = "eventTopic")
|
||||
public String topic;
|
||||
@ColumnInfo(name = "eventColor")
|
||||
public int color = -1;
|
||||
public static final int TYPE_UNDEFINED = -2;
|
||||
public static final int TYPE_HOMEWORK = -1;
|
||||
public static final int TYPE_DEFAULT = 0;
|
||||
public static final int TYPE_EXAM = 1;
|
||||
public static final int TYPE_SHORT_QUIZ = 2;
|
||||
public static final int TYPE_ESSAY = 3;
|
||||
public static final int TYPE_PROJECT = 4;
|
||||
public static final int TYPE_PT_MEETING = 5;
|
||||
public static final int TYPE_EXCURSION = 6;
|
||||
public static final int TYPE_READING = 7;
|
||||
public static final int TYPE_CLASS_EVENT = 8;
|
||||
public static final int TYPE_INFORMATION = 9;
|
||||
public static final int TYPE_TEACHER_ABSENCE = 10;
|
||||
public static final int COLOR_HOMEWORK = 0xff795548;
|
||||
public static final int COLOR_DEFAULT = 0xffffc107;
|
||||
public static final int COLOR_EXAM = 0xfff44336;
|
||||
public static final int COLOR_SHORT_QUIZ = 0xff76ff03;
|
||||
public static final int COLOR_ESSAY = 0xFF4050B5;
|
||||
public static final int COLOR_PROJECT = 0xFF673AB7;
|
||||
public static final int COLOR_PT_MEETING = 0xff90caf9;
|
||||
public static final int COLOR_EXCURSION = 0xFF4CAF50;
|
||||
public static final int COLOR_READING = 0xFFFFEB3B;
|
||||
public static final int COLOR_CLASS_EVENT = 0xff388e3c;
|
||||
public static final int COLOR_INFORMATION = 0xff039be5;
|
||||
public static final int COLOR_TEACHER_ABSENCE = 0xff039be5;
|
||||
@ColumnInfo(name = "eventType")
|
||||
public int type = TYPE_DEFAULT;
|
||||
@ColumnInfo(name = "eventAddedManually")
|
||||
public boolean addedManually;
|
||||
@ColumnInfo(name = "eventSharedBy")
|
||||
public String sharedBy = null;
|
||||
@ColumnInfo(name = "eventSharedByName")
|
||||
public String sharedByName = null;
|
||||
@ColumnInfo(name = "eventBlacklisted")
|
||||
public boolean blacklisted = false;
|
||||
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
public long teamId;
|
||||
|
||||
@Ignore
|
||||
public Event() {}
|
||||
|
||||
public Event(int profileId, long id, Date eventDate, Time startTime, String topic, int color, int type, boolean addedManually, long teacherId, long subjectId, long teamId)
|
||||
{
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.eventDate = eventDate;
|
||||
this.startTime = startTime;
|
||||
this.topic = topic;
|
||||
this.color = color;
|
||||
this.type = type;
|
||||
this.addedManually = addedManually;
|
||||
this.teacherId = teacherId;
|
||||
this.subjectId = subjectId;
|
||||
this.teamId = teamId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event{" +
|
||||
"profileId=" + profileId +
|
||||
", id=" + id +
|
||||
", eventDate=" + eventDate +
|
||||
", startTime=" + startTime +
|
||||
", topic='" + topic + '\'' +
|
||||
", color=" + color +
|
||||
", type=" + type +
|
||||
", addedManually=" + addedManually +
|
||||
", sharedBy='" + sharedBy + '\'' +
|
||||
", sharedByName='" + sharedByName + '\'' +
|
||||
", teacherId=" + teacherId +
|
||||
", subjectId=" + subjectId +
|
||||
", teamId=" + teamId +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.room.Transaction;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_EVENT;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_HOMEWORK;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_LESSON_CHANGE;
|
||||
|
||||
@Dao
|
||||
public abstract class EventDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Event event);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Event> eventList);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventId = :id")
|
||||
public abstract void remove(int profileId, long id);
|
||||
@Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
|
||||
public abstract void removeMetadata(int profileId, int thingType, long thingId);
|
||||
@Transaction
|
||||
public void remove(int profileId, int type, long id) {
|
||||
remove(profileId, id);
|
||||
removeMetadata(profileId, type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, id);
|
||||
}
|
||||
@Transaction
|
||||
public void remove(Event event) {
|
||||
remove(event.profileId, event.type, event.id);
|
||||
}
|
||||
@Transaction
|
||||
public void remove(int profileId, Event event) {
|
||||
remove(profileId, event.type, event.id);
|
||||
}
|
||||
@Query("DELETE FROM events WHERE teamId = :teamId AND eventId = :id")
|
||||
public abstract void removeByTeamId(long teamId, long id);
|
||||
|
||||
@RawQuery(observedEntities = {Event.class})
|
||||
abstract LiveData<List<EventFull>> getAll(SupportSQLiteQuery query);
|
||||
public LiveData<List<EventFull>> getAll(int profileId, String filter) {
|
||||
String query = "SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
|
||||
"eventTypes.eventTypeName AS typeName,\n" +
|
||||
"eventTypes.eventTypeColor AS typeColor\n" +
|
||||
"FROM events\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN eventTypes USING(profileId, eventType)\n" +
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
|
||||
"ORDER BY eventDate, eventStartTime ASC";
|
||||
Log.d("DB", query);
|
||||
return getAll(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
public LiveData<List<EventFull>> getAll(int profileId) {
|
||||
return getAll(profileId, "1");
|
||||
}
|
||||
public List<EventFull> getAllNow(int profileId) {
|
||||
return getAllNow(profileId, "1");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter);
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByType(int profileId, int type, String filter) {
|
||||
return getAll(profileId, "eventType = "+type+" AND "+filter);
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
|
||||
}
|
||||
public List<EventFull> getAllByDateNow(int profileId, @NonNull Date date) {
|
||||
return getAllNow(profileId, "eventDate = '"+date.getStringY_m_d()+"'");
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByDateTime(int profileId, @NonNull Date date, Time time) {
|
||||
if (time == null)
|
||||
return getAllByDate(profileId, date);
|
||||
return getAll(profileId, "eventDate = '"+date.getStringY_m_d()+"' AND eventStartTime = '"+time.getStringValue()+"'");
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
abstract List<EventFull> getAllNow(SupportSQLiteQuery query);
|
||||
public List<EventFull> getAllNow(int profileId, String filter) {
|
||||
return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName,\n" +
|
||||
"eventTypes.eventTypeName AS typeName,\n" +
|
||||
"eventTypes.eventTypeColor AS typeColor\n" +
|
||||
"FROM events \n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN eventTypes USING(profileId, eventType)\n" +
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE events.profileId = "+profileId+" AND events.eventBlacklisted = 0 AND "+filter+"\n" +
|
||||
"ORDER BY eventStartTime, addedDate ASC"));
|
||||
}
|
||||
public List<EventFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
public EventFull getByIdNow(int profileId, long eventId) {
|
||||
List<EventFull> eventList = getAllNow(profileId, "eventId = "+eventId);
|
||||
return eventList.size() == 0 ? null : eventList.get(0);
|
||||
}
|
||||
|
||||
@Query("UPDATE events SET eventAddedManually = 1 WHERE profileId = :profileId AND eventDate < :date")
|
||||
public abstract void convertOlderToManual(int profileId, Date date);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0")
|
||||
public abstract void removeNotManual(int profileId);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate")
|
||||
public abstract void removeFuture(int profileId, Date todayDate);
|
||||
|
||||
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = "+TYPE_EVENT+" OR thingType = "+TYPE_LESSON_CHANGE+" OR thingType = "+TYPE_HOMEWORK+") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
|
||||
public abstract void setSeenByDate(int profileId, Date date, boolean seen);
|
||||
|
||||
@Query("UPDATE events SET eventBlacklisted = :blacklisted WHERE profileId = :profileId AND eventId = :eventId")
|
||||
public abstract void setBlacklisted(int profileId, long eventId, boolean blacklisted);
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
public class EventFull extends Event {
|
||||
public String typeName = "";
|
||||
public int typeColor = -1;
|
||||
|
||||
public String teacherFullName = "";
|
||||
|
||||
public String subjectLongName = "";
|
||||
public String subjectShortName = "";
|
||||
|
||||
public String teamName = "";
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
|
||||
public int getColor() {
|
||||
return color == -1 ? typeColor : color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EventFull{" +
|
||||
"profileId=" + profileId +
|
||||
", id=" + id +
|
||||
", eventDate=" + eventDate +
|
||||
", startTime=" + startTime +
|
||||
", topic='" + topic + '\'' +
|
||||
", color=" + color +
|
||||
", type=" + type +
|
||||
", addedManually=" + addedManually +
|
||||
", sharedBy='" + sharedBy + '\'' +
|
||||
", sharedByName='" + sharedByName + '\'' +
|
||||
", blacklisted=" + blacklisted +
|
||||
", teacherId=" + teacherId +
|
||||
", subjectId=" + subjectId +
|
||||
", teamId=" + teamId +
|
||||
", typeName='" + typeName + '\'' +
|
||||
", teacherFullName='" + teacherFullName + '\'' +
|
||||
", subjectLongName='" + subjectLongName + '\'' +
|
||||
", subjectShortName='" + subjectShortName + '\'' +
|
||||
", teamName='" + teamName + '\'' +
|
||||
", seen=" + seen +
|
||||
", notified=" + notified +
|
||||
", addedDate=" + addedDate +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
|
||||
@Entity(tableName = "eventTypes",
|
||||
primaryKeys = {"profileId", "eventType"})
|
||||
public class EventType {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "eventType")
|
||||
public int id;
|
||||
|
||||
@ColumnInfo(name = "eventTypeName")
|
||||
public String name;
|
||||
@ColumnInfo(name = "eventTypeColor")
|
||||
public int color;
|
||||
|
||||
public EventType(int profileId, int id, String name, int color) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public EventType(int profileId, int id, String name, String color) {
|
||||
this(profileId, id, name, Color.parseColor(color));
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
@Dao
|
||||
public interface EventTypeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void add(EventType gradeCategory);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void addAll(List<EventType> gradeCategoryList);
|
||||
|
||||
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
|
||||
void clear(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
|
||||
EventType getByIdNow(int profileId, int typeId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
LiveData<List<EventType>> getAll(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
List<EventType> getAllNow(int profileId);
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "feedbackMessages")
|
||||
public class FeedbackMessage {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public int messageId;
|
||||
|
||||
public boolean received = false;
|
||||
public String fromUser = null;
|
||||
public String fromUserName = null;
|
||||
public long sentTime = System.currentTimeMillis();
|
||||
public String text;
|
||||
|
||||
public FeedbackMessage(boolean received, String text) {
|
||||
this.received = received;
|
||||
this.sentTime = System.currentTimeMillis();
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public FeedbackMessage() {
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
@Dao
|
||||
public interface FeedbackMessageDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void add(FeedbackMessage feedbackMessage);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void addAll(List<FeedbackMessage> feedbackMessageList);
|
||||
|
||||
@Query("DELETE FROM feedbackMessages")
|
||||
void clear();
|
||||
|
||||
@Query("SELECT * FROM feedbackMessages")
|
||||
List<FeedbackMessage> getAllNow();
|
||||
|
||||
@Query("SELECT * FROM feedbackMessages WHERE fromUser = :fromUser")
|
||||
List<FeedbackMessage> getAllByUserNow(String fromUser);
|
||||
|
||||
@Query("SELECT * FROM feedbackMessages")
|
||||
LiveData<List<FeedbackMessage>> getAll();
|
||||
|
||||
@Query("SELECT *, COUNT(*) AS messageCount FROM feedbackMessages GROUP BY fromUser ORDER BY sentTime DESC")
|
||||
List<FeedbackMessageWithCount> getAllWithCountNow();
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
public class FeedbackMessageWithCount extends FeedbackMessage {
|
||||
public int messageCount = 0;
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
@Entity(tableName = "grades",
|
||||
primaryKeys = {"profileId", "gradeId"},
|
||||
indices = {@Index(value = {"profileId"})})
|
||||
public class Grade {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "gradeId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "gradeCategory")
|
||||
public String category;
|
||||
@ColumnInfo(name = "gradeColor")
|
||||
public int color;
|
||||
@ColumnInfo(name = "gradeDescription")
|
||||
public String description;
|
||||
@ColumnInfo(name = "gradeComment")
|
||||
public String comment;
|
||||
@ColumnInfo(name = "gradeName")
|
||||
public String name;
|
||||
@ColumnInfo(name = "gradeValue")
|
||||
public float value;
|
||||
@ColumnInfo(name = "gradeValueMax")
|
||||
public float valueMax;
|
||||
@ColumnInfo(name = "gradeWeight")
|
||||
public float weight;
|
||||
@ColumnInfo(name = "gradeSemester")
|
||||
public int semester;
|
||||
@ColumnInfo(name = "gradeClassAverage")
|
||||
public float classAverage = -1;
|
||||
public static final int TYPE_NORMAL = 0;
|
||||
public static final int TYPE_SEMESTER1_PROPOSED = 1;
|
||||
public static final int TYPE_SEMESTER1_FINAL = 2;
|
||||
public static final int TYPE_SEMESTER2_PROPOSED = 3;
|
||||
public static final int TYPE_SEMESTER2_FINAL = 4;
|
||||
public static final int TYPE_YEAR_PROPOSED = 5;
|
||||
public static final int TYPE_YEAR_FINAL = 6;
|
||||
public static final int TYPE_POINT = 10;
|
||||
public static final int TYPE_BEHAVIOUR = 20;
|
||||
public static final int TYPE_DESCRIPTIVE = 30;
|
||||
public static final int TYPE_TEXT = 40;
|
||||
@ColumnInfo(name = "gradeType")
|
||||
public int type = TYPE_NORMAL;
|
||||
@ColumnInfo(name = "gradePointGrade")
|
||||
public boolean pointGrade = false;
|
||||
|
||||
/**
|
||||
* Applies for historical grades. It's the new/replacement grade's ID.
|
||||
*/
|
||||
@ColumnInfo(name = "gradeParentId")
|
||||
public long parentId = -1;
|
||||
|
||||
/**
|
||||
* Applies for current grades. If the grade was worse and this is the improved one.
|
||||
*/
|
||||
@ColumnInfo(name = "gradeIsImprovement")
|
||||
public boolean isImprovement = false;
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
|
||||
@Ignore
|
||||
public Grade() {}
|
||||
|
||||
public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.category = category;
|
||||
this.color = color;
|
||||
this.description = description;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.weight = weight;
|
||||
this.semester = semester;
|
||||
this.teacherId = teacherId;
|
||||
this.subjectId = subjectId;
|
||||
}
|
||||
|
||||
/*@Ignore
|
||||
public Grade(int profileId, long id, String description, String name, float value, float weight, int semester, long teacherId, long categoryId, long subjectId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.weight = weight;
|
||||
this.semester = semester;
|
||||
this.teacherId = teacherId;
|
||||
//this.categoryId = categoryId;
|
||||
this.subjectId = subjectId;
|
||||
}*/
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.Entity;
|
||||
|
||||
@Entity(tableName = "gradeCategories",
|
||||
primaryKeys = {"profileId", "categoryId"})
|
||||
public class GradeCategory {
|
||||
public int profileId;
|
||||
|
||||
public long categoryId;
|
||||
public float weight;
|
||||
public int color;
|
||||
public String text;
|
||||
public List<String> columns;
|
||||
public float valueFrom = 0;
|
||||
public float valueTo = 0;
|
||||
|
||||
public GradeCategory(int profileId, long categoryId, float weight, int color, String text) {
|
||||
this.profileId = profileId;
|
||||
this.categoryId = categoryId;
|
||||
this.weight = weight;
|
||||
this.color = color;
|
||||
this.text = text;
|
||||
this.columns = new ArrayList<>();
|
||||
}
|
||||
public GradeCategory setValueRange(float from, float to) {
|
||||
this.valueFrom = from;
|
||||
this.valueTo = to;
|
||||
return this;
|
||||
}
|
||||
public GradeCategory addColumn(String text) {
|
||||
columns.add(text);
|
||||
return this;
|
||||
}
|
||||
public GradeCategory addColumns(List<String> list) {
|
||||
columns.addAll(list);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static GradeCategory search(List<GradeCategory> gradeCategoryList, long categoryId) {
|
||||
for (GradeCategory gradeCategory: gradeCategoryList) {
|
||||
if (gradeCategory.categoryId == categoryId)
|
||||
return gradeCategory;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
@Dao
|
||||
public interface GradeCategoryDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void add(GradeCategory gradeCategory);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void addAll(List<GradeCategory> gradeCategoryList);
|
||||
|
||||
@Query("DELETE FROM gradeCategories WHERE profileId = :profileId")
|
||||
void clear(int profileId);
|
||||
|
||||
@Query("SELECT * FROM gradeCategories WHERE profileId = :profileId AND categoryId = :categoryId")
|
||||
GradeCategory getByIdNow(int profileId, int categoryId);
|
||||
|
||||
@Query("SELECT * FROM gradeCategories WHERE profileId = :profileId")
|
||||
LiveData<List<GradeCategory>> getAll(int profileId);
|
||||
|
||||
@Query("SELECT * FROM gradeCategories WHERE profileId = :profileId")
|
||||
List<GradeCategory> getAllNow(int profileId);
|
||||
}
|
||||
|
@ -0,0 +1,134 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.room.Transaction;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_GRADE;
|
||||
|
||||
@Dao
|
||||
public abstract class GradeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Grade grade);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Grade> gradeList);
|
||||
|
||||
@Query("DELETE FROM grades WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@Query("DELETE FROM grades WHERE profileId = :profileId AND gradeSemester = :semester")
|
||||
public abstract void clearForSemester(int profileId, int semester);
|
||||
|
||||
@RawQuery(observedEntities = {Grade.class})
|
||||
abstract LiveData<List<GradeFull>> getAll(SupportSQLiteQuery query);
|
||||
public LiveData<List<GradeFull>> getAll(int profileId, String filter, String orderBy) {
|
||||
return getAll(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM grades \n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN metadata ON gradeId = thingId AND thingType = " + TYPE_GRADE + " AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE grades.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY "+orderBy)); // TODO: 2019-04-30 why did I add sorting by gradeType???
|
||||
}
|
||||
public LiveData<List<GradeFull>> getAllOrderBy(int profileId, String orderBy) {
|
||||
return getAll(profileId, "1", orderBy);
|
||||
}
|
||||
public LiveData<List<GradeFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter, "addedDate DESC");
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
abstract List<GradeFull> getAllNow(SupportSQLiteQuery query);
|
||||
public List<GradeFull> getAllNow(int profileId, String filter) {
|
||||
return getAllNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM grades \n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN metadata ON gradeId = thingId AND thingType = " + TYPE_GRADE + " AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE grades.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
public List<GradeFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
public List<GradeFull> getAllWithParentIdNow(int profileId, long parentId) {
|
||||
return getAllNow(profileId, "gradeParentId = "+parentId);
|
||||
}
|
||||
|
||||
@RawQuery
|
||||
abstract GradeFull getNow(SupportSQLiteQuery query);
|
||||
public GradeFull getNow(int profileId, String filter) {
|
||||
return getNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM grades \n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN metadata ON gradeId = thingId AND thingType = " + TYPE_GRADE + " AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE grades.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
public GradeFull getByIdNow(int profileId, long gradeId) {
|
||||
return getNow(profileId, "gradeId = "+gradeId);
|
||||
}
|
||||
|
||||
@Query("UPDATE grades SET gradeClassAverage = :classAverage, gradeColor = :color WHERE profileId = :profileId AND gradeId = :gradeId")
|
||||
public abstract void updateDetailsById(int profileId, int gradeId, float classAverage, int color);
|
||||
|
||||
@Query("UPDATE metadata SET addedDate = :addedDate WHERE profileId = :profileId AND thingType = "+TYPE_GRADE+" AND thingId = :gradeId")
|
||||
public abstract void updateAddedDateById(int profileId, int gradeId, long addedDate);
|
||||
|
||||
@Transaction
|
||||
public void updateDetails(int profileId, SparseArray<Float> gradeAverages, SparseArray<Long> gradeAddedDates, SparseIntArray gradeColors) {
|
||||
for (int i = 0; i < gradeAverages.size(); i++) {
|
||||
int gradeId = gradeAverages.keyAt(i);
|
||||
float classAverage = gradeAverages.valueAt(i);
|
||||
long addedDate = gradeAddedDates.valueAt(i);
|
||||
int color = gradeColors.valueAt(i);
|
||||
updateDetailsById(profileId, gradeId, classAverage, color);
|
||||
updateAddedDateById(profileId, gradeId, addedDate);
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT gradeId FROM grades WHERE profileId = :profileId ORDER BY gradeId")
|
||||
public abstract List<Integer> getIds(int profileId);
|
||||
@Query("SELECT gradeClassAverage FROM grades WHERE profileId = :profileId ORDER BY gradeId")
|
||||
public abstract List<Float> getClassAverages(int profileId);
|
||||
@Query("SELECT gradeColor FROM grades WHERE profileId = :profileId ORDER BY gradeId")
|
||||
public abstract List<Integer> getColors(int profileId);
|
||||
@Query("SELECT addedDate FROM metadata WHERE profileId = :profileId AND thingType = "+TYPE_GRADE+" ORDER BY thingId")
|
||||
public abstract List<Long> getAddedDates(int profileId);
|
||||
@Transaction
|
||||
public void getDetails(int profileId, SparseArray<Long> gradeAddedDates, SparseArray<Float> gradeAverages, SparseIntArray gradeColors) {
|
||||
List<Integer> ids = getIds(profileId);
|
||||
List<Float> classAverages = getClassAverages(profileId);
|
||||
List<Integer> colors = getColors(profileId);
|
||||
List<Long> addedDates = getAddedDates(profileId);
|
||||
for (int index = 0; index < ids.size(); index++) {
|
||||
if (classAverages.size() > index) {
|
||||
gradeAverages.put(ids.get(index), classAverages.get(index));
|
||||
}
|
||||
if (colors.size() > index) {
|
||||
gradeColors.put(ids.get(index), colors.get(index));
|
||||
}
|
||||
if (addedDates.size() > index) {
|
||||
gradeAddedDates.put(ids.get(index), addedDates.get(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
public class GradeFull extends Grade {
|
||||
//public String category = "";
|
||||
//public int color;
|
||||
|
||||
public String subjectLongName = "";
|
||||
public String subjectShortName = "";
|
||||
|
||||
public String teacherFullName = "";
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
import pl.szczodrzynski.edziennik.models.Week;
|
||||
|
||||
@Entity(tableName = "lessons",
|
||||
primaryKeys = {"profileId", "lessonWeekDay", "lessonStartTime", "lessonEndTime"},
|
||||
indices = {@Index(value = {"profileId", "lessonWeekDay"})})
|
||||
public class Lesson {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "lessonWeekDay")
|
||||
public int weekDay;
|
||||
@NonNull
|
||||
@ColumnInfo(name = "lessonStartTime")
|
||||
public Time startTime;
|
||||
@NonNull
|
||||
@ColumnInfo(name = "lessonEndTime")
|
||||
public Time endTime;
|
||||
@ColumnInfo(name = "lessonClassroomName")
|
||||
public String classroomName;
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
public long teamId;
|
||||
|
||||
@Ignore
|
||||
public Lesson() {
|
||||
this.profileId = -1;
|
||||
this.startTime = new Time();
|
||||
this.endTime = new Time();
|
||||
}
|
||||
|
||||
public Lesson(int profileId, int weekDay, @NonNull Time startTime, @NonNull Time endTime) {
|
||||
this.profileId = profileId;
|
||||
this.weekDay = weekDay;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.teacherId = -1;
|
||||
this.subjectId = -1;
|
||||
this.teamId = -1;
|
||||
}
|
||||
|
||||
public Lesson(int profileId, int weekDay, String startTime, String endTime) {
|
||||
this(profileId, weekDay, new Time().parseFromYmdHm(startTime), new Time().parseFromYmdHm(endTime));
|
||||
}
|
||||
|
||||
public Lesson(int profileId, String dateStr, String startTime, String endTime) {
|
||||
this(profileId, Week.getWeekDayFromDate(dateStr), startTime, endTime);
|
||||
}
|
||||
|
||||
public static Lesson fromLessonChange(LessonChange lessonChange)
|
||||
{
|
||||
Lesson lesson = new Lesson(lessonChange.profileId, lessonChange.lessonDate.getWeekDay(), lessonChange.startTime, lessonChange.endTime);
|
||||
lesson.profileId = lessonChange.profileId;
|
||||
lesson.teacherId = lessonChange.teacherId;
|
||||
lesson.teamId = lessonChange.teamId;
|
||||
lesson.subjectId = lessonChange.subjectId;
|
||||
lesson.classroomName = lessonChange.classroomName;
|
||||
return lesson;
|
||||
}
|
||||
|
||||
public static Lesson getByWeekDayAndSubject(List<Lesson> lessonList, int weekDay, long subjectId) {
|
||||
for (Lesson lesson: lessonList) {
|
||||
if (lesson.weekDay == weekDay && lesson.subjectId == subjectId)
|
||||
return lesson;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
@Entity(tableName = "lessonChanges",
|
||||
primaryKeys = {"profileId", "lessonChangeDate", "lessonChangeStartTime", "lessonChangeEndTime"},
|
||||
indices = {@Index(value = {"profileId", "lessonChangeDate"})})
|
||||
public class LessonChange {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "lessonChangeId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "lessonChangeWeekDay")
|
||||
public int weekDay;
|
||||
@NonNull
|
||||
@ColumnInfo(name = "lessonChangeDate")
|
||||
public Date lessonDate;
|
||||
@NonNull
|
||||
@ColumnInfo(name = "lessonChangeStartTime")
|
||||
public Time startTime;
|
||||
@NonNull
|
||||
@ColumnInfo(name = "lessonChangeEndTime")
|
||||
public Time endTime;
|
||||
@ColumnInfo(name = "lessonChangeClassroomName")
|
||||
public String classroomName;
|
||||
@ColumnInfo(name = "lessonChangeType")
|
||||
public int type;
|
||||
public static int TYPE_CANCELLED = 1;
|
||||
public static int TYPE_CHANGE = 2;
|
||||
public static int TYPE_ADDED = 3;
|
||||
|
||||
public long teacherId;
|
||||
public long subjectId;
|
||||
public long teamId;
|
||||
|
||||
public LessonChange()
|
||||
{
|
||||
this.profileId = -1;
|
||||
this.lessonDate = Date.getToday();
|
||||
this.weekDay = this.lessonDate.getWeekDay();
|
||||
this.startTime = Time.getNow();
|
||||
this.endTime = Time.getNow();
|
||||
this.id = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public LessonChange(int profileId, String dateStr, String startTime, String endTime)
|
||||
{
|
||||
this(profileId, new Date().parseFromYmd(dateStr), new Time().parseFromYmdHm(startTime), new Time().parseFromYmdHm(endTime));
|
||||
}
|
||||
|
||||
public LessonChange(int profileId, @NonNull Date date, @NonNull Time startTime, @NonNull Time endTime)
|
||||
{
|
||||
this.profileId = profileId;
|
||||
this.lessonDate = date;
|
||||
this.weekDay = this.lessonDate.getWeekDay();
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.id = date.combineWith(startTime);
|
||||
}
|
||||
|
||||
public Lesson getOriginalLesson(List<Lesson> lessonList)
|
||||
{
|
||||
int weekDay = this.lessonDate.getWeekDay();
|
||||
for (Lesson lesson: lessonList) {
|
||||
if (lesson.weekDay == weekDay
|
||||
&& lesson.startTime.getValue() == this.startTime.getValue()
|
||||
&& lesson.endTime.getValue() == this.endTime.getValue())
|
||||
{
|
||||
return lesson;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean matches(Lesson lesson) {
|
||||
if (lesson == null) {
|
||||
return false;
|
||||
}
|
||||
// we are assuming the start and end time is equal
|
||||
return this.profileId == lesson.profileId
|
||||
&& this.teacherId == lesson.teacherId
|
||||
&& this.subjectId == lesson.subjectId
|
||||
&& this.teamId == lesson.teamId
|
||||
&& this.classroomName.equals(lesson.classroomName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,113 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.db.LessonChangeCounter;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_LESSON_CHANGE;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
||||
|
||||
@Dao
|
||||
public abstract class LessonChangeDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(LessonChange lessonChange);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<LessonChange> lessonChangeList);
|
||||
|
||||
@Query("DELETE FROM lessonChanges WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
public static String getQueryString(int profileId, String filter) {
|
||||
return "SELECT\n" +
|
||||
"lessonChanges.profileId AS lessonChangeProfileId,\n" +
|
||||
"lessonChanges.lessonChangeId,\n" +
|
||||
"lessonChanges.lessonChangeDate,\n" +
|
||||
"lessonChanges.lessonChangeStartTime,\n" +
|
||||
"lessonChanges.lessonChangeType,\n" +
|
||||
"lessonChanges.lessonChangeClassroomName,\n" +
|
||||
"lessonChanges.subjectId AS changeSubjectId,\n" +
|
||||
"lessonChanges.teacherId AS changeTeacherId,\n" +
|
||||
"lessonChanges.teamId AS changeTeamId,\n" +
|
||||
"subjects.subjectLongName AS changeSubjectLongName,\n" +
|
||||
"subjects.subjectShortName AS changeSubjectShortName,\n" +
|
||||
"teams.teamName AS changeTeamName," +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS changeTeacherFullName,\n" +
|
||||
"metadata.seen, metadata.notified, metadata.addedDate\n" +
|
||||
"FROM lessonChanges\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN metadata ON lessonChangeId = thingId AND thingType = " + TYPE_LESSON_CHANGE + " AND metadata.profileId = lessonChanges.profileId\n" +// TODO validate this works!. I hope so
|
||||
"WHERE "+(profileId == -1 ? "" : "lessonChanges.profileId = "+profileId+" AND ")+filter+"\n" +
|
||||
"ORDER BY lessonChanges.profileId, lessonChangeDate, lessonChangeStartTime ASC";
|
||||
}
|
||||
|
||||
@RawQuery(observedEntities = {Lesson.class, LessonChange.class})
|
||||
abstract LiveData<List<LessonFull>> getAll(SupportSQLiteQuery query);
|
||||
@RawQuery
|
||||
abstract List<LessonFull> getAllNow(SupportSQLiteQuery query);
|
||||
@RawQuery
|
||||
abstract LessonFull getNow(SupportSQLiteQuery query);
|
||||
|
||||
public String getQueryWithLessons(int profileId, String filter) {
|
||||
return "SELECT\n" +
|
||||
"lessonChanges.profileId,\n" +
|
||||
"lessonChangeId,\n" +
|
||||
"lessonChangeDate AS lessonDate,\n" +
|
||||
"lessonChangeStartTime,\n" +
|
||||
"lessonChangeType,\n" +
|
||||
"lessonChangeClassroomName,\n" +
|
||||
"lessonChanges.subjectId AS changeSubjectId,\n" +
|
||||
"lessonChanges.teacherId AS changeTeacherId,\n" +
|
||||
"lessonChanges.teamId AS changeTeamId,\n" +
|
||||
"subjects.subjectLongName AS changeSubjectLongName,\n" +
|
||||
"subjects.subjectShortName AS changeSubjectShortName,\n" +
|
||||
"lessonsFull.*,\n" +
|
||||
"teams.teamName AS changeTeamName,teachers.teacherName || ' ' || teachers.teacherSurname AS changeTeacherFullName\n" +
|
||||
",metadata.*FROM lessonChanges\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN metadata ON lessonChangeId = thingId AND thingType = 6 AND metadata.profileId = "+profileId+"\n" +
|
||||
"JOIN (\n" +
|
||||
"SELECT subjects.subjectLongName,\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"WHERE lessons.profileId = "+profileId+") lessonsFull ON lessonChangeWeekDay = lessonsFull.lessonWeekDay AND lessonChangeStartTime = lessonStartTime\n" +
|
||||
"WHERE lessonChanges.profileId = "+profileId+" AND "+filter+"\n" +
|
||||
"ORDER BY lessonChangeDate, lessonChangeStartTime ASC";
|
||||
}
|
||||
|
||||
public List<LessonFull> getAllChangesWithLessonsNow(int profileId) {
|
||||
String query = getQueryWithLessons(profileId, "1");
|
||||
d("DB", query);
|
||||
return getAllNow(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
public List<LessonFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(new SimpleSQLiteQuery(getQueryWithLessons(profileId, "notified = 0")));
|
||||
}
|
||||
|
||||
@Query("SELECT profileId, lessonChangeDate, count(*) AS lessonChangeCount FROM lessonChanges WHERE profileId = :profileId GROUP BY lessonChangeDate")
|
||||
public abstract List<LessonChangeCounter> getLessonChangeCountersNow(int profileId);
|
||||
|
||||
@Query("SELECT profileId, lessonChangeDate, count(*) AS lessonChangeCount FROM lessonChanges WHERE profileId = :profileId AND lessonChangeDate = :date GROUP BY lessonChangeDate")
|
||||
public abstract LiveData<LessonChangeCounter> getLessonChangeCounterByDate(int profileId, Date date);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
public class LessonChangeFull extends LessonChange {
|
||||
/*public String changeTeacherFullName = "";
|
||||
|
||||
public String changeSubjectLongName = "";
|
||||
public String changeSubjectShortName = "";
|
||||
|
||||
public String changeTeamName = "";
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;*/
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
import pl.szczodrzynski.edziennik.models.Time;
|
||||
|
||||
@Dao
|
||||
public abstract class LessonDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Lesson lesson);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Lesson> lessonList);
|
||||
|
||||
@Query("DELETE FROM lessons WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@RawQuery(observedEntities = {Lesson.class, LessonChange.class})
|
||||
abstract LiveData<List<LessonFull>> getAll(SupportSQLiteQuery query);
|
||||
@RawQuery
|
||||
abstract List<LessonFull> getAllNow(SupportSQLiteQuery query);
|
||||
@RawQuery
|
||||
abstract LessonFull getNow(SupportSQLiteQuery query);
|
||||
|
||||
public LiveData<List<LessonFull>> getAllByDate(int profileId, @NonNull Date date, @NonNull Time nowTime) {
|
||||
int weekDay = date.getWeekDay();
|
||||
String query = "SELECT\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectLongName,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"lessonChangesFull.*,\n" +
|
||||
"('"+nowTime.getStringValue()+"' > lessonEndTime AND lessonWeekDay = "+weekDay+") AS lessonPassed,\n" +
|
||||
"('"+nowTime.getStringValue()+"' BETWEEN lessonStartTime AND lessonEndTime AND lessonWeekDay = "+weekDay+") AS lessonCurrent,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN \n" +
|
||||
"("
|
||||
+LessonChangeDao.getQueryString(profileId, "lessonChangeDate = '"+date.getStringY_m_d()+"'")+
|
||||
") lessonChangesFull ON lessons.profileId = lessonChangesFull.lessonChangeProfileId AND lessonStartTime = lessonChangeStartTime\n" +
|
||||
"WHERE lessons.profileId = "+profileId+" AND lessonWeekDay = "+weekDay+"\n" +
|
||||
"ORDER BY lessonStartTime ASC";
|
||||
//Log.d("DB", "Query "+query);
|
||||
return getAll(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
public List<LessonFull> getAllWeekNow(int profileId, @NonNull Date weekBeginDate, @NonNull Date todayDate) {
|
||||
String query = "SELECT\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectLongName,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"lessonChangesFull.*,\n" +
|
||||
"date(\n" +
|
||||
"'"+weekBeginDate.getStringY_m_d()+"', \n" +
|
||||
"'+'||(case 1 when lessonWeekDay < "+todayDate.getWeekDay()+" then lessonWeekDay+7 else lessonWeekDay end)||' days'\n" +
|
||||
") AS lessonDate,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN \n" +
|
||||
"("
|
||||
+LessonChangeDao.getQueryString(profileId, "1")+
|
||||
") lessonChangesFull ON lessons.profileId = lessonChangesFull.lessonChangeProfileId AND lessonStartTime = lessonChangeStartTime AND lessonChangesFull.lessonChangeDate = lessonDate\n" +
|
||||
(profileId == -1 ? "" : "WHERE lessons.profileId = "+profileId+"\n") +
|
||||
"ORDER BY lessons.profileId, lessonDate, lessonStartTime ASC\n";
|
||||
//Log.d("DB", "Query "+query);
|
||||
return getAllNow(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
public LessonFull getByDateTimeNow(int profileId, @NonNull Date date, @NonNull Time time) {
|
||||
String query = "SELECT\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectLongName,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"lessonChangesFull.*,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN \n" +
|
||||
"("
|
||||
+LessonChangeDao.getQueryString(profileId, "lessonChangeDate = '"+date.getStringY_m_d()+"'")+
|
||||
") lessonChangesFull ON lessons.profileId = lessonChangesFull.lessonChangeProfileId AND lessonStartTime = lessonChangeStartTime\n" +
|
||||
"WHERE lessons.profileId = "+profileId+" AND lessonWeekDay = "+date.getWeekDay()+" AND ('"+time.getStringValue()+"' BETWEEN lessonStartTime AND lessonEndTime)\n" +
|
||||
"ORDER BY lessonStartTime ASC";
|
||||
//Log.d("DB", "Query "+query);
|
||||
return getNow(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
public List<LessonFull> getAllNearestNow(int profileId, @NonNull Date weekBeginDate, @NonNull Date todayDate, @NonNull Time nowTime) {
|
||||
int todayWeekDay = todayDate.getWeekDay();
|
||||
String query = "SELECT\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectLongName,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"lessonChangesFull.*,\n" +
|
||||
"date(\n" +
|
||||
"'"+weekBeginDate.getStringY_m_d()+"', \n" +
|
||||
"'+'||(case 1 when lessonWeekDay < "+todayWeekDay+" then lessonWeekDay+7 else lessonWeekDay end)||' days'\n" +
|
||||
") AS lessonDate,\n" +
|
||||
"('"+nowTime.getStringValue()+"' > lessonEndTime AND lessonWeekDay = "+todayWeekDay+") AS lessonPassed,\n" +
|
||||
"('"+nowTime.getStringValue()+"' BETWEEN lessonStartTime AND lessonEndTime AND lessonWeekDay = "+todayWeekDay+") AS lessonCurrent,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"LEFT JOIN \n" +
|
||||
"("
|
||||
+LessonChangeDao.getQueryString(profileId, "1")+
|
||||
") lessonChangesFull ON lessons.profileId = lessonChangesFull.lessonChangeProfileId AND lessonStartTime = lessonChangeStartTime AND lessonChangesFull.lessonChangeDate = lessonDate\n" +
|
||||
"WHERE lessons.profileId = "+profileId+" AND (lessonWeekDay != "+todayWeekDay+" OR '"+nowTime.getStringValue()+"' < lessonEndTime OR '"+nowTime.getStringValue()+"' > lessonStartTime)\n" +
|
||||
"ORDER BY lessonDate, lessonStartTime ASC";
|
||||
//Log.d("DB", "Query "+query);
|
||||
return getAllNow(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
public LiveData<List<LessonFull>> getAllByDateWithoutChanges(int profileId, @NonNull Date date) {
|
||||
String query = "SELECT\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectLongName,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"WHERE lessons.profileId = "+profileId+" AND lessonWeekDay = "+date.getWeekDay()+"\n" +
|
||||
"ORDER BY lessonStartTime ASC";
|
||||
//Log.d("DB", "Query "+query);
|
||||
return getAll(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
|
||||
public LessonFull getByDateTimeWithoutChangesNow(int profileId, @NonNull Date date, @NonNull Time time) {
|
||||
String query = "SELECT\n" +
|
||||
"lessons.*,\n" +
|
||||
"subjects.subjectLongName,\n" +
|
||||
"subjects.subjectShortName,\n" +
|
||||
"teams.teamName,\n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" +
|
||||
"FROM lessons\n" +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId)\n" +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId)\n" +
|
||||
"LEFT JOIN teams USING(profileId, teamId)\n" +
|
||||
"WHERE lessons.profileId = "+profileId+" AND lessonWeekDay = "+date.getWeekDay()+" AND lessonStartTime = '"+time.getStringValue()+"'\n" +
|
||||
"ORDER BY lessonStartTime ASC";
|
||||
//Log.d("DB", "Query "+query);
|
||||
return getNow(new SimpleSQLiteQuery(query));
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_ADDED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_CANCELLED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.LessonChange.TYPE_CHANGE;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
|
||||
public class LessonFull extends Lesson {
|
||||
public String teacherFullName = "";
|
||||
|
||||
public String subjectLongName = "";
|
||||
public String subjectShortName = "";
|
||||
|
||||
public String teamName = "";
|
||||
|
||||
@Nullable
|
||||
public Date lessonDate;
|
||||
|
||||
@ColumnInfo(name = "lessonChangeId")
|
||||
public long changeId;
|
||||
@ColumnInfo(name = "lessonChangeType")
|
||||
public int changeType = -1;
|
||||
@ColumnInfo(name = "lessonChangeClassroomName")
|
||||
public String changeClassroomName = "";
|
||||
public String changeTeacherFullName = "";
|
||||
|
||||
public String changeSubjectLongName = "";
|
||||
public String changeSubjectShortName = "";
|
||||
|
||||
public String changeTeamName = "";
|
||||
|
||||
public long changeTeacherId;
|
||||
public long changeSubjectId;
|
||||
public long changeTeamId;
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
|
||||
public boolean lessonPassed;
|
||||
public boolean lessonCurrent;
|
||||
|
||||
public boolean changedTeacherFullName() {
|
||||
return changeId != 0 && changeType != TYPE_CANCELLED && changeTeacherFullName != null && !changeTeacherFullName.equals(teacherFullName) && !changeTeacherFullName.equals("");
|
||||
}
|
||||
public boolean changedSubjectLongName() {
|
||||
return changeId != 0 && changeType != TYPE_CANCELLED && changeSubjectLongName != null && !changeSubjectLongName.equals(subjectLongName) && !changeSubjectLongName.equals("");
|
||||
}
|
||||
public boolean changedTeamName() {
|
||||
return changeId != 0 && changeType != TYPE_CANCELLED && changeTeamName != null && !changeTeamName.equals(teamName) && !changeTeamName.equals("");
|
||||
}
|
||||
public boolean changedClassroomName() {
|
||||
return changeId != 0 && changeType != TYPE_CANCELLED && changeClassroomName != null && !changeClassroomName.equals(classroomName) && !changeClassroomName.equals("");
|
||||
}
|
||||
|
||||
public String getTeacherFullName() {
|
||||
return getTeacherFullName(false);
|
||||
}
|
||||
public String getTeacherFullName(boolean formatted) {
|
||||
if (!changedTeacherFullName())
|
||||
return bs(teacherFullName);
|
||||
else
|
||||
return (formatted?bs(teacherFullName)+" -> ":"") + bs(changeTeacherFullName);
|
||||
}
|
||||
|
||||
public String getSubjectLongName() {
|
||||
return getSubjectLongName(false);
|
||||
}
|
||||
public String getSubjectLongName(boolean formatted) {
|
||||
if (!changedSubjectLongName())
|
||||
return bs(subjectLongName);
|
||||
else
|
||||
return (formatted?bs(subjectLongName)+" -> ":"") + bs(changeSubjectLongName);
|
||||
}
|
||||
|
||||
public String getTeamName() {
|
||||
return getTeamName(false);
|
||||
}
|
||||
public String getTeamName(boolean formatted) {
|
||||
if (!changedTeamName())
|
||||
return bs(teamName);
|
||||
else
|
||||
return (formatted?bs(teamName)+" -> ":"") + bs(changeTeamName);
|
||||
}
|
||||
|
||||
public String getClassroomName() {
|
||||
return getClassroomName(false);
|
||||
}
|
||||
public String getClassroomName(boolean formatted) {
|
||||
if (!changedClassroomName())
|
||||
return bs(classroomName);
|
||||
else
|
||||
return (formatted?bs(classroomName)+" -> ":"") + bs(changeClassroomName);
|
||||
}
|
||||
|
||||
public String changeTypeStr(Context context) {
|
||||
if (changeType == TYPE_CANCELLED) {
|
||||
return context.getString(R.string.lesson_cancelled);
|
||||
}
|
||||
if (changeType == TYPE_CHANGE) {
|
||||
return context.getString(R.string.lesson_change);
|
||||
}
|
||||
if (changeType == TYPE_ADDED) {
|
||||
return context.getString(R.string.lesson_added);
|
||||
}
|
||||
return context.getString(R.string.lesson_timetable_change);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LessonFull{" +
|
||||
"profileId=" + profileId +
|
||||
", weekDay=" + weekDay +
|
||||
", startTime=" + startTime +
|
||||
", endTime=" + endTime +
|
||||
", classroomName='" + classroomName + '\'' +
|
||||
", teacherId=" + teacherId +
|
||||
", subjectId=" + subjectId +
|
||||
", teamId=" + teamId +
|
||||
", teacherFullName='" + teacherFullName + '\'' +
|
||||
", subjectLongName='" + subjectLongName + '\'' +
|
||||
", subjectShortName='" + subjectShortName + '\'' +
|
||||
", teamName='" + teamName + '\'' +
|
||||
", lessonDate=" + lessonDate +
|
||||
", changeId=" + changeId +
|
||||
", changeType=" + changeType +
|
||||
", changeClassroomName='" + changeClassroomName + '\'' +
|
||||
", changeTeacherFullName='" + changeTeacherFullName + '\'' +
|
||||
", changeSubjectLongName='" + changeSubjectLongName + '\'' +
|
||||
", changeSubjectShortName='" + changeSubjectShortName + '\'' +
|
||||
", changeTeamName='" + changeTeamName + '\'' +
|
||||
", changeTeacherId=" + changeTeacherId +
|
||||
", changeSubjectId=" + changeSubjectId +
|
||||
", changeTeamId=" + changeTeamId +
|
||||
", seen=" + seen +
|
||||
", notified=" + notified +
|
||||
", addedDate=" + addedDate +
|
||||
", lessonPassed=" + lessonPassed +
|
||||
", lessonCurrent=" + lessonCurrent +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,190 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Ignore;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@Entity(tableName = "loginStores",
|
||||
primaryKeys = {"loginStoreId"})
|
||||
public class LoginStore {
|
||||
@ColumnInfo(name = "loginStoreId")
|
||||
public int id = -1;
|
||||
@ColumnInfo(name = "loginStoreType")
|
||||
public int type = -1;
|
||||
public final static int LOGIN_TYPE_MOBIDZIENNIK = 1;
|
||||
public final static int LOGIN_TYPE_LIBRUS = 2;
|
||||
public final static int LOGIN_TYPE_VULCAN = 4;
|
||||
public final static int LOGIN_TYPE_IUCZNIOWIE = 3;
|
||||
public final static int LOGIN_TYPE_DEMO = 20;
|
||||
@ColumnInfo(name = "loginStoreData")
|
||||
public JsonObject data;
|
||||
|
||||
@Ignore
|
||||
public int mode = 0;
|
||||
public static final int LOGIN_MODE_LIBRUS_EMAIL = 0;
|
||||
public static final int LOGIN_MODE_LIBRUS_SYNERGIA = 1;
|
||||
public static final int LOGIN_MODE_LIBRUS_JST = 2;
|
||||
|
||||
public LoginStore(int id, int type, JsonObject data) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static LoginStore fromProfileFull(ProfileFull profileFull) {
|
||||
return new LoginStore(profileFull.getLoginStoreId(), profileFull.getLoginStoreType(), profileFull.getLoginStoreData());
|
||||
}
|
||||
|
||||
public void copyFrom(Bundle args) {
|
||||
for (String key: args.keySet()) {
|
||||
Object o = args.get(key);
|
||||
if (o instanceof String) {
|
||||
putLoginData(key, (String) o);
|
||||
}
|
||||
else if (o instanceof Integer) {
|
||||
putLoginData(key, (Integer) o);
|
||||
}
|
||||
else if (o instanceof Long) {
|
||||
putLoginData(key, (Long) o);
|
||||
}
|
||||
else if (o instanceof Float) {
|
||||
putLoginData(key, (Float) o);
|
||||
}
|
||||
else if (o instanceof Boolean) {
|
||||
putLoginData(key, (Boolean) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Nullable
|
||||
public String getLoginData(String key, @Nullable String defaultValue) {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
return element.getAsString();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
@Nullable
|
||||
public int getLoginData(String key, int defaultValue) {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
return element.getAsInt();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
@Nullable
|
||||
public long getLoginData(String key, long defaultValue) {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
return element.getAsLong();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
@Nullable
|
||||
public float getLoginData(String key, float defaultValue) {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
return element.getAsFloat();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
public boolean getLoginData(String key, boolean defaultValue) {
|
||||
if (data == null)
|
||||
return defaultValue;
|
||||
JsonElement element = data.get(key);
|
||||
if (element != null) {
|
||||
return element.getAsBoolean();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void putLoginData(String key, String value) {
|
||||
forceLoginStore();
|
||||
data.addProperty(key, value);
|
||||
}
|
||||
public void putLoginData(String key, int value) {
|
||||
forceLoginStore();
|
||||
data.addProperty(key, value);
|
||||
}
|
||||
public void putLoginData(String key, long value) {
|
||||
forceLoginStore();
|
||||
data.addProperty(key, value);
|
||||
}
|
||||
public void putLoginData(String key, float value) {
|
||||
forceLoginStore();
|
||||
data.addProperty(key, value);
|
||||
}
|
||||
public void putLoginData(String key, boolean value) {
|
||||
forceLoginStore();
|
||||
data.addProperty(key, value);
|
||||
}
|
||||
|
||||
public void removeLoginData(String key) {
|
||||
if (data == null)
|
||||
return;
|
||||
data.remove(key);
|
||||
}
|
||||
|
||||
public void clearLoginStore() {
|
||||
data = new JsonObject();
|
||||
}
|
||||
|
||||
private void forceLoginStore() {
|
||||
if (data == null) {
|
||||
clearLoginStore();
|
||||
}
|
||||
}
|
||||
|
||||
public String type() {
|
||||
switch (type) {
|
||||
case LOGIN_TYPE_MOBIDZIENNIK:
|
||||
return "LOGIN_TYPE_MOBIDZIENNIK";
|
||||
case LOGIN_TYPE_LIBRUS:
|
||||
return "LOGIN_TYPE_LIBRUS";
|
||||
case LOGIN_TYPE_IUCZNIOWIE:
|
||||
return "LOGIN_TYPE_IUCZNIOWIE";
|
||||
case LOGIN_TYPE_VULCAN:
|
||||
return "LOGIN_TYPE_VULCAN";
|
||||
case LOGIN_TYPE_DEMO:
|
||||
return "LOGIN_TYPE_DEMO";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
public String mode() {
|
||||
switch (mode) {
|
||||
case LOGIN_MODE_LIBRUS_EMAIL:
|
||||
return "LOGIN_MODE_LIBRUS_EMAIL";
|
||||
case LOGIN_MODE_LIBRUS_SYNERGIA:
|
||||
return "LOGIN_MODE_LIBRUS_SYNERGIA";
|
||||
case LOGIN_MODE_LIBRUS_JST:
|
||||
return "LOGIN_MODE_LIBRUS_JST";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LoginStore{" +
|
||||
"id=" + id +
|
||||
", type=" + type() +
|
||||
", mode=" + mode() +
|
||||
", data=" + data +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public abstract class LoginStoreDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void add(LoginStore loginStore);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<LoginStore> loginStoreList);
|
||||
|
||||
@Query("DELETE FROM loginStores WHERE loginStoreId = :loginStoreId")
|
||||
public abstract void remove(int loginStoreId);
|
||||
|
||||
@Query("SELECT * FROM loginStores WHERE loginStoreId = :loginStoreId")
|
||||
public abstract LiveData<LoginStore> getById(int loginStoreId);
|
||||
|
||||
@Query("SELECT * FROM loginStores WHERE loginStoreId = :loginStoreId")
|
||||
public abstract LoginStore getByIdNow(int loginStoreId);
|
||||
|
||||
public void add(ProfileFull profileFull) {
|
||||
add(new LoginStore(profileFull.getLoginStoreId(), profileFull.getLoginStoreType(), profileFull.getLoginStoreData()));
|
||||
}
|
||||
|
||||
@Query("UPDATE loginStores SET loginStoreId = :targetId WHERE loginStoreId = :sourceId")
|
||||
public abstract void changeId(int sourceId, int targetId);
|
||||
|
||||
@Query("SELECT * FROM loginStores ORDER BY loginStoreId")
|
||||
public abstract List<LoginStore> getAllNow();
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
@Entity(tableName = "luckyNumbers",
|
||||
primaryKeys = {"profileId", "luckyNumberDate"})
|
||||
public class LuckyNumber {
|
||||
public int profileId;
|
||||
|
||||
@NonNull
|
||||
@ColumnInfo(name = "luckyNumberDate")
|
||||
public Date date;
|
||||
@ColumnInfo(name = "luckyNumber")
|
||||
public int number;
|
||||
|
||||
public LuckyNumber(int profileId, @NonNull Date date, int number) {
|
||||
this.profileId = profileId;
|
||||
this.date = date;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.models.Date;
|
||||
|
||||
@Dao
|
||||
public interface LuckyNumberDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void add(LuckyNumber luckyNumber);
|
||||
|
||||
@Query("DELETE FROM luckyNumbers WHERE profileId = :profileId")
|
||||
void clear(int profileId);
|
||||
|
||||
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date")
|
||||
LiveData<LuckyNumber> getByDate(int profileId, Date date);
|
||||
|
||||
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date")
|
||||
LuckyNumber getByDateNow(int profileId, Date date);
|
||||
|
||||
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId ORDER BY luckyNumberDate DESC")
|
||||
LiveData<List<LuckyNumber>> getAll(int profileId);
|
||||
|
||||
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId ORDER BY luckyNumberDate DESC")
|
||||
List<LuckyNumber> getAllNow(int profileId);
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.Index;
|
||||
|
||||
@Entity(tableName = "messages",
|
||||
primaryKeys = {"profileId", "messageId"},
|
||||
indices = {@Index(value = {"profileId"})})
|
||||
public class Message {
|
||||
int profileId;
|
||||
|
||||
@ColumnInfo(name = "messageId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "messageSubject")
|
||||
public String subject;
|
||||
@Nullable
|
||||
@ColumnInfo(name = "messageBody")
|
||||
public String body = null;
|
||||
|
||||
public static final int TYPE_RECEIVED = 0;
|
||||
public static final int TYPE_SENT = 1;
|
||||
public static final int TYPE_DELETED = 2;
|
||||
public static final int TYPE_DRAFT = 3;
|
||||
@ColumnInfo(name = "messageType")
|
||||
public int type = TYPE_RECEIVED;
|
||||
|
||||
public long senderId = -1; // -1 for sent messages
|
||||
public long senderReplyId = -1;
|
||||
public boolean overrideHasAttachments = false; // if the attachments are not yet downloaded but we already know there are some
|
||||
public List<Long> attachmentIds = null;
|
||||
public List<String> attachmentNames = null;
|
||||
public List<Long> attachmentSizes = null;
|
||||
|
||||
@Ignore
|
||||
public Message() {}
|
||||
|
||||
public Message(int profileId, long id, String subject, @Nullable String body, int type, long senderId, long senderReplyId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.subject = subject;
|
||||
this.body = body;
|
||||
this.type = type;
|
||||
this.senderId = senderId;
|
||||
this.senderReplyId = senderReplyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attachment
|
||||
* @param id attachment ID
|
||||
* @param name file name incl. extension
|
||||
* @param size file size or -1 if unknown
|
||||
* @return a Message to which the attachment has been added
|
||||
*/
|
||||
public Message addAttachment(long id, String name, long size) {
|
||||
if (attachmentIds == null)
|
||||
attachmentIds = new ArrayList<>();
|
||||
if (attachmentNames == null)
|
||||
attachmentNames = new ArrayList<>();
|
||||
if (attachmentSizes == null)
|
||||
attachmentSizes = new ArrayList<>();
|
||||
attachmentIds.add(id);
|
||||
attachmentNames.add(name);
|
||||
attachmentSizes.add(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void clearAttachments() {
|
||||
attachmentIds = null;
|
||||
attachmentNames = null;
|
||||
attachmentSizes = null;
|
||||
}
|
||||
|
||||
public Message setHasAttachments() {
|
||||
overrideHasAttachments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasAttachments() {
|
||||
return overrideHasAttachments || (attachmentIds != null && attachmentIds.size() > 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_DELETED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_RECEIVED;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Message.TYPE_SENT;
|
||||
import static pl.szczodrzynski.edziennik.datamodels.Metadata.TYPE_MESSAGE;
|
||||
|
||||
@Dao
|
||||
public abstract class MessageDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract long add(Message message);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
public abstract void addAllIgnore(List<Message> messageList);
|
||||
|
||||
@Query("DELETE FROM messages WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
|
||||
@RawQuery(observedEntities = {Message.class})
|
||||
abstract LiveData<List<MessageFull>> getAll(SupportSQLiteQuery query);
|
||||
@RawQuery(observedEntities = {Message.class, Metadata.class})
|
||||
abstract List<MessageFull> getNow(SupportSQLiteQuery query);
|
||||
@RawQuery(observedEntities = {Message.class, Metadata.class})
|
||||
abstract MessageFull getOneNow(SupportSQLiteQuery query);
|
||||
|
||||
public LiveData<List<MessageFull>> getWithMetadataAndSenderName(int profileId, int messageType, String filter) {
|
||||
return getAll(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
|
||||
"FROM messages \n" +
|
||||
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
|
||||
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE messages.profileId = "+profileId+" AND messageType = "+messageType+" AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
|
||||
public LiveData<List<MessageFull>> getWithMetadata(int profileId, int messageType, String filter) {
|
||||
return getAll(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"* \n" +
|
||||
"FROM messages \n" +
|
||||
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE messages.profileId = "+profileId+" AND messageType = "+messageType+" AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
|
||||
public MessageFull getById(int profileId, long messageId) {
|
||||
return getOneNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
|
||||
"FROM messages \n" +
|
||||
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
|
||||
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE messages.profileId = "+profileId+" AND messageId = "+messageId+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
|
||||
public LiveData<List<MessageFull>> getReceived(int profileId) {
|
||||
return getWithMetadataAndSenderName(profileId, TYPE_RECEIVED, "1");
|
||||
}
|
||||
public LiveData<List<MessageFull>> getDeleted(int profileId) {
|
||||
return getWithMetadataAndSenderName(profileId, TYPE_DELETED, "1");
|
||||
}
|
||||
public LiveData<List<MessageFull>> getSent(int profileId) {
|
||||
return getWithMetadata(profileId, TYPE_SENT, "1");
|
||||
}
|
||||
|
||||
public List<MessageFull> getReceivedNow(int profileId, String filter) {
|
||||
return getNow(new SimpleSQLiteQuery("SELECT \n" +
|
||||
"*, \n" +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName\n" +
|
||||
"FROM messages \n" +
|
||||
"LEFT JOIN teachers ON teachers.profileId = "+profileId+" AND teacherId = senderId\n" +
|
||||
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = "+profileId+"\n" +
|
||||
"WHERE messages.profileId = "+profileId+" AND messageType = 0 AND "+filter+"\n" +
|
||||
"ORDER BY addedDate DESC"));
|
||||
}
|
||||
public List<MessageFull> getReceivedNotNotifiedNow(int profileId) {
|
||||
return getReceivedNow(profileId, "notified = 0");
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package pl.szczodrzynski.edziennik.datamodels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.Ignore;
|
||||
|
||||
public class MessageFull extends Message {
|
||||
public String senderFullName = null;
|
||||
@Ignore
|
||||
public List<MessageRecipientFull> recipients = null;
|
||||
|
||||
public MessageFull addRecipient(MessageRecipientFull recipient) {
|
||||
if (recipients == null)
|
||||
recipients = new ArrayList<>();
|
||||
recipients.add(recipient);
|
||||
return this;
|
||||
}
|
||||
|
||||
// metadata
|
||||
public boolean seen;
|
||||
public boolean notified;
|
||||
public long addedDate;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user