Compare commits

3 Commits

Author SHA1 Message Date
Sweetbread d7d14389fc Display and send messages 2026-04-10 14:26:33 +03:00
Sweetbread 902af5e7b5 wip: show messages 2026-03-26 23:57:28 +03:00
Sweetbread 64de39f0ca update 2026-03-20 01:22:30 +03:00
7 changed files with 353 additions and 363 deletions
+10 -6
View File
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
/* /*
* Created by sweetbread * Created by sweetbread
* Copyright (c) 2026. All rights reserved. * Copyright (c) 2026. All rights reserved.
@@ -5,14 +7,13 @@
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose) alias(libs.plugins.kotlin.compose)
// alias(libs.plugins.ksp) // alias(libs.plugins.ksp)
} }
android { android {
namespace = "ru.risdeveau.pixeldragon" namespace = "ru.risdeveau.pixeldragon"
compileSdk = 35 compileSdk = 36
defaultConfig { defaultConfig {
applicationId = "ru.risdeveau.pixeldragon" applicationId = "ru.risdeveau.pixeldragon"
@@ -37,14 +38,17 @@ android {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions {
jvmTarget = "17"
}
buildFeatures { buildFeatures {
compose = true compose = true
} }
} }
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
dependencies { dependencies {
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
@@ -94,5 +98,5 @@ dependencies {
// Others // Others
implementation(libs.splitties.base) // Syntax sugar implementation(libs.splitties.base) // Syntax sugar
implementation(libs.jsoup) // HTML parser implementation(libs.jsoup) // HTML parser
implementation(libs.iconsax.compose) // Material icons
} }
@@ -40,6 +40,7 @@ import de.connect2x.trixnity.client.cryptodriver.vodozemac.vodozemac
import de.connect2x.trixnity.clientserverapi.client.SyncState import de.connect2x.trixnity.clientserverapi.client.SyncState
import ru.risdeveau.pixeldragon.R import ru.risdeveau.pixeldragon.R
import ru.risdeveau.pixeldragon.client import ru.risdeveau.pixeldragon.client
import ru.risdeveau.pixeldragon.ui.layout.Room
import ru.risdeveau.pixeldragon.ui.layout.RoomList import ru.risdeveau.pixeldragon.ui.layout.RoomList
import ru.risdeveau.pixeldragon.ui.theme.PixelDragonTheme import ru.risdeveau.pixeldragon.ui.theme.PixelDragonTheme
import ru.risdeveau.pixeldragon.util.getMediaStore import ru.risdeveau.pixeldragon.util.getMediaStore
@@ -94,9 +95,9 @@ class MainActivity : ComponentActivity() {
"room/{rid}", "room/{rid}",
arguments = listOf(navArgument("rid") { type = NavType.StringType }) arguments = listOf(navArgument("rid") { type = NavType.StringType })
) { navBackStackEntry -> ) { navBackStackEntry ->
// Room(Modifier Room(Modifier
// .padding(innerPadding) .padding(innerPadding)
// .fillMaxSize(), navBackStackEntry.arguments!!.getString("rid")!!) .fillMaxSize(), navBackStackEntry.arguments!!.getString("rid")!!)
} }
composable( composable(
"space/{rid}", "space/{rid}",
@@ -8,8 +8,7 @@ package ru.risdeveau.pixeldragon.ui.item
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons import io.github.rabehx.iconsax.Iconsax
import androidx.compose.material.icons.outlined.Warning
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -28,6 +27,7 @@ import coil3.request.ImageRequest
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import de.connect2x.trixnity.client.media import de.connect2x.trixnity.client.media
import de.connect2x.trixnity.clientserverapi.model.media.FileTransferProgress import de.connect2x.trixnity.clientserverapi.model.media.FileTransferProgress
import io.github.rabehx.iconsax.outline.Warning2
import ru.risdeveau.pixeldragon.client import ru.risdeveau.pixeldragon.client
enum class ImageLoadState { enum class ImageLoadState {
@@ -119,7 +119,7 @@ fun MXCImage(
} }
imageLoadState == ImageLoadState.Error -> { imageLoadState == ImageLoadState.Error -> {
Icon( Icon(
Icons.Outlined.Warning, Iconsax.Outline.Warning2,
contentDescription = "Error", contentDescription = "Error",
modifier = Modifier.align(Alignment.Center) modifier = Modifier.align(Alignment.Center)
) )
@@ -1,342 +1,322 @@
///* /*
// * Created by sweetbread * Created by sweetbread
// * Copyright (c) 2025. All rights reserved. * Copyright (c) 2025. All rights reserved.
// */ */
//
//package ru.risdeveau.pixeldragon.ui.layout package ru.risdeveau.pixeldragon.ui.layout
//
//import android.content.Context import android.annotation.SuppressLint
//import android.content.Intent import android.content.Context
//import android.net.Uri import android.content.Intent
//import android.webkit.WebResourceRequest import android.net.Uri
//import android.webkit.WebView import android.view.MotionEvent
//import android.webkit.WebViewClient import android.view.ViewGroup
//import androidx.compose.foundation.background import android.webkit.WebResourceRequest
//import androidx.compose.foundation.layout.Box import android.webkit.WebSettings
//import androidx.compose.foundation.layout.Column import android.webkit.WebView
//import androidx.compose.foundation.layout.fillMaxHeight import android.webkit.WebViewClient
//import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.animation.animateContentSize
//import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.background
//import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.Box
//import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.layout.Column
//import androidx.compose.foundation.lazy.items import androidx.compose.foundation.layout.Row
//import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.layout.fillMaxHeight
//import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.layout.fillMaxWidth
//import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.foundation.layout.heightIn
//import androidx.compose.material3.MaterialTheme import androidx.compose.foundation.layout.padding
//import androidx.compose.material3.Text import androidx.compose.foundation.lazy.LazyColumn
//import androidx.compose.runtime.Composable import androidx.compose.foundation.lazy.items
//import androidx.compose.runtime.DisposableEffect import androidx.compose.foundation.lazy.rememberLazyListState
//import androidx.compose.runtime.LaunchedEffect import androidx.compose.material3.ExperimentalMaterial3Api
//import androidx.compose.runtime.getValue import androidx.compose.material3.Icon
//import androidx.compose.runtime.mutableStateOf import androidx.compose.material3.IconButton
//import androidx.compose.runtime.remember import androidx.compose.material3.MaterialTheme
//import androidx.compose.runtime.setValue import androidx.compose.material3.OutlinedTextField
//import androidx.compose.ui.Modifier import androidx.compose.material3.Text
//import androidx.compose.ui.draw.clip import androidx.compose.material3.TextField
//import androidx.compose.ui.graphics.Color import androidx.compose.runtime.Composable
//import androidx.compose.ui.graphics.toArgb import androidx.compose.runtime.DisposableEffect
//import androidx.compose.ui.platform.LocalContext import androidx.compose.runtime.LaunchedEffect
//import androidx.compose.ui.text.font.FontWeight import androidx.compose.runtime.collectAsState
//import androidx.compose.ui.unit.dp import androidx.compose.runtime.getValue
//import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.runtime.mutableStateOf
//import kotlinx.coroutines.Dispatchers import androidx.compose.runtime.remember
//import kotlinx.coroutines.withContext import androidx.compose.runtime.setValue
//import org.jsoup.Jsoup import androidx.compose.ui.Modifier
//import org.jsoup.safety.Safelist import androidx.compose.ui.draw.alpha
//import ru.risdeveau.pixeldragon.api.Event import androidx.compose.ui.graphics.Color
//import ru.risdeveau.pixeldragon.api.getAccountData import androidx.compose.ui.graphics.toArgb
//import ru.risdeveau.pixeldragon.api.getEventsAround import androidx.compose.ui.platform.LocalContext
//import ru.risdeveau.pixeldragon.ui.activity.ME import androidx.compose.ui.text.style.TextAlign
//import ru.risdeveau.pixeldragon.ui.item.MXCImage import androidx.compose.ui.unit.dp
// import androidx.compose.ui.viewinterop.AndroidView
//@Composable import de.connect2x.trixnity.client.room
//fun Room(modifier: Modifier = Modifier, rid: String) { import de.connect2x.trixnity.client.room.message.MessageBuilder
// var eventsId by remember { mutableStateOf(listOf<Event>()) } import de.connect2x.trixnity.client.room.message.text
// val listState = rememberLazyListState() import de.connect2x.trixnity.client.room.toFlowList
// import de.connect2x.trixnity.client.store.TimelineEvent
// LaunchedEffect(Unit) { import de.connect2x.trixnity.client.store.eventId
// withContext(Dispatchers.IO) { import de.connect2x.trixnity.client.store.unsigned
// val readMark = getAccountData(ME!!.userId, rid, "m.fully_read") import de.connect2x.trixnity.core.model.RoomId
// val eventsAround = getEventsAround(rid, readMark!!.getString("event_id")) //FIXME: Null check import de.connect2x.trixnity.core.model.events.ClientEvent
// eventsId = eventsAround.let { import de.connect2x.trixnity.core.model.events.m.room.AvatarEventContent
// it.before + listOf(it.base) + it.after import de.connect2x.trixnity.core.model.events.m.room.CanonicalAliasEventContent
// } import de.connect2x.trixnity.core.model.events.m.room.HistoryVisibilityEventContent
// } import de.connect2x.trixnity.core.model.events.m.room.MemberEventContent
// } import de.connect2x.trixnity.core.model.events.m.room.NameEventContent
// import de.connect2x.trixnity.core.model.events.m.room.PowerLevelsEventContent
// LazyColumn(modifier = modifier, state = listState, reverseLayout = true) { import de.connect2x.trixnity.core.model.events.m.room.RoomMessageEventContent
// items(eventsId.reversed()) { event -> import de.connect2x.trixnity.core.model.events.m.room.TopicEventContent
// EventItem(event) import io.github.rabehx.iconsax.Iconsax
// } import io.github.rabehx.iconsax.filled.ArrowRight
// import io.github.rabehx.iconsax.filled.Send
// item { import kotlinx.coroutines.CoroutineScope
// if (eventsId.isEmpty()) { import kotlinx.coroutines.Dispatchers
// Text("Empty room") import kotlinx.coroutines.ExperimentalCoroutinesApi
// } import kotlinx.coroutines.flow.MutableStateFlow
// } import kotlinx.coroutines.flow.combine
// } import kotlinx.coroutines.flow.flatMapLatest
//} import kotlinx.coroutines.flow.flowOf
// import kotlinx.coroutines.launch
//@OptIn(ExperimentalMaterial3Api::class) import org.jsoup.Jsoup
//@Composable import org.jsoup.safety.Safelist
//fun EventItem(event: Event) { import ru.risdeveau.pixeldragon.client
// Box (Modifier.fillMaxWidth()) {
// when (event.type) { @OptIn(ExperimentalCoroutinesApi::class)
// "m.room.message" -> Column( @Composable
// Modifier fun Room(modifier: Modifier = Modifier, rid: String) {
// .fillMaxSize() val roomId = remember(rid) { RoomId(rid) }
// .then( val limit = remember { MutableStateFlow(50) }
// if (event.sender != ME!!.userId)
// Modifier.padding(end = 16.dp) val eventsFlow = remember(roomId) {
// else client!!.room
// Modifier.padding(start = 16.dp) .getLastTimelineEvents(roomId)
// ) .toFlowList(limit)
// .padding(4.dp) .flatMapLatest { flows ->
// .background( if (flows.isEmpty()) flowOf(emptyList())
// if (event.sender != ME?.userId) else combine(flows) { it.toList() }
// MaterialTheme.colorScheme.surfaceContainer }
// else }
// MaterialTheme.colorScheme.primaryContainer val events by eventsFlow.collectAsState(initial = emptyList())
// )
// .clip(RoundedCornerShape(16.dp)) val listState = rememberLazyListState()
// .padding(4.dp)
// ) { Column(modifier) {
// Text(event.sender, maxLines = 1, fontWeight = FontWeight.Bold) var message by remember { mutableStateOf("") }
//
// when (val msgtype = event.content.optString("msgtype", null)) { LazyColumn(Modifier.weight(1f), state = listState, reverseLayout = true) {
// "m.text" -> when (event.content.optString("format")) { items(events, key = { it.eventId.full }) { event ->
// "org.matrix.custom.html" -> { EventItem(event)
// if (event.content.getString("body") == event.content.getString("formatted_body")) }
// Text(event.content.getString("body"))
// HtmlRenderer(event.content.getString("formatted_body")) if (events.isEmpty()) {
// } item {
// Text("Empty room", modifier = Modifier.padding(16.dp))
// else -> Text(event.content.getString("body")) }
// } }
// }
// "m.image" ->
// MXCImage(event.content.getString("url"), Modifier.fillMaxWidth(.9f)) Row (Modifier.fillMaxWidth()) {
// OutlinedTextField(
// null -> Text(event.content.toString(2)) modifier = Modifier
// .padding(4.dp)
// else -> Text("Unknown type: $msgtype", color = MaterialTheme.colorScheme.error) .weight(1f),
// } value = message,
// onValueChange = { message = it.trim() },
// } )
// IconButton(
// else -> Text(event.type, enabled = message.isNotBlank(),
// Modifier content = { Icon(Iconsax.Filled.ArrowRight, contentDescription = "Send") },
// .fillMaxHeight() onClick = {
// .padding(4.dp) CoroutineScope(Dispatchers.IO).launch {
// .background(MaterialTheme.colorScheme.errorContainer) client!!.room.sendMessage(RoomId(rid)) {
// .padding(4.dp) text(message)
// ) }
// } }
// } message = ""
//} }
// )
//private fun String.sanitizeHTML(): String { }
// val matrixSafelist = Safelist() }
// .addTags( }
// "h1", "h2", "h3", "h4", "h5", "h6",
// "b", "i", "u", "strong", "s", "del", @OptIn(ExperimentalMaterial3Api::class)
// "sup", "sub", "code", @Composable
// "table", "thead", "tbody", fun EventItem(event: TimelineEvent) {
// "tr", "th", "td", "ul", "ol", "li", val content = event.content?.getOrNull()
// "blockquote", "details", "summary", Box(Modifier
// "em", "code", "div", "pre", "span", "img" .fillMaxWidth()
// ) .heightIn(min = 24.dp)) {
//// .addAttributes("span", when {
//// "data-mx-bg-color", "data-mx-color", content == null -> Text("Not decrypted",
//// "data-mx-spoiler", "data-mx-maths" Modifier
//// ) .fillMaxHeight()
// .addAttributes("a", .padding(4.dp)
// "target", "href" .background(MaterialTheme.colorScheme.errorContainer)
// ) .padding(4.dp)
// .addAttributes("img", )
// "width", "height", "alt", "title", "src"
// ) content is RoomMessageEventContent -> {
// .addAttributes("ol", "start") val formatted = content.formattedBody
// .addAttributes("code", "class") if (formatted != null) {
// .addAttributes("div", "data-mx-maths") HtmlRenderer(
// htmlContent = formatted,
// val doc = Jsoup.parse(this) plainText = content.body
// doc.select("mx-reply").remove() )
// } else {
// val out = Jsoup.clean(doc.body().toString(), matrixSafelist) Text(
// return out text = content.body,
//} modifier = Modifier.padding(4.dp)
// )
// }
//@Composable }
//fun HtmlRenderer(
// htmlContent: String, event.event is ClientEvent.RoomEvent.StateEvent -> Text(
// modifier: Modifier = Modifier when (content) {
//) { is AvatarEventContent -> "Avatar changed"
// val context = LocalContext.current is PowerLevelsEventContent -> "Permissions changed"
// val webView = remember { WebView(context).apply { is MemberEventContent -> "Membership changed"
// settings.apply { is CanonicalAliasEventContent -> "Canonical alias changed"
// javaScriptEnabled = false is TopicEventContent -> "Topic changed"
// loadWithOverviewMode = true is NameEventContent -> "Name changed"
// useWideViewPort = true is HistoryVisibilityEventContent -> "History visibility changed"
// } else -> content.toString()
// isVerticalScrollBarEnabled = false },
// setBackgroundColor(Color.Transparent.toArgb()) Modifier.fillMaxWidth(),
// } } textAlign = TextAlign.Center
// )
// val css = """
// body { else -> Text(content.toString(),
// font-family: -apple-system, sans-serif; Modifier
// font-size: 16px; .fillMaxHeight()
// line-height: 1.6; .padding(4.dp)
// color: ${colorToCss(MaterialTheme.colorScheme.onBackground)}; .background(MaterialTheme.colorScheme.errorContainer)
// margin: 0; .padding(4.dp)
// padding: 0; )
// } }
// h1, h2, h3, h4, h5, h6 { }
// font-weight: bold; }
// padding: 0;
// } private fun String.sanitizeHTML(): String {
// h1 { font-size: 24px; } val matrixSafelist = Safelist()
// h2 { font-size: 22px; } .addTags(
// h3 { font-size: 20px; } "h1", "h2", "h3", "h4", "h5", "h6",
// h4 { font-size: 18px; } "b", "i", "u", "strong", "s", "del",
// h5 { font-size: 16px; } "sup", "sub", "code",
// h6 { font-size: 14px; } "table", "thead", "tbody",
// a { "tr", "th", "td", "ul", "ol", "li",
// color: ${colorToCss(MaterialTheme.colorScheme.primary)}; "blockquote", "details", "summary",
// text-decoration: none; "em", "code", "div", "pre", "span", "img"
// } )
// img { .addAttributes("a", "target", "href")
// max-width: 100%; .addAttributes("img", "width", "height", "alt", "title", "src")
// height: auto; .addAttributes("ol", "start")
// display: block; .addAttributes("code", "class")
// margin: 12px 0; .addAttributes("div", "data-mx-maths")
// }
// table { val doc = Jsoup.parse(this)
// width: 100%; doc.select("mx-reply").remove()
// border-collapse: collapse; return Jsoup.clean(doc.body().toString(), matrixSafelist)
// margin: 16px 0; }
// }
// th, td {
// border: 1px solid ${colorToCss(MaterialTheme.colorScheme.outline)}; @SuppressLint("ClickableViewAccessibility")
// padding: 12px; @Composable
// text-align: left; fun HtmlRenderer(
// } htmlContent: String,
// th { modifier: Modifier = Modifier,
// background-color: ${colorToCss(MaterialTheme.colorScheme.surfaceVariant)}; plainText: String = ""
// font-weight: bold; ) {
// } val context = LocalContext.current
// blockquote { var isLoaded by remember(htmlContent) { mutableStateOf(false) }
// border-left: 4px solid ${colorToCss(MaterialTheme.colorScheme.primary)};
// padding-left: 16px; val webView = remember { WebView(context).apply {
// margin-left: 0; layoutParams = ViewGroup.LayoutParams(
// color: ${colorToCss(MaterialTheme.colorScheme.onSurfaceVariant)}; ViewGroup.LayoutParams.MATCH_PARENT,
// } ViewGroup.LayoutParams.WRAP_CONTENT
// pre { )
// background-color: ${colorToCss(MaterialTheme.colorScheme.surfaceVariant)}; settings.apply {
// padding: 16px; javaScriptEnabled = false
// overflow: auto; loadWithOverviewMode = true
// border-radius: 4px; useWideViewPort = true
// } layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
// code { }
// font-family: monospace; isVerticalScrollBarEnabled = false
// background-color: ${colorToCss(MaterialTheme.colorScheme.surfaceVariant)}; setBackgroundColor(Color.Transparent.toArgb())
// padding: 2px 4px; setOnTouchListener { _, e -> e.action == MotionEvent.ACTION_MOVE }
// border-radius: 4px; } }
// }
// hr { val css = """
// border: 0; body {
// height: 1px; font-family: -apple-system, sans-serif;
// background-color: ${colorToCss(MaterialTheme.colorScheme.outline)}; font-size: 16px;
// margin: 24px 0; line-height: 1.5;
// } color: ${colorToCss(MaterialTheme.colorScheme.onBackground)};
// ul, ol { margin: 0; padding: 2px 0;
// padding-left: 24px; word-wrap: break-word;
// margin: 12px 0; }
// } a { color: ${colorToCss(MaterialTheme.colorScheme.primary)}; text-decoration: none; }
// li { img { max-width: 100%; height: auto; display: block; margin: 8px 0; }
// margin-bottom: 8px; table { width: 100%; border-collapse: collapse; margin: 8px 0; }
// } th, td { border: 1px solid ${colorToCss(MaterialTheme.colorScheme.outline)}; padding: 8px; text-align: left; }
// details { blockquote { border-left: 4px solid ${colorToCss(MaterialTheme.colorScheme.primary)}; padding-left: 12px; margin: 8px 0; }
// margin: 12px 0; pre, code { background-color: ${colorToCss(MaterialTheme.colorScheme.surfaceVariant)}; padding: 2px 4px; border-radius: 4px; }
// } """.trimIndent()
// summary {
// font-weight: bold; LaunchedEffect(htmlContent) {
// cursor: pointer; isLoaded = false
// } webView.webViewClient = object : WebViewClient() {
// @media (prefers-color-scheme: dark) { override fun onPageFinished(view: WebView?, url: String?) {
// :root { isLoaded = true
// --border-color: ${colorToCss(MaterialTheme.colorScheme.outline)}; view?.requestLayout()
// } }
// } override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
// """.trimIndent() return try {
// context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(request.url.toString())))
// LaunchedEffect(htmlContent) { true
// webView.loadDataWithBaseURL( } catch (e: Exception) { false }
// null, }
// wrapHtml(htmlContent, css), }
// "text/html", webView.loadDataWithBaseURL(null, wrapHtml(htmlContent, css), "text/html", "UTF-8", null)
// "UTF-8", }
// null
// ) DisposableEffect(webView) {
// } onDispose { /* webView.destroy() */ }
// }
// DisposableEffect(webView) {
// onDispose { Box(
// webView.destroy() modifier = modifier
// } .fillMaxWidth()
// } .heightIn(min = 24.dp)
// .animateContentSize()
// AndroidView( ) {
// factory = { webView }, if (!isLoaded) {
// modifier = modifier, Text(
// update = { view -> text = plainText.ifBlank { " " },
// view.webViewClient = SafeWebViewClient(context) modifier = Modifier
// } .padding(vertical = 2.dp)
// ) .alpha(0.6f),
//} style = MaterialTheme.typography.bodyMedium
// )
//private class SafeWebViewClient( }
// private val context: Context
//) : WebViewClient() { AndroidView(
// override fun shouldOverrideUrlLoading( factory = { webView },
// view: WebView, modifier = Modifier
// request: WebResourceRequest .fillMaxWidth()
// ): Boolean { .alpha(if (isLoaded) 1f else 0f)
// val url = request.url.toString() )
// try { }
// // Открываем ссылки во внешнем браузере }
// context.startActivity(
// Intent(Intent.ACTION_VIEW, Uri.parse(url)) private fun wrapHtml(content: String, css: String): String {
// ) return """
// return true <!DOCTYPE html><html><head>
// } catch (e: Exception) { <meta name="viewport" content="width=device-width, initial-scale=1">
// // Обработка ошибок открытия ссылки <style>$css</style></head><body>${content.sanitizeHTML()}</body></html>
// return false """.trimIndent()
// } }
// }
//} private fun colorToCss(color: Color): String {
// return String.format("#%06X", 0xFFFFFF and color.toArgb())
//private fun wrapHtml(content: String, css: String): String { }
// return """
// <!DOCTYPE html>
// <html>
// <head>
// <meta name="viewport" content="width=device-width, initial-scale=1">
// <style>
// $css
// </style>
// </head>
// <body>
// ${content.sanitizeHTML()}
// </body>
// </html>
// """.trimIndent()
//}
//
//private fun colorToCss(color: Color): String {
// val argb = color.toArgb()
// return String.format("#%06X", 0xFFFFFF and argb)
//}
+10 -8
View File
@@ -1,18 +1,19 @@
[versions] [versions]
agp = "8.13.0" agp = "9.1.0"
coil = "3.1.0" coil = "3.4.0"
jsoup = "1.20.1" iconsaxCompose = "0.0.5"
jsoup = "1.22.1"
kotlin = "2.2.21" kotlin = "2.2.21"
coreKtx = "1.15.0" coreKtx = "1.15.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.2.1" junitVersion = "1.3.0"
espressoCore = "3.6.1" espressoCore = "3.7.0"
kotlinxSerializationJson = "1.7.3" kotlinxSerializationJson = "1.7.3"
ktor = "3.1.0" ktor = "3.4.1"
lifecycleRuntimeKtx = "2.8.7" lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.0" activityCompose = "1.10.0"
composeBom = "2025.02.00" composeBom = "2026.03.00"
navigationCompose = "2.8.8" navigationCompose = "2.9.7"
room = "2.6.1" room = "2.6.1"
splittiesFunPackAndroidBase = "3.0.0" splittiesFunPackAndroidBase = "3.0.0"
trixnityClient = "5.2.0" trixnityClient = "5.2.0"
@@ -29,6 +30,7 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" } coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
iconsax-compose = { module = "io.github.rabehx:iconsax-compose", version.ref = "iconsaxCompose" }
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+1 -1
View File
@@ -6,6 +6,6 @@
#Thu Feb 20 10:45:47 GMT 2025 #Thu Feb 20 10:45:47 GMT 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+3
View File
@@ -16,6 +16,9 @@ pluginManagement {
gradlePluginPortal() gradlePluginPortal()
} }
} }
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {