mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-02-20 13:54:43 +01:00
[Messages] Add sending messages and fix stuff.
This commit is contained in:
parent
f292b3637d
commit
eae7189981
45
.idea/jarRepositories.xml
generated
Normal file
45
.idea/jarRepositories.xml
generated
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven2" />
|
||||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://kotlin.bintray.com/kotlinx/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven3" />
|
||||
<option name="name" value="maven3" />
|
||||
<option name="url" value="https://dl.bintray.com/wulkanowy/wulkanowy" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="BintrayJCenter" />
|
||||
<option name="name" value="BintrayJCenter" />
|
||||
<option name="url" value="https://jcenter.bintray.com/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="Google" />
|
||||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
13
app/sampledata/format-bold/ic_format_bold.xml
Normal file
13
app/sampledata/format-bold/ic_format_bold.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.5,15.5H10V12.5H13.5A1.5,1.5 0,0 1,15 14A1.5,1.5 0,0 1,13.5 15.5M10,6.5H13A1.5,1.5 0,0 1,14.5 8A1.5,1.5 0,0 1,13 9.5H10M15.6,10.79C16.57,10.11 17.25,9 17.25,8C17.25,5.74 15.5,4 13.25,4H7V18H14.04C16.14,18 17.75,16.3 17.75,14.21C17.75,12.69 16.89,11.39 15.6,10.79Z"/>
|
||||
</vector>
|
13
app/sampledata/format/ic_format_italic.xml
Normal file
13
app/sampledata/format/ic_format_italic.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z"/>
|
||||
</vector>
|
13
app/sampledata/format/ic_format_underline.xml
Normal file
13
app/sampledata/format/ic_format_underline.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M5,21H19V19H5V21M12,17A6,6 0,0 0,18 11V3H15.5V11A3.5,3.5 0,0 1,12 14.5A3.5,3.5 0,0 1,8.5 11V3H6V11A6,6 0,0 0,12 17Z"/>
|
||||
</vector>
|
@ -32,11 +32,6 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.modules.messages.MessagesComposeActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/messages_compose_title"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
<activity
|
||||
android:name=".ui.modules.feedback.FeedbackActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
|
@ -1,6 +1,7 @@
|
||||
<h3>Wersja 4.0-beta.1, 2020-01-02</h3>
|
||||
<ul>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
|
||||
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
||||
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
||||
@ -20,10 +21,9 @@
|
||||
<b>Uwaga.</b> Ponieważ to wersja <i>beta</i>, niektóre funkcje mogą nie działać prawidłowo.<br>
|
||||
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
|
||||
<ul>
|
||||
<li>Wysyłanie wiadomości może czasami nie działać - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
|
||||
<li>Widget powiadomień</li>
|
||||
<li>Zgłaszanie błędów</li>
|
||||
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
|
||||
<li>Obsługa błędów - rzadko występuje</li>
|
||||
<li>Brak generowania blokowego planu lekcji</li>
|
||||
</ul>
|
||||
<br>
|
||||
|
@ -67,8 +67,9 @@ import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.TlsVersion;
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing;
|
||||
import pl.szczodrzynski.edziennik.config.Config;
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing;
|
||||
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
|
||||
@ -438,6 +439,11 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
|
||||
"Vulcan"
|
||||
);
|
||||
|
||||
if (config.getRunSync()) {
|
||||
config.setRunSync(false);
|
||||
EdziennikTask.Companion.sync().enqueue(this);
|
||||
}
|
||||
|
||||
try {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
|
@ -4,6 +4,7 @@ import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
@ -15,6 +16,7 @@ import android.text.*
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.Base64
|
||||
import android.util.Base64.NO_WRAP
|
||||
import android.util.Base64.encodeToString
|
||||
import android.util.LongSparseArray
|
||||
@ -42,10 +44,10 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.getColorFromRes
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.math.BigInteger
|
||||
import java.nio.charset.Charset
|
||||
import java.security.MessageDigest
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@ -99,12 +101,31 @@ fun Bundle?.getString(key: String, defaultValue: String): String {
|
||||
return this?.getString(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
fun String.fixName(): String {
|
||||
return this.fixWhiteSpaces().toProperCase()
|
||||
/**
|
||||
* ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. `
|
||||
*
|
||||
* converts to
|
||||
*
|
||||
* `The Quick Brown_fox Jumps Over The Lazy-Dog.`
|
||||
*/
|
||||
fun String?.fixName(): String {
|
||||
return this?.fixWhiteSpaces()?.toProperCase() ?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
* `The quick BROWN_fox Jumps OveR THE LAZy-DOG.`
|
||||
*
|
||||
* converts to
|
||||
*
|
||||
* `The Quick Brown_fox Jumps Over The Lazy-Dog.`
|
||||
*/
|
||||
fun String.toProperCase(): String = changeStringCase(this)
|
||||
|
||||
/**
|
||||
* `John Smith` -> `Smith John`
|
||||
*
|
||||
* `JOHN SMith` -> `SMith JOHN`
|
||||
*/
|
||||
fun String.swapFirstLastName(): String {
|
||||
return this.split(" ").let {
|
||||
if (it.size > 1)
|
||||
@ -150,33 +171,52 @@ fun String.getShortName(): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun List<String>.join(delimiter: String): String {
|
||||
return this.joinToString(delimiter)
|
||||
/**
|
||||
* "John Smith" -> "JS"
|
||||
*
|
||||
* "JOHN SMith" -> "JS"
|
||||
*
|
||||
* "John" -> "J"
|
||||
*
|
||||
* "John " -> "J"
|
||||
*
|
||||
* "John Smith " -> "JS"
|
||||
*
|
||||
* " " -> ""
|
||||
*
|
||||
* " " -> ""
|
||||
*/
|
||||
fun String?.getNameInitials(): String {
|
||||
if (this.isNullOrBlank()) return ""
|
||||
return this.toUpperCase().fixWhiteSpaces().split(" ").take(2).map { it[0] }.joinToString("")
|
||||
}
|
||||
|
||||
fun colorFromName(context: Context, name: String?): Int {
|
||||
fun List<String>.join(delimiter: String): String {
|
||||
return concat(delimiter).toString()
|
||||
}
|
||||
|
||||
fun colorFromName(name: String?): Int {
|
||||
var crc = (name ?: "").crc16()
|
||||
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)
|
||||
return when (crc) {
|
||||
13 -> 0xffF44336
|
||||
4 -> 0xffF50057
|
||||
2 -> 0xffD500F9
|
||||
9 -> 0xff6200EA
|
||||
5 -> 0xff3F51B5
|
||||
1 -> 0xff304FFE
|
||||
6 -> 0xff18FFFF
|
||||
14 -> 0xff26A69A
|
||||
15 -> 0xff4CAF50
|
||||
7 -> 0xffFFD600
|
||||
3 -> 0xffFF3D00
|
||||
8 -> 0xffDD2C00
|
||||
10 -> 0xff795548
|
||||
12 -> 0xffBDBDBD
|
||||
11 -> 0xff78909C
|
||||
else -> 0xff64DD17
|
||||
}.toInt()
|
||||
}
|
||||
|
||||
fun colorFromCssName(name: String): Int {
|
||||
@ -242,6 +282,21 @@ fun <T> SparseArray<T>.values(): List<T> {
|
||||
return result
|
||||
}
|
||||
|
||||
fun <K, V> List<Pair<K, V>>.keys(): List<K> {
|
||||
val result = mutableListOf<K>()
|
||||
forEach { pair ->
|
||||
result += pair.first
|
||||
}
|
||||
return result
|
||||
}
|
||||
fun <K, V> List<Pair<K, V>>.values(): List<V> {
|
||||
val result = mutableListOf<V>()
|
||||
forEach { pair ->
|
||||
result += pair.second
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun <T> List<T>.toSparseArray(destination: SparseArray<T>, key: (T) -> Int) {
|
||||
forEach {
|
||||
destination.put(key(it), it)
|
||||
@ -429,6 +484,24 @@ fun CharSequence?.asBoldSpannable(): Spannable {
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
if (substring == null) {
|
||||
spans.forEach {
|
||||
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
else if (substring.isNotEmpty()) {
|
||||
var index = indexOf(substring, ignoreCase = ignoreCase)
|
||||
while (index >= 0) {
|
||||
spans.forEach {
|
||||
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase);
|
||||
}
|
||||
}
|
||||
return spannable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new read-only list only of those given elements, that are not empty.
|
||||
@ -436,7 +509,7 @@ fun CharSequence?.asBoldSpannable(): Spannable {
|
||||
*/
|
||||
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
|
||||
|
||||
fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
|
||||
fun List<CharSequence?>.concat(delimiter: CharSequence? = null): CharSequence {
|
||||
if (this.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
@ -445,11 +518,13 @@ fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
|
||||
return this[0] ?: ""
|
||||
}
|
||||
|
||||
var spanned = false
|
||||
for (piece in this) {
|
||||
if (piece is Spanned) {
|
||||
spanned = true
|
||||
break
|
||||
var spanned = delimiter is Spanned
|
||||
if (!spanned) {
|
||||
for (piece in this) {
|
||||
if (piece is Spanned) {
|
||||
spanned = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,8 +572,44 @@ fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonArray(vararg properties: Any?): JsonArray {
|
||||
return JsonArray().apply {
|
||||
for (property in properties) {
|
||||
when (property) {
|
||||
is JsonElement -> add(property as JsonElement?)
|
||||
is String -> add(property as String?)
|
||||
is Char -> add(property as Char?)
|
||||
is Number -> add(property as Number?)
|
||||
is Boolean -> add(property as Boolean?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
|
||||
return Bundle().apply {
|
||||
for (property in properties) {
|
||||
when (property.second) {
|
||||
is String -> putString(property.first, property.second as String?)
|
||||
is Char -> putChar(property.first, property.second as Char)
|
||||
is Int -> putInt(property.first, property.second as Int)
|
||||
is Long -> putLong(property.first, property.second as Long)
|
||||
is Float -> putFloat(property.first, property.second as Float)
|
||||
is Short -> putShort(property.first, property.second as Short)
|
||||
is Double -> putDouble(property.first, property.second as Double)
|
||||
is Boolean -> putBoolean(property.first, property.second as Boolean)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
|
||||
fun JsonArray.isEmpty(): Boolean = this.size() == 0
|
||||
operator fun JsonArray.plusAssign(o: JsonElement) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: String) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: Char) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: Number) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: Boolean) = this.add(o)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
|
||||
@ -696,3 +807,75 @@ val Throwable.stackTraceString: String
|
||||
printStackTrace(PrintWriter(sw))
|
||||
return sw.toString()
|
||||
}
|
||||
|
||||
inline fun <T> LongSparseArray<T>.filter(predicate: (T) -> Boolean): List<T> {
|
||||
val destination = ArrayList<T>()
|
||||
this.forEach { _, element -> if (predicate(element)) destination.add(element) }
|
||||
return destination
|
||||
}
|
||||
|
||||
fun CharSequence.replace(oldValue: String, newValue: CharSequence, ignoreCase: Boolean = false): CharSequence =
|
||||
splitToSequence(oldValue, ignoreCase = ignoreCase).toList().concat(newValue)
|
||||
|
||||
fun Int.toColorStateList(): ColorStateList {
|
||||
val states = arrayOf(
|
||||
intArrayOf( android.R.attr.state_enabled ),
|
||||
intArrayOf(-android.R.attr.state_enabled ),
|
||||
intArrayOf(-android.R.attr.state_checked ),
|
||||
intArrayOf( android.R.attr.state_pressed )
|
||||
)
|
||||
|
||||
val colors = intArrayOf(
|
||||
this,
|
||||
this,
|
||||
this,
|
||||
this
|
||||
)
|
||||
|
||||
return ColorStateList(states, colors);
|
||||
}
|
||||
|
||||
fun SpannableStringBuilder.appendText(text: CharSequence): SpannableStringBuilder {
|
||||
append(text)
|
||||
return this
|
||||
}
|
||||
fun SpannableStringBuilder.appendSpan(text: CharSequence, what: Any, flags: Int): SpannableStringBuilder {
|
||||
val start: Int = length
|
||||
append(text)
|
||||
setSpan(what, start, length, flags)
|
||||
return this
|
||||
}
|
||||
|
||||
fun joinNotNullStrings(delimiter: String = "", vararg parts: String?): String {
|
||||
var first = true
|
||||
val sb = StringBuilder()
|
||||
for (part in parts) {
|
||||
if (part == null)
|
||||
continue
|
||||
if (!first)
|
||||
sb += delimiter
|
||||
first = false
|
||||
sb += part
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun String.notEmptyOrNull(): String? {
|
||||
return if (isEmpty())
|
||||
null
|
||||
else
|
||||
this
|
||||
}
|
||||
|
||||
fun String.base64Encode(): String {
|
||||
return encodeToString(toByteArray(), NO_WRAP)
|
||||
}
|
||||
fun ByteArray.base64Encode(): String {
|
||||
return encodeToString(this, NO_WRAP)
|
||||
}
|
||||
fun String.base64Decode(): ByteArray {
|
||||
return Base64.decode(this, Base64.DEFAULT)
|
||||
}
|
||||
fun String.base64DecodeToString(): String {
|
||||
return Base64.decode(this, Base64.DEFAULT).toString(Charset.defaultCharset())
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.danimahardhika.cafebar.CafeBar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.mikepenz.iconics.IconicsColor
|
||||
@ -53,6 +54,7 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar
|
||||
import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
||||
@ -63,7 +65,9 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
||||
@ -120,6 +124,7 @@ class MainActivity : AppCompatActivity() {
|
||||
const val TARGET_HELP = 502
|
||||
const val TARGET_FEEDBACK = 120
|
||||
const val TARGET_MESSAGES_DETAILS = 503
|
||||
const val TARGET_MESSAGES_COMPOSE = 504
|
||||
const val TARGET_WEB_PUSH = 140
|
||||
|
||||
const val HOME_ID = DRAWER_ITEM_HOME
|
||||
@ -211,7 +216,8 @@ class MainActivity : AppCompatActivity() {
|
||||
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, MessageFragment::class)
|
||||
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES)
|
||||
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
|
||||
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
|
||||
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
|
||||
|
||||
@ -223,6 +229,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val navView: NavView by lazy { b.navView }
|
||||
val drawer: NavDrawer by lazy { navView.drawer }
|
||||
val bottomSheet: NavBottomSheet by lazy { navView.bottomSheet }
|
||||
val mainSnackbar: MainSnackbar by lazy { MainSnackbar(this) }
|
||||
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
|
||||
|
||||
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
|
||||
@ -233,10 +240,11 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private val fragmentManager by lazy { supportFragmentManager }
|
||||
private lateinit var navTarget: NavTarget
|
||||
private var navArguments: Bundle? = null
|
||||
val navTargetId
|
||||
get() = navTarget.id
|
||||
|
||||
private val navBackStack = mutableListOf<NavTarget>()
|
||||
private val navBackStack = mutableListOf<Pair<NavTarget, Bundle?>>()
|
||||
private var navLoading = true
|
||||
|
||||
/* ____ _____ _
|
||||
@ -258,6 +266,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
Log.d(TAG, Signing.appPassword)
|
||||
|
||||
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
|
||||
navLoading = true
|
||||
@ -552,7 +561,7 @@ class MainActivity : AppCompatActivity() {
|
||||
).enqueue(this)
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncStartedEvent(event: ApiTaskStartedEvent) {
|
||||
fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) {
|
||||
swipeRefreshLayout.isRefreshing = true
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
@ -563,7 +572,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncProgressEvent(event: ApiTaskProgressEvent) {
|
||||
fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = null
|
||||
@ -576,8 +585,8 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncProfileFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
@ -586,17 +595,18 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncErrorEvent(event: ApiTaskErrorEvent) {
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread
|
||||
subtitle = "Gotowe"
|
||||
}
|
||||
mainSnackbar.dismiss()
|
||||
errorSnackbar.addError(event.error).show()
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
@ -800,6 +810,10 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
AsyncTask.execute {
|
||||
app.profileLoadById(id)
|
||||
MessagesFragment.pageSelection = -1
|
||||
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
|
||||
this.runOnUiThread {
|
||||
if (app.profile == null) {
|
||||
@ -825,7 +839,7 @@ class MainActivity : AppCompatActivity() {
|
||||
loadId = DRAWER_ITEM_HOME
|
||||
}
|
||||
val target = navTargetList
|
||||
.singleOrNull { it.id == loadId }
|
||||
.firstOrNull { it.id == loadId }
|
||||
if (target == null) {
|
||||
Toast.makeText(this, getString(R.string.error_invalid_fragment, id), Toast.LENGTH_LONG).show()
|
||||
loadTarget(navTargetList.first(), arguments)
|
||||
@ -835,7 +849,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
private fun loadTarget(target: NavTarget, arguments: Bundle? = null) {
|
||||
d("NavDebug", "loadItem(id = ${target.id})")
|
||||
d("NavDebug", "loadTarget(target = $target, arguments = $arguments)")
|
||||
|
||||
bottomSheet.close()
|
||||
bottomSheet.removeAllContextual()
|
||||
@ -862,7 +876,7 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
else {
|
||||
navBackStack.lastIndexOf(target).let {
|
||||
navBackStack.keys().lastIndexOf(target).let {
|
||||
if (it == -1)
|
||||
return@let target
|
||||
// pop the back stack up until that target
|
||||
@ -893,8 +907,9 @@ class MainActivity : AppCompatActivity() {
|
||||
R.anim.task_open_enter,
|
||||
R.anim.task_open_exit
|
||||
)
|
||||
navBackStack.add(navTarget)
|
||||
navBackStack.add(navTarget to arguments)
|
||||
navTarget = target
|
||||
navArguments = arguments
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,7 +924,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
d("NavDebug", "Current fragment ${navTarget.fragmentClass?.java?.simpleName}, pop to home ${navTarget.popToHome}, back stack:")
|
||||
navBackStack.forEachIndexed { index, target2 ->
|
||||
d("NavDebug", " - $index: ${target2.fragmentClass?.java?.simpleName}")
|
||||
d("NavDebug", " - $index: ${target2.first.fragmentClass?.java?.simpleName}")
|
||||
}
|
||||
|
||||
transaction.replace(R.id.fragment, fragment)
|
||||
@ -934,11 +949,18 @@ class MainActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
// TODO back stack argument support
|
||||
if (navTarget.popToHome) {
|
||||
loadTarget(HOME_ID)
|
||||
}
|
||||
else {
|
||||
loadTarget(navBackStack.last())
|
||||
when {
|
||||
navTarget.popToHome -> {
|
||||
loadTarget(HOME_ID)
|
||||
}
|
||||
navTarget.popTo != null -> {
|
||||
loadTarget(navTarget.popTo ?: HOME_ID)
|
||||
}
|
||||
else -> {
|
||||
navBackStack.last().let {
|
||||
loadTarget(it.first, it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -953,6 +975,8 @@ class MainActivity : AppCompatActivity() {
|
||||
* that something has changed in the bottom sheet.
|
||||
*/
|
||||
fun gainAttention() {
|
||||
if (app.config.ui.bottomSheetOpened || true)
|
||||
return
|
||||
b.navView.postDelayed({
|
||||
navView.gainAttentionOnBottomBar()
|
||||
}, 2000)
|
||||
@ -1076,4 +1100,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun snackbar(text: String, actionText: String? = null, onClick: (() -> Unit)? = null) = mainSnackbar.snackbar(text, actionText, onClick)
|
||||
fun snackbarDismiss() = mainSnackbar.dismiss()
|
||||
}
|
||||
|
@ -64,6 +64,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
get() { mAppRateSnackbarTime = mAppRateSnackbarTime ?: values.get("appRateSnackbarTime", 0L); return mAppRateSnackbarTime ?: 0L }
|
||||
set(value) { set("appRateSnackbarTime", value); mAppRateSnackbarTime = value }
|
||||
|
||||
private var mRunSync: Boolean? = null
|
||||
var runSync: Boolean
|
||||
get() { mRunSync = mRunSync ?: values.get("runSync", false); return mRunSync ?: false }
|
||||
set(value) { set("runSync", value); mRunSync = value }
|
||||
|
||||
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
||||
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
||||
init {
|
||||
@ -76,6 +81,9 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
fun getFor(profileId: Int): ProfileConfig {
|
||||
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
|
||||
}
|
||||
fun forProfile(): ProfileConfig {
|
||||
return profileConfigs[App.profileId] ?: ProfileConfig(db, App.profileId, rawEntries)
|
||||
}
|
||||
|
||||
fun setProfile(profileId: Int) {
|
||||
}
|
||||
|
@ -59,4 +59,9 @@ class ConfigUI(private val config: Config) {
|
||||
var snowfall: Boolean
|
||||
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
|
||||
set(value) { config.set("snowfall", value); mSnowfall = value }
|
||||
|
||||
private var mBottomSheetOpened: Boolean? = null
|
||||
var bottomSheetOpened: Boolean
|
||||
get() { mBottomSheetOpened = mBottomSheetOpened ?: config.values.get("bottomSheetOpened", false); return mBottomSheetOpened ?: false }
|
||||
set(value) { config.set("bottomSheetOpened", value); mBottomSheetOpened = value }
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class ApiService : Service() {
|
||||
SzkolnyTask.sync(syncingProfiles),
|
||||
NotifyTask()
|
||||
)
|
||||
private val allTaskList = mutableListOf<IApiTask>()
|
||||
private val taskQueue = mutableListOf<IApiTask>()
|
||||
private val errorList = mutableListOf<ApiError>()
|
||||
|
||||
@ -73,7 +74,7 @@ class ApiService : Service() {
|
||||
override fun onCompleted() {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
|
||||
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
|
||||
EventBus.getDefault().postSticky(ApiTaskFinishedEvent(taskProfileId))
|
||||
clearTask()
|
||||
|
||||
notification.setIdle().post()
|
||||
@ -84,7 +85,7 @@ class ApiService : Service() {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId threw an error - $apiError")
|
||||
apiError.profileId = taskProfileId
|
||||
EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
|
||||
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
|
||||
errorList.add(apiError)
|
||||
apiError.throwable?.printStackTrace()
|
||||
if (apiError.isCritical) {
|
||||
@ -194,7 +195,7 @@ class ApiService : Service() {
|
||||
*/
|
||||
private fun stopIfTaskFrozen() {
|
||||
if (checkIfTaskFrozen()) {
|
||||
stopSelf()
|
||||
allCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +214,7 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
private fun allCompleted() {
|
||||
EventBus.getDefault().post(ApiTaskAllFinishedEvent())
|
||||
EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent())
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
@ -228,6 +229,11 @@ class ApiService : Service() {
|
||||
EventBus.getDefault().removeStickyEvent(task)
|
||||
d(TAG, task.toString())
|
||||
|
||||
// fix for duplicated tasks, thank you EventBus
|
||||
if (task in allTaskList)
|
||||
return
|
||||
allTaskList += task
|
||||
|
||||
if (task is EdziennikTask) {
|
||||
when (task.request) {
|
||||
is EdziennikTask.SyncRequest -> app.db.profileDao().idsForSyncNow.forEach {
|
||||
@ -270,7 +276,7 @@ class ApiService : Service() {
|
||||
serviceClosed = true
|
||||
taskCancelled = true
|
||||
taskRunning?.cancel()
|
||||
stopSelf()
|
||||
allCompleted()
|
||||
}
|
||||
|
||||
/* _____ _ _ _
|
||||
|
@ -70,6 +70,8 @@ const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcz
|
||||
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"
|
||||
const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci"
|
||||
const val IDZIENNIK_WEB_GET_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/PobierzWiadomosc"
|
||||
const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic"
|
||||
const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc"
|
||||
const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx"
|
||||
|
||||
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
|
||||
@ -102,6 +104,7 @@ const val VULCAN_API_ENDPOINT_ATTENDANCE = "mobile-api/Uczen.v3.Uczen/Frekwencje
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_RECEIVED = "mobile-api/Uczen.v3.Uczen/WiadomosciOdebrane"
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/WiadomosciWyslane"
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci"
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc"
|
||||
const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken"
|
||||
|
||||
const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}"
|
||||
|
@ -81,3 +81,32 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
|
||||
progressCount = targetLoginMethodIds.size + targetEndpointIds.size
|
||||
progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat()
|
||||
}
|
||||
|
||||
fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
|
||||
val possibleLoginMethods = this.loginMethods.toMutableList()
|
||||
|
||||
loginMethods.forEach {
|
||||
if (it.isPossible(profile, loginStore))
|
||||
possibleLoginMethods += it.loginMethodId
|
||||
}
|
||||
|
||||
targetEndpointIds.clear()
|
||||
targetLoginMethodIds.clear()
|
||||
|
||||
// check the login method for any dependencies
|
||||
var requiredLoginMethod: Int? = loginMethodId
|
||||
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
|
||||
loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let {
|
||||
if (requiredLoginMethod != null)
|
||||
targetLoginMethodIds.add(requiredLoginMethod!!)
|
||||
requiredLoginMethod = it.requiredLoginMethod(profile, loginStore)
|
||||
}
|
||||
}
|
||||
|
||||
// sort and distinct every login method
|
||||
targetLoginMethodIds = targetLoginMethodIds.toHashSet().toMutableList()
|
||||
targetLoginMethodIds.sort()
|
||||
|
||||
progressCount = 0
|
||||
progressStep = 0f
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ const val CODE_LIBRUS_DISCONNECTED = 31
|
||||
const val CODE_PROFILE_ARCHIVED = 30*/
|
||||
|
||||
const val ERROR_APP_CRASH = 1
|
||||
const val ERROR_MESSAGE_NOT_SENT = 10
|
||||
|
||||
const val ERROR_REQUEST_FAILURE = 50
|
||||
const val ERROR_REQUEST_HTTP_400 = 51
|
||||
@ -40,10 +41,13 @@ const val ERROR_REQUEST_HTTP_403 = 53
|
||||
const val ERROR_REQUEST_HTTP_404 = 54
|
||||
const val ERROR_REQUEST_HTTP_405 = 55
|
||||
const val ERROR_REQUEST_HTTP_410 = 56
|
||||
const val ERROR_REQUEST_HTTP_500 = 57
|
||||
const val ERROR_REQUEST_HTTP_424 = 57
|
||||
const val ERROR_REQUEST_HTTP_500 = 58
|
||||
const val ERROR_REQUEST_HTTP_503 = 59
|
||||
const val ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND = 60
|
||||
const val ERROR_REQUEST_FAILURE_TIMEOUT = 61
|
||||
const val ERROR_REQUEST_FAILURE_NO_INTERNET = 62
|
||||
const val ERROR_REQUEST_FAILURE_SSL_ERROR = 63
|
||||
const val ERROR_RESPONSE_EMPTY = 100
|
||||
const val ERROR_LOGIN_DATA_MISSING = 101
|
||||
const val ERROR_PROFILE_MISSING = 105
|
||||
@ -159,6 +163,7 @@ const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440
|
||||
const val ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA = 441
|
||||
const val ERROR_IDZIENNIK_API_ACCESS_DENIED = 450
|
||||
const val ERROR_IDZIENNIK_API_OTHER = 451
|
||||
const val ERROR_IDZIENNIK_API_NO_REGISTER = 452
|
||||
|
||||
const val ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN = 501
|
||||
const val ERROR_LOGIN_EDUDZIENNIK_WEB_OTHER = 510
|
||||
|
@ -55,6 +55,10 @@ object Regexes {
|
||||
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_MESSAGE_RECIPIENTS_JSON by lazy {
|
||||
"""odbiorcy: (\[.+?\]),${'$'}""".toRegex(RegexOption.MULTILINE)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
|
||||
@ -78,6 +82,9 @@ object Regexes {
|
||||
val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy {
|
||||
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_MESSAGES_RECIPIENT_PARENT by lazy {
|
||||
"""(.+?)\s\((.+)\)""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -147,7 +147,7 @@ class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Dat
|
||||
fun getEventType(longId: String, name: String): EventType {
|
||||
val id = longId.crc16().toLong()
|
||||
return eventTypes.singleOrNull { it.id == id } ?: run {
|
||||
val eventType = EventType(profileId, id, name, colorFromName(app, name))
|
||||
val eventType = EventType(profileId, id, name, colorFromName(name))
|
||||
eventTypes.put(id, eventType)
|
||||
eventType
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -29,6 +30,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataEdudziennik
|
||||
private var afterLogin: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
data = DataEdudziennik(app, profile, loginStore).apply {
|
||||
@ -55,21 +57,17 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
EdudziennikLogin(data) {
|
||||
EdudziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
login()
|
||||
}
|
||||
|
||||
private fun login() {
|
||||
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
|
||||
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
loginMethodId?.let { data.prepareFor(edudziennikLoginMethods, it) }
|
||||
afterLogin?.let { this.afterLogin = it }
|
||||
EdudziennikLogin(data) {
|
||||
data()
|
||||
}
|
||||
@ -81,18 +79,14 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
EdudziennikData(data) {
|
||||
afterLogin?.invoke() ?: EdudziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
}
|
||||
override fun getMessage(message: MessageFull) {}
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {}
|
||||
override fun markAllAnnouncementsAsRead() {}
|
||||
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {
|
||||
EdudziennikLoginWeb(data) {
|
||||
@ -102,16 +96,10 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
EdudziennikFirstLogin(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {}
|
||||
override fun getRecipientList() {}
|
||||
|
||||
override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } }
|
||||
override fun cancel() {
|
||||
d(TAG, "Cancelled")
|
||||
data.cancel()
|
||||
@ -119,18 +107,9 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
||||
|
||||
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
|
||||
return object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
callback.onProgress(step)
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
callback.onStartProgress(stringRes)
|
||||
}
|
||||
|
||||
override fun onCompleted() { callback.onCompleted() }
|
||||
override fun onProgress(step: Float) { callback.onProgress(step) }
|
||||
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
|
||||
override fun onError(apiError: ApiError) {
|
||||
if (apiError.errorCode in internalErrorList) {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
|
@ -6,23 +6,23 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetAttachment
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetRecipientList
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSendMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.idziennikLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.prepare
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -32,6 +32,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataIdziennik
|
||||
private var afterLogin: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
data = DataIdziennik(app, profile, loginStore).apply {
|
||||
@ -58,45 +59,69 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
login()
|
||||
}
|
||||
|
||||
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
|
||||
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
loginMethodId?.let { data.prepareFor(idziennikLoginMethods, it) }
|
||||
afterLogin?.let { this.afterLogin = it }
|
||||
IdziennikLogin(data) {
|
||||
IdziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
data()
|
||||
}
|
||||
}
|
||||
|
||||
private fun data() {
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
afterLogin?.invoke() ?: IdziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
IdziennikLoginWeb(data) {
|
||||
login(LOGIN_METHOD_IDZIENNIK_WEB) {
|
||||
IdziennikWebGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
|
||||
login(LOGIN_METHOD_IDZIENNIK_API) {
|
||||
IdziennikWebSendMessage(data, recipients, subject, text) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {
|
||||
|
||||
}
|
||||
override fun markAllAnnouncementsAsRead() {}
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
IdziennikLoginWeb(data) {
|
||||
login(LOGIN_METHOD_IDZIENNIK_WEB) {
|
||||
IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
IdziennikFirstLogin(data) {
|
||||
completed()
|
||||
override fun getRecipientList() {
|
||||
login(LOGIN_METHOD_IDZIENNIK_WEB) {
|
||||
IdziennikWebGetRecipientList(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() { IdziennikFirstLogin(data) { completed() } }
|
||||
override fun cancel() {
|
||||
d(TAG, "Cancelled")
|
||||
data.cancel()
|
||||
@ -104,28 +129,29 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
|
||||
return object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
callback.onProgress(step)
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
callback.onStartProgress(stringRes)
|
||||
}
|
||||
|
||||
override fun onCompleted() { callback.onCompleted() }
|
||||
override fun onProgress(step: Float) { callback.onProgress(step) }
|
||||
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
|
||||
override fun onError(apiError: ApiError) {
|
||||
if (apiError.errorCode in internalErrorList) {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
return
|
||||
}
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION,
|
||||
ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH,
|
||||
ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER,
|
||||
ERROR_IDZIENNIK_WEB_ACCESS_DENIED,
|
||||
ERROR_IDZIENNIK_API_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_IDZIENNIK_WEB)
|
||||
data.prepareFor(idziennikLoginMethods, LOGIN_METHOD_IDZIENNIK_WEB)
|
||||
data.loginExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
ERROR_IDZIENNIK_API_NO_REGISTER -> {
|
||||
data()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ open class IdziennikApi(open val data: DataIdziennik) {
|
||||
}
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
"Uczeń nie posiada aktywnej pozycji w dzienniku" -> ERROR_IDZIENNIK_API_NO_REGISTER
|
||||
"Authorization has been denied for this request." -> ERROR_IDZIENNIK_API_ACCESS_DENIED
|
||||
else -> ERROR_IDZIENNIK_API_OTHER
|
||||
}.let { errorCode ->
|
||||
@ -107,6 +108,7 @@ open class IdziennikApi(open val data: DataIdziennik) {
|
||||
}
|
||||
}
|
||||
}
|
||||
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_INTERNAL_ERROR)
|
||||
.callback(callback)
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_DELETED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.getBoolean
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.crc32
|
||||
@ -51,11 +52,18 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
|
||||
val sentDate = Date.fromIso(jMessage.getString("dataWyslania"))
|
||||
|
||||
val sender = jMessage.getAsJsonObject("nadawca")
|
||||
var firstName = sender.getString("imie")
|
||||
var lastName = sender.getString("nazwisko")
|
||||
if (firstName.isNullOrEmpty() || lastName.isNullOrEmpty()) {
|
||||
firstName = "usunięty"
|
||||
lastName = "użytkownik"
|
||||
}
|
||||
val rTeacher = data.getTeacher(
|
||||
sender.getString("imie") ?: "",
|
||||
sender.getString("nazwisko") ?: ""
|
||||
firstName,
|
||||
lastName
|
||||
)
|
||||
rTeacher.loginId = sender.getString("id") + ":" + sender.getString("usr")
|
||||
rTeacher.loginId = /*sender.getString("id") + ":" + */sender.getString("usr")
|
||||
rTeacher.setTeacherType(Teacher.TYPE_OTHER)
|
||||
|
||||
val message = Message(
|
||||
profileId,
|
||||
|
@ -7,11 +7,11 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api
|
||||
import com.google.gson.JsonArray
|
||||
import pl.szczodrzynski.edziennik.DAY
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
||||
import pl.szczodrzynski.edziennik.asJsonObjectList
|
||||
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_API_MESSAGES_SENT
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
|
||||
import pl.szczodrzynski.edziennik.asJsonObjectList
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
@ -62,7 +62,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
|
||||
lastName = "użytkownik"
|
||||
}
|
||||
val rTeacher = data.getTeacher(firstName, lastName)
|
||||
rTeacher.loginId = recipient.get("id").asString + ":" + recipient.get("usr").asString
|
||||
rTeacher.loginId = /*recipient.get("id").asString + ":" + */recipient.get("usr").asString
|
||||
|
||||
val messageRecipient = MessageRecipient(
|
||||
profileId,
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-30.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
|
||||
|
||||
import androidx.room.OnConflictStrategy
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
|
||||
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_RECIPIENT_LIST
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.events.RecipientListGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
class IdziennikWebGetRecipientList(
|
||||
override val data: DataIdziennik, val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebGetRecipientList"
|
||||
}
|
||||
|
||||
init {
|
||||
webApiGet(TAG, IDZIENNIK_WEB_GET_RECIPIENT_LIST, mapOf(
|
||||
"idP" to data.registerId
|
||||
)) { result ->
|
||||
val json = result.getJsonObject("d") ?: run {
|
||||
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
|
||||
.withApiResponse(result))
|
||||
return@webApiGet
|
||||
}
|
||||
|
||||
json.getJsonArray("ListK_Pracownicy")?.asJsonObjectList()?.forEach { recipient ->
|
||||
val name = recipient.getString("ImieNazwisko") ?: ": "
|
||||
val (fullName, subject) = name.split(": ").let {
|
||||
Pair(it.getOrNull(0), it.getOrNull(1))
|
||||
}
|
||||
val guid = recipient.getString("Id") ?: ""
|
||||
// get teacher by ID or create it
|
||||
val teacher = data.getTeacherByFirstLast(fullName ?: " ")
|
||||
teacher.loginId = guid
|
||||
teacher.setTeacherType(Teacher.TYPE_TEACHER)
|
||||
// unset OTHER that is automatically set in IdziennikApiMessages*
|
||||
teacher.unsetTeacherType(Teacher.TYPE_OTHER)
|
||||
teacher.typeDescription = subject
|
||||
}
|
||||
|
||||
json.getJsonArray("ListK_Opiekunowie")?.asJsonObjectList()?.forEach { recipient ->
|
||||
val name = recipient.getString("ImieNazwisko") ?: ": "
|
||||
val (fullName, parentOf) = Regexes.IDZIENNIK_MESSAGES_RECIPIENT_PARENT.find(name)?.let {
|
||||
Pair(it.groupValues.getOrNull(1), it.groupValues.getOrNull(2))
|
||||
} ?: Pair(null, null)
|
||||
val guid = recipient.getString("Id") ?: ""
|
||||
// get teacher by ID or create it
|
||||
val teacher = data.getTeacherByFirstLast(fullName ?: " ")
|
||||
teacher.loginId = guid
|
||||
teacher.setTeacherType(Teacher.TYPE_PARENT)
|
||||
// unset OTHER that is automatically set in IdziennikApiMessages*
|
||||
teacher.unsetTeacherType(Teacher.TYPE_OTHER)
|
||||
teacher.typeDescription = parentOf
|
||||
}
|
||||
|
||||
val event = RecipientListGetEvent(
|
||||
data.profileId,
|
||||
data.teacherList.filter { it.loginId != null }
|
||||
)
|
||||
|
||||
profile?.lastReceiversSync = System.currentTimeMillis()
|
||||
|
||||
data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-30.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
|
||||
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_SEND_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiMessagesSent
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import java.util.*
|
||||
|
||||
class IdziennikWebSendMessage(
|
||||
override val data: DataIdziennik,
|
||||
val recipients: List<Teacher>,
|
||||
val subject: String,
|
||||
val text: String,
|
||||
val onSuccess: () -> Unit
|
||||
) : IdziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebSendMessage"
|
||||
}
|
||||
|
||||
init {
|
||||
val recipientsArray = JsonArray()
|
||||
for (teacher in recipients) {
|
||||
teacher.loginId?.let {
|
||||
recipientsArray += it
|
||||
}
|
||||
}
|
||||
|
||||
webApiGet(TAG, IDZIENNIK_WEB_SEND_MESSAGE, mapOf(
|
||||
"Wiadomosc" to JsonObject(
|
||||
"Tytul" to subject,
|
||||
"Tresc" to text,
|
||||
"Confirmation" to false,
|
||||
"GuidMessage" to UUID.randomUUID().toString().toUpperCase(Locale.ROOT),
|
||||
"Odbiorcy" to recipientsArray
|
||||
)
|
||||
)) { result ->
|
||||
val json = result.getJsonObject("d") ?: run {
|
||||
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
|
||||
.withApiResponse(result))
|
||||
return@webApiGet
|
||||
}
|
||||
|
||||
if (json.getBoolean("CzyJestBlad") != false) {
|
||||
// TODO error
|
||||
return@webApiGet
|
||||
}
|
||||
|
||||
IdziennikApiMessagesSent(data) {
|
||||
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
|
||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
||||
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
|
||||
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,9 +11,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusData
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api.LibrusApiAnnouncementMarkAsRead
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetAttachment
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
@ -22,6 +24,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -31,6 +34,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataLibrus
|
||||
private var afterLogin: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
data = DataLibrus(app, profile, loginStore).apply {
|
||||
@ -60,12 +64,14 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
login()
|
||||
}
|
||||
|
||||
private fun login() {
|
||||
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
|
||||
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
loginMethodId?.let { data.prepareFor(librusLoginMethods, it) }
|
||||
afterLogin?.let { this.afterLogin = it }
|
||||
LibrusLogin(data) {
|
||||
data()
|
||||
}
|
||||
@ -77,67 +83,60 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
LibrusData(data) {
|
||||
afterLogin?.invoke() ?: LibrusData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
|
||||
LibrusMessagesGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
|
||||
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
|
||||
LibrusMessagesSendMessage(data, recipients, subject, text) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
|
||||
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusApiAnnouncementMarkAsRead(data, announcement) {
|
||||
completed()
|
||||
}
|
||||
login(LOGIN_METHOD_LIBRUS_API) {
|
||||
LibrusApiAnnouncementMarkAsRead(data, announcement) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
|
||||
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
LibrusFirstLogin(data) {
|
||||
completed()
|
||||
override fun getRecipientList() {
|
||||
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
|
||||
LibrusMessagesGetRecipientList(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() { LibrusFirstLogin(data) { completed() } }
|
||||
override fun cancel() {
|
||||
d(TAG, "Cancelled")
|
||||
data.cancel()
|
||||
@ -145,18 +144,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
|
||||
return object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
callback.onProgress(step)
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
callback.onStartProgress(stringRes)
|
||||
}
|
||||
|
||||
override fun onCompleted() { callback.onCompleted() }
|
||||
override fun onProgress(step: Float) { callback.onProgress(step) }
|
||||
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
|
||||
override fun onError(apiError: ApiError) {
|
||||
if (apiError.errorCode in internalErrorList) {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
@ -167,30 +157,26 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
when (apiError.errorCode) {
|
||||
ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_PORTAL)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_PORTAL)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_PORTAL)
|
||||
data.portalTokenExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_LIBRUS_API_ACCESS_DENIED,
|
||||
ERROR_LIBRUS_API_TOKEN_EXPIRED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_API)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_API)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_API)
|
||||
data.apiTokenExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_SYNERGIA)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_SYNERGIA)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_SYNERGIA)
|
||||
data.synergiaSessionIdExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_MESSAGES)
|
||||
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_MESSAGES)
|
||||
data.targetLoginMethodIds.sort()
|
||||
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_MESSAGES)
|
||||
data.messagesSessionIdExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
@ -12,6 +13,8 @@ import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import org.json.JSONObject
|
||||
import org.json.XML
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.parser.Parser
|
||||
@ -38,10 +41,10 @@ open class LibrusMessages(open val data: DataLibrus) {
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
fun messagesGet(tag: String, endpoint: String, method: Int = POST,
|
||||
fun messagesGet(tag: String, module: String, method: Int = POST,
|
||||
parameters: Map<String, Any>? = null, onSuccess: (doc: Document) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$endpoint")
|
||||
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$module")
|
||||
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
@ -107,22 +110,92 @@ open class LibrusMessages(open val data: DataLibrus) {
|
||||
transformer.transform(DOMSource(doc), StreamResult(stringWriter))
|
||||
val requestXml = stringWriter.toString()
|
||||
|
||||
/*val requestXml = xml("service") {
|
||||
"header" { }
|
||||
"data" {
|
||||
for ((key, value) in parameters.orEmpty()) {
|
||||
key {
|
||||
-value.toString()
|
||||
Request.builder()
|
||||
.url("$LIBRUS_MESSAGES_URL/$module")
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
|
||||
.apply {
|
||||
when (method) {
|
||||
GET -> get()
|
||||
POST -> post()
|
||||
}
|
||||
}
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
fun messagesGetJson(tag: String, module: String, method: Int = POST,
|
||||
parameters: Map<String, Any>? = null, onSuccess: (json: JsonObject?) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$module")
|
||||
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
if (text.isNullOrEmpty()) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
|
||||
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
|
||||
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
|
||||
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
|
||||
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
|
||||
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
|
||||
}
|
||||
|
||||
try {
|
||||
val json: JSONObject? = XML.toJSONObject(text)
|
||||
onSuccess(JsonParser().parse(json?.toString() ?: "{}")?.asJsonObject)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
.withResponse(response)
|
||||
.withThrowable(e)
|
||||
.withApiResponse(text))
|
||||
}
|
||||
}
|
||||
}.toString(PrintOptions(
|
||||
singleLineTextElements = true,
|
||||
useSelfClosingTags = true
|
||||
))*/
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
data.app.cookieJar.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("DZIENNIKSID")
|
||||
.value(data.messagesSessionId!!)
|
||||
.domain("wiadomosci.librus.pl")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
|
||||
|
||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val doc = docBuilder.newDocument()
|
||||
val serviceElement = doc.createElement("service")
|
||||
val headerElement = doc.createElement("header")
|
||||
val dataElement = doc.createElement("data")
|
||||
for ((key, value) in parameters.orEmpty()) {
|
||||
val element = doc.createElement(key)
|
||||
element.appendChild(doc.createTextNode(value.toString()))
|
||||
dataElement.appendChild(element)
|
||||
}
|
||||
serviceElement.appendChild(headerElement)
|
||||
serviceElement.appendChild(dataElement)
|
||||
doc.appendChild(serviceElement)
|
||||
val transformer = TransformerFactory.newInstance().newTransformer()
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
|
||||
val stringWriter = StringWriter()
|
||||
transformer.transform(DOMSource(doc), StreamResult(stringWriter))
|
||||
val requestXml = stringWriter.toString()
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_MESSAGES_URL/$endpoint")
|
||||
.url("$LIBRUS_MESSAGES_URL/$module")
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
|
||||
.apply {
|
||||
|
@ -25,7 +25,14 @@ class LibrusApiUsers(override val data: DataLibrus,
|
||||
val firstName = user.getString("FirstName")?.fixName() ?: ""
|
||||
val lastName = user.getString("LastName")?.fixName() ?: ""
|
||||
|
||||
data.teacherList.put(id, Teacher(profileId, id, firstName, lastName))
|
||||
val teacher = Teacher(profileId, id, firstName, lastName)
|
||||
|
||||
if (user.getBoolean("IsSchoolAdministrator") == true)
|
||||
teacher.setTeacherType(Teacher.TYPE_SCHOOL_ADMIN)
|
||||
if (user.getBoolean("IsPedagogue") == true)
|
||||
teacher.setTeacherType(Teacher.TYPE_PEDAGOGUE)
|
||||
|
||||
data.teacherList.put(id, teacher)
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_USERS, 4*DAY)
|
||||
|
@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -51,12 +52,12 @@ class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int
|
||||
val recipientFirstName = element.select(when (type) {
|
||||
TYPE_RECEIVED -> "senderFirstName"
|
||||
else -> "receiverFirstName"
|
||||
}).text().trim()
|
||||
}).text().fixName()
|
||||
|
||||
val recipientLastName = element.select(when (type) {
|
||||
TYPE_RECEIVED -> "senderLastName"
|
||||
else -> "receiverLastName"
|
||||
}).text().trim()
|
||||
}).text().fixName()
|
||||
|
||||
val recipientId = data.teacherList.singleOrNull {
|
||||
it.name == recipientFirstName && it.surname == recipientLastName
|
||||
|
@ -14,7 +14,10 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.notEmptyOrNull
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.nio.charset.Charset
|
||||
@ -56,11 +59,45 @@ class LibrusMessagesGetMessage(
|
||||
|
||||
when (messageObject.type) {
|
||||
TYPE_RECEIVED -> {
|
||||
val senderLoginId = message.select("senderId").text()
|
||||
data.teacherList.singleOrNull { it.id == messageObject.senderId }?.loginId = senderLoginId
|
||||
val senderLoginId = message.select("senderId").text().notEmptyOrNull()
|
||||
val senderGroupId = message.select("senderGroupId").text().toIntOrNull()
|
||||
val userClass = message.select("userClass").text().notEmptyOrNull()
|
||||
data.teacherList.singleOrNull { it.id == messageObject.senderId }?.apply {
|
||||
loginId = senderLoginId
|
||||
setTeacherType(when (senderGroupId) {
|
||||
/* https://api.librus.pl/2.0/Messages/Role */
|
||||
0, 1, 99 -> Teacher.TYPE_SUPER_ADMIN
|
||||
2 -> Teacher.TYPE_SCHOOL_ADMIN
|
||||
3 -> Teacher.TYPE_PRINCIPAL
|
||||
4 -> Teacher.TYPE_TEACHER
|
||||
5, 9 -> {
|
||||
if (typeDescription == null)
|
||||
typeDescription = userClass
|
||||
Teacher.TYPE_PARENT
|
||||
}
|
||||
7 -> Teacher.TYPE_SECRETARIAT
|
||||
8 -> {
|
||||
if (typeDescription == null)
|
||||
typeDescription = userClass
|
||||
Teacher.TYPE_STUDENT
|
||||
}
|
||||
10 -> Teacher.TYPE_PEDAGOGUE
|
||||
11 -> Teacher.TYPE_LIBRARIAN
|
||||
12 -> Teacher.TYPE_SPECIALIST
|
||||
21 -> {
|
||||
typeDescription = "Jednostka Nadrzędna"
|
||||
Teacher.TYPE_OTHER
|
||||
}
|
||||
50 -> {
|
||||
typeDescription = "Jednostka Samorządu Terytorialnego"
|
||||
Teacher.TYPE_OTHER
|
||||
}
|
||||
else -> Teacher.TYPE_OTHER
|
||||
})
|
||||
}
|
||||
|
||||
val readDateText = message.select("readDate").text()
|
||||
val readDate = when (readDateText.isNotEmpty()) {
|
||||
val readDate = when (readDateText.isNotNullNorEmpty()) {
|
||||
true -> Date.fromIso(readDateText)
|
||||
else -> 0
|
||||
}
|
||||
@ -73,7 +110,7 @@ class LibrusMessagesGetMessage(
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
messageRecipientObject.fullName = profile.accountNameLong ?: profile.studentNameLong
|
||||
messageRecipientObject.fullName = profile.accountNameLong ?: profile.studentNameLong ?: ""
|
||||
|
||||
messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
@ -90,7 +127,7 @@ class LibrusMessagesGetMessage(
|
||||
teacher?.loginId = receiverLoginId
|
||||
|
||||
val readDateText = message.select("readed").text()
|
||||
val readDate = when (readDateText.isNotEmpty()) {
|
||||
val readDate = when (readDateText.isNotNullNorEmpty()) {
|
||||
true -> Date.fromIso(readDateText)
|
||||
else -> 0
|
||||
}
|
||||
|
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-31.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages
|
||||
|
||||
import androidx.core.util.set
|
||||
import androidx.room.OnConflictStrategy
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
|
||||
import pl.szczodrzynski.edziennik.data.api.events.RecipientListGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
class LibrusMessagesGetRecipientList(
|
||||
override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusMessages(data) {
|
||||
companion object {
|
||||
private const val TAG = "LibrusMessagesGetRecipientList"
|
||||
}
|
||||
|
||||
private val listTypes = mutableListOf<Pair<String, String>>()
|
||||
|
||||
init {
|
||||
messagesGet(TAG, "Receivers/action/GetTypes", parameters = mapOf(
|
||||
"includeClass" to 1
|
||||
)) { doc ->
|
||||
doc.select("response GetTypes data list ArrayItem")?.forEach {
|
||||
val id = it.getElementsByTag("id")?.firstOrNull()?.ownText() ?: return@forEach
|
||||
val name = it.getElementsByTag("name")?.firstOrNull()?.ownText() ?: return@forEach
|
||||
listTypes += id to name
|
||||
}
|
||||
|
||||
getLists()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLists() {
|
||||
if (listTypes.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
val type = listTypes.removeAt(0)
|
||||
if (type.first == "contactsGroups") {
|
||||
getLists()
|
||||
return
|
||||
}
|
||||
messagesGetJson(TAG, "Receivers/action/GetListForType", parameters = mapOf(
|
||||
"receiverType" to type.first
|
||||
)) { json ->
|
||||
val dataEl = json?.getJsonObject("response")?.getJsonObject("GetListForType")?.get("data")
|
||||
if (dataEl is JsonObject) {
|
||||
val listEl = dataEl.get("ArrayItem")
|
||||
if (listEl is JsonArray) {
|
||||
listEl.asJsonObjectList()?.forEach { item ->
|
||||
processElement(item, type.first, type.second)
|
||||
}
|
||||
}
|
||||
if (listEl is JsonObject) {
|
||||
processElement(listEl, type.first, type.second)
|
||||
}
|
||||
}
|
||||
|
||||
getLists()
|
||||
}
|
||||
}
|
||||
|
||||
private fun processElement(element: JsonObject, typeId: String, typeName: String, listName: String? = null) {
|
||||
val listEl = element.getJsonObject("list")?.get("ArrayItem")
|
||||
if (listEl is JsonArray) {
|
||||
listEl.asJsonObjectList()?.let { list ->
|
||||
val label = element.getString("label") ?: ""
|
||||
list.forEach { item ->
|
||||
processElement(item, typeId, typeName, label)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (listEl is JsonObject) {
|
||||
val label = element.getString("label") ?: ""
|
||||
processElement(listEl, typeId, typeName, label)
|
||||
return
|
||||
}
|
||||
processRecipient(element, typeId, typeName, listName)
|
||||
}
|
||||
|
||||
private fun processRecipient(recipient: JsonObject, typeId: String, typeName: String, listName: String? = null) {
|
||||
val id = recipient.getLong("id") ?: return
|
||||
val label = recipient.getString("label") ?: return
|
||||
|
||||
val fullNameLastFirst: String
|
||||
val description: String?
|
||||
if (typeId == "parentsCouncil" || typeId == "schoolParentsCouncil") {
|
||||
val delimiterIndex = label.lastIndexOf(" - ")
|
||||
if (delimiterIndex == -1) {
|
||||
fullNameLastFirst = label.fixName()
|
||||
description = null
|
||||
}
|
||||
else {
|
||||
fullNameLastFirst = label.substring(0, delimiterIndex).fixName()
|
||||
description = label.substring(delimiterIndex+3)
|
||||
}
|
||||
}
|
||||
else {
|
||||
fullNameLastFirst = label.fixName()
|
||||
description = null
|
||||
}
|
||||
|
||||
var typeDescription: String? = null
|
||||
val type = when (typeId) {
|
||||
"tutors" -> Teacher.TYPE_EDUCATOR
|
||||
"teachers" -> Teacher.TYPE_TEACHER
|
||||
"classParents" -> Teacher.TYPE_PARENT
|
||||
"guardians" -> Teacher.TYPE_PARENT
|
||||
"parentsCouncil" -> {
|
||||
typeDescription = joinNotNullStrings(": ", listName, description)
|
||||
Teacher.TYPE_PARENTS_COUNCIL
|
||||
}
|
||||
"schoolParentsCouncil" -> {
|
||||
typeDescription = joinNotNullStrings(": ", listName, description)
|
||||
Teacher.TYPE_SCHOOL_PARENTS_COUNCIL
|
||||
}
|
||||
"pedagogue" -> Teacher.TYPE_PEDAGOGUE
|
||||
"librarian" -> Teacher.TYPE_LIBRARIAN
|
||||
"admin" -> Teacher.TYPE_SCHOOL_ADMIN
|
||||
"secretary" -> Teacher.TYPE_SECRETARIAT
|
||||
"sadmin" -> Teacher.TYPE_SUPER_ADMIN
|
||||
else -> {
|
||||
typeDescription = typeName
|
||||
Teacher.TYPE_OTHER
|
||||
}
|
||||
}
|
||||
|
||||
// get teacher by fullName AND type or create it
|
||||
val teacher = data.teacherList.singleOrNull {
|
||||
it.fullNameLastFirst == fullNameLastFirst && ((type != Teacher.TYPE_SCHOOL_ADMIN && type != Teacher.TYPE_PEDAGOGUE) || it.isType(type))
|
||||
} ?: Teacher(data.profileId, id).apply {
|
||||
if (typeId == "sadmin" && id == 2L) {
|
||||
name = "Pomoc"
|
||||
surname = "Techniczna LIBRUS"
|
||||
}
|
||||
else {
|
||||
name = fullNameLastFirst
|
||||
fullNameLastFirst.splitName()?.let {
|
||||
name = it.second
|
||||
surname = it.first
|
||||
}
|
||||
}
|
||||
data.teacherList[id] = this
|
||||
}
|
||||
|
||||
teacher.apply {
|
||||
this.loginId = id.toString()
|
||||
this.setTeacherType(type)
|
||||
if (this.typeDescription.isNullOrBlank())
|
||||
this.typeDescription = typeDescription
|
||||
}
|
||||
}
|
||||
|
||||
private fun finish() {
|
||||
val event = RecipientListGetEvent(
|
||||
data.profileId,
|
||||
data.teacherList.filter { it.loginId != null }
|
||||
)
|
||||
|
||||
profile?.lastReceiversSync = System.currentTimeMillis()
|
||||
|
||||
data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-2.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.base64Encode
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusMessages
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.getLong
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
|
||||
class LibrusMessagesSendMessage(
|
||||
override val data: DataLibrus,
|
||||
val recipients: List<Teacher>,
|
||||
val subject: String,
|
||||
val text: String,
|
||||
val onSuccess: () -> Unit
|
||||
) : LibrusMessages(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusMessages"
|
||||
}
|
||||
|
||||
init {
|
||||
val params = mapOf<String, Any>(
|
||||
"topic" to subject.base64Encode(),
|
||||
"message" to text.base64Encode(),
|
||||
"receivers" to recipients
|
||||
.filter { it.loginId != null }
|
||||
.joinToString(",") { it.loginId ?: "" },
|
||||
"actions" to "<Actions/>".base64Encode()
|
||||
)
|
||||
|
||||
messagesGetJson(TAG, "SendMessage", parameters = params) { json ->
|
||||
|
||||
val response = json.getJsonObject("response").getJsonObject("SendMessage")
|
||||
val id = response.getLong("data")
|
||||
|
||||
if (response.getString("status") != "ok" || id == null) {
|
||||
val message = response.getString("message")
|
||||
// TODO error
|
||||
return@messagesGetJson
|
||||
}
|
||||
|
||||
LibrusMessagesGetList(data, type = Message.TYPE_SENT) {
|
||||
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id }
|
||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
||||
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
|
||||
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_NO_STUDENTS_IN_ACCOUNT
|
||||
import pl.szczodrzynski.edziennik.data.api.FAKE_LIBRUS_ACCOUNTS
|
||||
import pl.szczodrzynski.edziennik.data.api.LIBRUS_ACCOUNTS_URL
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_MODE_LIBRUS_EMAIL
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusPortal
|
||||
@ -13,8 +10,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPortal
|
||||
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_LIBRUS_DISCONNECTED
|
||||
import pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_SYNERGIA_NOT_ACTIVATED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
@ -46,8 +41,8 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
|
||||
val state = account.getString("state")
|
||||
when (state) {
|
||||
"requiring_an_action" -> CODE_LIBRUS_DISCONNECTED
|
||||
"need-activation" -> CODE_SYNERGIA_NOT_ACTIVATED
|
||||
"requiring_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED
|
||||
"need-activation" -> ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED
|
||||
else -> null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
|
@ -59,7 +59,7 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () ->
|
||||
data.apiTokenExpiryTime = response.getUnixDate() + 6 * 60 * 60
|
||||
|
||||
// TODO remove this
|
||||
data.profile?.studentNameLong = json.getString("studentName")
|
||||
data.profile?.studentNameLong = json.getString("studentName") ?: ""
|
||||
val nameParts = json.getString("studentName")?.split(" ")
|
||||
data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0)
|
||||
|
||||
|
@ -6,23 +6,23 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikData
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGetAttachment
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGetMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGetRecipientList
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebSendMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.firstlogin.MobidziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.mobidziennikLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.prepare
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -34,6 +34,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataMobidziennik
|
||||
private var afterLogin: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
data = DataMobidziennik(app, profile, loginStore).apply {
|
||||
@ -60,45 +61,69 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
login()
|
||||
}
|
||||
|
||||
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
|
||||
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
loginMethodId?.let { data.prepareFor(mobidziennikLoginMethods, it) }
|
||||
afterLogin?.let { this.afterLogin = it }
|
||||
MobidziennikLogin(data) {
|
||||
MobidziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
data()
|
||||
}
|
||||
}
|
||||
|
||||
private fun data() {
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
afterLogin?.invoke() ?: MobidziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
MobidziennikLoginWeb(data) {
|
||||
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
|
||||
MobidziennikWebGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
|
||||
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
|
||||
MobidziennikWebSendMessage(data, recipients, subject, text) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {
|
||||
|
||||
}
|
||||
override fun markAllAnnouncementsAsRead() {}
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
MobidziennikLoginWeb(data) {
|
||||
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
|
||||
MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
MobidziennikFirstLogin(data) {
|
||||
completed()
|
||||
override fun getRecipientList() {
|
||||
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
|
||||
MobidziennikWebGetRecipientList(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() { MobidziennikFirstLogin(data) { completed() } }
|
||||
override fun cancel() {
|
||||
d(TAG, "Cancelled")
|
||||
data.cancel()
|
||||
@ -106,28 +131,25 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
|
||||
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
|
||||
return object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
callback.onProgress(step)
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
callback.onStartProgress(stringRes)
|
||||
}
|
||||
|
||||
override fun onCompleted() { callback.onCompleted() }
|
||||
override fun onProgress(step: Float) { callback.onProgress(step) }
|
||||
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
|
||||
override fun onError(apiError: ApiError) {
|
||||
if (apiError.errorCode in internalErrorList) {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
return
|
||||
}
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
ERROR_MOBIDZIENNIK_WEB_ACCESS_DENIED,
|
||||
ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY,
|
||||
ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE,
|
||||
ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_MOBIDZIENNIK_WEB)
|
||||
data.prepareFor(mobidziennikLoginMethods, LOGIN_METHOD_MOBIDZIENNIK_WEB)
|
||||
data.webSessionIdExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
|
@ -26,8 +26,15 @@ open class MobidziennikWeb(open val data: DataMobidziennik) {
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
fun webGet(tag: String, endpoint: String, method: Int = GET, onSuccess: (text: String) -> Unit) {
|
||||
val url = "https://${data.loginServerName}.mobidziennik.pl$endpoint"
|
||||
fun webGet(
|
||||
tag: String,
|
||||
endpoint: String,
|
||||
method: Int = GET,
|
||||
parameters: List<Pair<String, Any>> = emptyList(),
|
||||
fullUrl: String? = null,
|
||||
onSuccess: (text: String) -> Unit
|
||||
) {
|
||||
val url = fullUrl ?: "https://${data.loginServerName}.mobidziennik.pl$endpoint"
|
||||
|
||||
d(tag, "Request: Mobidziennik/Web - $url")
|
||||
|
||||
@ -91,6 +98,15 @@ open class MobidziennikWeb(open val data: DataMobidziennik) {
|
||||
Request.builder()
|
||||
.url(url)
|
||||
.userAgent(MOBIDZIENNIK_USER_AGENT)
|
||||
.apply {
|
||||
when (method) {
|
||||
GET -> get()
|
||||
POST -> post()
|
||||
}
|
||||
parameters.map { (name, value) ->
|
||||
addParameter(name, value)
|
||||
}
|
||||
}
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
|
@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.monthFromName
|
||||
@ -67,7 +68,7 @@ class MobidziennikWebGetMessage(
|
||||
message.id
|
||||
)
|
||||
|
||||
recipient.fullName = profile?.accountNameLong ?: profile?.studentNameLong
|
||||
recipient.fullName = profile?.accountNameLong ?: profile?.studentNameLong ?: ""
|
||||
|
||||
messageRecipientList.add(recipient)
|
||||
} else {
|
||||
@ -76,7 +77,7 @@ class MobidziennikWebGetMessage(
|
||||
|
||||
content.select("table.spis tr:has(td)")?.forEach { recipientEl ->
|
||||
val senderEl = recipientEl.select("td:eq(0)").first()
|
||||
val senderName = senderEl.text()
|
||||
val senderName = senderEl.text().fixName()
|
||||
|
||||
val teacher = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }
|
||||
val receiverId = teacher?.id ?: -1
|
||||
@ -117,7 +118,7 @@ class MobidziennikWebGetMessage(
|
||||
|
||||
// this needs to be at the end
|
||||
message.apply {
|
||||
this.body = body.html()
|
||||
this.body = body.html().replace("\n", "<br>")
|
||||
|
||||
clearAttachments()
|
||||
content.select("ul li").map { it.select("a").first() }.forEach {
|
||||
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web
|
||||
|
||||
import androidx.core.util.set
|
||||
import androidx.room.OnConflictStrategy
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.events.RecipientListGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
class MobidziennikWebGetRecipientList(
|
||||
override val data: DataMobidziennik, val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebGetRecipientList"
|
||||
}
|
||||
|
||||
init {
|
||||
webGet(TAG, "/mobile/dodajwiadomosc") { text ->
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_RECIPIENTS_JSON.find(text)?.let { match ->
|
||||
val recipientLists = JsonParser().parse(match[1]).asJsonArray
|
||||
recipientLists?.asJsonObjectList()?.forEach { list ->
|
||||
val listType = list.getString("typ")?.toIntOrNull() ?: -1
|
||||
val listName = list.getString("nazwa") ?: ""
|
||||
list.getJsonArray("dane")?.asJsonObjectList()?.forEach { recipient ->
|
||||
if (recipient.getBoolean("lista") == true) {
|
||||
recipient.getJsonArray("dane")?.asJsonObjectList()?.forEach {
|
||||
processRecipient(listType, recipient.getString("nazwa") ?: "", it)
|
||||
}
|
||||
}
|
||||
else
|
||||
processRecipient(listType, listName, recipient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val event = RecipientListGetEvent(
|
||||
data.profileId,
|
||||
data.teacherList.filter { it.loginId != null }
|
||||
)
|
||||
|
||||
profile?.lastReceiversSync = System.currentTimeMillis()
|
||||
|
||||
data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
private fun processRecipient(listType: Int, listName: String, recipient: JsonObject) {
|
||||
val id = recipient.getLong("id") ?: -1
|
||||
// get teacher by ID or create it
|
||||
val teacher = data.teacherList[id] ?: Teacher(data.profileId, id).apply {
|
||||
val fullName = recipient.getString("nazwa")?.fixName()
|
||||
name = fullName ?: ""
|
||||
fullName?.splitName()?.let {
|
||||
name = it.second
|
||||
surname = it.first
|
||||
}
|
||||
data.teacherList[id] = this
|
||||
}
|
||||
|
||||
teacher.apply {
|
||||
loginId = id.toString()
|
||||
when (listType) {
|
||||
1 -> setTeacherType(Teacher.TYPE_PRINCIPAL)
|
||||
2 -> setTeacherType(Teacher.TYPE_TEACHER)
|
||||
3 -> setTeacherType(Teacher.TYPE_PARENT)
|
||||
4 -> setTeacherType(Teacher.TYPE_STUDENT)
|
||||
//5 -> Użytkownicy zewnętrzni
|
||||
//6 -> Samorządy klasowe
|
||||
7 -> setTeacherType(Teacher.TYPE_PARENTS_COUNCIL) // Rady oddziałowe rodziców
|
||||
8 -> {
|
||||
setTeacherType(Teacher.TYPE_EDUCATOR)
|
||||
typeDescription = listName
|
||||
}
|
||||
9 -> setTeacherType(Teacher.TYPE_PEDAGOGUE)
|
||||
10 -> setTeacherType(Teacher.TYPE_SPECIALIST)
|
||||
else -> when (listName) {
|
||||
"Administratorzy" -> setTeacherType(Teacher.TYPE_SCHOOL_ADMIN)
|
||||
"Sekretarka" -> setTeacherType(Teacher.TYPE_SECRETARIAT)
|
||||
"Wsparcie techniczne mobiDziennik" -> setTeacherType(Teacher.TYPE_SUPER_ADMIN)
|
||||
else -> {
|
||||
setTeacherType(Teacher.TYPE_OTHER)
|
||||
typeDescription = listName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@ -55,14 +56,15 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
||||
|
||||
if (type == TYPE_RECEIVED) {
|
||||
// search sender teacher
|
||||
val senderName = senderEl.text()
|
||||
val senderName = senderEl.text().fixName()
|
||||
senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1
|
||||
data.messageRecipientList.add(MessageRecipient(profileId, -1, id))
|
||||
} else {
|
||||
// TYPE_SENT, so multiple recipients possible
|
||||
val recipientNames = senderEl.text().split(", ")
|
||||
for (recipientName in recipientNames) {
|
||||
val recipientId = data.teacherList.singleOrNull { it.fullNameLastFirst == recipientName }?.id ?: -1
|
||||
val name = recipientName.fixName()
|
||||
val recipientId = data.teacherList.singleOrNull { it.fullNameLastFirst == name }?.id ?: -1
|
||||
data.messageRecipientIgnoreList.add(MessageRecipient(profileId, recipientId, id))
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@ -48,7 +49,7 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
|
||||
val addedDate = Date.fromIsoHm(addedDateEl.text())
|
||||
|
||||
val senderEl = item.select("td:eq(2)").first()
|
||||
val senderName = senderEl.ownText()
|
||||
val senderName = senderEl.ownText().fixName()
|
||||
val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1
|
||||
data.messageRecipientIgnoreList.add(MessageRecipient(profileId, -1, id))
|
||||
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-26.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.data.api.POST
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
class MobidziennikWebSendMessage(
|
||||
override val data: DataMobidziennik,
|
||||
val recipients: List<Teacher>,
|
||||
val subject: String,
|
||||
val text: String,
|
||||
val onSuccess: () -> Unit
|
||||
) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebSendMessage"
|
||||
}
|
||||
|
||||
init {
|
||||
val params = mutableListOf<Pair<String, Any>>(
|
||||
"nazwa" to subject,
|
||||
"tresc" to text
|
||||
)
|
||||
for (teacher in recipients) {
|
||||
teacher.loginId?.let {
|
||||
params += "odbiorcy[]" to it
|
||||
}
|
||||
}
|
||||
|
||||
webGet(TAG, endpoint = "/dziennik/dodajwiadomosc", method = POST, parameters = params) { text ->
|
||||
|
||||
if (!text.contains(">Wiadomość została wysłana.<")) {
|
||||
// TODO error
|
||||
return@webGet
|
||||
}
|
||||
|
||||
// TODO create MobidziennikWebMessagesSent and replace this
|
||||
MobidziennikWebMessagesAll(data) {
|
||||
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
|
||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
|
||||
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
|
||||
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -68,6 +69,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
}
|
||||
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
}
|
||||
@ -80,6 +85,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
}
|
||||
|
||||
override fun getRecipientList() {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
TemplateFirstLogin(data) {
|
||||
completed()
|
||||
|
@ -171,7 +171,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
"P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl"
|
||||
"P90" -> "http://efeb-komunikacja-pro-mwujakowska.neo.win.vulcan.pl"
|
||||
"FK1", "FS1" -> "http://api.fakelog.cf"
|
||||
"SZ9" -> "http://vulcan.szkolny.eu"
|
||||
"SZ9" -> "http://hack.szkolny.eu"
|
||||
else -> null
|
||||
}
|
||||
return if (url != null) "$url/$symbol" else loginStore.getLoginData("apiUrl", null)
|
||||
|
@ -6,22 +6,24 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_VULCAN_API
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanData
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiMessagesChangeStatus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api.VulcanApiSendMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.firstlogin.VulcanFirstLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.prepare
|
||||
import pl.szczodrzynski.edziennik.data.api.prepareFor
|
||||
import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -31,6 +33,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataVulcan
|
||||
private var afterLogin: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
data = DataVulcan(app, profile, loginStore).apply {
|
||||
@ -57,18 +60,44 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
login()
|
||||
}
|
||||
|
||||
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
|
||||
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
loginMethodId?.let { data.prepareFor(vulcanLoginMethods, it) }
|
||||
afterLogin?.let { this.afterLogin = it }
|
||||
VulcanLogin(data) {
|
||||
VulcanData(data) {
|
||||
data()
|
||||
}
|
||||
}
|
||||
|
||||
private fun data() {
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
if (internalErrorList.isNotEmpty()) {
|
||||
d(TAG, " - Internal errors:")
|
||||
internalErrorList.forEach { d(TAG, " - code $it") }
|
||||
}
|
||||
afterLogin?.invoke() ?: VulcanData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
login(LOGIN_METHOD_VULCAN_API) {
|
||||
VulcanApiMessagesChangeStatus(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
VulcanLoginApi(data) {
|
||||
VulcanApiMessagesChangeStatus(data, message) {
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
|
||||
login(LOGIN_METHOD_VULCAN_API) {
|
||||
VulcanApiSendMessage(data, recipients, subject, text) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
@ -86,12 +115,11 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
VulcanFirstLogin(data) {
|
||||
completed()
|
||||
}
|
||||
override fun getRecipientList() {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() { VulcanFirstLogin(data) { completed() } }
|
||||
override fun cancel() {
|
||||
d(TAG, "Cancelled")
|
||||
data.cancel()
|
||||
@ -99,29 +127,17 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
|
||||
return object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
callback.onProgress(step)
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
callback.onStartProgress(stringRes)
|
||||
}
|
||||
|
||||
override fun onCompleted() { callback.onCompleted() }
|
||||
override fun onProgress(step: Float) { callback.onProgress(step) }
|
||||
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
|
||||
override fun onError(apiError: ApiError) {
|
||||
if (apiError.errorCode in internalErrorList) {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
return
|
||||
}
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ open class VulcanApi(open val data: DataVulcan) {
|
||||
val profileId
|
||||
get() = data.profile?.id ?: -1
|
||||
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
fun apiGet(
|
||||
tag: String,
|
||||
endpoint: String,
|
||||
|
@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.text.replace
|
||||
|
||||
class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
companion object {
|
||||
@ -26,11 +27,11 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
|
||||
val startDate: String = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d
|
||||
else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d
|
||||
val startDate = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).inUnix
|
||||
else -> Date.getToday().stepForward(0, -2, 0).inUnix
|
||||
}
|
||||
val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d
|
||||
val endDate = Date.getToday().stepForward(0, 1, 0).inUnix
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf(
|
||||
"DataPoczatkowa" to startDate,
|
||||
@ -71,7 +72,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
|
||||
profileId,
|
||||
id,
|
||||
subject,
|
||||
body,
|
||||
body.replace("\n", "<br>"),
|
||||
TYPE_RECEIVED,
|
||||
senderId,
|
||||
-1
|
||||
|
@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.text.replace
|
||||
|
||||
class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
companion object {
|
||||
@ -25,11 +26,12 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
val startDate: Long = when (profile.empty) {
|
||||
|
||||
val startDate = when (profile.empty) {
|
||||
true -> profile.getSemesterStart(profile.currentSemester).inUnix
|
||||
else -> Date.getToday().stepForward(0, -1, 0).inUnix
|
||||
else -> Date.getToday().stepForward(0, -2, 0).inUnix
|
||||
}
|
||||
val endDate: Long = profile.getSemesterEnd(profile.currentSemester).inUnix
|
||||
val endDate = Date.getToday().stepForward(0, 1, 0).inUnix
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_SENT, parameters = mapOf(
|
||||
"DataPoczatkowa" to startDate,
|
||||
@ -90,7 +92,7 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
profileId,
|
||||
id,
|
||||
subject,
|
||||
body,
|
||||
body.replace("\n", "<br>"),
|
||||
TYPE_SENT,
|
||||
-1,
|
||||
-1
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_MESSAGES_ADD
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
class VulcanApiSendMessage(
|
||||
override val data: DataVulcan,
|
||||
val recipients: List<Teacher>,
|
||||
val subject: String,
|
||||
val text: String,
|
||||
val onSuccess: () -> Unit
|
||||
) : VulcanApi(data) {
|
||||
companion object {
|
||||
private const val TAG = "VulcanApiSendMessage"
|
||||
}
|
||||
|
||||
init {
|
||||
val recipientsArray = JsonArray()
|
||||
for (teacher in recipients) {
|
||||
teacher.loginId?.let {
|
||||
recipientsArray += JsonObject(
|
||||
"LoginId" to it,
|
||||
"Nazwa" to "${teacher.fullNameLastFirst} - pracownik"
|
||||
)
|
||||
}
|
||||
}
|
||||
val params = mapOf(
|
||||
"NadawcaWiadomosci" to (profile?.accountNameLong ?: profile?.studentNameLong ?: ""),
|
||||
"Tytul" to subject,
|
||||
"Tresc" to text,
|
||||
"Adresaci" to recipientsArray,
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_ADD, parameters = params) { json, _ ->
|
||||
val messageId = json.getJsonObject("Data").getLong("WiadomoscId")
|
||||
|
||||
if (messageId == null) {
|
||||
// TODO error
|
||||
return@apiGet
|
||||
}
|
||||
|
||||
VulcanApiMessagesSent(data) {
|
||||
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
|
||||
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId }
|
||||
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
|
||||
|
||||
EventBus.getDefault().postSticky(event)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-27.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
|
||||
data class MessageSentEvent(val profileId: Int, val message: Message?, val sentDate: Long?)
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
data class RecipientListGetEvent(val profileId: Int, val teacherList: List<Teacher>)
|
@ -8,13 +8,16 @@ import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
interface EdziennikInterface {
|
||||
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
||||
fun getMessage(message: MessageFull)
|
||||
fun sendMessage(recipients: List<Teacher>, subject: String, text: String)
|
||||
fun markAllAnnouncementsAsRead()
|
||||
fun getAnnouncement(announcement: AnnouncementFull)
|
||||
fun getAttachment(message: Message, attachmentId: Long, attachmentName: String)
|
||||
fun getRecipientList()
|
||||
fun firstLogin()
|
||||
fun cancel()
|
||||
}
|
||||
|
@ -8,10 +8,11 @@ import android.content.Context
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest
|
||||
import pl.szczodrzynski.edziennik.stackTraceString
|
||||
|
||||
class ApiError(val tag: String, val errorCode: Int) {
|
||||
class ApiError(val tag: String, var errorCode: Int) {
|
||||
val id = System.currentTimeMillis()
|
||||
var profileId: Int? = null
|
||||
var throwable: Throwable? = null
|
||||
@ -61,7 +62,7 @@ class ApiError(val tag: String, val errorCode: Int) {
|
||||
if (it != 0)
|
||||
context.getString(it)
|
||||
else
|
||||
"?"
|
||||
context.getString(R.string.error_unknown_format, errorCode, tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,324 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-26
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.models;
|
||||
|
||||
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 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 == null ? null : 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 == null ? 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 == 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 == null ? null : 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 == null ? 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";
|
||||
}
|
||||
}
|
||||
}
|
@ -3,13 +3,12 @@ package pl.szczodrzynski.edziennik.data.api.models
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import androidx.core.util.size
|
||||
import androidx.room.OnConflictStrategy
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Response
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.DataNotifications
|
||||
import pl.szczodrzynski.edziennik.data.api.EXCEPTION_NOTIFY
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EndpointCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.models.AppError.*
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
|
||||
@ -117,6 +116,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
val lessonRanges = SparseArray<LessonRange>()
|
||||
val gradeCategories = LongSparseArray<GradeCategory>()
|
||||
|
||||
var teacherOnConflictStrategy = OnConflictStrategy.IGNORE
|
||||
|
||||
val classrooms = LongSparseArray<Classroom>()
|
||||
val attendanceTypes = LongSparseArray<AttendanceType>()
|
||||
val noticeTypes = LongSparseArray<NoticeType>()
|
||||
@ -257,9 +258,10 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
|
||||
// always present and not empty, during every sync
|
||||
db.endpointTimerDao().addAll(endpointTimers)
|
||||
db.teacherDao().clear(profileId)
|
||||
db.teacherDao().addAll(teacherList.values())
|
||||
db.subjectDao().clear(profileId)
|
||||
if (teacherOnConflictStrategy == OnConflictStrategy.IGNORE)
|
||||
db.teacherDao().addAllIgnore(teacherList.values())
|
||||
else if (teacherOnConflictStrategy == OnConflictStrategy.REPLACE)
|
||||
db.teacherDao().addAll(teacherList.values())
|
||||
db.subjectDao().addAll(subjectList.values())
|
||||
db.teamDao().clear(profileId)
|
||||
db.teamDao().addAll(teamList.values())
|
||||
@ -380,7 +382,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday().value) ?: -1) == -1
|
||||
}
|
||||
|
||||
fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {
|
||||
/*fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {
|
||||
val code = when (throwable) {
|
||||
is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET
|
||||
is SocketTimeoutException -> CODE_TIMEOUT
|
||||
@ -392,23 +394,36 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
error(ApiError(tag, code).apply {
|
||||
profileId = profile?.id ?: -1
|
||||
}.withResponse(response).withThrowable(throwable).withApiResponse(apiResponse))
|
||||
}
|
||||
}*/
|
||||
|
||||
fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) {
|
||||
val code = when (null) {
|
||||
is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET
|
||||
is SocketTimeoutException -> CODE_TIMEOUT
|
||||
else -> when (response?.code()) {
|
||||
400, 401, 424, 500, 503, 404 -> CODE_MAINTENANCE
|
||||
else -> errorCode
|
||||
}
|
||||
}
|
||||
error(ApiError(tag, code).apply {
|
||||
error(ApiError(tag, errorCode).apply {
|
||||
profileId = profile?.id ?: -1
|
||||
}.withResponse(response).withApiResponse(apiResponse))
|
||||
}
|
||||
|
||||
fun error(apiError: ApiError) {
|
||||
apiError.errorCode = when (apiError.throwable) {
|
||||
is UnknownHostException -> ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND
|
||||
is SSLException -> ERROR_REQUEST_FAILURE_SSL_ERROR
|
||||
is InterruptedIOException -> ERROR_REQUEST_FAILURE_NO_INTERNET
|
||||
is SocketTimeoutException -> ERROR_REQUEST_FAILURE_TIMEOUT
|
||||
else ->
|
||||
if (apiError.errorCode == ERROR_REQUEST_FAILURE)
|
||||
when (apiError.response?.code()) {
|
||||
400 -> ERROR_REQUEST_HTTP_400
|
||||
401 -> ERROR_REQUEST_HTTP_401
|
||||
403 -> ERROR_REQUEST_HTTP_403
|
||||
404 -> ERROR_REQUEST_HTTP_404
|
||||
405 -> ERROR_REQUEST_HTTP_405
|
||||
410 -> ERROR_REQUEST_HTTP_410
|
||||
424 -> ERROR_REQUEST_HTTP_424
|
||||
500 -> ERROR_REQUEST_HTTP_500
|
||||
503 -> ERROR_REQUEST_HTTP_503
|
||||
else -> apiError.errorCode
|
||||
}
|
||||
else apiError.errorCode
|
||||
}
|
||||
callback.onError(apiError)
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
|
||||
companion object {
|
||||
@ -26,9 +27,11 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments))
|
||||
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
||||
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
|
||||
fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
|
||||
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
|
||||
fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement))
|
||||
fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName))
|
||||
fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest())
|
||||
}
|
||||
|
||||
private lateinit var loginStore: LoginStore
|
||||
@ -77,10 +80,12 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
viewId = request.viewIds?.get(0)?.first,
|
||||
arguments = request.arguments)
|
||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
|
||||
is MessageSendRequest -> edziennikInterface?.sendMessage(request.recipients, request.subject, request.text)
|
||||
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
||||
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
|
||||
is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement)
|
||||
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName)
|
||||
is RecipientListGetRequest -> edziennikInterface?.getRecipientList()
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +102,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val arguments: JsonObject? = null)
|
||||
data class SyncProfileListRequest(val profileList: List<Int>)
|
||||
data class MessageGetRequest(val message: MessageFull)
|
||||
data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
|
||||
class AnnouncementsReadRequest
|
||||
data class AnnouncementGetRequest(val announcement: AnnouncementFull)
|
||||
data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String)
|
||||
class RecipientListGetRequest
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
AttendanceType.class,
|
||||
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
|
||||
ConfigEntry.class,
|
||||
Metadata.class}, version = 70)
|
||||
Metadata.class}, version = 71)
|
||||
@TypeConverters({
|
||||
ConverterTime.class,
|
||||
ConverterDate.class,
|
||||
@ -836,6 +836,19 @@ public abstract class AppDb extends RoomDatabase {
|
||||
database.execSQL("DELETE FROM announcements");
|
||||
}
|
||||
};
|
||||
private static final Migration MIGRATION_70_71 = new Migration(70, 71) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("DELETE FROM messages WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);");
|
||||
database.execSQL("DELETE FROM messageRecipients WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);");
|
||||
database.execSQL("DELETE FROM teachers WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);");
|
||||
database.execSQL("DELETE FROM endpointTimers WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);");
|
||||
database.execSQL("DELETE FROM metadata WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0) AND thingType = 8;");
|
||||
database.execSQL("UPDATE profiles SET empty = 1 WHERE archived = 0;");
|
||||
database.execSQL("UPDATE profiles SET lastReceiversSync = 0 WHERE archived = 0;");
|
||||
database.execSQL("INSERT INTO config (profileId, `key`, value) VALUES (-1, \"runSync\", \"true\");");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static AppDb getDatabase(final Context context) {
|
||||
@ -903,7 +916,8 @@ public abstract class AppDb extends RoomDatabase {
|
||||
MIGRATION_66_67,
|
||||
MIGRATION_67_68,
|
||||
MIGRATION_68_69,
|
||||
MIGRATION_69_70
|
||||
MIGRATION_69_70,
|
||||
MIGRATION_70_71
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
//.fallbackToDestructiveMigration()
|
||||
|
@ -1,17 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.messages;
|
||||
|
||||
public class MessageRecipientFull extends MessageRecipient {
|
||||
public String fullName = null;
|
||||
|
||||
public MessageRecipientFull(int profileId, long id, long replyId, long readDate, long messageId) {
|
||||
super(profileId, id, replyId, readDate, messageId);
|
||||
}
|
||||
|
||||
public MessageRecipientFull(int profileId, long id, long messageId) {
|
||||
super(profileId, id, messageId);
|
||||
}
|
||||
|
||||
public MessageRecipientFull() {
|
||||
super();
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.messages
|
||||
|
||||
import androidx.room.Ignore
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
|
||||
class MessageRecipientFull : MessageRecipient {
|
||||
var fullName: String? = ""
|
||||
get() {
|
||||
return field?.fixName() ?: ""
|
||||
}
|
||||
|
||||
@Ignore
|
||||
constructor(profileId: Int, id: Long, replyId: Long, readDate: Long, messageId: Long) : super(profileId, id, replyId, readDate, messageId) {}
|
||||
@Ignore
|
||||
constructor(profileId: Int, id: Long, messageId: Long) : super(profileId, id, messageId) {}
|
||||
constructor() : super() {}
|
||||
}
|
@ -176,7 +176,7 @@ open class Profile : IDrawerProfile {
|
||||
}
|
||||
|
||||
return context.getDrawableFromRes(R.drawable.profile).also {
|
||||
it.colorFilter = PorterDuffColorFilter(colorFromName(context, name), PorterDuff.Mode.DST_OVER)
|
||||
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
|
||||
}
|
||||
|
||||
/*if (profileImage == null) {
|
||||
@ -192,11 +192,11 @@ open class Profile : IDrawerProfile {
|
||||
try {
|
||||
ImageHolder(image ?: "")
|
||||
} catch (_: Exception) {
|
||||
ImageHolder(R.drawable.profile, colorFromName(context, name))
|
||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImageHolder(R.drawable.profile, colorFromName(context, name))
|
||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||
}
|
||||
}
|
||||
override fun applyImageTo(imageView: ImageView) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.subjects;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.ExtensionsKt;
|
||||
|
||||
@Entity(tableName = "subjects",
|
||||
primaryKeys = {"profileId", "subjectId"})
|
||||
@ -26,7 +27,7 @@ public class Subject {
|
||||
this.id = id;
|
||||
this.longName = longName;
|
||||
this.shortName = shortName;
|
||||
this.color = Colors.stringToColor(this.longName);
|
||||
this.color = ExtensionsKt.colorFromName(longName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,189 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.teachers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.ns;
|
||||
|
||||
@Entity(tableName = "teachers",
|
||||
primaryKeys = {"profileId", "teacherId"})
|
||||
public class Teacher {
|
||||
public int profileId;
|
||||
|
||||
@ColumnInfo(name = "teacherId")
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "teacherLoginId")
|
||||
public String loginId = null;
|
||||
|
||||
@ColumnInfo(name = "teacherName")
|
||||
public String name;
|
||||
@ColumnInfo(name = "teacherSurname")
|
||||
public String surname;
|
||||
|
||||
@ColumnInfo(name = "teacherType")
|
||||
public int type;
|
||||
public static final int TYPE_TEACHER = 0; // 1
|
||||
public static final int TYPE_EDUCATOR = 1; // 2
|
||||
public static final int TYPE_PEDAGOGUE = 2; // 4
|
||||
public static final int TYPE_LIBRARIAN = 3; // 8
|
||||
public static final int TYPE_SECRETARIAT = 4; // 16
|
||||
public static final int TYPE_PRINCIPAL = 5; // 32
|
||||
public static final int TYPE_SCHOOL_ADMIN = 6; // 64
|
||||
// not teachers
|
||||
public static final int TYPE_SUPER_ADMIN = 7; // 128
|
||||
public static final int TYPE_STUDENT = 8; // 256
|
||||
public static final int TYPE_PARENT = 9; // 512
|
||||
public static final int TYPE_PARENTS_COUNCIL = 10; // 1024
|
||||
public static final int TYPE_SCHOOL_PARENTS_COUNCIL = 11; // 2048
|
||||
public static final int TYPE_OTHER = 12; // 4096
|
||||
public static final int IS_TEACHER_MASK = 0b1111111;
|
||||
@ColumnInfo(name = "teacherTypeDescription")
|
||||
public String typeDescription = null;
|
||||
|
||||
public boolean isType(int checkingType) {
|
||||
return (type & (1 << checkingType)) >= 1;
|
||||
}
|
||||
public boolean isTeacher() {
|
||||
return type <= IS_TEACHER_MASK;
|
||||
}
|
||||
public void setType(int setType) {
|
||||
type |= (1 << setType);
|
||||
}
|
||||
public void unsetType(int unsetType) {
|
||||
type &= ~(1 << unsetType);
|
||||
}
|
||||
|
||||
public static String typeString(Context c, int type) {
|
||||
return typeString(c, type, null);
|
||||
}
|
||||
public static String typeString(Context c, int type, String typeDescription) {
|
||||
String suffix = bs(" - ", typeDescription);
|
||||
switch (type) {
|
||||
default:
|
||||
case TYPE_TEACHER:
|
||||
return c.getString(R.string.teacher_teacher)+suffix;
|
||||
case TYPE_PARENTS_COUNCIL:
|
||||
return c.getString(R.string.teacher_parents_council)+suffix;
|
||||
case TYPE_SCHOOL_PARENTS_COUNCIL:
|
||||
return c.getString(R.string.teacher_school_parents_council)+suffix;
|
||||
case TYPE_PEDAGOGUE:
|
||||
return c.getString(R.string.teacher_pedagogue)+suffix;
|
||||
case TYPE_LIBRARIAN:
|
||||
return c.getString(R.string.teacher_librarian)+suffix;
|
||||
case TYPE_SCHOOL_ADMIN:
|
||||
return c.getString(R.string.teacher_school_admin)+suffix;
|
||||
case TYPE_SUPER_ADMIN:
|
||||
return c.getString(R.string.teacher_super_admin)+suffix;
|
||||
case TYPE_SECRETARIAT:
|
||||
return c.getString(R.string.teacher_secretariat)+suffix;
|
||||
case TYPE_PRINCIPAL:
|
||||
return c.getString(R.string.teacher_principal)+suffix;
|
||||
case TYPE_EDUCATOR:
|
||||
return c.getString(R.string.teacher_educator)+suffix;
|
||||
case TYPE_PARENT:
|
||||
return c.getString(R.string.teacher_parent)+suffix;
|
||||
case TYPE_STUDENT:
|
||||
return c.getString(R.string.teacher_student)+suffix;
|
||||
case TYPE_OTHER:
|
||||
return c.getString(R.string.teacher_other)+suffix;
|
||||
}
|
||||
}
|
||||
public String getType(Context c) {
|
||||
return typeString(c, Utils.leftmostSetBit(type), typeDescription);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public Bitmap image = null;
|
||||
@Ignore
|
||||
public String displayName = null;
|
||||
|
||||
@Ignore
|
||||
public Teacher(int profileId, long id, String name, String surname) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.surname = surname;
|
||||
}
|
||||
|
||||
public Teacher(int profileId, long id, String name, String surname, String loginId) {
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.surname = surname;
|
||||
this.loginId = loginId;
|
||||
}
|
||||
|
||||
public String getFullName()
|
||||
{
|
||||
return name+" "+surname;
|
||||
}
|
||||
public String getFullNameLastFirst()
|
||||
{
|
||||
return surname+" "+name;
|
||||
}
|
||||
|
||||
public String getShortName()
|
||||
{
|
||||
return (name.length() == 0 ? " " : name.charAt(0))+"."+surname;
|
||||
}
|
||||
|
||||
// USED IN IUCZNIOWIE + getRecipientList
|
||||
public static Teacher getById(List<Teacher> teacherList, long id)
|
||||
{
|
||||
for (Teacher teacher: teacherList) {
|
||||
if (teacher.id == id)
|
||||
{
|
||||
return teacher;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static Teacher getByFullName(List<Teacher> teacherList, String name)
|
||||
{
|
||||
for (Teacher teacher: teacherList) {
|
||||
if ((teacher.name + " " + teacher.surname).equals(name))
|
||||
{
|
||||
return teacher;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static Teacher getByFullNameLastFirst(List<Teacher> teacherList, String name)
|
||||
{
|
||||
for (Teacher teacher: teacherList) {
|
||||
if ((teacher.surname + " " + teacher.name).equals(name))
|
||||
{
|
||||
return teacher;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static Teacher getByShortName(List<Teacher> teacherList, String shortName) /* F.Lastname */
|
||||
{
|
||||
for (Teacher teacher: teacherList) {
|
||||
if (teacher.name.length() > 0 && ((teacher.name.charAt(0) + "." + teacher.surname).equals(shortName)))
|
||||
{
|
||||
return teacher;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFullName();
|
||||
}
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.teachers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.join
|
||||
import java.util.*
|
||||
|
||||
@Entity(tableName = "teachers",
|
||||
primaryKeys = ["profileId", "teacherId"])
|
||||
open class Teacher {
|
||||
companion object {
|
||||
const val TYPE_TEACHER = 0 // 1
|
||||
const val TYPE_EDUCATOR = 1 // 2
|
||||
const val TYPE_PEDAGOGUE = 2 // 4
|
||||
const val TYPE_LIBRARIAN = 3 // 8
|
||||
const val TYPE_SECRETARIAT = 4 // 16
|
||||
const val TYPE_PRINCIPAL = 5 // 32
|
||||
const val TYPE_SCHOOL_ADMIN = 6 // 64
|
||||
// not teachers
|
||||
const val TYPE_SPECIALIST = 7 // 128
|
||||
const val TYPE_SUPER_ADMIN = 10 // 1024
|
||||
const val TYPE_STUDENT = 12 // 4096
|
||||
const val TYPE_PARENT = 14 // 16384
|
||||
const val TYPE_PARENTS_COUNCIL = 15 // 32768
|
||||
const val TYPE_SCHOOL_PARENTS_COUNCIL = 16 // 65536
|
||||
const val TYPE_OTHER = 24 // 16777216
|
||||
const val IS_TEACHER_MASK = 127
|
||||
|
||||
val types: List<Int> by lazy { listOf(
|
||||
TYPE_TEACHER,
|
||||
TYPE_EDUCATOR,
|
||||
TYPE_PEDAGOGUE,
|
||||
TYPE_LIBRARIAN,
|
||||
TYPE_SECRETARIAT,
|
||||
TYPE_PRINCIPAL,
|
||||
TYPE_SCHOOL_ADMIN,
|
||||
TYPE_SPECIALIST,
|
||||
TYPE_SUPER_ADMIN,
|
||||
TYPE_STUDENT,
|
||||
TYPE_PARENT,
|
||||
TYPE_PARENTS_COUNCIL,
|
||||
TYPE_SCHOOL_PARENTS_COUNCIL,
|
||||
TYPE_OTHER
|
||||
) }
|
||||
|
||||
fun typeName(c: Context, type: Int, typeDescription: String? = null): String {
|
||||
val suffix = typeDescription?.let { " ($typeDescription)" } ?: ""
|
||||
return when (type) {
|
||||
TYPE_TEACHER -> c.getString(R.string.teacher_teacher)
|
||||
TYPE_PARENTS_COUNCIL -> c.getString(R.string.teacher_parents_council) + suffix
|
||||
TYPE_SCHOOL_PARENTS_COUNCIL -> c.getString(R.string.teacher_school_parents_council)
|
||||
TYPE_PEDAGOGUE -> c.getString(R.string.teacher_pedagogue)
|
||||
TYPE_LIBRARIAN -> c.getString(R.string.teacher_librarian)
|
||||
TYPE_SCHOOL_ADMIN -> c.getString(R.string.teacher_school_admin)
|
||||
TYPE_SUPER_ADMIN -> c.getString(R.string.teacher_super_admin)
|
||||
TYPE_SECRETARIAT -> c.getString(R.string.teacher_secretariat)
|
||||
TYPE_PRINCIPAL -> c.getString(R.string.teacher_principal)
|
||||
TYPE_EDUCATOR -> c.getString(R.string.teacher_educator) + suffix
|
||||
TYPE_PARENT -> c.getString(R.string.teacher_parent) + suffix
|
||||
TYPE_STUDENT -> c.getString(R.string.teacher_student) + suffix
|
||||
TYPE_SPECIALIST -> c.getString(R.string.teacher_specialist)
|
||||
else -> c.getString(R.string.teacher_other) + suffix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var profileId: Int
|
||||
|
||||
@ColumnInfo(name = "teacherId")
|
||||
var id: Long
|
||||
|
||||
@ColumnInfo(name = "teacherLoginId")
|
||||
var loginId: String? = null
|
||||
|
||||
@ColumnInfo(name = "teacherName")
|
||||
var name: String? = ""
|
||||
|
||||
@ColumnInfo(name = "teacherSurname")
|
||||
var surname: String? = ""
|
||||
|
||||
@ColumnInfo(name = "teacherType")
|
||||
var type = 0
|
||||
|
||||
@ColumnInfo(name = "teacherTypeDescription")
|
||||
var typeDescription: String? = null
|
||||
|
||||
fun isType(checkingType: Int): Boolean {
|
||||
return type and (1 shl checkingType) >= 1
|
||||
}
|
||||
|
||||
val isTeacher: Boolean
|
||||
get() = type <= IS_TEACHER_MASK
|
||||
|
||||
fun setTeacherType(i: Int) {
|
||||
type = type or (1 shl i)
|
||||
}
|
||||
|
||||
fun unsetTeacherType(i: Int) {
|
||||
type = type and (1 shl i).inv()
|
||||
}
|
||||
|
||||
fun getTypeText(c: Context): String {
|
||||
val list = mutableListOf<String>()
|
||||
types.forEach {
|
||||
if (isType(it))
|
||||
list += typeName(c, it, typeDescription)
|
||||
}
|
||||
return list.join(", ")
|
||||
}
|
||||
|
||||
|
||||
@Ignore
|
||||
var image: Bitmap? = null
|
||||
|
||||
/**
|
||||
* Used in Message composing - searching in AutoComplete bolds
|
||||
* the typed part of the full name.
|
||||
*/
|
||||
@Ignore
|
||||
var recipientDisplayName: CharSequence? = null
|
||||
/**
|
||||
* Used in Message composing - determining the priority
|
||||
* of search result, based on the search phrase match
|
||||
* (beginning of sentence, beginning of word, middle of word).
|
||||
*/
|
||||
@Ignore
|
||||
var recipientWeight: Int = 0
|
||||
|
||||
@Ignore
|
||||
constructor(profileId: Int, id: Long) {
|
||||
this.profileId = profileId
|
||||
this.id = id
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Ignore
|
||||
constructor(profileId: Int, id: Long, name: String, surname: String) {
|
||||
this.profileId = profileId
|
||||
this.id = id
|
||||
this.name = name
|
||||
this.surname = surname
|
||||
}
|
||||
|
||||
constructor(profileId: Int, id: Long, name: String, surname: String, loginId: String?) {
|
||||
this.profileId = profileId
|
||||
this.id = id
|
||||
this.name = name
|
||||
this.surname = surname
|
||||
this.loginId = loginId
|
||||
}
|
||||
|
||||
@Ignore
|
||||
constructor(teacher: Teacher) {
|
||||
teacher.let {
|
||||
this.profileId = it.profileId
|
||||
this.id = it.id
|
||||
this.loginId = it.loginId
|
||||
this.name = it.name
|
||||
this.surname = it.surname
|
||||
this.type = it.type
|
||||
this.typeDescription = it.typeDescription
|
||||
this.image = it.image
|
||||
this.recipientDisplayName = it.recipientDisplayName
|
||||
}
|
||||
}
|
||||
|
||||
@delegate:Ignore
|
||||
val fullName by lazy { "$name $surname".fixName() }
|
||||
|
||||
@delegate:Ignore
|
||||
val fullNameLastFirst by lazy { "$surname $name".fixName() }
|
||||
|
||||
val shortName: String
|
||||
get() = (if (name == null || name?.length == 0) "" else name!![0].toString()) + "." + surname
|
||||
|
||||
override fun toString(): String {
|
||||
return "Teacher{" +
|
||||
"profileId=" + profileId +
|
||||
", id=" + id +
|
||||
", loginId='" + loginId + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", surname='" + surname + '\'' +
|
||||
", type=" + dumpType() +
|
||||
", typeDescription='" + typeDescription + '\'' +
|
||||
'}'
|
||||
}
|
||||
|
||||
private fun dumpType(): String {
|
||||
val typeList: MutableList<String> = ArrayList()
|
||||
if (isType(TYPE_TEACHER)) typeList.add("TYPE_TEACHER")
|
||||
if (isType(TYPE_EDUCATOR)) typeList.add("TYPE_EDUCATOR($typeDescription)")
|
||||
if (isType(TYPE_PEDAGOGUE)) typeList.add("TYPE_PEDAGOGUE")
|
||||
if (isType(TYPE_LIBRARIAN)) typeList.add("TYPE_PEDAGOGUE")
|
||||
if (isType(TYPE_SECRETARIAT)) typeList.add("TYPE_SECRETARIAT")
|
||||
if (isType(TYPE_PRINCIPAL)) typeList.add("TYPE_PRINCIPAL")
|
||||
if (isType(TYPE_SCHOOL_ADMIN)) typeList.add("TYPE_SCHOOL_ADMIN")
|
||||
if (isType(TYPE_SPECIALIST)) typeList.add("TYPE_SPECIALIST")
|
||||
if (isType(TYPE_SUPER_ADMIN)) typeList.add("TYPE_SUPER_ADMIN")
|
||||
if (isType(TYPE_STUDENT)) typeList.add("TYPE_STUDENT")
|
||||
if (isType(TYPE_PARENT)) typeList.add("TYPE_PARENT")
|
||||
if (isType(TYPE_PARENTS_COUNCIL)) typeList.add("TYPE_PARENTS_COUNCIL")
|
||||
if (isType(TYPE_SCHOOL_PARENTS_COUNCIL)) typeList.add("TYPE_SCHOOL_PARENTS_COUNCIL")
|
||||
if (isType(TYPE_OTHER)) typeList.add("TYPE_OTHER($typeDescription)")
|
||||
return typeList.join(", ")
|
||||
}
|
||||
}
|
@ -1,39 +1,37 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.teachers;
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.teachers
|
||||
|
||||
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 androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
public interface TeacherDao {
|
||||
interface TeacherDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void add(Teacher teacher);
|
||||
fun add(teacher: Teacher)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void addAll(List<Teacher> teacherList);
|
||||
fun addAll(teacherList: List<Teacher>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
void addAllIgnore(List<Teacher> teacherList);
|
||||
fun addAllIgnore(teacherList: List<Teacher>)
|
||||
|
||||
@Query("DELETE FROM teachers WHERE profileId = :profileId")
|
||||
void clear(int profileId);
|
||||
fun clear(profileId: Int)
|
||||
|
||||
@Query("SELECT * FROM teachers WHERE profileId = :profileId AND teacherId = :id")
|
||||
LiveData<Teacher> getById(int profileId, long id);
|
||||
fun getById(profileId: Int, id: Long): LiveData<Teacher?>
|
||||
|
||||
@Query("SELECT * FROM teachers WHERE profileId = :profileId AND teacherId = :id")
|
||||
Teacher getByIdNow(int profileId, long id);
|
||||
fun getByIdNow(profileId: Int, id: Long): Teacher?
|
||||
|
||||
@Query("SELECT * FROM teachers WHERE profileId = :profileId AND teacherType <= 127 ORDER BY teacherName, teacherSurname ASC")
|
||||
LiveData<List<Teacher>> getAllTeachers(int profileId);
|
||||
fun getAllTeachers(profileId: Int): LiveData<List<Teacher>>
|
||||
|
||||
@Query("SELECT * FROM teachers WHERE profileId = :profileId ORDER BY teacherName, teacherSurname ASC")
|
||||
List<Teacher> getAllNow(int profileId);
|
||||
fun getAllNow(profileId: Int): List<Teacher>
|
||||
|
||||
@Query("UPDATE teachers SET teacherLoginId = :loginId WHERE profileId = :profileId AND teacherId = :id")
|
||||
void updateLoginId(int profileId, long id, String loginId);
|
||||
}
|
||||
fun updateLoginId(profileId: Int, id: Long, loginId: String?)
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.base
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.navlib.getColorFromAttr
|
||||
|
||||
class MainSnackbar(val activity: AppCompatActivity) {
|
||||
companion object {
|
||||
private const val TAG = "MainSnackbar"
|
||||
}
|
||||
|
||||
private var snackbar: Snackbar? = null
|
||||
private lateinit var coordinator: CoordinatorLayout
|
||||
|
||||
fun setCoordinator(coordinatorLayout: CoordinatorLayout, showAbove: View? = null) {
|
||||
this.coordinator = coordinatorLayout
|
||||
snackbar = Snackbar.make(coordinator, "", Snackbar.LENGTH_INDEFINITE)
|
||||
snackbar?.setAction(R.string.more) {
|
||||
|
||||
}
|
||||
val bgColor = ColorUtils.compositeColors(
|
||||
getColorFromAttr(activity, R.attr.colorOnSurface) and 0xcfffffff.toInt(),
|
||||
getColorFromAttr(activity, R.attr.colorSurface)
|
||||
)
|
||||
snackbar?.setBackgroundTint(bgColor)
|
||||
showAbove?.let { snackbar?.anchorView = it }
|
||||
}
|
||||
|
||||
fun snackbar(text: String, actionText: String? = null, onClick: (() -> Unit)? = null) {
|
||||
snackbar?.apply {
|
||||
setText(text)
|
||||
setAction(actionText) {
|
||||
onClick?.invoke()
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
fun dismiss() = snackbar?.dismiss()
|
||||
}
|
@ -56,7 +56,6 @@ import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentHomeOldBinding;
|
||||
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginLibrusCaptchaActivity;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
@ -104,7 +103,6 @@ public class HomeFragmentOld extends Fragment {
|
||||
b.devMode.setVisibility(App.devMode ? View.VISIBLE : View.GONE);
|
||||
|
||||
b.composeButton.setOnClickListener((v -> {
|
||||
startActivity(new Intent(activity, MessagesComposeActivity.class));
|
||||
}));
|
||||
|
||||
b.pruneWorkButton.setOnClickListener((v -> WorkManager.getInstance(app).pruneWork()));
|
||||
|
@ -20,6 +20,7 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.CardHomeDebugBinding
|
||||
import pl.szczodrzynski.edziennik.dp
|
||||
@ -28,7 +29,6 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginLibrusCaptchaActivity
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeDebugCard(
|
||||
@ -54,10 +54,27 @@ class HomeDebugCard(
|
||||
}
|
||||
holder.root += b.root
|
||||
|
||||
b.composeButton.onClick {
|
||||
app.startActivity(Intent(activity, MessagesComposeActivity::class.java));
|
||||
b.composeNewButton.onClick {
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_COMPOSE)
|
||||
}
|
||||
|
||||
b.migrate71.onClick {
|
||||
app.db.compileStatement("DELETE FROM messages WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
||||
app.db.compileStatement("DELETE FROM messageRecipients WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
||||
app.db.compileStatement("DELETE FROM teachers WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
||||
app.db.compileStatement("DELETE FROM endpointTimers WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);").executeUpdateDelete()
|
||||
app.db.compileStatement("DELETE FROM metadata WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0) AND thingType = 8;").executeUpdateDelete()
|
||||
app.db.compileStatement("UPDATE profiles SET empty = 1 WHERE archived = 0;").executeUpdateDelete()
|
||||
app.db.compileStatement("UPDATE profiles SET lastReceiversSync = 0 WHERE archived = 0;").executeUpdateDelete()
|
||||
app.profile.lastReceiversSync = 0
|
||||
app.profile.empty = true
|
||||
}
|
||||
|
||||
b.syncReceivers.onClick {
|
||||
EdziennikTask.recipientListGet(App.profileId).enqueue(activity)
|
||||
}
|
||||
|
||||
|
||||
b.pruneWorkButton.onClick {
|
||||
WorkManager.getInstance(app).pruneWork()
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentLoginIuczniowieBinding;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_LOGIN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_SCHOOL_NAME;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_IUCZNIOWIE;
|
||||
|
||||
public class LoginIuczniowieFragment extends Fragment {
|
||||
@ -57,10 +57,10 @@ public class LoginIuczniowieFragment extends Fragment {
|
||||
ApiError error = LoginActivity.error;
|
||||
if (error != null) {
|
||||
switch (error.getErrorCode()) {
|
||||
case CODE_INVALID_SCHOOL_NAME:
|
||||
case ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME:
|
||||
b.loginSchoolNameLayout.setError(getString(R.string.login_error_incorrect_school_name));
|
||||
break;
|
||||
case CODE_INVALID_LOGIN:
|
||||
case ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN:
|
||||
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
|
||||
break;
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentLoginMobidziennikBinding;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_ARCHIVED;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_LOGIN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_SERVER_ADDRESS;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_OLD_PASSWORD;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
|
||||
|
||||
public class LoginMobidziennikFragment extends Fragment {
|
||||
@ -59,16 +59,16 @@ public class LoginMobidziennikFragment extends Fragment {
|
||||
ApiError error = LoginActivity.error;
|
||||
if (error != null) {
|
||||
switch (error.getErrorCode()) {
|
||||
case CODE_INVALID_LOGIN:
|
||||
case ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN:
|
||||
b.loginPasswordLayout.setError(getString(R.string.login_error_incorrect_login_or_password));
|
||||
break;
|
||||
case CODE_OLD_PASSWORD:
|
||||
case ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD:
|
||||
b.loginPasswordLayout.setError(getString(R.string.login_error_old_password));
|
||||
break;
|
||||
case CODE_ARCHIVED:
|
||||
case ERROR_LOGIN_MOBIDZIENNIK_WEB_ARCHIVED:
|
||||
b.loginUsernameLayout.setError(getString(R.string.sync_error_archived));
|
||||
break;
|
||||
case CODE_INVALID_SERVER_ADDRESS:
|
||||
case ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_ADDRESS:
|
||||
b.loginServerAddressLayout.setError(getString(R.string.login_error_incorrect_address));
|
||||
break;
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class LoginProgressFragment extends Fragment {
|
||||
nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
public void onSyncErrorEvent(ApiTaskErrorEvent event) {
|
||||
LoginActivity.error = event.getError();
|
||||
if (getActivity() == null)
|
||||
|
@ -39,10 +39,10 @@ import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.webpush.QrScannerActivity;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_EXPIRED_TOKEN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_PIN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_SYMBOL;
|
||||
import static pl.szczodrzynski.edziennik.data.api.models.AppError.CODE_INVALID_TOKEN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_VULCAN_EXPIRED_TOKEN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_VULCAN_INVALID_PIN;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_VULCAN_INVALID_SYMBOL;
|
||||
import static pl.szczodrzynski.edziennik.data.api.ErrorsKt.ERROR_LOGIN_VULCAN_INVALID_TOKEN;
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN;
|
||||
|
||||
public class LoginVulcanFragment extends Fragment {
|
||||
@ -79,16 +79,16 @@ public class LoginVulcanFragment extends Fragment {
|
||||
ApiError error = LoginActivity.error;
|
||||
if (error != null) {
|
||||
switch (error.getErrorCode()) {
|
||||
case CODE_INVALID_TOKEN:
|
||||
case ERROR_LOGIN_VULCAN_INVALID_TOKEN:
|
||||
b.loginTokenLayout.setError(getString(R.string.login_error_incorrect_token));
|
||||
break;
|
||||
case CODE_EXPIRED_TOKEN:
|
||||
case ERROR_LOGIN_VULCAN_EXPIRED_TOKEN:
|
||||
b.loginTokenLayout.setError(getString(R.string.login_error_expired_token));
|
||||
break;
|
||||
case CODE_INVALID_SYMBOL:
|
||||
case ERROR_LOGIN_VULCAN_INVALID_SYMBOL:
|
||||
b.loginSymbolLayout.setError(getString(R.string.login_error_incorrect_symbol));
|
||||
break;
|
||||
case CODE_INVALID_PIN:
|
||||
case ERROR_LOGIN_VULCAN_INVALID_PIN:
|
||||
/*if (!"?".equals(error.errorText)) {
|
||||
b.loginPinLayout.setError(getString(R.string.login_error_incorrect_pin_format, error.errorText));
|
||||
break;
|
||||
|
@ -34,7 +34,9 @@ import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.T
|
||||
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent.Companion.TYPE_PROGRESS
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_IUCZNIOWIE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.databinding.MessageFragmentBinding
|
||||
@ -88,30 +90,6 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
)
|
||||
b.closeButton.setOnClickListener { activity.navigateUp() }
|
||||
|
||||
val messageId = arguments?.getLong("messageId")
|
||||
if (messageId == null) {
|
||||
activity.navigateUp()
|
||||
return
|
||||
}
|
||||
|
||||
launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
val msg = app.db.messageDao().getById(App.profileId, messageId)?.also {
|
||||
it.recipients = app.db.messageRecipientDao().getAllByMessageId(it.profileId, it.id)
|
||||
if (it.body != null && !it.seen) {
|
||||
app.db.metadataDao().setSeen(it.profileId, it, true)
|
||||
}
|
||||
}
|
||||
msg
|
||||
}
|
||||
val msg = deferred.await() ?: run {
|
||||
return@launch
|
||||
}
|
||||
message = msg
|
||||
b.subject.text = message.subject
|
||||
checkMessage()
|
||||
}
|
||||
|
||||
// click to expand subject and sender
|
||||
b.subject.onClick {
|
||||
it.maxLines = if (it.maxLines == 30) 2 else 30
|
||||
@ -119,6 +97,56 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
b.sender.onClick {
|
||||
it.maxLines = if (it.maxLines == 30) 2 else 30
|
||||
}
|
||||
|
||||
b.replyButton.onClick {
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_COMPOSE, Bundle(
|
||||
"message" to app.gson.toJson(message),
|
||||
"type" to "reply"
|
||||
))
|
||||
}
|
||||
b.forwardButton.onClick {
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_COMPOSE, Bundle(
|
||||
"message" to app.gson.toJson(message),
|
||||
"type" to "forward"
|
||||
))
|
||||
}
|
||||
|
||||
launch {
|
||||
|
||||
val messageString = arguments?.getString("message")
|
||||
val messageId = arguments?.getLong("messageId")
|
||||
if (messageId == null) {
|
||||
activity.navigateUp()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val msg = withContext(Dispatchers.Default) {
|
||||
|
||||
val msg =
|
||||
if (messageString != null)
|
||||
app.gson.fromJson(messageString, MessageFull::class.java)?.also {
|
||||
//it.body = null
|
||||
it.addedDate = arguments?.getLong("sentDate") ?: System.currentTimeMillis()
|
||||
}
|
||||
else
|
||||
app.db.messageDao().getById(App.profileId, messageId)
|
||||
|
||||
msg?.also {
|
||||
it.recipients = app.db.messageRecipientDao().getAllByMessageId(it.profileId, it.id)
|
||||
if (it.body != null && !it.seen) {
|
||||
app.db.metadataDao().setSeen(it.profileId, it, true)
|
||||
}
|
||||
}
|
||||
|
||||
} ?: run {
|
||||
activity.navigateUp()
|
||||
return@launch
|
||||
}
|
||||
|
||||
message = msg
|
||||
b.subject.text = message.subject
|
||||
checkMessage()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
@ -146,7 +174,7 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
|
||||
|
||||
// if a sent msg is not read by everyone, download it again to check the read status
|
||||
if (!checkRecipients()) {
|
||||
if (!checkRecipients() && app.profile.loginStoreType != LoginStore.LOGIN_TYPE_VULCAN) {
|
||||
EdziennikTask.messageGet(App.profileId, message).enqueue(activity)
|
||||
return
|
||||
}
|
||||
@ -157,7 +185,7 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
private fun checkRecipients(): Boolean {
|
||||
message.recipients?.forEach { recipient ->
|
||||
if (recipient.id == -1L)
|
||||
recipient.fullName = app.profile.accountNameLong ?: app.profile.studentNameLong
|
||||
recipient.fullName = app.profile.accountNameLong ?: app.profile.studentNameLong ?: ""
|
||||
if (message.type == TYPE_SENT && recipient.readDate < 1)
|
||||
return false
|
||||
}
|
||||
@ -174,6 +202,22 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
|
||||
b.subject.text = message.subject
|
||||
|
||||
b.replyButton.visibility = if (message.type == TYPE_RECEIVED) View.VISIBLE else View.INVISIBLE
|
||||
if (message.type == TYPE_RECEIVED) {
|
||||
activity.navView.apply {
|
||||
bottomBar.apply {
|
||||
fabEnable = true
|
||||
fabExtendedText = getString(R.string.messages_reply)
|
||||
fabIcon = CommunityMaterial.Icon2.cmd_reply
|
||||
}
|
||||
|
||||
setFabOnClickListener(View.OnClickListener {
|
||||
b.replyButton.performClick()
|
||||
})
|
||||
}
|
||||
activity.gainAttentionFAB()
|
||||
}
|
||||
|
||||
val messageRecipients = StringBuilder("<ul>")
|
||||
message.recipients?.forEach { recipient ->
|
||||
when (recipient.readDate) {
|
||||
|
@ -1,201 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.hootsuite.nachos.ChipConfiguration;
|
||||
import com.hootsuite.nachos.NachoTextView;
|
||||
import com.hootsuite.nachos.chip.Chip;
|
||||
import com.hootsuite.nachos.chip.ChipInfo;
|
||||
import com.hootsuite.nachos.chip.ChipSpan;
|
||||
import com.hootsuite.nachos.chip.ChipSpanChipCreator;
|
||||
import com.hootsuite.nachos.tokenizer.SpanChipTokenizer;
|
||||
import com.hootsuite.nachos.validator.IllegalCharacterIdentifier;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher;
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityComposeMessageBinding;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
|
||||
public class MessagesComposeActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = "MessageCompose";
|
||||
private App app;
|
||||
private ActivityComposeMessageBinding b;
|
||||
private List<Teacher> teachers = new ArrayList<>();
|
||||
private ActionBar actionBar;
|
||||
private MessagesComposeInfo composeInfo;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
app = (App)getApplication();
|
||||
setTheme(Themes.INSTANCE.getAppTheme());
|
||||
b = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_compose_message, null, false);
|
||||
setContentView(b.getRoot());
|
||||
|
||||
/*composeInfo = Edziennik.getApi(app, app.profile.getLoginStoreType()).getComposeInfo(app.profile);
|
||||
|
||||
Toolbar toolbar = b.toolbar;
|
||||
setSupportActionBar(toolbar);
|
||||
actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.messages_compose_title);
|
||||
}
|
||||
|
||||
List<Teacher> categories = new ArrayList<>();
|
||||
for (int i = 0; i < 11; i++) {
|
||||
categories.add(new Teacher(-1, -1*i, Teacher.typeString(this, i), ""));
|
||||
}
|
||||
|
||||
Edziennik.getApi(app, app.profile.getLoginStoreType()).getRecipientList(this, new SyncCallback() {
|
||||
@Override public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) { }
|
||||
@Override public void onSuccess(Context activityContext, ProfileFull profileFull) { }
|
||||
@Override public void onProgress(int progressStep) { }
|
||||
@Override public void onActionStarted(int stringResId) { }
|
||||
@Override
|
||||
public void onError(Context activityContext, AppError error) {
|
||||
new Handler(activityContext.getMainLooper()).post(() -> {
|
||||
app.apiEdziennik.guiShowErrorDialog(MessagesComposeActivity.this, error, R.string.messages_recipient_list_download_error);
|
||||
});
|
||||
}
|
||||
}, app.profile, teacherList -> {
|
||||
teachers.clear();
|
||||
for (Teacher teacher: teacherList) {
|
||||
if (teacher.loginId != null)
|
||||
teachers.add(teacher);
|
||||
}
|
||||
teachers.addAll(categories);
|
||||
MessagesComposeSuggestionAdapter adapter = new MessagesComposeSuggestionAdapter(this, teachers);
|
||||
//ArrayAdapter<Teacher> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, teachers);
|
||||
b.nachoTextView.setAdapter(adapter);
|
||||
});*/
|
||||
/*app.db.teacherDao().getAllTeachers(App.profileId).observe(this, teachers -> {
|
||||
|
||||
});*/
|
||||
|
||||
/*int[][] states = new int[][] {
|
||||
new int[] {}
|
||||
};
|
||||
int[] colors = new int[] {
|
||||
getResources().getColor(ThemeUtils.getChipColorRes())
|
||||
};*/
|
||||
//ColorStateList chipStateList = new ColorStateList(states, colors);
|
||||
|
||||
b.nachoTextView.setChipTokenizer(new SpanChipTokenizer<>(this, new ChipSpanChipCreator() {
|
||||
@Override
|
||||
public ChipSpan createChip(@NonNull Context context, @NonNull CharSequence text, Object data) {
|
||||
Teacher teacher = (Teacher) data;
|
||||
if (teacher.id <= 0) {
|
||||
int type = (int) (teacher.id * -1);
|
||||
List<Teacher> category = new ArrayList<>();
|
||||
List<String> categoryNames = new ArrayList<>();
|
||||
for (Teacher teacher1: teachers) {
|
||||
if (teacher1.isType(type)) {
|
||||
category.add(teacher1);
|
||||
categoryNames.add(teacher1.getFullName());
|
||||
}
|
||||
}
|
||||
new MaterialDialog.Builder(MessagesComposeActivity.this)
|
||||
.title(R.string.messages_compose_recipients_title)
|
||||
.content(getString(R.string.messages_compose_recipients_text_format, Teacher.typeString(MessagesComposeActivity.this, type)))
|
||||
.items(categoryNames)
|
||||
.itemsCallbackMultiChoice(null, ((dialog, which, text1) -> {
|
||||
List<ChipInfo> chipInfoList = new ArrayList<>();
|
||||
for (int index: which) {
|
||||
Teacher selected = category.get(index);
|
||||
selected.image = MessagesUtils.getProfileImage(48, 24, 16, 12, 1, selected.getFullName());
|
||||
chipInfoList.add(new ChipInfo(selected.getFullName(), selected));
|
||||
}
|
||||
b.nachoTextView.addTextWithChips(chipInfoList);
|
||||
return true;
|
||||
}))
|
||||
.positiveText(R.string.ok)
|
||||
.negativeText(R.string.cancel)
|
||||
.show();
|
||||
return null;
|
||||
}
|
||||
ChipSpan chipSpan = new ChipSpan(context, text, new BitmapDrawable(context.getResources(), teacher.image), teacher);
|
||||
chipSpan.setIconBackgroundColor(Colors.stringToMaterialColor(teacher.getFullName()));
|
||||
return chipSpan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureChip(@NonNull ChipSpan chip, @NonNull ChipConfiguration chipConfiguration) {
|
||||
super.configureChip(chip, chipConfiguration);
|
||||
//chip.setBackgroundColor(chipStateList);
|
||||
chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(MessagesComposeActivity.this));
|
||||
}
|
||||
}, ChipSpan.class));
|
||||
|
||||
|
||||
b.nachoTextView.setIllegalCharacterIdentifier(new IllegalCharacterIdentifier() {
|
||||
@Override
|
||||
public boolean isCharacterIllegal(Character c) {
|
||||
return c.toString().matches("[\\n;:_ ]");
|
||||
}
|
||||
});
|
||||
//b.nachoTextView.addChipTerminator('\n', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_ALL);
|
||||
//b.nachoTextView.addChipTerminator(' ', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_TO_TERMINATOR);
|
||||
//b.nachoTextView.addChipTerminator(';', ChipTerminatorHandler.BEHAVIOR_CHIPIFY_CURRENT_TOKEN);
|
||||
//b.nachoTextView.setNachoValidator(new ChipifyingNachoValidator());
|
||||
//b.nachoTextView.enableEditChipOnTouch(false, false);
|
||||
//b.nachoTextView.disableEditChipOnTouch();
|
||||
b.nachoTextView.setOnChipClickListener(new NachoTextView.OnChipClickListener() {
|
||||
@Override
|
||||
public void onChipClick(Chip chip, MotionEvent motionEvent) {
|
||||
Toast.makeText(app, "onChipClick: " + chip.getText(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
b.nachoTextView.setOnChipRemoveListener(new NachoTextView.OnChipRemoveListener() {
|
||||
@Override
|
||||
public void onChipRemove(Chip chip) {
|
||||
Log.d(TAG, "onChipRemoved: " + chip.getText());
|
||||
b.nachoTextView.setSelection(b.nachoTextView.getText().length());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_compose, menu);
|
||||
|
||||
menu.findItem(R.id.action_send).setIcon(
|
||||
new IconicsDrawable(this, CommunityMaterial.Icon2.cmd_send)
|
||||
.actionBar()
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(20))
|
||||
);
|
||||
menu.findItem(R.id.action_attachment).setIcon(
|
||||
new IconicsDrawable(this, CommunityMaterial.Icon.cmd_attachment)
|
||||
.actionBar()
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(20))
|
||||
);
|
||||
menu.findItem(R.id.action_attachment).setVisible(composeInfo.maxAttachmentNumber != 0);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.Bundle
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.AbsoluteSizeSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.hootsuite.nachos.ChipConfiguration
|
||||
import com.hootsuite.nachos.chip.ChipInfo
|
||||
import com.hootsuite.nachos.chip.ChipSpan
|
||||
import com.hootsuite.nachos.chip.ChipSpanChipCreator
|
||||
import com.hootsuite.nachos.tokenizer.SpanChipTokenizer
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.ERROR_MESSAGE_NOT_SENT
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.events.RecipientListGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils.getProfileImage
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Themes.getPrimaryTextColor
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.elevateSurface
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.text.replace
|
||||
|
||||
class MessagesComposeFragment : Fragment(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "MessagesComposeFragment"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: MessagesComposeFragmentBinding
|
||||
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private var teachers = mutableListOf<Teacher>()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
||||
if (app.profile == null)
|
||||
return inflater.inflate(R.layout.fragment_loading, container, false)
|
||||
// activity, context and profile is valid
|
||||
b = MessagesComposeFragmentBinding.inflate(inflater)
|
||||
return b.root
|
||||
}
|
||||
override fun onDestroy() {
|
||||
EventBus.getDefault().unregister(this)
|
||||
super.onDestroy()
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// TODO check if app, activity, b can be null
|
||||
if (app.profile == null || !isAdded)
|
||||
return
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
|
||||
b.breakpoints.visibility = if (App.devMode) View.VISIBLE else View.GONE
|
||||
b.breakpoints.setOnClickListener {
|
||||
b.breakpoints.isEnabled = true
|
||||
b.breakpoints.text = "Breakpoints!"
|
||||
// do your job
|
||||
}
|
||||
|
||||
/*b.fontStyleBold.icon = IconicsDrawable(activity, CommunityMaterial.Icon.cmd_format_bold)
|
||||
.sizeDp(24)
|
||||
.colorAttr(activity, R.attr.colorOnPrimary)
|
||||
b.fontStyleItalic.icon = IconicsDrawable(activity, CommunityMaterial.Icon.cmd_format_italic)
|
||||
.sizeDp(24)
|
||||
.colorAttr(activity, R.attr.colorOnPrimary)
|
||||
b.fontStyleUnderline.icon = IconicsDrawable(activity, CommunityMaterial.Icon.cmd_format_underline)
|
||||
.sizeDp(24)
|
||||
.colorAttr(activity, R.attr.colorOnPrimary)
|
||||
|
||||
b.fontStyle.addOnButtonCheckedListener { _, checkedId, isChecked ->
|
||||
val span: Any = when (checkedId) {
|
||||
R.id.fontStyleBold -> StyleSpan(Typeface.BOLD)
|
||||
R.id.fontStyleItalic -> StyleSpan(Typeface.ITALIC)
|
||||
R.id.fontStyleUnderline -> UnderlineSpan()
|
||||
else -> StyleSpan(Typeface.NORMAL)
|
||||
}
|
||||
|
||||
if (isChecked) {
|
||||
val flags = if (b.text.selectionStart == b.text.selectionEnd)
|
||||
SpannableString.SPAN_INCLUSIVE_INCLUSIVE
|
||||
else
|
||||
SpannableString.SPAN_EXCLUSIVE_INCLUSIVE
|
||||
b.text.text?.setSpan(span, b.text.selectionStart, b.text.selectionEnd, flags)
|
||||
}
|
||||
else {
|
||||
b.text.text?.getSpans(b.text.selectionStart, b.text.selectionEnd, span.javaClass)?.forEach {
|
||||
if (it is StyleSpan && span is StyleSpan && it.style == span.style)
|
||||
b.text.text?.removeSpan(it)
|
||||
else if (it.javaClass == span.javaClass)
|
||||
b.text.text?.removeSpan(it)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
launch {
|
||||
delay(100)
|
||||
getRecipientList()
|
||||
|
||||
createView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRecipientList() {
|
||||
if (System.currentTimeMillis() - app.profile.lastReceiversSync > 1 * DAY * 1000 && app.profile.loginStoreType != LoginStore.LOGIN_TYPE_VULCAN) {
|
||||
activity.snackbar("Pobieranie listy odbiorców...")
|
||||
EdziennikTask.recipientListGet(App.profileId).enqueue(activity)
|
||||
}
|
||||
else {
|
||||
launch {
|
||||
val list = withContext(Dispatchers.Default) {
|
||||
app.db.teacherDao().getAllNow(App.profileId).filter { it.loginId != null }
|
||||
}
|
||||
updateRecipientList(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createView() {
|
||||
b.recipientsLayout.setBoxCornerRadii(0f, 0f, 0f, 0f)
|
||||
b.subjectLayout.setBoxCornerRadii(0f, 0f, 0f, 0f)
|
||||
b.textLayout.setBoxCornerRadii(0f, 0f, 0f, 0f)
|
||||
|
||||
b.recipients.addTextChangedListener(onTextChanged = { _, _, _, _ ->
|
||||
b.recipientsLayout.error = null
|
||||
})
|
||||
b.subject.addTextChangedListener(onTextChanged = { _, _, _, _ ->
|
||||
b.subjectLayout.error = null
|
||||
})
|
||||
b.text.addTextChangedListener(onTextChanged = { _, _, _, _ ->
|
||||
b.textLayout.error = null
|
||||
})
|
||||
|
||||
b.subjectLayout.counterMaxLength = when (app.profile.loginStoreType) {
|
||||
LoginStore.LOGIN_TYPE_MOBIDZIENNIK -> 100
|
||||
LoginStore.LOGIN_TYPE_LIBRUS -> 150
|
||||
LoginStore.LOGIN_TYPE_VULCAN -> 200
|
||||
LoginStore.LOGIN_TYPE_IUCZNIOWIE -> 180
|
||||
LoginStore.LOGIN_TYPE_EDUDZIENNIK -> 0
|
||||
else -> -1
|
||||
}
|
||||
b.textLayout.counterMaxLength = when (app.profile.loginStoreType) {
|
||||
LoginStore.LOGIN_TYPE_MOBIDZIENNIK -> -1
|
||||
LoginStore.LOGIN_TYPE_LIBRUS -> 20000
|
||||
LoginStore.LOGIN_TYPE_VULCAN -> -1
|
||||
LoginStore.LOGIN_TYPE_IUCZNIOWIE -> 1983
|
||||
LoginStore.LOGIN_TYPE_EDUDZIENNIK -> 0
|
||||
else -> -1
|
||||
}
|
||||
|
||||
b.recipients.chipTokenizer = SpanChipTokenizer<ChipSpan>(activity, object : ChipSpanChipCreator() {
|
||||
override fun createChip(context: Context, text: CharSequence, data: Any?): ChipSpan? {
|
||||
val teacher = data as Teacher?
|
||||
if (teacher!!.id in -24L..-1L) {
|
||||
val type = (teacher.id * -1).toInt()
|
||||
|
||||
val textColorPrimary = android.R.attr.textColorPrimary.resolveAttr(activity)
|
||||
val textColorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||
|
||||
val category: MutableList<Teacher> = ArrayList()
|
||||
val categoryNames: MutableList<CharSequence> = ArrayList()
|
||||
teachers.forEach {
|
||||
if (it.isType(type)) {
|
||||
category += it
|
||||
val name = it.fullName
|
||||
val description = when (type) {
|
||||
Teacher.TYPE_TEACHER -> null
|
||||
Teacher.TYPE_PARENTS_COUNCIL -> it.typeDescription
|
||||
Teacher.TYPE_SCHOOL_PARENTS_COUNCIL -> null
|
||||
Teacher.TYPE_PEDAGOGUE -> null
|
||||
Teacher.TYPE_LIBRARIAN -> null
|
||||
Teacher.TYPE_SCHOOL_ADMIN -> null
|
||||
Teacher.TYPE_SUPER_ADMIN -> null
|
||||
Teacher.TYPE_SECRETARIAT -> null
|
||||
Teacher.TYPE_PRINCIPAL -> null
|
||||
Teacher.TYPE_EDUCATOR -> it.typeDescription
|
||||
Teacher.TYPE_PARENT -> it.typeDescription
|
||||
Teacher.TYPE_STUDENT -> it.typeDescription
|
||||
Teacher.TYPE_SPECIALIST -> null
|
||||
else -> it.typeDescription
|
||||
}
|
||||
categoryNames += listOfNotNull(
|
||||
name.asSpannable(
|
||||
ForegroundColorSpan(textColorPrimary)
|
||||
),
|
||||
description?.asSpannable(
|
||||
ForegroundColorSpan(textColorSecondary),
|
||||
AbsoluteSizeSpan(14.dp)
|
||||
)
|
||||
).concat("\n")
|
||||
}
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle("Dodaj odbiorców - "+Teacher.typeName(activity, type))
|
||||
//.setMessage(getString(R.string.messages_compose_recipients_text_format, Teacher.typeName(activity, type)))
|
||||
.setPositiveButton("OK", null)
|
||||
.setNeutralButton("Anuluj", null)
|
||||
.setMultiChoiceItems(categoryNames.toTypedArray(), categoryNames.map { false }.toBooleanArray()) { dialog, which, isChecked ->
|
||||
val chipInfoList = mutableListOf<ChipInfo>()
|
||||
val selected = category[which]
|
||||
selected.image = getProfileImage(48, 24, 16, 12, 1, selected.fullName)
|
||||
chipInfoList.add(ChipInfo(selected.fullName, selected))
|
||||
b.recipients.addTextWithChips(chipInfoList)
|
||||
}
|
||||
.show()
|
||||
|
||||
/*MaterialDialog.Builder(activity)
|
||||
.title(R.string.messages_compose_recipients_title)
|
||||
.content(getString(R.string.messages_compose_recipients_text_format, Teacher.typeName(activity, type)))
|
||||
.items(categoryNames)
|
||||
.itemsCallbackMultiChoice(null) { dialog: MaterialDialog?, which: Array<Int>, text1: Array<CharSequence?>? ->
|
||||
val chipInfoList = mutableListOf<ChipInfo>()
|
||||
for (index in which) {
|
||||
val selected = category[index]
|
||||
selected.image = getProfileImage(48, 24, 16, 12, 1, selected.fullName)
|
||||
chipInfoList.add(ChipInfo(selected.fullName, selected))
|
||||
}
|
||||
b.recipients.addTextWithChips(chipInfoList)
|
||||
true
|
||||
}
|
||||
.positiveText(R.string.ok)
|
||||
.negativeText(R.string.cancel)
|
||||
.show()*/
|
||||
return null
|
||||
}
|
||||
val chipSpan = ChipSpan(context, teacher.fullName, BitmapDrawable(context.resources, teacher.image), teacher)
|
||||
chipSpan.setIconBackgroundColor(Colors.stringToMaterialColor(teacher.fullName))
|
||||
return chipSpan
|
||||
}
|
||||
|
||||
override fun configureChip(chip: ChipSpan, chipConfiguration: ChipConfiguration) {
|
||||
super.configureChip(chip, chipConfiguration)
|
||||
chip.setBackgroundColor(elevateSurface(activity, 8).toColorStateList())
|
||||
chip.setTextColor(getPrimaryTextColor(activity))
|
||||
}
|
||||
}, ChipSpan::class.java)
|
||||
|
||||
b.recipients.setIllegalCharacterIdentifier { c ->
|
||||
c.toString().matches("[\\n;:_ ]".toRegex())
|
||||
}
|
||||
b.recipients.setOnChipClickListener { chip, _ ->
|
||||
Toast.makeText(app, "onChipClick: " + chip.text, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
b.recipients.setOnChipRemoveListener { chip ->
|
||||
Log.d(TAG, "onChipRemoved: " + chip.text)
|
||||
b.recipients.setSelection(b.recipients.text.length)
|
||||
}
|
||||
|
||||
b.recipientsLayout.isEnabled = false
|
||||
b.subjectLayout.isEnabled = false
|
||||
b.textLayout.isEnabled = false
|
||||
|
||||
activity.navView.apply {
|
||||
bottomBar.apply {
|
||||
fabEnable = true
|
||||
fabExtendedText = getString(R.string.messages_compose_send)
|
||||
fabIcon = CommunityMaterial.Icon2.cmd_send
|
||||
}
|
||||
|
||||
setFabOnClickListener(View.OnClickListener {
|
||||
sendMessage()
|
||||
})
|
||||
}
|
||||
|
||||
activity.gainAttentionFAB()
|
||||
}
|
||||
|
||||
private fun updateRecipientList(list: List<Teacher>) { launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
teachers = list.sortedBy { it.fullName }.toMutableList()
|
||||
Teacher.types.mapTo(teachers) {
|
||||
Teacher(-1, -it.toLong(), Teacher.typeName(activity, it), "")
|
||||
}
|
||||
/*teachers.forEach {
|
||||
println(it)
|
||||
}*/
|
||||
}
|
||||
|
||||
b.recipientsLayout.isEnabled = true
|
||||
b.subjectLayout.isEnabled = true
|
||||
b.textLayout.isEnabled = true
|
||||
|
||||
val adapter = MessagesComposeSuggestionAdapter(activity, teachers)
|
||||
b.recipients.setAdapter(adapter)
|
||||
|
||||
handleReplyMessage()
|
||||
}}
|
||||
|
||||
private fun handleReplyMessage() { launch {
|
||||
val replyMessage = arguments?.getString("message")
|
||||
if (replyMessage != null) {
|
||||
val chipList = mutableListOf<ChipInfo>()
|
||||
var subject = ""
|
||||
val span = SpannableStringBuilder()
|
||||
var body: CharSequence = ""
|
||||
|
||||
withContext(Dispatchers.Default) {
|
||||
val msg = app.gson.fromJson(replyMessage, MessageFull::class.java)
|
||||
val dateString = getString(R.string.messages_date_time_format, Date.fromMillis(msg.addedDate).formattedStringShort, Time.fromMillis(msg.addedDate).stringHM)
|
||||
// add original message info
|
||||
span.appendText("W dniu ")
|
||||
span.appendSpan(dateString, StyleSpan(Typeface.ITALIC), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
span.appendText(", ")
|
||||
span.appendSpan(msg.senderFullName.fixName(), StyleSpan(Typeface.ITALIC), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
span.appendText(" napisał(a):")
|
||||
span.setSpan(StyleSpan(Typeface.BOLD), 0, span.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
span.appendText("\n\n")
|
||||
|
||||
if (arguments?.getString("type") == "reply") {
|
||||
// add greeting text
|
||||
span.replace(0, 0, "\n\nPozdrawiam,\n${app.profile.accountNameLong
|
||||
?: app.profile.studentNameLong ?: ""}\n\n\n")
|
||||
|
||||
teachers.firstOrNull { it.id == msg.senderId }?.let { teacher ->
|
||||
teacher.image = getProfileImage(48, 24, 16, 12, 1, teacher.fullName)
|
||||
chipList += ChipInfo(teacher.fullName, teacher)
|
||||
}
|
||||
subject = "Re: ${msg.subject}"
|
||||
} else {
|
||||
span.replace(0, 0, "\n\n")
|
||||
subject = "Fwd: ${msg.subject}"
|
||||
}
|
||||
body = MessagesUtils.htmlToSpannable(msg.body ?: "Nie udało się wczytać oryginalnej wiadomości.")//Html.fromHtml(msg.body?.replace("<br\\s?/?>".toRegex(), "\n") ?: "Nie udało się wczytać oryginalnej wiadomości.")
|
||||
}
|
||||
|
||||
b.recipients.addTextWithChips(chipList)
|
||||
if (b.recipients.text.isNullOrEmpty())
|
||||
b.recipients.requestFocus()
|
||||
else
|
||||
b.text.requestFocus()
|
||||
b.subject.setText(subject)
|
||||
b.text.apply {
|
||||
text = span.appendText(body)
|
||||
setSelection(0)
|
||||
}
|
||||
b.root.scrollTo(0, 0)
|
||||
}
|
||||
else {
|
||||
b.recipients.requestFocus()
|
||||
}
|
||||
}}
|
||||
|
||||
private fun sendMessage() {
|
||||
b.recipientsLayout.error = null
|
||||
b.subjectLayout.error = null
|
||||
b.textLayout.error = null
|
||||
|
||||
if (b.recipients.tokenValues.isNotEmpty()) {
|
||||
b.recipientsLayout.error = getString(R.string.messages_compose_recipients_error)
|
||||
return
|
||||
}
|
||||
val recipients = mutableListOf<Teacher>()
|
||||
b.recipients.allChips.forEach { chip ->
|
||||
if (chip.data !is Teacher)
|
||||
return@forEach
|
||||
val teacher = chip.data as Teacher
|
||||
|
||||
recipients += teacher
|
||||
//println(teacher)
|
||||
}
|
||||
val subject = b.subject.text?.toString()
|
||||
val text = b.text.text
|
||||
if (recipients.isEmpty()) {
|
||||
b.recipientsLayout.error = getString(R.string.messages_compose_recipients_empty)
|
||||
return
|
||||
}
|
||||
if (subject.isNullOrBlank() || subject.length < 3) {
|
||||
b.subjectLayout.error = getString(R.string.messages_compose_subject_empty)
|
||||
return
|
||||
}
|
||||
if (text.isNullOrBlank() || text.length < 3) {
|
||||
b.textLayout.error = getString(R.string.messages_compose_text_empty)
|
||||
return
|
||||
}
|
||||
|
||||
if (b.subjectLayout.counterMaxLength != -1 && b.subject.length() > b.subjectLayout.counterMaxLength)
|
||||
return
|
||||
if (b.textLayout.counterMaxLength != -1 && b.text.length() > b.textLayout.counterMaxLength)
|
||||
return
|
||||
|
||||
var textHtml = if (app.profile.loginStoreType != LoginStore.LOGIN_TYPE_VULCAN && app.profile.loginStoreType != LoginStore.LOGIN_TYPE_IUCZNIOWIE) {
|
||||
HtmlCompat.toHtml(SpannableString(text), HtmlCompat.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL)
|
||||
.replace("\n", "")
|
||||
.replace(" dir=\"ltr\"", "")
|
||||
}
|
||||
else {
|
||||
text.toString()
|
||||
}
|
||||
|
||||
if (app.profile.loginStoreType == LoginStore.LOGIN_TYPE_MOBIDZIENNIK) {
|
||||
textHtml = textHtml
|
||||
.replace("p style=\"margin-top:0; margin-bottom:0;\"", "span")
|
||||
.replace("</p>", "</span>")
|
||||
.replace("<b>", "<strong>")
|
||||
.replace("</b>", "</strong>")
|
||||
.replace("<i>", "<em>")
|
||||
.replace("</i>", "</em>")
|
||||
.replace("<u>", "<span style=\"text-decoration: underline;\">")
|
||||
.replace("</u>", "</span>")
|
||||
}
|
||||
|
||||
activity.bottomSheet.hideKeyboard()
|
||||
|
||||
EdziennikTask.messageSend(App.profileId, recipients, subject.trim(), textHtml.trim()).enqueue(activity)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onRecipientListGetEvent(event: RecipientListGetEvent) {
|
||||
if (event.profileId != App.profileId)
|
||||
return
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
|
||||
activity.snackbarDismiss()
|
||||
updateRecipientList(event.teacherList)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onMessageSentEvent(event: MessageSentEvent) {
|
||||
if (event.profileId != App.profileId)
|
||||
return
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
|
||||
if (event.message == null) {
|
||||
activity.errorSnackbar.addError(ApiError(TAG, ERROR_MESSAGE_NOT_SENT)).show()
|
||||
return
|
||||
}
|
||||
|
||||
activity.snackbar(app.getString(R.string.messages_sent_success), app.getString(R.string.ok))
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, Bundle(
|
||||
"messageId" to (event.message.id ?: -1),
|
||||
"message" to app.gson.toJson(event.message),
|
||||
"sentDate" to event.sentDate
|
||||
))
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages;
|
||||
|
||||
public class MessagesComposeInfo {
|
||||
/**
|
||||
* 0 means no attachments.
|
||||
* -1 means unlimited number.
|
||||
*/
|
||||
public int maxAttachmentNumber = 0;
|
||||
/**
|
||||
* -1 means unlimited size.
|
||||
*/
|
||||
public long attachmentSizeLimit = 0;
|
||||
/**
|
||||
* -1 means unlimited length.
|
||||
*/
|
||||
public int maxSubjectLength = -1;
|
||||
/**
|
||||
* -1 means unlimited length.
|
||||
*/
|
||||
public int maxBodyLength = -1;
|
||||
|
||||
public MessagesComposeInfo(int maxAttachmentNumber, long attachmentSizeLimit, int maxSubjectLength, int maxBodyLength) {
|
||||
this.maxAttachmentNumber = maxAttachmentNumber;
|
||||
this.attachmentSizeLimit = attachmentSizeLimit;
|
||||
this.maxSubjectLength = maxSubjectLength;
|
||||
this.maxBodyLength = maxBodyLength;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
||||
|
||||
class MessagesComposeInfo(
|
||||
/**
|
||||
* 0 means no attachments.
|
||||
* -1 means unlimited number.
|
||||
*/
|
||||
@JvmField var maxAttachmentNumber: Int,
|
||||
/**
|
||||
* -1 means unlimited size.
|
||||
*/
|
||||
var attachmentSizeLimit: Long,
|
||||
/**
|
||||
* -1 means unlimited length.
|
||||
*/
|
||||
var maxSubjectLength: Int,
|
||||
/**
|
||||
* -1 means unlimited length.
|
||||
*/
|
||||
var maxBodyLength: Int
|
||||
)
|
@ -1,191 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher;
|
||||
|
||||
public class MessagesComposeSuggestionAdapter extends ArrayAdapter<Teacher> {
|
||||
|
||||
private Context context;
|
||||
private List<Teacher> teacherList;
|
||||
private ArrayList<Teacher> originalList = null;
|
||||
private ArrayFilter mFilter;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
MessagesComposeSuggestionAdapter(@NonNull Context context, List<Teacher> teacherList) {
|
||||
super(context, 0, teacherList);
|
||||
this.context = context;
|
||||
this.teacherList = teacherList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
View listItem = convertView;
|
||||
if(listItem == null)
|
||||
listItem = LayoutInflater.from(context).inflate(R.layout.messages_compose_suggestion_item, parent, false);
|
||||
|
||||
Teacher teacher = teacherList.get(position);
|
||||
|
||||
TextView name = listItem.findViewById(R.id.name);
|
||||
TextView type = listItem.findViewById(R.id.type);
|
||||
ImageView image = listItem.findViewById(R.id.image);
|
||||
|
||||
teacher.image = MessagesUtils.getProfileImage(48, 24, 16, 12, 1, teacher.getFullName());
|
||||
|
||||
if (teacher.id <= 0) {
|
||||
name.setText(Teacher.typeString(context, (int) (teacher.id * -1)));
|
||||
type.setText(R.string.teachers_browse_category);
|
||||
image.setImageBitmap(null);
|
||||
}
|
||||
else {
|
||||
if (teacher.displayName == null)
|
||||
name.setText(teacher.getFullName());
|
||||
else
|
||||
name.setText(Html.fromHtml(teacher.displayName));
|
||||
type.setText(teacher.getType(context));
|
||||
image.setImageBitmap(teacher.image);
|
||||
}
|
||||
|
||||
return listItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return teacherList.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Teacher getItem(int position) {
|
||||
return teacherList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosition(@Nullable Teacher item) {
|
||||
return teacherList.indexOf(item);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
if (mFilter == null) {
|
||||
mFilter = new ArrayFilter();
|
||||
}
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
private class TeacherWeighted extends Teacher {
|
||||
public int weight;
|
||||
|
||||
public TeacherWeighted(Teacher teacher, int weight) {
|
||||
super(teacher.profileId, teacher.id, teacher.name, teacher.surname, teacher.loginId);
|
||||
this.weight = weight;
|
||||
this.image = teacher.image;
|
||||
this.type = teacher.type;
|
||||
this.typeDescription = teacher.typeDescription;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFullName();
|
||||
}
|
||||
}
|
||||
private Comparator<? super Teacher> comparator = (o1, o2) -> ((TeacherWeighted) o1).weight - ((TeacherWeighted) o2).weight;
|
||||
|
||||
private class ArrayFilter extends Filter {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence prefix) {
|
||||
final FilterResults results = new FilterResults();
|
||||
|
||||
if (originalList == null) {
|
||||
synchronized (mLock) {
|
||||
originalList = new ArrayList<>(teacherList);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefix == null || prefix.length() == 0) {
|
||||
ArrayList<Teacher> list;
|
||||
synchronized (mLock) {
|
||||
list = new ArrayList<>(originalList);
|
||||
}
|
||||
results.values = list;
|
||||
results.count = list.size();
|
||||
} else {
|
||||
String prefixString = prefix.toString().toLowerCase();
|
||||
|
||||
ArrayList<Teacher> values;
|
||||
synchronized (mLock) {
|
||||
values = new ArrayList<>(originalList);
|
||||
}
|
||||
|
||||
int count = values.size();
|
||||
ArrayList<Teacher> newValues = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Teacher teacher = values.get(i);
|
||||
String teacherFullName = teacher.getFullName().toLowerCase();
|
||||
teacher.displayName = teacherFullName.replace(prefixString, "<b>"+prefixString+"</b>");
|
||||
|
||||
// First match against the whole, non-splitted value
|
||||
boolean found = false;
|
||||
if (teacherFullName.startsWith(prefixString)) {
|
||||
newValues.add(new TeacherWeighted(teacher, 1));
|
||||
found = true;
|
||||
} else {
|
||||
// check if prefix matches any of the words
|
||||
String[] words = teacherFullName.split(" ");
|
||||
for (String word : words) {
|
||||
if (word.startsWith(prefixString)) {
|
||||
newValues.add(new TeacherWeighted(teacher, 2));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally check if the prefix matches any part of the name
|
||||
if (!found && teacherFullName.contains(prefixString)) {
|
||||
newValues.add(new TeacherWeighted(teacher, 3));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Collections.sort(newValues, comparator);
|
||||
|
||||
results.values = newValues;
|
||||
results.count = newValues.size();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint, FilterResults results) {
|
||||
//noinspection unchecked
|
||||
teacherList = (List<Teacher>) results.values;
|
||||
if (results.count > 0) {
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
notifyDataSetInvalidated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface.BOLD
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Filter
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.asSpannable
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.resolveAttr
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils.getProfileImage
|
||||
import java.util.*
|
||||
|
||||
class MessagesComposeSuggestionAdapter(
|
||||
context: Context,
|
||||
val originalList: List<Teacher>
|
||||
) : ArrayAdapter<Teacher>(context, 0, originalList)
|
||||
{
|
||||
|
||||
private var teacherList = originalList.toList()
|
||||
private val filter by lazy { ArrayFilter() }
|
||||
private val comparator by lazy { Comparator { o1: Teacher, o2: Teacher -> o1.recipientWeight - o2.recipientWeight } }
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val listItem = convertView ?: LayoutInflater.from(context).inflate(R.layout.messages_compose_suggestion_item, parent, false)
|
||||
|
||||
val teacher = teacherList[position]
|
||||
val name = listItem.findViewById<TextView>(R.id.name)
|
||||
val type = listItem.findViewById<TextView>(R.id.type)
|
||||
val image = listItem.findViewById<ImageView>(R.id.image)
|
||||
|
||||
if (teacher.image == null)
|
||||
teacher.image = getProfileImage(48, 24, 16, 12, 1, teacher.fullName)
|
||||
|
||||
if (teacher.id in -24L..-1L) {
|
||||
name.text = Teacher.typeName(context, (teacher.id * -1).toInt())
|
||||
type.setText(R.string.teachers_browse_category)
|
||||
image.setImageBitmap(null)
|
||||
} else {
|
||||
name.text = teacher.recipientDisplayName ?: teacher.fullName
|
||||
type.text = teacher.getTypeText(context)
|
||||
image.setImageBitmap(teacher.image)
|
||||
}
|
||||
return listItem
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return teacherList.size
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Teacher? {
|
||||
return teacherList[position]
|
||||
}
|
||||
|
||||
override fun getPosition(item: Teacher?): Int {
|
||||
return teacherList.indexOf(item)
|
||||
}
|
||||
|
||||
override fun getFilter(): Filter {
|
||||
return filter
|
||||
}
|
||||
|
||||
inner class ArrayFilter : Filter() {
|
||||
override fun performFiltering(prefix: CharSequence?): FilterResults {
|
||||
val results = FilterResults()
|
||||
|
||||
if (prefix.isNullOrEmpty()) {
|
||||
results.values = originalList
|
||||
results.count = originalList.size
|
||||
} else {
|
||||
val prefixString = prefix.toString()
|
||||
|
||||
val list = mutableListOf<Teacher>()
|
||||
|
||||
originalList.forEach { teacher ->
|
||||
val teacherFullName = teacher.fullName
|
||||
teacher.recipientWeight = 0
|
||||
|
||||
// First match against the whole, non-split value
|
||||
var found = false
|
||||
if (teacherFullName.startsWith(prefixString, ignoreCase = true)) {
|
||||
teacher.recipientWeight = 1
|
||||
found = true
|
||||
} else {
|
||||
// check if prefix matches any of the words
|
||||
val words = teacherFullName.split(" ").toTypedArray()
|
||||
for (word in words) {
|
||||
if (word.startsWith(prefixString, ignoreCase = true)) {
|
||||
teacher.recipientWeight = 2
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// finally check if the prefix matches any part of the name
|
||||
if (!found && teacherFullName.contains(prefixString, ignoreCase = true)) {
|
||||
teacher.recipientWeight = 3
|
||||
}
|
||||
|
||||
if (teacher.recipientWeight != 0) {
|
||||
teacher.recipientDisplayName = teacherFullName.asSpannable(
|
||||
StyleSpan(BOLD),
|
||||
BackgroundColorSpan(R.attr.colorControlHighlight.resolveAttr(context)),
|
||||
substring = prefixString,
|
||||
ignoreCase = true
|
||||
)
|
||||
list += teacher
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(list, comparator)
|
||||
results.values = list
|
||||
results.count = list.size
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
|
||||
results.values?.let { teacherList = it as List<Teacher> }
|
||||
if (results.count > 0) {
|
||||
notifyDataSetChanged()
|
||||
} else {
|
||||
notifyDataSetInvalidated()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,580 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.messages;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.google.android.material.chip.Chip;
|
||||
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 org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.MessagesDetailsBinding;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorDialog;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.getStringFromFile;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.readableFileSize;
|
||||
|
||||
public class MessagesDetailsFragment extends Fragment {
|
||||
private long messageId = -1;
|
||||
|
||||
private App app = null;
|
||||
private MainActivity activity = null;
|
||||
private MessagesDetailsBinding b = null;
|
||||
|
||||
private MessageFull message;
|
||||
|
||||
private List<Attachment> attachmentList = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
if (getActivity() == null || getContext() == null)
|
||||
return null;
|
||||
app = (App) activity.getApplication();
|
||||
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
|
||||
if (app.profile == null)
|
||||
return inflater.inflate(R.layout.fragment_loading, container, false);
|
||||
// activity, context and profile is valid
|
||||
b = DataBindingUtil.inflate(inflater, R.layout.messages_details, container, false);
|
||||
return b.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
if (getArguments() != null)
|
||||
messageId = getArguments().getLong("messageId", -1);
|
||||
|
||||
b.messageContent.setVisibility(View.GONE);
|
||||
|
||||
/*if (messageId != -1) {
|
||||
AsyncTask.execute(() -> {
|
||||
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
MessageFull messageRaw = app.db.messageDao().getById(App.profileId, messageId);
|
||||
Edziennik.getApi(app, app.profile.getLoginStoreType()).getMessage(activity, new SyncCallback() {
|
||||
@Override public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) { }
|
||||
@Override public void onSuccess(Context activityContext, ProfileFull profileFull) { }
|
||||
@Override public void onProgress(int progressStep) { }
|
||||
@Override public void onActionStarted(int stringResId) { }
|
||||
@Override
|
||||
public void onError(Context activityContext, AppError error) {
|
||||
new Handler(activityContext.getMainLooper()).post(() -> {
|
||||
app.apiEdziennik.guiShowErrorDialog(activity, error, R.string.messages_download_error);
|
||||
});
|
||||
}
|
||||
}, app.profile, messageRaw, messageFull -> {
|
||||
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
message = messageFull;
|
||||
if (message.body == null) {
|
||||
return;
|
||||
}
|
||||
b.messageBody.setText(Html.fromHtml(message.body.replaceAll("\\[META:[A-z0-9]+;[0-9-]+]", "")));
|
||||
b.progress.setVisibility(View.GONE);
|
||||
Anim.fadeIn(b.messageContent, 200, null);
|
||||
|
||||
MessagesFragment.Companion.setPageSelection(Math.min(message.type, 1));
|
||||
|
||||
MessagesUtils.MessageInfo messageInfo = MessagesUtils.getMessageInfo(app, message, 40, 20, 14, 10);
|
||||
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage);
|
||||
b.messageSender.setText(messageInfo.profileName);
|
||||
|
||||
b.messageSubject.setText(message.subject);
|
||||
b.messageDate.setText(getString(R.string.messages_date_time_format, Date.fromMillis(message.addedDate).getFormattedStringShort(), Time.fromMillis(message.addedDate).getStringHM()));
|
||||
|
||||
StringBuilder messageRecipients = new StringBuilder("<ul>");
|
||||
for (MessageRecipientFull recipient: message.recipients) {
|
||||
if (recipient.readDate == -1) messageRecipients.append(getString(
|
||||
R.string.messages_recipients_list_unknown_state_format,
|
||||
recipient.fullName
|
||||
));
|
||||
else if (recipient.readDate == 0) messageRecipients.append(getString(
|
||||
R.string.messages_recipients_list_unread_format,
|
||||
recipient.fullName
|
||||
));
|
||||
else if (recipient.readDate == 1) messageRecipients.append(getString(
|
||||
R.string.messages_recipients_list_read_unknown_date_format,
|
||||
recipient.fullName
|
||||
));
|
||||
else messageRecipients.append(getString(
|
||||
R.string.messages_recipients_list_read_format,
|
||||
recipient.fullName,
|
||||
Date.fromMillis(recipient.readDate).getFormattedString(),
|
||||
Time.fromMillis(recipient.readDate).getStringHM()
|
||||
));
|
||||
}
|
||||
messageRecipients.append("</ul>");
|
||||
b.messageRecipients.setText(Html.fromHtml(messageRecipients.toString()));
|
||||
|
||||
if (message.attachmentIds != null) {
|
||||
// there are some attachments: attachmentIds, attachmentNames, attachmentSizes
|
||||
ViewGroup insertPoint = b.messageAttachments;
|
||||
|
||||
FrameLayout.LayoutParams chipLayoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
chipLayoutParams.setMargins(0, Utils.dpToPx(8), 0, Utils.dpToPx(8));
|
||||
|
||||
FrameLayout.LayoutParams progressLayoutParams = new FrameLayout.LayoutParams(Utils.dpToPx(18), Utils.dpToPx(18));
|
||||
progressLayoutParams.setMargins(Utils.dpToPx(8), 0, Utils.dpToPx(8), 0);
|
||||
progressLayoutParams.gravity = END | CENTER_VERTICAL;
|
||||
|
||||
// CREATE VIEWS AND AN OBJECT FOR EVERY ATTACHMENT
|
||||
|
||||
int attachmentIndex = 0;
|
||||
for (String attachmentName: message.attachmentNames) {
|
||||
long messageId = message.id;
|
||||
long attachmentId = message.attachmentIds.get(attachmentIndex);
|
||||
long attachmentSize = message.attachmentSizes.get(attachmentIndex);
|
||||
// create the parent
|
||||
FrameLayout attachmentLayout = new FrameLayout(b.getRoot().getContext());
|
||||
|
||||
Chip attachmentChip = new Chip(attachmentLayout.getContext());
|
||||
//attachmentChip.setChipBackgroundColorResource(ThemeUtils.getChipColorRes());
|
||||
attachmentChip.setLayoutParams(chipLayoutParams);
|
||||
attachmentChip.setHeight(Utils.dpToPx(40));
|
||||
// show the file size or not
|
||||
if (attachmentSize == -1)
|
||||
attachmentChip.setText(getString(R.string.messages_attachment_no_size_format, attachmentName));
|
||||
else
|
||||
attachmentChip.setText(getString(R.string.messages_attachment_format, attachmentName, readableFileSize(attachmentSize)));
|
||||
attachmentChip.setEllipsize(TextUtils.TruncateAt.MIDDLE);
|
||||
// create an icon for the attachment
|
||||
IIcon icon = CommunityMaterial.Icon.cmd_file;
|
||||
switch (Utils.getExtensionFromFileName(attachmentName)) {
|
||||
case "txt":
|
||||
icon = CommunityMaterial.Icon.cmd_file_document;
|
||||
break;
|
||||
case "doc":
|
||||
case "docx":
|
||||
case "odt":
|
||||
case "rtf":
|
||||
icon = CommunityMaterial.Icon.cmd_file_word;
|
||||
break;
|
||||
case "xls":
|
||||
case "xlsx":
|
||||
case "ods":
|
||||
icon = CommunityMaterial.Icon.cmd_file_excel;
|
||||
break;
|
||||
case "ppt":
|
||||
case "pptx":
|
||||
case "odp":
|
||||
icon = CommunityMaterial.Icon.cmd_file_powerpoint;
|
||||
break;
|
||||
case "pdf":
|
||||
icon = CommunityMaterial.Icon.cmd_file_pdf;
|
||||
break;
|
||||
case "mp3":
|
||||
case "wav":
|
||||
case "aac":
|
||||
icon = CommunityMaterial.Icon.cmd_file_music;
|
||||
break;
|
||||
case "mp4":
|
||||
case "avi":
|
||||
case "3gp":
|
||||
case "mkv":
|
||||
case "flv":
|
||||
icon = CommunityMaterial.Icon.cmd_file_video;
|
||||
break;
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "bmp":
|
||||
case "gif":
|
||||
icon = CommunityMaterial.Icon.cmd_file_image;
|
||||
break;
|
||||
case "zip":
|
||||
case "rar":
|
||||
case "tar":
|
||||
case "7z":
|
||||
icon = CommunityMaterial.Icon.cmd_file_lock;
|
||||
break;
|
||||
}
|
||||
attachmentChip.setChipIcon(new IconicsDrawable(activity).color(IconicsColor.colorRes(R.color.colorPrimary)).icon(icon).size(IconicsSize.dp(26)));
|
||||
attachmentChip.setCloseIcon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_check).size(IconicsSize.dp(18)).color(IconicsColor.colorInt(Utils.getAttr(activity, android.R.attr.textColorPrimary))));
|
||||
attachmentChip.setCloseIconVisible(false);
|
||||
// set the object's index in the attachmentList as the tag
|
||||
attachmentChip.setTag(attachmentIndex);
|
||||
attachmentChip.setOnClickListener(v -> {
|
||||
if (v.getTag() instanceof Integer) {
|
||||
downloadAttachment((Integer) v.getTag());
|
||||
}
|
||||
});
|
||||
attachmentLayout.addView(attachmentChip);
|
||||
|
||||
ProgressBar attachmentProgress = new ProgressBar(attachmentLayout.getContext());
|
||||
attachmentProgress.setLayoutParams(progressLayoutParams);
|
||||
attachmentProgress.setVisibility(View.GONE);
|
||||
attachmentLayout.addView(attachmentProgress);
|
||||
|
||||
insertPoint.addView(attachmentLayout);
|
||||
// create an object and add to the list
|
||||
Attachment a = new Attachment(App.profileId, messageId, attachmentId, attachmentName, attachmentSize, attachmentLayout, attachmentChip, attachmentProgress);
|
||||
attachmentList.add(a);
|
||||
// check if the file is already downloaded. Show the check icon if necessary and set `downloaded` to true.
|
||||
checkAttachment(a);
|
||||
|
||||
attachmentIndex++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no attachments found
|
||||
b.messageAttachmentsTitle.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}*/
|
||||
|
||||
// click to expand subject and sender
|
||||
b.messageSubject.setOnClickListener(v -> {
|
||||
if (b.messageSubject.getMaxLines() == 2) {
|
||||
b.messageSubject.setMaxLines(30);
|
||||
}
|
||||
else {
|
||||
b.messageSubject.setMaxLines(2);
|
||||
}
|
||||
});
|
||||
b.messageSender.setOnClickListener(v -> {
|
||||
if (b.messageSender.getMaxLines() == 3) {
|
||||
b.messageSender.setMaxLines(30);
|
||||
}
|
||||
else {
|
||||
b.messageSender.setMaxLines(3);
|
||||
}
|
||||
});
|
||||
|
||||
// message close button
|
||||
b.messageClose.setImageDrawable(new IconicsDrawable(activity).icon(CommunityMaterial.Icon2.cmd_window_close).color(IconicsColor.colorInt(Utils.getAttr(activity, android.R.attr.textColorSecondary))).size(IconicsSize.dp(12)));
|
||||
b.messageClose.setOnClickListener(v -> {
|
||||
activity.navigateUp();
|
||||
});
|
||||
|
||||
// enter, exit transitions
|
||||
//setExitTransition(new Fade());
|
||||
|
||||
/*View content = b.getRoot();
|
||||
content.setAlpha(0f);
|
||||
|
||||
ValueAnimator animator = ObjectAnimator.ofFloat(content, View.ALPHA, 0f, 1f);
|
||||
animator.setStartDelay(50);
|
||||
animator.setDuration(150);
|
||||
animator.start();*/
|
||||
}
|
||||
|
||||
private void checkAttachment(Attachment a) {
|
||||
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu");
|
||||
storageDir.mkdirs();
|
||||
|
||||
File attachmentDataFile = new File(storageDir, "."+a.profileId+"_"+a.messageId+"_"+a.attachmentId);
|
||||
if (attachmentDataFile.exists()) {
|
||||
try {
|
||||
String attachmentFileName = getStringFromFile(attachmentDataFile);
|
||||
File attachmentFile = new File(attachmentFileName);
|
||||
if (attachmentFile.exists()) {
|
||||
a.downloaded = attachmentFileName;
|
||||
a.chip.setCloseIconVisible(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
new ErrorDialog(activity, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadAttachment(int index) {
|
||||
Attachment a = attachmentList.get(index);
|
||||
|
||||
if (a.downloaded != null) {
|
||||
Utils.openFile(activity, new File(a.downloaded));
|
||||
return;
|
||||
}
|
||||
|
||||
a.chip.setEnabled(false);
|
||||
a.chip.setTextColor(Themes.INSTANCE.getSecondaryTextColor(activity));
|
||||
a.progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
File storageDir = Utils.getStorageDir();
|
||||
|
||||
/*Edziennik.getApi(app, app.profile.getLoginStoreType()).getAttachment(activity, new SyncCallback() {
|
||||
@Override
|
||||
public void onLoginFirst(List<Profile> profileList, LoginStore loginStore) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Context activityContext, ProfileFull profileFull) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Context activityContext, AppError error) {
|
||||
new Handler(activityContext.getMainLooper()).post(() -> {
|
||||
a.progressBar.setVisibility(View.GONE);
|
||||
a.chip.setEnabled(true);
|
||||
a.chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
|
||||
a.chip.setCloseIconVisible(false);
|
||||
app.apiEdziennik.guiShowErrorDialog(activity, error, R.string.messages_attachment_cannot_download);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(int progressStep) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionStarted(int stringResId) {
|
||||
|
||||
}
|
||||
}, app.profile, message, a.attachmentId, builder ->
|
||||
builder.callbackThreadMode(im.wangchao.mhttp.ThreadMode.SENDING)
|
||||
.callback(new FileCallbackHandler(new File(storageDir, a.attachmentName)) {
|
||||
@Override
|
||||
public void onSuccess(File file, Response response) {
|
||||
AttachmentEvent event = new AttachmentEvent();
|
||||
event.profileId = a.profileId;
|
||||
event.messageId = a.messageId;
|
||||
event.attachmentId = a.attachmentId;
|
||||
event.eventType = AttachmentEvent.TYPE_FINISHED;
|
||||
event.fileName = file.getAbsolutePath();
|
||||
|
||||
try {
|
||||
File attachmentDataFile = new File(Utils.getStorageDir(), "."+event.profileId+"_"+event.messageId+"_"+event.attachmentId);
|
||||
Utils.writeStringToFile(attachmentDataFile, event.fileName);
|
||||
} catch (IOException e) {
|
||||
event.eventType = AttachmentEvent.TYPE_ERROR;
|
||||
event.exception = e;
|
||||
}
|
||||
finally {
|
||||
EventBus.getDefault().post(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Response response, Throwable throwable) {
|
||||
AttachmentEvent event = new AttachmentEvent();
|
||||
event.profileId = a.profileId;
|
||||
event.messageId = a.messageId;
|
||||
event.attachmentId = a.attachmentId;
|
||||
event.eventType = AttachmentEvent.TYPE_ERROR;
|
||||
event.exception = new Exception(throwable);
|
||||
EventBus.getDefault().post(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesWritten, long bytesTotal) {
|
||||
AttachmentEvent event = new AttachmentEvent();
|
||||
event.profileId = a.profileId;
|
||||
event.messageId = a.messageId;
|
||||
event.attachmentId = a.attachmentId;
|
||||
event.eventType = AttachmentEvent.TYPE_PROGRESS;
|
||||
event.progress = (float)bytesWritten / (float)bytesTotal * 100.0f;
|
||||
EventBus.getDefault().post(event);
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.enqueue());*/
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.POSTING)
|
||||
public void downloadAttachmentEvent(AttachmentEvent event) {
|
||||
try {
|
||||
for (Attachment a: attachmentList) {
|
||||
if (a.profileId == event.profileId
|
||||
&& a.messageId == event.messageId
|
||||
&& a.attachmentId == event.attachmentId) {
|
||||
if (event.eventType == AttachmentEvent.TYPE_PROGRESS) {
|
||||
// show downloading progress
|
||||
a.chip.setText(getString(R.string.messages_attachment_downloading_format, a.attachmentName, Math.round(event.progress)));
|
||||
}
|
||||
else if (event.eventType == AttachmentEvent.TYPE_FINISHED) {
|
||||
// save the downloaded file name
|
||||
a.downloaded = event.fileName;
|
||||
// set the correct name (and size)
|
||||
if (a.attachmentSize == -1)
|
||||
a.chip.setText(getString(R.string.messages_attachment_no_size_format, a.attachmentName));
|
||||
else
|
||||
a.chip.setText(getString(R.string.messages_attachment_format, a.attachmentName, readableFileSize(a.attachmentSize)));
|
||||
// hide the progress bar and show a tick icon
|
||||
a.progressBar.setVisibility(View.GONE);
|
||||
a.chip.setEnabled(true);
|
||||
a.chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
|
||||
a.chip.setCloseIconVisible(true);
|
||||
// open the file
|
||||
Utils.openFile(activity, new File(a.downloaded));
|
||||
}
|
||||
else if (event.eventType == AttachmentEvent.TYPE_ERROR) {
|
||||
a.progressBar.setVisibility(View.GONE);
|
||||
a.chip.setEnabled(true);
|
||||
a.chip.setTextColor(Themes.INSTANCE.getPrimaryTextColor(activity));
|
||||
a.chip.setCloseIconVisible(false);
|
||||
new MaterialDialog.Builder(activity)
|
||||
.title(R.string.messages_attachment_cannot_download)
|
||||
.content(R.string.messages_attachment_cannot_download_text)
|
||||
.positiveText(R.string.ok)
|
||||
.neutralColor(R.string.report)
|
||||
.onNeutral((dialog, which) -> {
|
||||
new ErrorDialog(activity, event.exception);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
new ErrorDialog(activity, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class Attachment {
|
||||
int profileId;
|
||||
long messageId;
|
||||
long attachmentId;
|
||||
String attachmentName;
|
||||
long attachmentSize;
|
||||
FrameLayout parent;
|
||||
Chip chip;
|
||||
ProgressBar progressBar;
|
||||
/**
|
||||
* An absolute path of the downloaded file. {@code null} if not downloaded yet.
|
||||
*/
|
||||
String downloaded = null;
|
||||
|
||||
public Attachment(int profileId, long messageId, long attachmentId, String attachmentName, long attachmentSize, FrameLayout parent, Chip chip, ProgressBar progressBar) {
|
||||
this.profileId = profileId;
|
||||
this.messageId = messageId;
|
||||
this.attachmentId = attachmentId;
|
||||
this.attachmentName = attachmentName;
|
||||
this.attachmentSize = attachmentSize;
|
||||
this.parent = parent;
|
||||
this.chip = chip;
|
||||
this.progressBar = progressBar;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AttachmentEvent {
|
||||
public int profileId;
|
||||
public long messageId;
|
||||
public long attachmentId;
|
||||
|
||||
public static final int TYPE_PROGRESS = 0;
|
||||
public static final int TYPE_FINISHED = 1;
|
||||
public static final int TYPE_ERROR = 2;
|
||||
public int eventType = TYPE_PROGRESS;
|
||||
|
||||
public float progress = 0.0f;
|
||||
public String fileName = null;
|
||||
public Exception exception = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*if (message.type == TYPE_RECEIVED) {
|
||||
b.messageSender.setText(message.senderFullName);
|
||||
}
|
||||
else if (message.type == TYPE_SENT && message.recipients != null && message.recipients.size() > 0) {
|
||||
StringBuilder senderText = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (MessageRecipientFull recipient: message.recipients) {
|
||||
if (!first) {
|
||||
senderText.append(", ");
|
||||
}
|
||||
first = false;
|
||||
senderText.append(recipient.fullName);
|
||||
}
|
||||
b.messageSender.setText(senderText.toString());
|
||||
}
|
||||
|
||||
if (message.type == TYPE_RECEIVED) {
|
||||
if (app.appConfig.teacherImages != null && app.appConfig.teacherImages.size() > 0 && app.appConfig.teacherImages.containsKey(message.senderId)) {
|
||||
Bitmap profileImage;
|
||||
profileImage = BitmapFactory.decodeFile(app.getFilesDir().getAbsolutePath()+"/teacher_"+message.senderId+".jpg");
|
||||
profileImage = ThumbnailUtils.extractThumbnail(profileImage, Math.min(profileImage.getWidth(), profileImage.getHeight()), Math.min(profileImage.getWidth(), profileImage.getHeight()));
|
||||
RoundedBitmapDrawable roundDrawable = RoundedBitmapDrawableFactory.create(app.getResources(), profileImage);
|
||||
roundDrawable.setCircular(true);
|
||||
b.messageProfileImage.setImageDrawable(roundDrawable);
|
||||
}
|
||||
else {
|
||||
int color = Colors.stringToMaterialColor(message.senderFullName);
|
||||
b.messageProfileBackground.getDrawable().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
|
||||
b.messageProfileName.setTextColor(ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f));
|
||||
if (message.senderFullName != null) {
|
||||
String[] nameParts = message.senderFullName.split(" ");
|
||||
b.messageProfileName.setText(nameParts[0].toUpperCase().charAt(0) + "" + nameParts[1].toUpperCase().charAt(0));
|
||||
}
|
||||
else {
|
||||
b.messageProfileName.setText("N");
|
||||
}
|
||||
}
|
||||
|
||||
View.OnClickListener onClickListener = v -> new MaterialDialog.Builder(activity)
|
||||
.title(R.string.settings_profile_change_title)
|
||||
.items(
|
||||
getString(R.string.settings_profile_change_image),
|
||||
getString(R.string.settings_profile_remove_image)
|
||||
)
|
||||
.itemsCallback((dialog, itemView, position, text) -> {
|
||||
switch (position) {
|
||||
case 0:
|
||||
CropImage.activity()
|
||||
.setAspectRatio(1, 1)
|
||||
//.setMaxCropResultSize(512, 512)
|
||||
.setCropShape(CropImageView.CropShape.OVAL)
|
||||
.setGuidelines(CropImageView.Guidelines.ON)
|
||||
.start(activity, MessagesDetailsFragment.this);
|
||||
break;
|
||||
case 1:
|
||||
if (app.appConfig.teacherImages != null) {
|
||||
app.appConfig.teacherImages.remove(message.senderId);
|
||||
app.saveConfig("teacherImages");
|
||||
}
|
||||
break;
|
||||
}
|
||||
})
|
||||
.negativeText(R.string.cancel)
|
||||
.show();
|
||||
b.messageSender.setOnClickListener(onClickListener);
|
||||
b.messageProfileBackground.setOnClickListener(onClickListener);
|
||||
}*/
|
@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
@ -90,6 +91,20 @@ class MessagesFragment : Fragment() {
|
||||
|
||||
b.tabLayout.setupWithViewPager(b.viewPager)
|
||||
|
||||
activity.navView.apply {
|
||||
bottomBar.apply {
|
||||
fabEnable = true
|
||||
fabExtendedText = getString(R.string.compose)
|
||||
fabIcon = CommunityMaterial.Icon2.cmd_pencil_outline
|
||||
}
|
||||
|
||||
setFabOnClickListener(View.OnClickListener {
|
||||
activity.loadTarget(MainActivity.TARGET_MESSAGES_COMPOSE)
|
||||
})
|
||||
}
|
||||
|
||||
activity.gainAttentionFAB()
|
||||
|
||||
/*if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null) {
|
||||
MaterialDialog.Builder(activity)
|
||||
.title("Wiadomości w systemie Synergia")
|
||||
|
@ -46,9 +46,9 @@ public class MessagesListFragment extends Fragment {
|
||||
static final long TRANSITION_DURATION = 300L;
|
||||
static final String TAP_POSITION = "tap_position";
|
||||
|
||||
private static int[] tapPositions = {NO_POSITION, NO_POSITION};
|
||||
private static int[] topPositions = {NO_POSITION, NO_POSITION};
|
||||
private static int[] bottomPositions = {NO_POSITION, NO_POSITION};
|
||||
public static int[] tapPositions = {NO_POSITION, NO_POSITION};
|
||||
public static int[] topPositions = {NO_POSITION, NO_POSITION};
|
||||
public static int[] bottomPositions = {NO_POSITION, NO_POSITION};
|
||||
|
||||
private int messageType = Message.TYPE_RECEIVED;
|
||||
|
||||
|
@ -9,21 +9,14 @@ import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.getNameInitials
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object MessagesUtils {
|
||||
private fun getInitials(name: String?): String {
|
||||
if (name == null || name.isEmpty()) return ""
|
||||
val nameUppercase = name.toUpperCase()
|
||||
val nameParts = nameUppercase.split(" ").toTypedArray()
|
||||
return if (nameParts.size <= 1) (if (nameUppercase.isEmpty()) '?' else nameUppercase[0]).toString()
|
||||
else (if (nameParts[0].isEmpty()) '?' else nameParts[0][0]).toString() +
|
||||
if (nameParts[1].isEmpty()) "?" else nameParts[1][0]
|
||||
}
|
||||
|
||||
private fun getPaintCenter(textPaint: Paint): Int {
|
||||
return ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
|
||||
}
|
||||
@ -52,7 +45,7 @@ object MessagesUtils {
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeBig
|
||||
canvas.drawArc(rectF, 0f, 360f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 2, diameter / 2 - getPaintCenter(textPaint), textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 2, diameter / 2 - getPaintCenter(textPaint), textPaint)
|
||||
}
|
||||
count == 2 -> { // top
|
||||
name = names[0]
|
||||
@ -60,14 +53,14 @@ object MessagesUtils {
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeMedium
|
||||
canvas.drawArc(rectF, 180f, 180f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 2, diameter / 4 - getPaintCenter(textPaint), textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 2, diameter / 4 - getPaintCenter(textPaint), textPaint)
|
||||
// bottom
|
||||
name = names[1]
|
||||
circlePaint.color = Colors.stringToMaterialColor(name).also { color = it }
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeMedium
|
||||
canvas.drawArc(rectF, 0f, 180f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 2, diameter / 4 * 3 - getPaintCenter(textPaint), textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 2, diameter / 4 * 3 - getPaintCenter(textPaint), textPaint)
|
||||
}
|
||||
count == 3 -> { // upper left
|
||||
name = names[0]
|
||||
@ -75,21 +68,21 @@ object MessagesUtils {
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeSmall
|
||||
canvas.drawArc(rectF, 180f, 90f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 4, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 4, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
// upper right
|
||||
name = names[1]
|
||||
circlePaint.color = Colors.stringToMaterialColor(name).also { color = it }
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeSmall
|
||||
canvas.drawArc(rectF, 270f, 90f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 4 * 3, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 4 * 3, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
// bottom
|
||||
name = names[2]
|
||||
circlePaint.color = Colors.stringToMaterialColor(name).also { color = it }
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeMedium
|
||||
canvas.drawArc(rectF, 0f, 180f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 2, diameter / 4 * 3 - getPaintCenter(textPaint), textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 2, diameter / 4 * 3 - getPaintCenter(textPaint), textPaint)
|
||||
}
|
||||
count >= 4 -> { // upper left
|
||||
name = names[0]
|
||||
@ -97,21 +90,21 @@ object MessagesUtils {
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeSmall
|
||||
canvas.drawArc(rectF, 180f, 90f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 4, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 4, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
// upper right
|
||||
name = names[1]
|
||||
circlePaint.color = Colors.stringToMaterialColor(name).also { color = it }
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeSmall
|
||||
canvas.drawArc(rectF, 270f, 90f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 4 * 3, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 4 * 3, diameter / 4 - getPaintCenter(textPaint) + diameter / 32, textPaint)
|
||||
// bottom left
|
||||
name = names[2]
|
||||
circlePaint.color = Colors.stringToMaterialColor(name).also { color = it }
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeSmall
|
||||
canvas.drawArc(rectF, 90f, 90f, true, circlePaint)
|
||||
canvas.drawText(getInitials(name), diameter / 4, diameter / 4 * 3 - getPaintCenter(textPaint) - diameter / 32, textPaint)
|
||||
canvas.drawText(name.getNameInitials(), diameter / 4, diameter / 4 * 3 - getPaintCenter(textPaint) - diameter / 32, textPaint)
|
||||
// bottom right
|
||||
if (count == 4) name = names[3]
|
||||
if (count > 4) name = "..."
|
||||
@ -119,7 +112,7 @@ object MessagesUtils {
|
||||
textPaint.color = ColorUtils.blendARGB(Colors.legibleTextColor(color), color, 0.30f)
|
||||
textPaint.textSize = textSizeSmall
|
||||
canvas.drawArc(rectF, 0f, 90f, true, circlePaint)
|
||||
canvas.drawText(if (count > 4) "+" + (count - 3) else getInitials(name), diameter / 4 * 3, diameter / 4 * 3 - getPaintCenter(textPaint) - diameter / 32, textPaint)
|
||||
canvas.drawText(if (count > 4) "+" + (count - 3) else name.getNameInitials(), diameter / 4 * 3, diameter / 4 * 3 - getPaintCenter(textPaint) - diameter / 32, textPaint)
|
||||
}
|
||||
}
|
||||
return bitmap
|
||||
@ -129,8 +122,8 @@ object MessagesUtils {
|
||||
var profileImage: Bitmap? = null
|
||||
var profileName: String? = null
|
||||
if (message.type == Message.TYPE_RECEIVED || message.type == Message.TYPE_DELETED) {
|
||||
profileName = message.senderFullName
|
||||
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, message.senderFullName)
|
||||
profileName = message.senderFullName?.fixName()
|
||||
profileImage = getProfileImage(diameterDp, textSizeBigDp, textSizeMediumDp, textSizeSmallDp, 1, profileName)
|
||||
} else if (message.type == Message.TYPE_SENT || message.type == Message.TYPE_DRAFT && message.recipients != null) {
|
||||
when (val count = message.recipients?.size ?: 0) {
|
||||
0 -> {
|
||||
|
@ -84,6 +84,11 @@ public class Colors {
|
||||
0xFF6D4C41
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for teacher's images (e.g. in messages or announcements).
|
||||
* @param s teacher's fullName
|
||||
* @return a material color based on the fullName hash
|
||||
*/
|
||||
public static int stringToMaterialColor(String s) {
|
||||
long seed = 1;
|
||||
try {
|
||||
|
@ -17,6 +17,7 @@ data class NavTarget(
|
||||
var isStatic: Boolean = false
|
||||
var isBelowSeparator: Boolean = false
|
||||
var popToHome: Boolean = false
|
||||
var popTo: Int? = null
|
||||
var badgeTypeId: Int? = null
|
||||
var canHideInDrawer: Boolean = true
|
||||
var canHideInMiniDrawer: Boolean = true
|
||||
@ -64,6 +65,11 @@ data class NavTarget(
|
||||
return this
|
||||
}
|
||||
|
||||
fun withPopTo(popTo: Int): NavTarget {
|
||||
this.popTo = popTo
|
||||
return this
|
||||
}
|
||||
|
||||
fun withBadgeTypeId(badgeTypeId: Int?): NavTarget {
|
||||
this.badgeTypeId = badgeTypeId
|
||||
return this
|
||||
|
@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.modules.messages.MessagesComposeActivity">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="4dp"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||
tools:title="@string/messages_compose_title" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.hootsuite.nachos.NachoTextView
|
||||
android:id="@+id/nacho_text_view"
|
||||
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/messages_compose_to_hint" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/messages_compose_subject_hint" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/toolbar_elevation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="5dp"
|
||||
android:background="@drawable/shadow_top"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_collapseMode="pin"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
@ -32,6 +32,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Librus Captcha" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Network"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/runChucker"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
@ -39,31 +45,38 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Launch Chucker" />
|
||||
|
||||
<LinearLayout
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center">
|
||||
android:text="Messages"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/composeButton"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:text="Compose" />
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/composeNewButton"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Compose 2" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/composeNewButton"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:text="Compose 2" />
|
||||
</LinearLayout>
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/migrate71"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Migration 71" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/syncReceivers"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sync receivers" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sync"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/pruneWorkButton"
|
||||
|
@ -68,7 +68,8 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="40dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -253,10 +254,11 @@
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
android:visibility="visible"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/replyButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="4dp"
|
||||
@ -270,6 +272,15 @@
|
||||
android:paddingRight="4dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/replyIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:padding="4dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-reply"
|
||||
tools:srcCompat="@android:drawable/ic_menu_revert" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -277,16 +288,10 @@
|
||||
android:text="Odpowiedz"
|
||||
android:textAppearance="@style/NavView.TextView.Small" />
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:padding="4dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-reply"
|
||||
tools:srcCompat="@android:drawable/ic_menu_revert" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/forwardButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="4dp"
|
||||
@ -300,6 +305,15 @@
|
||||
android:paddingRight="4dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/forwardIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:padding="4dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-arrow-right-thick"
|
||||
tools:srcCompat="@android:drawable/ic_media_ff" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -308,13 +322,6 @@
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="@style/NavView.TextView.Small" />
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:padding="4dp"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-share"
|
||||
tools:srcCompat="@android:drawable/ic_media_ff" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@ -329,14 +336,8 @@
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Usuń"
|
||||
android:textAppearance="@style/NavView.TextView.Small" />
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="invisible">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:layout_width="24dp"
|
||||
@ -345,6 +346,14 @@
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-delete"
|
||||
tools:srcCompat="@android:drawable/ic_menu_delete" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Usuń"
|
||||
android:textAppearance="@style/NavView.TextView.Small" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
132
app/src/main/res/layout/messages_compose_fragment.xml
Normal file
132
app/src/main/res/layout/messages_compose_fragment.xml
Normal file
@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-22.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="40dp"
|
||||
android:orientation="vertical"><!-- half of the FAB's size -->
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/recipientsLayout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:boxBackgroundColor="@android:color/transparent"
|
||||
app:boxBackgroundMode="filled">
|
||||
|
||||
<com.hootsuite.nachos.NachoTextView
|
||||
android:id="@+id/recipients"
|
||||
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox.Dense"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:focusedByDefault="true"
|
||||
android:hint="@string/messages_compose_to_hint" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/subjectLayout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:boxBackgroundColor="@android:color/transparent"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:counterEnabled="true"
|
||||
tools:counterMaxLength="180">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/subject"
|
||||
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:hint="@string/messages_compose_subject_hint"
|
||||
android:singleLine="true"
|
||||
tools:text="kachoomba"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/textLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:boxBackgroundColor="@android:color/transparent"
|
||||
app:boxBackgroundMode="filled"
|
||||
app:counterEnabled="true"
|
||||
tools:counterMaxLength="1983">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:minLines="3"
|
||||
android:hint="@string/messages_compose_text_hint"
|
||||
android:ems="10"
|
||||
android:gravity="start|top"
|
||||
tools:text="Witam,\n\nchciałem przekazać bardzo ważną wiadomość.\nJest to cytat znanej osoby.\n\n"To jest tak, ale nie. Pamiętaj żeby oczywiście"\n\nCytat ma bardzo duże przesłanie i ogromny wpływ na dzisiejszą kulturę i rozwój współczesnej cywilizacji.\n\nJako zadanie domowe, należy wypisać 5 pierwszych liczb pierwszych. Uzasadnij decyzję, odwołując się do cytatu i 3 innych przykładów z literatury lub filmu.\n\nPozdrawiam,\nJa."
|
||||
android:inputType="textMultiLine" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!--<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/fontStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginTop="4dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fontStyleBold"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
tools:icon="@sample/format-bold"
|
||||
app:iconPadding="0dp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fontStyleItalic"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
tools:icon="@sample/format-bold"
|
||||
app:iconPadding="0dp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fontStyleUnderline"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="48dp"
|
||||
tools:icon="@sample/format-bold"
|
||||
app:iconPadding="0dp"/>
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>-->
|
||||
|
||||
<Button
|
||||
android:id="@+id/breakpoints"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Breakpoints!"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
@ -254,7 +254,9 @@
|
||||
android:text="Wyniki sprawdzianu z matematyki.pdf"
|
||||
android:visibility="visible"
|
||||
app:chipIcon="@drawable/googleg_standard_color_18"
|
||||
app:chipMinHeight="36dp" />
|
||||
app:chipMinHeight="36dp"
|
||||
app:closeIcon="@drawable/background_transparent"
|
||||
app:closeIconVisible="true"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar3"
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
<resources>
|
||||
<string name="error_1" translatable="false">ERROR_APP_CRASH</string>
|
||||
<string name="error_10" translatable="false">ERROR_MESSAGE_NOT_SENT</string>
|
||||
|
||||
<string name="error_50" translatable="false">ERROR_REQUEST_FAILURE</string>
|
||||
<string name="error_51" translatable="false">ERROR_REQUEST_HTTP_400</string>
|
||||
@ -13,7 +14,13 @@
|
||||
<string name="error_54" translatable="false">ERROR_REQUEST_HTTP_404</string>
|
||||
<string name="error_55" translatable="false">ERROR_REQUEST_HTTP_405</string>
|
||||
<string name="error_56" translatable="false">ERROR_REQUEST_HTTP_410</string>
|
||||
<string name="error_57" translatable="false">ERROR_REQUEST_HTTP_500</string>
|
||||
<string name="error_57" translatable="false">ERROR_REQUEST_HTTP_424</string>
|
||||
<string name="error_58" translatable="false">ERROR_REQUEST_HTTP_500</string>
|
||||
<string name="error_59" translatable="false">ERROR_REQUEST_HTTP_503</string>
|
||||
<string name="error_60" translatable="false">ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND</string>
|
||||
<string name="error_61" translatable="false">ERROR_REQUEST_FAILURE_TIMEOUT</string>
|
||||
<string name="error_62" translatable="false">ERROR_REQUEST_FAILURE_NO_INTERNET</string>
|
||||
<string name="error_63" translatable="false">ERROR_REQUEST_FAILURE_SSL_ERROR</string>
|
||||
<string name="error_100" translatable="false">ERROR_RESPONSE_EMPTY</string>
|
||||
<string name="error_101" translatable="false">ERROR_LOGIN_DATA_MISSING</string>
|
||||
<string name="error_102" translatable="false">ERROR_LOGIN_DATA_INVALID</string>
|
||||
@ -81,6 +88,7 @@
|
||||
<string name="error_179" translatable="false">ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN</string>
|
||||
<string name="error_180" translatable="false">ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN</string>
|
||||
<string name="error_181" translatable="false">ERROR_LIBRUS_API_MAINTENANCE</string>
|
||||
<string name="error_182" translatable="false">ERROR_LIBRUS_PORTAL_MAINTENANCE</string>
|
||||
|
||||
<string name="error_201" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN</string>
|
||||
<string name="error_202" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD</string>
|
||||
@ -129,6 +137,7 @@
|
||||
<string name="error_441" translatable="false">ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA</string>
|
||||
<string name="error_450" translatable="false">ERROR_IDZIENNIK_API_ACCESS_DENIED</string>
|
||||
<string name="error_451" translatable="false">ERROR_IDZIENNIK_API_OTHER</string>
|
||||
<string name="error_452" translatable="false">ERROR_IDZIENNIK_API_NO_REGISTER</string>
|
||||
|
||||
<string name="error_501" translatable="false">ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN</string>
|
||||
<string name="error_510" translatable="false">ERROR_LOGIN_EDUDZIENNIK_WEB_OTHER</string>
|
||||
@ -148,7 +157,9 @@
|
||||
<string name="error_905" translatable="false">EXCEPTION_LIBRUS_SYNERGIA_REQUEST</string>
|
||||
<string name="error_906" translatable="false">EXCEPTION_MOBIDZIENNIK_WEB_REQUEST</string>
|
||||
<string name="error_907" translatable="false">EXCEPTION_VULCAN_API_REQUEST</string>
|
||||
<string name="error_910" translatable="false">EXCEPTION_NOTIFY_AND_SYNC</string>
|
||||
<string name="error_908" translatable="false">EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST</string>
|
||||
<string name="error_909" translatable="false">EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST</string>
|
||||
<string name="error_910" translatable="false">EXCEPTION_NOTIFY</string>
|
||||
<string name="error_911" translatable="false">EXCEPTION_LIBRUS_MESSAGES_REQUEST</string>
|
||||
<string name="error_912" translatable="false">EXCEPTION_IDZIENNIK_WEB_REQUEST</string>
|
||||
<string name="error_913" translatable="false">EXCEPTION_IDZIENNIK_WEB_API_REQUEST</string>
|
||||
@ -159,6 +170,7 @@
|
||||
<string name="error_1201" translatable="false">LOGIN_NO_ARGUMENTS</string>
|
||||
|
||||
<string name="error_1_reason">Aplikacja przestała działać</string>
|
||||
<string name="error_10_reason">Nie udało się wysłać wiadomości: nowa wiadomość nie została odnaleziona na liście wiadomości wysłanych</string>
|
||||
|
||||
<string name="error_50_reason">Błąd odpowiedzi serwera</string>
|
||||
<string name="error_51_reason">Błąd serwera: nieprawidłowe zapytanie</string>
|
||||
@ -167,7 +179,13 @@
|
||||
<string name="error_54_reason">Błąd serwera: plik nie znaleziony</string>
|
||||
<string name="error_55_reason">Błąd serwera: nieprawidłowa metoda zapytania</string>
|
||||
<string name="error_56_reason">Błąd serwera: odpowiedź niedostępna</string>
|
||||
<string name="error_57_reason">Wewnętrzny błąd serwera</string>
|
||||
<string name="error_57_reason">Błąd serwera: niespełnione zależności</string>
|
||||
<string name="error_58_reason">Wewnętrzny błąd serwera</string>
|
||||
<string name="error_59_reason">Usługa tymczasowo niedostępna</string>
|
||||
<string name="error_60_reason">Brak internetu: nie znaleziono adresu serwera</string>
|
||||
<string name="error_61_reason">Brak internetu: przekroczono czas oczekiwania</string>
|
||||
<string name="error_62_reason">Brak internetu</string>
|
||||
<string name="error_63_reason">Brak internetu: połączenie SSL nie powiodło się</string>
|
||||
<string name="error_100_reason">Brak odpowiedzi serwera</string>
|
||||
<string name="error_101_reason">Dane logowania niekompletne</string>
|
||||
<string name="error_102_reason">Nieprawidłowe dane logowania</string>
|
||||
@ -235,6 +253,7 @@
|
||||
<string name="error_179_reason">Librus Wiadomości: nieprawidłowe dane logowania</string>
|
||||
<string name="error_180_reason">Librus Portal: nieprawidłowe dane logowania</string>
|
||||
<string name="error_181_reason">Librus API: przerwa techniczna</string>
|
||||
<string name="error_182_reason">Librus Portal: przerwa techniczna</string>
|
||||
|
||||
<string name="error_201_reason">ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN</string>
|
||||
<string name="error_202_reason">ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD</string>
|
||||
@ -283,6 +302,7 @@
|
||||
<string name="error_441_reason">ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA</string>
|
||||
<string name="error_450_reason">ERROR_IDZIENNIK_API_ACCESS_DENIED</string>
|
||||
<string name="error_451_reason">ERROR_IDZIENNIK_API_OTHER</string>
|
||||
<string name="error_452_reason">ERROR_IDZIENNIK_API_NO_REGISTER</string>
|
||||
|
||||
<string name="error_501_reason">Błędny email lub hasło</string>
|
||||
<string name="error_510_reason">Inny błąd logowania</string>
|
||||
@ -302,7 +322,9 @@
|
||||
<string name="error_905_reason">EXCEPTION_LIBRUS_SYNERGIA_REQUEST</string>
|
||||
<string name="error_906_reason">EXCEPTION_MOBIDZIENNIK_WEB_REQUEST</string>
|
||||
<string name="error_907_reason">EXCEPTION_VULCAN_API_REQUEST</string>
|
||||
<string name="error_910_reason">EXCEPTION_NOTIFY_AND_SYNC</string>
|
||||
<string name="error_908_reason">EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST</string>
|
||||
<string name="error_909_reason">EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST</string>
|
||||
<string name="error_910_reason">EXCEPTION_NOTIFY</string>
|
||||
<string name="error_911_reason">EXCEPTION_LIBRUS_MESSAGES_REQUEST</string>
|
||||
<string name="error_912_reason">EXCEPTION_IDZIENNIK_WEB_REQUEST</string>
|
||||
<string name="error_913_reason">EXCEPTION_IDZIENNIK_WEB_API_REQUEST</string>
|
||||
|
@ -483,6 +483,7 @@
|
||||
<string name="messages_compose_title">Napisz wiadomość</string>
|
||||
<string name="messages_compose_to_hint">Do</string>
|
||||
<string name="messages_date_time_format" translatable="false">%s, %s</string>
|
||||
<string name="messages_reply_date_time_format">%s o %s</string>
|
||||
<string name="messages_download_error">Błąd pobierania wiadomości</string>
|
||||
<string name="messages_draft_title">Wersja robocza</string>
|
||||
<string name="messages_recipient_list_download_error">Błąd pobierania listy odbiorców</string>
|
||||
@ -1125,6 +1126,8 @@
|
||||
<string name="widget_timetable_no_lessons_found">Brak lekcji przez następne 7 dni.</string>
|
||||
<string name="widget_timetable_short_no_timetable">Nie pobrano planu lekcji.</string>
|
||||
<string name="widget_timetable_short_no_lessons">Brak lekcji przez nast. 7 dni.</string>
|
||||
<string name="messages_compose_text_hint">Napisz wiadomość...</string>
|
||||
<string name="menu_message_compose">Napisz wiadomość</string>
|
||||
<string name="no_lessons_today">Nie ma dzisiaj żadnych lekcji!</string>
|
||||
<string name="lessons_finished">Nie ma dzisiaj więcej lekcji!</string>
|
||||
<string name="login_edudziennik_title">Zaloguj się - Edudziennik</string>
|
||||
@ -1133,4 +1136,14 @@
|
||||
<string name="dialog_error_details_title">Szczegóły błędu</string>
|
||||
<string name="edziennik_progress_endpoint_descriptive_grades">Pobieranie ocen opisowych…</string>
|
||||
<string name="edziennik_progress_endpoint_point_grades">Pobieranie ocen punktowych…</string>
|
||||
<string name="teacher_specialist">Specjaliści</string>
|
||||
<string name="messages_compose_send">Wyślij</string>
|
||||
<string name="messages_compose_recipients_error">Sprawdź poprawność wybranych odbiorców</string>
|
||||
<string name="messages_compose_recipients_empty">Wybierz odbiorców</string>
|
||||
<string name="messages_compose_subject_empty">Wpisz temat o długości co najmniej 3 znaków</string>
|
||||
<string name="messages_compose_text_empty">Wpisz treść wiadomości</string>
|
||||
<string name="messages_sent_success">Wiadomość została wysłana</string>
|
||||
<string name="compose">Napisz</string>
|
||||
<string name="messages_reply">Odpowiedz</string>
|
||||
<string name="error_unknown_format">Kod błędu: %d (w %s)</string>
|
||||
</resources>
|
||||
|
@ -69,6 +69,23 @@
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.MaterialAlertDialog" parent="ThemeOverlay.MaterialComponents.MaterialAlertDialog">
|
||||
<item name="materialAlertDialogTitleTextStyle">@style/AppTheme.MaterialAlertDialog.TitleText</item>
|
||||
<item name="materialAlertDialogBodyTextStyle">@style/AppTheme.MaterialAlertDialog.BodyText</item>
|
||||
</style>
|
||||
<style name="AppTheme.MaterialAlertDialog.TitleText" parent="MaterialAlertDialog.MaterialComponents.Title.Text">
|
||||
<item name="textSize">20sp</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="textColor">?android:textColorPrimary</item>
|
||||
<item name="android:textColor">?android:textColorPrimary</item>
|
||||
</style>
|
||||
<style name="AppTheme.MaterialAlertDialog.BodyText">
|
||||
<item name="textSize">16sp</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="textColor">?android:textColorSecondary</item>
|
||||
<item name="android:textColor">?android:textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="NavView" />
|
||||
|
||||
<!-- Base application theme. -->
|
||||
@ -104,6 +121,8 @@
|
||||
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||
<item name="hourDividerColor">#b0b0b0</item>
|
||||
<item name="halfHourDividerColor">#e0e0e0</item>
|
||||
|
||||
<item name="materialAlertDialogTheme">@style/AppTheme.MaterialAlertDialog</item>
|
||||
</style>
|
||||
<style name="AppTheme.Dark" parent="NavView.Dark">
|
||||
<item name="colorPrimary">#64b5f6</item>
|
||||
@ -137,6 +156,8 @@
|
||||
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||
<item name="hourDividerColor">#7fffffff</item>
|
||||
<item name="halfHourDividerColor">#40ffffff</item>
|
||||
|
||||
<item name="materialAlertDialogTheme">@style/AppTheme.MaterialAlertDialog</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
BIN
libs/java-json.jar
Normal file
BIN
libs/java-json.jar
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user