- Refactoring services to be part of the App Lifecycle,

- Migrated services to Flows that are active while their flows remain subscribed
- Improved OTS Decoupling
- Moved OTS verification procedures from the local cache to the data source
- Migrated the forceProxy options to lambdas that return an OkHttpClient
- Isolated Connectivity services, from Compose to Flow
- Isolated Tor services, from Compose to Tor (only starts if the Tor option is marked as internal)
- Isolated Memory trimming services, from Compose to Flow
- Isolated Image Caching services, from Compose to Flow
- Isolated Video Caching services
- Isolated Logging services
- Isolated NIP-95 Caching services
- Isolated Pokey receiver services
- Improved support for Tor in push notifications
- Isolated OkHttpClient services as flows
- Reduced the coupling of Context objects with singleton objects.
- Migrated UserFeedStates, StringFeedStates and ZapFeedStates to MutableStateFlow, avoiding feed updates on Main
- Forces a reconnect into relays that lost connection if any tor, connectivity or account changes
- Speeds up Base64 parser
This commit is contained in:
Vitor Pamplona
2025-04-09 18:51:17 -04:00
parent 652373bd01
commit 3833f5f822
111 changed files with 1970 additions and 1063 deletions
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.commons.base64Image
import android.R.attr.data
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import java.util.Base64
import java.util.regex.Pattern
class Base64Image {
companion object {
val pattern = Pattern.compile("data:image/(${RichTextParser.Companion.imageExtensions.joinToString(separator = "|") { it } });base64,([a-zA-Z0-9+/]+={0,2})")
fun isBase64(content: String): Boolean {
val matcher = pattern.matcher(content)
return matcher.find()
}
fun toBitmap(content: String): Bitmap {
val matcher = pattern.matcher(data.toString())
if (matcher.find()) {
val base64String = matcher.group(2)
val byteArray = Base64.getDecoder().decode(base64String)
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
}
throw Exception("Unable to convert base64 to image $content")
}
}
}
@@ -24,6 +24,7 @@ import android.util.Log
import android.util.Patterns
import com.linkedin.urls.detection.UrlDetector
import com.linkedin.urls.detection.UrlDetectorOptions
import com.vitorpamplona.amethyst.commons.base64Image.Base64Image
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
import com.vitorpamplona.quartz.experimental.inlineMetadata.Nip54InlineMetadata
import com.vitorpamplona.quartz.nip02FollowList.ImmutableListOfLists
@@ -103,11 +104,6 @@ class RichTextParser {
}
}
private fun checkBase64(content: String): Boolean {
val matcher = base64contentPattern.matcher(content)
return matcher.find()
}
fun parseValidUrls(content: String): LinkedHashSet<String> {
val urls = UrlDetector(content, UrlDetectorOptions.Default).detect()
@@ -239,9 +235,7 @@ class RichTextParser {
): Segment {
if (word.isEmpty()) return RegularTextSegment(word)
if (word.startsWith("data:image/")) {
if (checkBase64(word)) return Base64Segment(word)
}
if (word.startsWith("data:image/") && Base64Image.isBase64(word)) return Base64Segment(word)
if (images.contains(word)) return ImageSegment(word)
@@ -361,8 +355,6 @@ class RichTextParser {
val imageExtensions = imageExt + imageExt.map { it.uppercase() }
val videoExtensions = videoExt + videoExt.map { it.uppercase() }
val base64contentPattern = Pattern.compile("data:image/(${imageExtensions.joinToString(separator = "|") { it } });base64,([a-zA-Z0-9+/]+={0,2})")
val tagIndex = Pattern.compile("\\#\\[([0-9]+)\\](.*)")
val hashTagsPattern: Pattern =
Pattern.compile("#([^\\s!@#\$%^&*()=+./,\\[{\\]};:'\"?><]+)(.*)", Pattern.CASE_INSENSITIVE)