diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
index e9865b05..b2a62c8c 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
@@ -1007,8 +1007,29 @@ inline fun LongSparseArray.filter(predicate: (T) -> Boolean): List {
return destination
}
-fun CharSequence.replace(oldValue: String, newValue: CharSequence, ignoreCase: Boolean = false): CharSequence =
- splitToSequence(oldValue, ignoreCase = ignoreCase).toList().concat(newValue)
+fun CharSequence.replaceSpanned(oldValue: String, newValue: CharSequence, ignoreCase: Boolean = false): CharSequence {
+ var seq = this
+ var index = seq.indexOf(oldValue, ignoreCase = ignoreCase)
+ while (index != -1) {
+ val sb = SpannableStringBuilder()
+ sb.appendRange(seq, 0, index)
+ sb.append(newValue)
+ sb.appendRange(seq, index + oldValue.length, seq.length)
+ seq = sb
+ index = seq.indexOf(oldValue, startIndex = index + 1, ignoreCase = ignoreCase)
+ }
+ return seq
+}
+
+fun SpannableStringBuilder.replaceSpan(spanClass: Class<*>, prefix: CharSequence, suffix: CharSequence): SpannableStringBuilder {
+ getSpans(0, length, spanClass).forEach {
+ val spanStart = getSpanStart(it)
+ insert(spanStart, prefix)
+ val spanEnd = getSpanEnd(it)
+ insert(spanEnd, suffix)
+ }
+ return this
+}
fun Int.toColorStateList(): ColorStateList {
val states = arrayOf(
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGetHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGetHomework.kt
index 5213b7eb..12c13d68 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGetHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGetHomework.kt
@@ -1,6 +1,5 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
-import android.text.Html
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
@@ -9,6 +8,7 @@ import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
class EdudziennikWebGetHomework(
override val data: DataEdudziennik,
@@ -26,7 +26,8 @@ class EdudziennikWebGetHomework(
webGet(TAG, "Homework/$id") { text ->
val description = Regexes.EDUDZIENNIK_HOMEWORK_DESCRIPTION.find(text)?.get(1)?.trim()
- if (description != null) event.topic = Html.fromHtml(description).toString()
+ if (description != null)
+ event.topic = BetterHtml.fromHtml(context = null, description).toString()
event.homeworkBody = ""
event.isDownloaded = true
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetHomework.kt
index 75903907..4ba502f0 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetHomework.kt
@@ -1,12 +1,12 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia
-import android.text.Html
import org.greenrobot.eventbus.EventBus
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
class LibrusSynergiaGetHomework(override val data: DataLibrus,
val event: EventFull,
@@ -23,7 +23,10 @@ class LibrusSynergiaGetHomework(override val data: DataLibrus,
val table = doc.select("table.decorated tbody > tr")
event.topic = table[1].select("td")[1].text()
- event.homeworkBody = Html.fromHtml(table[5].select("td")[1].html()).toString()
+ event.homeworkBody = BetterHtml.fromHtml(
+ context = null,
+ html = table[5].select("td")[1].html(),
+ ).toString()
event.isDownloaded = true
event.attachmentIds = mutableListOf()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt
index 683a8820..a79eaded 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt
@@ -4,12 +4,12 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api
-import android.text.Html
import androidx.core.util.contains
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@@ -26,7 +26,7 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List) {
val id = cols[0].toLong()
val teacherId = cols[7].toLong()
val subjectId = cols[6].toLong()
- val topic = Html.fromHtml(cols[1])?.toString()?.trim() ?: ""
+ val topic = BetterHtml.fromHtml(context = null, cols[1]).toString().trim()
val eventDate = Date.fromYmd(cols[2])
val startTime = Time.fromYmdHm(cols[3])
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt
index da64cbc3..3b574b96 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/SzkolnyApi.kt
@@ -245,7 +245,10 @@ class SzkolnyApi(val app: App) : CoroutineScope {
seen = profile.empty
notified = profile.empty
- if (profile.userCode == event.sharedBy) sharedBy = "self"
+ if (profile.userCode == event.sharedBy) {
+ sharedBy = "self"
+ addedManually = true
+ }
}
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt
index 43cc1455..69f44523 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt
@@ -69,80 +69,90 @@ class Notifications(val app: App, val notifications: MutableList,
}
private fun eventNotifications() {
- for (event in app.db.eventDao().getNotNotifiedNow().filter { it.date >= today }) {
+ app.db.eventDao().getNotNotifiedNow().filter {
+ it.date >= today
+ }.forEach { event ->
val text = if (event.isHomework)
app.getString(
- if (event.subjectLongName.isNullOrEmpty())
- R.string.notification_homework_no_subject_format
- else
- R.string.notification_homework_format,
- event.subjectLongName,
- event.date.formattedString
+ if (event.subjectLongName.isNullOrEmpty())
+ R.string.notification_homework_no_subject_format
+ else
+ R.string.notification_homework_format,
+ event.subjectLongName,
+ event.date.formattedString
)
else
app.getString(
- if (event.subjectLongName.isNullOrEmpty())
- R.string.notification_event_no_subject_format
- else
- R.string.notification_event_format,
- event.typeName ?: "wydarzenie",
- event.date.formattedString,
- event.subjectLongName
+ if (event.subjectLongName.isNullOrEmpty())
+ R.string.notification_event_no_subject_format
+ else
+ R.string.notification_event_format,
+ event.typeName ?: "wydarzenie",
+ event.date.formattedString,
+ event.subjectLongName
)
val textLong = app.getString(
- R.string.notification_event_long_format,
- event.typeName ?: "-",
- event.subjectLongName ?: "-",
- event.date.formattedString,
- Week.getFullDayName(event.date.weekDay),
- event.time?.stringHM ?: app.getString(R.string.event_all_day),
- event.topic.take(200)
+ R.string.notification_event_long_format,
+ event.typeName ?: "-",
+ event.subjectLongName ?: "-",
+ event.date.formattedString,
+ Week.getFullDayName(event.date.weekDay),
+ event.time?.stringHM ?: app.getString(R.string.event_all_day),
+ event.topic.take(200)
)
- val type = if (event.isHomework) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
+ val type = if (event.isHomework)
+ Notification.TYPE_NEW_HOMEWORK
+ else
+ Notification.TYPE_NEW_EVENT
notifications += Notification(
- id = Notification.buildId(event.profileId, type, event.id),
- title = app.getNotificationTitle(type),
- text = text,
- textLong = textLong,
- type = type,
- profileId = event.profileId,
- profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
- viewId = if (event.isHomework) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
- addedDate = event.addedDate
+ id = Notification.buildId(event.profileId, type, event.id),
+ title = app.getNotificationTitle(type),
+ text = text,
+ textLong = textLong,
+ type = type,
+ profileId = event.profileId,
+ profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
+ viewId = if (event.isHomework) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
+ addedDate = event.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
}
}
fun sharedEventNotifications() {
- for (event in app.db.eventDao().getNotNotifiedNow().filter { it.date >= today && it.sharedBy != null }) {
+ app.db.eventDao().getNotNotifiedNow().filter {
+ it.date >= today && it.sharedBy != null && it.sharedBy != "self"
+ }.forEach { event ->
val text = app.getString(
- R.string.notification_shared_event_format,
- event.sharedByName,
- event.typeName ?: "wydarzenie",
- event.date.formattedString,
- event.topic
+ R.string.notification_shared_event_format,
+ event.sharedByName,
+ event.typeName ?: "wydarzenie",
+ event.date.formattedString,
+ event.topicHtml
)
val textLong = app.getString(
- R.string.notification_shared_event_long_format,
- event.sharedByName,
- event.typeName ?: "-",
- event.subjectLongName ?: "-",
- event.date.formattedString,
- Week.getFullDayName(event.date.weekDay),
- event.time?.stringHM ?: app.getString(R.string.event_all_day),
- event.topic.take(200)
+ R.string.notification_shared_event_long_format,
+ event.sharedByName,
+ event.typeName ?: "-",
+ event.subjectLongName ?: "-",
+ event.date.formattedString,
+ Week.getFullDayName(event.date.weekDay),
+ event.time?.stringHM ?: app.getString(R.string.event_all_day),
+ event.topicHtml.take(200)
)
- val type = if (event.isHomework) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
+ val type = if (event.isHomework)
+ Notification.TYPE_NEW_HOMEWORK
+ else
+ Notification.TYPE_NEW_EVENT
notifications += Notification(
- id = Notification.buildId(event.profileId, type, event.id),
- title = app.getNotificationTitle(type),
- text = text,
- textLong = textLong,
- type = type,
- profileId = event.profileId,
- profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
- viewId = if (event.isHomework) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
- addedDate = event.addedDate
+ id = Notification.buildId(event.profileId, type, event.id),
+ title = app.getNotificationTitle(type),
+ text = text,
+ textLong = textLong,
+ type = type,
+ profileId = event.profileId,
+ profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
+ viewId = if (event.isHomework) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
+ addedDate = event.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong())
}
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt
index 897d53ff..5c64613e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt
@@ -84,6 +84,10 @@ abstract class EventDao : BaseDao {
fun getAllByDateNow(profileId: Int, date: Date) =
getRawNow("$QUERY WHERE $NOT_BLACKLISTED AND events.profileId = $profileId AND eventDate = '${date.stringY_m_d}' $ORDER_BY")
+ // GET ONE - LIVE DATA
+ fun getById(profileId: Int, id: Long) =
+ getOne("$QUERY WHERE events.profileId = $profileId AND eventId = $id")
+
// GET ONE - NOW
fun getByIdNow(profileId: Int, id: Long) =
getOneNow("$QUERY WHERE events.profileId = $profileId AND eventId = $id")
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt
index 928f0931..af313865 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt
@@ -75,6 +75,7 @@ open class Event(
@ColumnInfo(name = "eventAddedManually")
var addedManually: Boolean = false
+ get() = field || sharedBy == "self"
@ColumnInfo(name = "eventSharedBy")
var sharedBy: String? = null
@ColumnInfo(name = "eventSharedByName")
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt
index c68ecedd..5e3e62de 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt
@@ -7,6 +7,7 @@ import androidx.room.Ignore
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.ui.modules.search.Searchable
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@@ -48,6 +49,20 @@ class EventFull(
var teamName: String? = null
var teamCode: String? = null
+ @delegate:Ignore
+ @delegate:Transient
+ val topicHtml by lazy {
+ BetterHtml.fromHtml(context = null, topic, nl2br = true)
+ }
+
+ @delegate:Ignore
+ @delegate:Transient
+ val bodyHtml by lazy {
+ homeworkBody?.let {
+ BetterHtml.fromHtml(context = null, it, nl2br = true)
+ }
+ }
+
@Ignore
@Transient
override var searchPriority = 0
@@ -60,7 +75,7 @@ class EventFull(
@delegate:Transient
override val searchKeywords by lazy {
listOf(
- listOf(topic, homeworkBody),
+ listOf(topicHtml.toString(), bodyHtml?.toString()),
attachmentNames,
listOf(subjectLongName),
listOf(teacherName),
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt
index 534ad11c..8ff6fd73 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt
@@ -3,12 +3,12 @@
*/
package pl.szczodrzynski.edziennik.data.db.full
-import androidx.core.text.HtmlCompat
import androidx.room.Ignore
import androidx.room.Relation
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.ui.modules.search.Searchable
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
class MessageFull(
profileId: Int, id: Long, type: Int,
@@ -33,11 +33,10 @@ class MessageFull(
@delegate:Transient
val bodyHtml by lazy {
body?.let {
- HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY)
+ BetterHtml.fromHtml(context = null, it)
}
}
-
@Ignore
@Transient
override var searchPriority = 0
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt
index a0e70326..13eb0980 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt
@@ -122,7 +122,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message:
id = json.getLong("id") ?: return,
date = json.getInt("eventDate")?.let { Date.fromValue(it) } ?: return,
time = json.getInt("startTime")?.let { Time.fromValue(it) },
- topic = json.getString("topic") ?: "",
+ topic = json.getString("topicHtml") ?: json.getString("topic") ?: "",
color = json.getInt("color"),
type = json.getLong("type") ?: 0,
teacherId = json.getLong("teacherId") ?: -1,
@@ -135,7 +135,10 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message:
event.sharedBy = json.getString("sharedBy")
event.sharedByName = json.getString("sharedByName")
- if (profile.userCode == event.sharedBy) event.sharedBy = "self"
+ if (profile.userCode == event.sharedBy) {
+ event.sharedBy = "self"
+ event.addedManually = true
+ }
val metadata = Metadata(
event.profileId,
@@ -148,7 +151,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message:
val type = if (event.isHomework) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
- if (!notificationFilter.contains(type)) {
+ if (!notificationFilter.contains(type) && event.sharedBy != "self") {
val notification = Notification(
id = Notification.buildId(event.profileId, type, event.id),
title = app.getNotificationTitle(type),
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/UpdateWorker.kt b/app/src/main/java/pl/szczodrzynski/edziennik/sync/UpdateWorker.kt
index 80e21e6b..c574e9c8 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/UpdateWorker.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/UpdateWorker.kt
@@ -9,7 +9,6 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.text.Html
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.work.*
@@ -20,6 +19,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.utils.Utils
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext
@@ -115,7 +115,7 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
.setStyle(NotificationCompat.BigTextStyle()
.bigText(listOf(
app.getString(R.string.notification_updates_text, update.versionName),
- update.releaseNotes?.let { Html.fromHtml(it) }
+ update.releaseNotes?.let { BetterHtml.fromHtml(context = null, it) }
).concat("\n")))
.setColor(0xff2196f3.toInt())
.setLights(0xFF00FFFF.toInt(), 2000, 2000)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/StyledTextDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/StyledTextDialog.kt
new file mode 100644
index 00000000..8072b616
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/StyledTextDialog.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2021-10-11.
+ */
+
+package pl.szczodrzynski.edziennik.ui.dialogs
+
+import android.content.res.ColorStateList
+import android.text.Editable
+import android.text.SpannableStringBuilder
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import pl.szczodrzynski.edziennik.App
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.databinding.StyledTextDialogBinding
+import pl.szczodrzynski.edziennik.utils.DefaultTextStyles
+import pl.szczodrzynski.edziennik.utils.Themes
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.SIMPLE
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
+
+class StyledTextDialog(
+ val activity: AppCompatActivity,
+ val initialText: Editable?,
+ val onSuccess: (text: Editable) -> Unit,
+ val onShowListener: ((tag: String) -> Unit)? = null,
+ val onDismissListener: ((tag: String) -> Unit)? = null
+) {
+ companion object {
+ private const val TAG = "StyledTextDialog"
+ }
+
+ private lateinit var app: App
+ private lateinit var b: StyledTextDialogBinding
+ private lateinit var dialog: AlertDialog
+ private lateinit var config: StylingConfig
+
+ private val manager
+ get() = app.textStylingManager
+
+ init {
+ show()
+ }
+
+ fun show() {
+ if (activity.isFinishing)
+ return
+ onShowListener?.invoke(TAG)
+ app = activity.applicationContext as App
+ b = StyledTextDialogBinding.inflate(activity.layoutInflater)
+
+ dialog = MaterialAlertDialogBuilder(activity)
+ .setTitle(R.string.styled_text_dialog_title)
+ .setView(b.root)
+ .setPositiveButton(R.string.save) { _, _ ->
+ onSuccess(b.editText.text ?: SpannableStringBuilder(""))
+ }
+ .setNeutralButton(R.string.cancel, null)
+ .setOnDismissListener {
+ onDismissListener?.invoke(TAG)
+ }
+ .show()
+
+ config = StylingConfig(
+ editText = b.editText,
+ fontStyleGroup = b.fontStyle.styles,
+ fontStyleClear = b.fontStyle.clear,
+ styles = DefaultTextStyles.getAsList(b.fontStyle),
+ textHtml = if (App.devMode) b.htmlText else null,
+ htmlMode = SIMPLE,
+ )
+
+ manager.attach(config)
+
+ b.editText.text = initialText
+
+ // this is awful
+ if (Themes.isDark) {
+ val colorStateList = ColorStateList.valueOf(0x40ffffff)
+ b.fontStyle.bold.strokeColor = colorStateList
+ b.fontStyle.italic.strokeColor = colorStateList
+ b.fontStyle.underline.strokeColor = colorStateList
+ b.fontStyle.strike.strokeColor = colorStateList
+ b.fontStyle.subscript.strokeColor = colorStateList
+ b.fontStyle.superscript.strokeColor = colorStateList
+ b.fontStyle.clear.strokeColor = colorStateList
+ }
+ }
+}
+
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/UpdateAvailableDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/UpdateAvailableDialog.kt
index 6446170b..3c54dc92 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/UpdateAvailableDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/UpdateAvailableDialog.kt
@@ -4,7 +4,6 @@
package pl.szczodrzynski.edziennik.ui.dialogs
-import android.text.Html
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -14,6 +13,7 @@ import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
class UpdateAvailableDialog(
@@ -48,7 +48,7 @@ class UpdateAvailableDialog(
R.string.update_available_format,
BuildConfig.VERSION_NAME,
update.versionName,
- update.releaseNotes?.let { Html.fromHtml(it) } ?: "---"
+ update.releaseNotes?.let { BetterHtml.fromHtml(activity, it) } ?: "---"
)
.setPositiveButton(R.string.update_available_button) { dialog, _ ->
activity.startService(Intent(app, UpdateDownloaderService::class.java))
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/changelog/ChangelogDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/changelog/ChangelogDialog.kt
index 40ea90b2..aab73a69 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/changelog/ChangelogDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/changelog/ChangelogDialog.kt
@@ -5,7 +5,6 @@
package pl.szczodrzynski.edziennik.ui.dialogs.changelog
import android.os.Build
-import android.text.Html
import android.widget.ScrollView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
@@ -18,6 +17,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
class ChangelogDialog(
@@ -52,12 +52,11 @@ class ChangelogDialog(
text = text.replace("""\[(.+?)]\(@([A-z0-9-]+)\)""".toRegex(), "$1")
text = text.replace("""\s@([A-z0-9-]+)""".toRegex(), " @$1")
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- textView.text = Html.fromHtml(text)
- }
- else {
- textView.text = Html.fromHtml(text.replace("", "
- "))
- }
+ val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ text
+ else
+ text.replace("", "
- ")
+ textView.text = BetterHtml.fromHtml(activity, html)
textView.movementMethod = BetterLinkMovementMethod.getInstance()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt
index fff65b43..4a309536 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/sync/RegistrationConfigDialog.kt
@@ -4,7 +4,6 @@
package pl.szczodrzynski.edziennik.ui.dialogs.sync
-import android.text.Html
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -14,6 +13,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.AppSync
import pl.szczodrzynski.edziennik.data.db.entity.Profile
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
class RegistrationConfigDialog(
@@ -61,7 +61,7 @@ class RegistrationConfigDialog(
onShowListener?.invoke(TAG + "Enable")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_title)
- .setMessage(Html.fromHtml(app.getString(R.string.registration_config_enable_text)))
+ .setMessage(BetterHtml.fromHtml(activity, R.string.registration_config_enable_text))
.setPositiveButton(R.string.i_agree) { _, _ ->
enableRegistration()
}
@@ -76,7 +76,7 @@ class RegistrationConfigDialog(
onShowListener?.invoke(TAG + "Disable")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_title)
- .setMessage(Html.fromHtml(app.getString(R.string.registration_config_disable_text)))
+ .setMessage(R.string.registration_config_disable_text)
.setPositiveButton(R.string.ok) { _, _ ->
disableRegistration()
}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt
index 0b39b913..bd892fd4 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/base/CrashActivity.kt
@@ -6,7 +6,6 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
-import android.text.Html
import android.view.View
import android.widget.Button
import android.widget.Toast
@@ -22,6 +21,7 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.Themes.appTheme
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
/*
@@ -90,7 +90,7 @@ class CrashActivity : AppCompatActivity(), CoroutineScope {
moreInfoButton.setOnClickListener {
MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialogMonospace)
.setTitle(R.string.crash_details)
- .setMessage(Html.fromHtml(getErrorString(intent, false)))
+ .setMessage(BetterHtml.fromHtml(context = null, getErrorString(intent, false)))
.setPositiveButton(R.string.close, null)
.setNeutralButton(R.string.copy_to_clipboard) { _, _ -> copyErrorToClipboard() }
.show()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventDetailsDialog.kt
index ae848edb..1b23d45e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventDetailsDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventDetailsDialog.kt
@@ -31,6 +31,7 @@ import kotlin.coroutines.CoroutineContext
class EventDetailsDialog(
val activity: AppCompatActivity,
+ // this event is observed for changes
var event: EventFull,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
@@ -85,7 +86,11 @@ class EventDetailsDialog(
showRemoveEventDialog()
}
- update()
+ // watch the event for changes
+ app.db.eventDao().getById(event.profileId, event.id).observe(activity) {
+ event = it ?: return@observe
+ update()
+ }
}}
private fun update() {
@@ -93,6 +98,9 @@ class EventDetailsDialog(
b.eventShared = eventShared
b.eventOwn = eventOwn
+ b.topic.text = event.topicHtml
+ b.body.text = event.bodyHtml
+
if (!event.seen) {
manager.markAsSeen(event)
}
@@ -170,8 +178,9 @@ class EventDetailsDialog(
dialog.dismiss()
return@EventManualDialog
}
- event = it
- update()
+ // this should not be needed as the event is observed by the ID
+ // event = it
+ // update()
},
onShowListener = onShowListener,
onDismissListener = onDismissListener
@@ -350,7 +359,7 @@ class EventDetailsDialog(
val intent = Intent(Intent.ACTION_EDIT).apply {
data = Events.CONTENT_URI
putExtra(Events.TITLE, title)
- putExtra(Events.DESCRIPTION, event.topic)
+ putExtra(Events.DESCRIPTION, event.topicHtml.toString())
if (event.time == null) {
putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, true)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventManualDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventManualDialog.kt
index 5385ee96..51557351 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventManualDialog.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventManualDialog.kt
@@ -13,6 +13,9 @@ import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jaredrummler.android.colorpicker.ColorPickerDialog
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
+import com.mikepenz.iconics.IconicsDrawable
+import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
+import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
@@ -27,9 +30,13 @@ import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
+import pl.szczodrzynski.edziennik.ui.dialogs.StyledTextDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
import pl.szczodrzynski.edziennik.utils.Anim
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.SIMPLE
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfigBase
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
@@ -59,6 +66,10 @@ class EventManualDialog(
private lateinit var b: DialogEventManualV2Binding
private lateinit var dialog: AlertDialog
private lateinit var profile: Profile
+ private lateinit var stylingConfig: StylingConfigBase
+
+ private val textStylingManager
+ get() = app.textStylingManager
private var customColor: Int? = null
private val editingShared = editingEvent?.sharedBy != null
@@ -128,6 +139,23 @@ class EventManualDialog(
}
}
+ b.topicLayout.endIconDrawable = IconicsDrawable(activity, CommunityMaterial.Icon3.cmd_open_in_new).apply {
+ sizeDp = 24
+ }
+ b.topicLayout.setEndIconOnClickListener {
+ StyledTextDialog(
+ activity,
+ initialText = b.topic.text,
+ onSuccess = {
+ b.topic.text = it
+ },
+ onShowListener,
+ onDismissListener
+ )
+ }
+
+ stylingConfig = StylingConfigBase(editText = b.topic, htmlMode = SIMPLE)
+
updateShareText()
b.shareSwitch.onChange { _, isChecked ->
updateShareText(isChecked)
@@ -332,7 +360,7 @@ class EventManualDialog(
// copy data from event being edited
editingEvent?.let {
- b.topic.setText(it.topic)
+ b.topic.setText(BetterHtml.fromHtml(activity, it.topic, nl2br = true))
if (it.color != -1)
customColor = it.color
}
@@ -458,12 +486,13 @@ class EventManualDialog(
val id = System.currentTimeMillis()
+ val topicHtml = textStylingManager.getHtmlText(stylingConfig)
val eventObject = Event(
profileId = profileId,
id = editingEvent?.id ?: id,
date = date,
time = startTime,
- topic = topic,
+ topic = topicHtml,
color = customColor,
type = type?.id ?: Event.TYPE_DEFAULT,
teacherId = teacher?.id ?: -1,
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventViewHolder.kt
index 399a597d..8aff06dc 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventViewHolder.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/event/EventViewHolder.kt
@@ -9,7 +9,6 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
-import com.mikepenz.iconics.utils.buildIconics
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.databinding.EventListItemBinding
@@ -99,15 +98,13 @@ class EventViewHolder(
/* 3$ */
item.teamName?.let { bullet + it } ?: "",
)
+ // workaround for the span data lost during setText above
val addedBySpanned = adapter.highlightSearchText(
item = item,
text = addedBy,
color = colorHighlight
)
- b.addedBy.text = b.addedBy.text.replace(addedBy, addedBySpanned)
- // for now, as CharSequence.replace() converts the original sequence to string,
- // so the Iconics span data is lost and the share icon set above does not display
- b.addedBy.buildIconics()
+ b.addedBy.text = b.addedBy.text.replaceSpanned(addedBy, addedBySpanned)
b.attachmentIcon.isVisible = item.hasAttachments
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeAvailabilityCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeAvailabilityCard.kt
index 613658f4..411d3aef 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeAvailabilityCard.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeAvailabilityCard.kt
@@ -8,7 +8,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
-import androidx.core.text.HtmlCompat
import androidx.core.view.isVisible
import androidx.core.view.plusAssign
import androidx.core.view.setMargins
@@ -25,6 +24,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
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.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
class HomeAvailabilityCard(
@@ -61,8 +61,8 @@ class HomeAvailabilityCard(
// show "register unavailable" only when disabled
if (status?.userMessage != null) {
- b.homeAvailabilityTitle.text = HtmlCompat.fromHtml(status.userMessage.title, HtmlCompat.FROM_HTML_MODE_LEGACY)
- b.homeAvailabilityText.text = HtmlCompat.fromHtml(status.userMessage.contentShort, HtmlCompat.FROM_HTML_MODE_LEGACY)
+ b.homeAvailabilityTitle.text = BetterHtml.fromHtml(activity, status.userMessage.title)
+ b.homeAvailabilityText.text = BetterHtml.fromHtml(activity, status.userMessage.contentShort)
b.homeAvailabilityUpdate.isVisible = false
b.homeAvailabilityIcon.setImageResource(R.drawable.ic_sync)
if (status.userMessage.icon != null)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt
index 71a5b804..c69d3ec2 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.kt
@@ -10,7 +10,6 @@ import android.app.Activity
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
-import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -31,6 +30,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity
import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager.Error.Type
import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
@@ -218,7 +218,7 @@ class LoginChooserFragment : Fragment(), CoroutineScope {
if (!app.config.privacyPolicyAccepted) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.privacy_policy)
- .setMessage(Html.fromHtml(activity.getString(R.string.privacy_policy_dialog_html)))
+ .setMessage(BetterHtml.fromHtml(activity, R.string.privacy_policy_dialog_html))
.setPositiveButton(R.string.i_agree) { _, _ ->
app.config.privacyPolicyAccepted = true
onLoginModeClicked(loginType, loginMode)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/compose/MessagesComposeFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/compose/MessagesComposeFragment.kt
index bd468ea7..f8aefd1f 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/compose/MessagesComposeFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/compose/MessagesComposeFragment.kt
@@ -38,15 +38,17 @@ import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.databinding.MessagesComposeFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.messages.list.MessagesFragment
+import pl.szczodrzynski.edziennik.utils.DefaultTextStyles
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.managers.MessageManager.UIConfig
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.COMPATIBLE
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.ORIGINAL
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
import pl.szczodrzynski.edziennik.utils.span.*
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import kotlin.coroutines.CoroutineContext
-
class MessagesComposeFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "MessagesComposeFragment"
@@ -103,6 +105,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
b.breakpoints.visibility = if (App.devMode) View.VISIBLE else View.GONE
b.breakpoints.setOnClickListener {
b.breakpoints.isEnabled = true
+ @SuppressLint("SetTextI18n")
b.breakpoints.text = "Breakpoints!"
// do your job
}
@@ -232,45 +235,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
b.subjectLayout.isEnabled = false
b.textLayout.isEnabled = false
- val styles = listOf(
- StylingConfig.Style(
- button = b.fontStyleBold,
- spanClass = BoldSpan::class.java,
- icon = CommunityMaterial.Icon2.cmd_format_bold,
- hint = R.string.hint_style_bold,
- ),
- StylingConfig.Style(
- button = b.fontStyleItalic,
- spanClass = ItalicSpan::class.java,
- icon = CommunityMaterial.Icon2.cmd_format_italic,
- hint = R.string.hint_style_italic,
- ),
- StylingConfig.Style(
- button = b.fontStyleUnderline,
- // a custom span is used to prevent issues with keyboards which underline words
- spanClass = UnderlineCustomSpan::class.java,
- icon = CommunityMaterial.Icon2.cmd_format_underline,
- hint = R.string.hint_style_underline,
- ),
- StylingConfig.Style(
- button = b.fontStyleStrike,
- spanClass = StrikethroughSpan::class.java,
- icon = CommunityMaterial.Icon2.cmd_format_strikethrough,
- hint = R.string.hint_style_strike,
- ),
- StylingConfig.Style(
- button = b.fontStyleSubscript,
- spanClass = SubscriptSizeSpan::class.java,
- icon = CommunityMaterial.Icon2.cmd_format_subscript,
- hint = R.string.hint_style_subscript,
- ),
- StylingConfig.Style(
- button = b.fontStyleSuperscript,
- spanClass = SuperscriptSizeSpan::class.java,
- icon = CommunityMaterial.Icon2.cmd_format_superscript,
- hint = R.string.hint_style_superscript,
- ),
- )
+ val styles = DefaultTextStyles.getAsList(b.fontStyle)
uiConfig = UIConfig(
context = activity,
@@ -285,28 +250,24 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
)
stylingConfig = StylingConfig(
editText = b.text,
- fontStyleGroup = b.fontStyle,
- fontStyleClear = b.fontStyleClear,
+ fontStyleGroup = b.fontStyle.styles,
+ fontStyleClear = b.fontStyle.clear,
styles = styles,
textHtml = if (App.devMode) b.textHtml else null,
- htmlCompatibleMode = app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK,
+ htmlMode = when (app.profile.loginStoreType) {
+ LOGIN_TYPE_MOBIDZIENNIK -> COMPATIBLE
+ else -> ORIGINAL
+ },
)
- b.fontStyleLayout.isVisible = enableTextStyling
+ b.fontStyle.root.isVisible = enableTextStyling
if (enableTextStyling) {
textStylingManager.attach(stylingConfig)
- b.fontStyle.addOnButtonCheckedListener { _, _, _ ->
+ b.fontStyle.styles.addOnButtonCheckedListener { _, _, _ ->
changedBody = true
}
}
- if (App.devMode) {
- b.textHtml.isVisible = true
- b.text.addTextChangedListener {
- b.textHtml.text = getMessageBody()
- }
- }
-
activity.navView.bottomBar.apply {
fabEnable = true
fabExtendedText = getString(R.string.messages_compose_send)
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/single/MessageFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/single/MessageFragment.kt
index ad86f287..f17c207e 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/single/MessageFragment.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/single/MessageFragment.kt
@@ -5,7 +5,6 @@
package pl.szczodrzynski.edziennik.ui.modules.messages.single
import android.os.Bundle
-import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -32,6 +31,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
import pl.szczodrzynski.edziennik.ui.modules.messages.list.MessagesFragment
import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.BetterLink
+import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
@@ -255,7 +255,7 @@ class MessageFragment : Fragment(), CoroutineScope {
}
}
messageRecipients.append("")
- b.recipients.text = Html.fromHtml(messageRecipients.toString())
+ b.recipients.text = BetterHtml.fromHtml(activity, messageRecipients)
showAttachments()
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/timetable/WidgetTimetableFactory.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/timetable/WidgetTimetableFactory.java
index 369f6783..01fda6d9 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/timetable/WidgetTimetableFactory.java
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/timetable/WidgetTimetableFactory.java
@@ -4,6 +4,8 @@
package pl.szczodrzynski.edziennik.ui.widgets.timetable;
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
@@ -16,7 +18,6 @@ import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
-import android.text.Html;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -32,14 +33,13 @@ import com.mikepenz.iconics.utils.IconicsDrawableExtensionsKt;
import java.util.List;
import kotlin.Unit;
+import pl.szczodrzynski.edziennik.ExtensionsKt;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.ItemWidgetTimetableModel;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.models.Week;
-import static android.util.TypedValue.COMPLEX_UNIT_SP;
-
public class WidgetTimetableFactory implements RemoteViewsService.RemoteViewsFactory {
private static final String TAG = "WidgetTimetableProvider";
@@ -309,17 +309,17 @@ public class WidgetTimetableFactory implements RemoteViewsService.RemoteViewsFac
views.setViewVisibility(R.id.widgetTimetableOldSubjectName, View.GONE);
if (lesson.lessonChange) {
- views.setTextViewText(R.id.widgetTimetableSubjectName, Html.fromHtml(""+lesson.subjectName+""));
+ views.setTextViewText(R.id.widgetTimetableSubjectName, ExtensionsKt.asItalicSpannable(lesson.subjectName));
if (lesson.lessonChangeNoClassroom) {
- views.setTextViewText(R.id.widgetTimetableClassroomName, Html.fromHtml(""+lesson.classroomName+""));
+ views.setTextViewText(R.id.widgetTimetableClassroomName, ExtensionsKt.asStrikethroughSpannable(lesson.classroomName));
}
else {
- views.setTextViewText(R.id.widgetTimetableClassroomName, Html.fromHtml("" + lesson.classroomName + ""));
+ views.setTextViewText(R.id.widgetTimetableClassroomName, ExtensionsKt.asItalicSpannable(lesson.classroomName));
}
}
else if (lesson.lessonCancelled) {
- views.setTextViewText(R.id.widgetTimetableSubjectName, Html.fromHtml(""+lesson.subjectName+""));
- views.setTextViewText(R.id.widgetTimetableClassroomName, Html.fromHtml(""+lesson.classroomName+""));
+ views.setTextViewText(R.id.widgetTimetableSubjectName, ExtensionsKt.asStrikethroughSpannable(lesson.subjectName));
+ views.setTextViewText(R.id.widgetTimetableClassroomName, ExtensionsKt.asStrikethroughSpannable(lesson.classroomName));
}
else {
views.setTextViewText(R.id.widgetTimetableSubjectName, lesson.subjectName);
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/DefaultTextStyles.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/DefaultTextStyles.kt
new file mode 100644
index 00000000..7f1fa02a
--- /dev/null
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/DefaultTextStyles.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) Kuba Szczodrzyński 2021-10-11.
+ */
+
+package pl.szczodrzynski.edziennik.utils
+
+import android.text.style.StrikethroughSpan
+import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
+import pl.szczodrzynski.edziennik.R
+import pl.szczodrzynski.edziennik.databinding.StyledTextButtonsBinding
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
+import pl.szczodrzynski.edziennik.utils.span.*
+
+object DefaultTextStyles {
+
+ fun getAsList(b: StyledTextButtonsBinding) = listOf(
+ StylingConfig.Style(
+ button = b.bold,
+ spanClass = BoldSpan::class.java,
+ icon = CommunityMaterial.Icon2.cmd_format_bold,
+ hint = R.string.hint_style_bold,
+ ),
+ StylingConfig.Style(
+ button = b.italic,
+ spanClass = ItalicSpan::class.java,
+ icon = CommunityMaterial.Icon2.cmd_format_italic,
+ hint = R.string.hint_style_italic,
+ ),
+ StylingConfig.Style(
+ button = b.underline,
+ // a custom span is used to prevent issues with keyboards which underline words
+ spanClass = UnderlineCustomSpan::class.java,
+ icon = CommunityMaterial.Icon2.cmd_format_underline,
+ hint = R.string.hint_style_underline,
+ ),
+ StylingConfig.Style(
+ button = b.strike,
+ spanClass = StrikethroughSpan::class.java,
+ icon = CommunityMaterial.Icon2.cmd_format_strikethrough,
+ hint = R.string.hint_style_strike,
+ ),
+ StylingConfig.Style(
+ button = b.subscript,
+ spanClass = SubscriptSizeSpan::class.java,
+ icon = CommunityMaterial.Icon2.cmd_format_subscript,
+ hint = R.string.hint_style_subscript,
+ ),
+ StylingConfig.Style(
+ button = b.superscript,
+ spanClass = SuperscriptSizeSpan::class.java,
+ icon = CommunityMaterial.Icon2.cmd_format_superscript,
+ hint = R.string.hint_style_superscript,
+ ),
+ )
+}
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt
index 70886ec9..206dacf7 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt
@@ -32,8 +32,14 @@ object BetterHtml {
SuperscriptSizeSpan::class.java,
)
+ fun fromHtml(context: Context, stringRes: Int) = fromHtml(
+ context,
+ context.getString(stringRes),
+ nl2br = true,
+ )
+
@JvmStatic
- fun fromHtml(context: Context, html: String): Spanned {
+ fun fromHtml(context: Context?, html: CharSequence, nl2br: Boolean = false): Spanned {
val hexPattern = "(#[a-fA-F0-9]{6})"
val colorRegex = "(?:color=\"$hexPattern\")|(?:style=\"color: ?${hexPattern})"
.toRegex(RegexOption.IGNORE_CASE)
@@ -42,29 +48,35 @@ object BetterHtml {
.replace("\\[META:[A-z0-9]+;[0-9-]+]".toRegex(), "")
.replace("background-color: ?$hexPattern;".toRegex(), "")
- val colorBackground = android.R.attr.colorBackground.resolveAttr(context)
- val textColorPrimary = android.R.attr.textColorPrimary.resolveAttr(context) and 0xffffff
+ if (nl2br) {
+ text = text.replace("\n", "
")
+ }
- colorRegex.findAll(text).forEach { result ->
- val group = result.groups.drop(1).firstOrNull { it != null } ?: return@forEach
+ if (context != null) {
+ val colorBackground = android.R.attr.colorBackground.resolveAttr(context)
+ val textColorPrimary = android.R.attr.textColorPrimary.resolveAttr(context) and 0xffffff
- val color = Color.parseColor(group.value)
- var newColor = 0xff000000.toInt() or color
+ colorRegex.findAll(text).forEach { result ->
+ val group = result.groups.drop(1).firstOrNull { it != null } ?: return@forEach
- var blendAmount = 1
- var numIterations = 0
+ val color = Color.parseColor(group.value)
+ var newColor = 0xff000000.toInt() or color
- while (numIterations < 100 && ColorUtils.calculateContrast(
- colorBackground,
- newColor
- ) < 4.5f
- ) {
- blendAmount += 2
- newColor = blendColors(color, blendAmount shl 24 or textColorPrimary)
- numIterations++
+ var blendAmount = 1
+ var numIterations = 0
+
+ while (numIterations < 100 && ColorUtils.calculateContrast(
+ colorBackground,
+ newColor
+ ) < 4.5f
+ ) {
+ blendAmount += 2
+ newColor = blendColors(color, blendAmount shl 24 or textColorPrimary)
+ numIterations++
+ }
+
+ text = text.replaceRange(group.range, "#" + (newColor and 0xffffff).toString(16))
}
-
- text = text.replaceRange(group.range, "#" + (newColor and 0xffffff).toString(16))
}
/*val olRegex = """(.+?)\s*?ol>"""
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/EventManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/EventManager.kt
index 6594aa01..a16175f4 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/EventManager.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/EventManager.kt
@@ -45,16 +45,13 @@ class EventManager(val app: App) : CoroutineScope {
showType: Boolean = true,
doneIconColor: Int? = null
) {
- var eventTopic = if (showType)
- "${event.typeName ?: "wydarzenie"} - ${event.topic}"
- else
- event.topic
+ val topicSpan = event.topicHtml
- if (event.addedManually) {
- eventTopic = "{cmd-clipboard-edit-outline} $eventTopic"
- }
-
- title.text = eventTopic
+ title.text = listOfNotNull(
+ if (event.addedManually) "{cmd-clipboard-edit-outline} " else null,
+ if (showType) "${event.typeName ?: "wydarzenie"} - " else null,
+ topicSpan,
+ ).concat()
title.setCompoundDrawables(
null,
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/MessageManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/MessageManager.kt
index c72a770f..f421cc74 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/MessageManager.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/MessageManager.kt
@@ -25,6 +25,7 @@ import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesUtils
import pl.szczodrzynski.edziennik.utils.TextInputKeyboardEdit
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.ORIGINAL
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@@ -148,7 +149,7 @@ class MessageManager(private val app: App) {
suspend fun saveAsDraft(config: UIConfig, stylingConfig: StylingConfig, profileId: Int, messageId: Long?) {
val teachers = config.recipients.allChips.mapNotNull { it.data as? Teacher }
val subject = config.subject.text?.toString() ?: ""
- val body = textStylingManager.getHtmlText(stylingConfig, enableHtmlCompatible = false)
+ val body = textStylingManager.getHtmlText(stylingConfig, htmlMode = ORIGINAL)
withContext(Dispatchers.Default) {
if (messageId != null) {
diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/TextStylingManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/TextStylingManager.kt
index b60df935..516792db 100644
--- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/TextStylingManager.kt
+++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/TextStylingManager.kt
@@ -6,18 +6,25 @@ package pl.szczodrzynski.edziennik.utils.managers
import android.text.SpannableStringBuilder
import android.text.Spanned
+import android.text.style.StrikethroughSpan
+import android.text.style.SubscriptSpan
+import android.text.style.SuperscriptSpan
+import android.text.style.UnderlineSpan
import android.widget.Button
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.core.text.HtmlCompat
+import androidx.core.view.isVisible
+import androidx.core.widget.addTextChangedListener
import com.google.android.material.button.MaterialButton
import com.google.android.material.button.MaterialButtonToggleGroup
import com.mikepenz.iconics.typeface.IIcon
-import pl.szczodrzynski.edziennik.App
-import pl.szczodrzynski.edziennik.attachToastHint
-import pl.szczodrzynski.edziennik.hasSet
+import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.utils.TextInputKeyboardEdit
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
+import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.*
+import pl.szczodrzynski.edziennik.utils.span.BoldSpan
+import pl.szczodrzynski.edziennik.utils.span.ItalicSpan
class TextStylingManager(private val app: App) {
companion object {
@@ -28,14 +35,45 @@ class TextStylingManager(private val app: App) {
"((?:
)+)
".toRegex()
}
- data class StylingConfig(
+ enum class HtmlMode {
+ /**
+ * The default mode, suitable for fromHtml conversion.
+ */
+ ORIGINAL,
+
+ /**
+ * A more browser-compatible mode.
+ */
+ COMPATIBLE,
+
+ /**
+ * A simple, paragraph-stripped mode with \n instead of
.
+ * The converted text has no HTML tags when no spans in source.
+ */
+ SIMPLE,
+
+ /**
+ * Markdown-compatible text mode.
+ */
+ MARKDOWN,
+ }
+
+ open class StylingConfigBase(
val editText: TextInputKeyboardEdit,
+ val htmlMode: HtmlMode = ORIGINAL,
+ ) {
+ var watchStyleChecked = true
+ var watchSelectionChanged = true
+ }
+
+ class StylingConfig(
+ editText: TextInputKeyboardEdit,
val fontStyleGroup: MaterialButtonToggleGroup,
val fontStyleClear: Button,
val styles: List