From 442cdfdf2a44d3fbbeb549332a6ebe0e141b105f Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Wed, 27 Mar 2024 21:19:52 +0900 Subject: [PATCH 1/3] move MetaTagsParser to common module --- .../amethyst/service/previews/UrlPreviewUtils.kt | 2 ++ .../amethyst/commons/preview}/MetaTagsParserTest.kt | 2 +- .../amethyst/commons/preview}/MetaTagsParser.kt | 13 +++++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) rename {app/src/test/java/com/vitorpamplona/amethyst/service/previews => commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview}/MetaTagsParserTest.kt (98%) rename {app/src/main/java/com/vitorpamplona/amethyst/service/previews => commons/src/main/java/com/vitorpamplona/amethyst/commons/preview}/MetaTagsParser.kt (96%) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt index 71553b77b..ea2425fc3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/previews/UrlPreviewUtils.kt @@ -20,6 +20,8 @@ */ package com.vitorpamplona.amethyst.service.previews +import com.vitorpamplona.amethyst.commons.preview.MetaTag +import com.vitorpamplona.amethyst.commons.preview.MetaTagsParser import com.vitorpamplona.amethyst.service.HttpClientManager import com.vitorpamplona.amethyst.service.checkNotInMainThread import kotlinx.coroutines.Dispatchers diff --git a/app/src/test/java/com/vitorpamplona/amethyst/service/previews/MetaTagsParserTest.kt b/commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParserTest.kt similarity index 98% rename from app/src/test/java/com/vitorpamplona/amethyst/service/previews/MetaTagsParserTest.kt rename to commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParserTest.kt index ef5cc8311..ddf88e065 100644 --- a/app/src/test/java/com/vitorpamplona/amethyst/service/previews/MetaTagsParserTest.kt +++ b/commons/src/androidTest/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParserTest.kt @@ -18,7 +18,7 @@ * 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.service.previews +package com.vitorpamplona.amethyst.commons.preview import org.junit.Assert.assertEquals import org.junit.Test diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/MetaTagsParser.kt b/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt similarity index 96% rename from app/src/main/java/com/vitorpamplona/amethyst/service/previews/MetaTagsParser.kt rename to commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt index 5245e92c0..093ba98e6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/previews/MetaTagsParser.kt +++ b/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt @@ -18,20 +18,25 @@ * 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.service.previews +package com.vitorpamplona.amethyst.commons.preview import kotlinx.collections.immutable.toImmutableMap import java.lang.StringBuilder -internal data class MetaTag(private val attrs: Map) { +data class MetaTag(private val attrs: Map) { + /** + * Returns a value of an attribute specified by its name (case insensitive), or empty string if it doesn't exist. + */ fun attr(name: String): String = attrs[name.lowercase()] ?: "" } -// parse a partial HTML document and extract meta tags -internal object MetaTagsParser { +object MetaTagsParser { private val NON_ATTR_NAME_CHARS = setOf(Char(0x0), '"', '\'', '>', '/') private val NON_UNQUOTED_ATTR_VALUE_CHARS = setOf('"', '\'', '=', '>', '<', '`') + /** + * Lazily parse a partial HTML document and extract meta tags. + */ fun parse(input: String): Sequence = sequence { val s = TagScanner(input) From fc6f4600632b826d332e16b93201a5bfe7c2813b Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Wed, 27 Mar 2024 22:52:16 +0900 Subject: [PATCH 2/3] fix contact link in relay information dialog --- .../benchmark/MetaTagsParserBenchmark.kt | 55 + .../androidTest/assets/github_amethyst.html | 7868 +++++++++++++++++ 2 files changed, 7923 insertions(+) create mode 100644 benchmark/src/androidTest/java/com/vitorpamplona/amethyst/benchmark/MetaTagsParserBenchmark.kt create mode 100644 quartz/src/androidTest/assets/github_amethyst.html diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/amethyst/benchmark/MetaTagsParserBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/amethyst/benchmark/MetaTagsParserBenchmark.kt new file mode 100644 index 000000000..53df80577 --- /dev/null +++ b/benchmark/src/androidTest/java/com/vitorpamplona/amethyst/benchmark/MetaTagsParserBenchmark.kt @@ -0,0 +1,55 @@ +/** + * 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.benchmark + +import androidx.benchmark.junit4.BenchmarkRule +import androidx.benchmark.junit4.measureRepeated +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.vitorpamplona.amethyst.commons.preview.MetaTagsParser +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.nio.charset.Charset + +@RunWith(AndroidJUnit4::class) +class MetaTagsParserBenchmark { + private val html = + getInstrumentation().context.assets.open("github_amethyst.html") + .readBytes().toString(Charset.forName("utf-8")) + + @get:Rule + val benchmarkRule = BenchmarkRule() + + @Test + fun parseMetaTags() { + benchmarkRule.measureRepeated { + val metaOgTitle = MetaTagsParser.parse(html).find { it.attr("property") == "og:title" } + assertNotNull(metaOgTitle) + assertEquals( + "GitHub - vitorpamplona/amethyst: Nostr client for Android", + metaOgTitle!!.attr("content"), + ) + } + } +} diff --git a/quartz/src/androidTest/assets/github_amethyst.html b/quartz/src/androidTest/assets/github_amethyst.html new file mode 100644 index 000000000..0b65f48f5 --- /dev/null +++ b/quartz/src/androidTest/assets/github_amethyst.html @@ -0,0 +1,7868 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GitHub - vitorpamplona/amethyst: Nostr client for Android + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ Skip + to content + + + + + + + + + + + + +
+
+ + + + + + + +
+ +
+ + +
+ + + +
+ + + + + +
+
+
+ + + + + + +
+ + +

vitorpamplona/amethyst

+
+
+ + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + +
+ + +
+
+
+
+ + +
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+ + +

Folders and + files

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Name + +
+ Last commit message +
+
+
+ Last commit date +
+
+
+

+ Latest commit

+
+   +
+
+
+

+ History

4,428 Commits +
+ +
+
+
+
+ +
+

+
+ .github +
+

+
+
+
+
+ +
+

+
+ .github +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ .idea +
+

+
+
+
+
+ +
+

+
+ .idea +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ app +
+

+
+
+
+
+ +
+

+
+ app +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ benchmark +
+

+
+
+
+
+ +
+

+
+ benchmark +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ commons +
+

+
+
+
+
+ +
+

+
+ commons +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ docs +
+

+
+
+
+
+ +
+

+
+ docs +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ + +
+
+
+ + +
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ git-hooks +
+

+
+
+
+
+ +
+

+
+ git-hooks +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ gradle +
+

+
+
+
+
+ +
+

+
+ gradle +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ quartz +
+

+
+
+
+
+ +
+

+
+ quartz +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ spotless +
+

+
+
+
+
+ +
+

+
+ spotless +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ LICENSE +
+

+
+
+
+
+ +
+

+
+ LICENSE +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ README.md +
+

+
+
+
+
+ +
+

+
+ README.md +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+
+ gradlew +
+

+
+
+
+
+ +
+

+
+ gradlew +
+

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+

+ +

+
+
+
+
+ +
+

+ +

+
+
+
+
+   +
+
+
+   +
+
+
+ +
+
+
+
+
+
+

+ Repository files + navigation

+ + +
+
+
+
+ + Amethyst Logo + +

+ Amethyst

+ +
+

+ Nostr + Client for + Android

+ +
+

Join the + social network you + control.

+

GitHub downloads + PlayStore downloads +

+

Last Version + JitPack version + CI + License: Apache-2.0 +

+

+ Download and + Install

+ +
+

Get it on Obtaininum + Get it on GitHub + Get it on F-Droid + Get it on Google Play +

+
+

+ Supported + Features

+ +
+

+

+
    +
  • + + Events / Relay + Subscriptions + (NIP-01) +
  • +
  • + + Follow List (NIP-02) +
  • +
  • + + OpenTimestamps + Attestations + (NIP-03) +
  • +
  • + + Private Messages + (NIP-04) +
  • +
  • + + DNS Address (NIP-05) +
  • +
  • + + Mnemonic seed phrase + (NIP-06) +
  • +
  • + + WebBrowser Signer + (NIP-07, Not + applicable) +
  • +
  • + + Old-style mentions + (NIP-08) +
  • +
  • + + Event Deletion + (NIP-09) +
  • +
  • + + Replies, mentions, + Threads, and + Notifications + (NIP-10) +
  • +
  • + + Relay Information + Document (NIP-11) +
  • +
  • + + Generic Tag Queries + (NIP-12) +
  • +
  • + + Proof of Work + Display (NIP-13) +
  • +
  • + + Proof of Work + Calculations + (NIP-13) +
  • +
  • + + Events with a + Subject (NIP-14) +
  • +
  • + + Marketplace (NIP-15) +
  • +
  • + + Event Treatment + (NIP-16) +
  • +
  • + + Image/Video/Url/LnInvoice + Previews +
  • +
  • + + Reposts, Quotes, + Generic Reposts + (NIP-18) +
  • +
  • + + Bech Encoding + support (NIP-19) +
  • +
  • + + Command Results + (NIP-20) +
  • +
  • + + URI Support (NIP-21) +
  • +
  • + + Long-form Content + (NIP-23) +
  • +
  • + + User Profile Fields + / Relay list + (NIP-24) +
  • +
  • + + Reactions (NIP-25) +
  • +
  • + + Delegated Event + Signing (NIP-26, + Will not implement) +
  • +
  • + + Text Note References + (NIP-27) +
  • +
  • + + Public Chats + (NIP-28) +
  • +
  • + + Custom Emoji + (NIP-30) +
  • +
  • + + Event kind summaries + (NIP-31) +
  • +
  • + + Labeling (NIP-32) +
  • +
  • + + Parameterized + Replaceable Events + (NIP-33) +
  • +
  • + + Git Stuff + (NIP-34/Draft) +
  • +
  • + + Sensitive Content + (NIP-36) +
  • +
  • + + Note Edits + (NIP-37/Draft) +
  • +
  • + + User Status Event + (NIP-38) +
  • +
  • + + External Identities + (NIP-39) +
  • +
  • + + Expiration Support + (NIP-40) +
  • +
  • + + Relay Authentication + (NIP-42) +
  • +
  • + + Event Counts + (NIP-45, Will not + implement) +
  • +
  • + + Nostr Connect + (NIP-46) +
  • +
  • + + Wallet Connect API + (NIP-47) +
  • +
  • + + Proxy Tags (NIP-48, + Not applicable) +
  • +
  • + + Private key + encryption for + import/export + (NIP-49) +
  • +
  • + + Online Relay Search + (NIP-50) +
  • +
  • + + Lists (NIP-51) +
  • +
  • + + Calendar Events + (NIP-52) +
  • +
  • + + Live Activities + & Live Chats + (NIP-53) +
  • +
  • + + Inline Metadata + (NIP-55 - Draft) +
  • +
  • + + Reporting (NIP-56) +
  • +
  • + + Lightning Tips +
  • +
  • + + Zaps (NIP-57) +
  • +
  • + + Private Zaps +
  • +
  • + + Zap Splits (NIP-57) +
  • +
  • + + Gift Wraps & + Seals (NIP-59) +
  • +
  • + + Zapraiser (NIP-TBD) +
  • +
  • + + Badges (NIP-58) +
  • +
  • + + Relay List Metadata + (NIP-65) +
  • +
  • + + Polls (NIP-69) +
  • +
  • + + Moderated + Communities (NIP-72) +
  • +
  • + + Zap Goals (NIP-75) +
  • +
  • + + Arbitrary Custom App + Data (NIP-78) +
  • +
  • + + Highlights (NIP-84) +
  • +
  • + + Recommended + Application Handlers + (NIP-89) +
  • +
  • + + Data Vending Machine + (NIP-90) +
  • +
  • + + Inline Metadata + (NIP-92) +
  • +
  • + + Verifiable file URLs + (NIP-94) +
  • +
  • + + Binary Blobs + (NIP-95) +
  • +
  • + + HTTP File Storage + Integration (NIP-96 + Draft) +
  • +
  • + + HTTP Auth (NIP-98) +
  • +
  • + + Classifieds (NIP-99) +
  • +
  • + + Private Messages and + Small Groups + (NIP-24/Draft) +
  • +
  • + + Versioned Encrypted + Payloads + (NIP-44/Draft) +
  • +
  • + + Audio Tracks + (zapstr.live) + (kind:31337) +
  • +
  • + + Push Notifications + (Google and Unified + Push) +
  • +
  • + + In-Device Automatic + Translations +
  • +
  • + + Hashtag Following + and Custom Hashtags +
  • +
  • + + Login with QR +
  • +
  • + + Bounty support + (nostrbounties.com) +
  • +
  • + + De-googled F-Droid + flavor +
  • +
  • + + Multiple Accounts +
  • +
  • + + Markdown Support +
  • +
  • + + FHIR Payloads + (kind:82) +
  • +
  • + + Decentralized Wiki + (kind:30818) +
  • +
  • + + Embed events +
  • +
  • + + Image/Video Capture + in the app +
  • +
  • + + Local Database +
  • +
  • + + Workspaces +
  • +
  • + + Infinity Scroll +
  • +
+

+ Privacy + and Information + Permanence

+ +
+

Relays know + your IP address, your + name, your location + (guessed from IP), your + pub key, all your + contacts, and other + relays, and can read + every action you do + (post, like, boost, + quote, report, etc) + except for Private Zaps + and Private DMs. While + the content of direct + messages (DMs) is only + visible to you and your + DM counterparty, + everyone can see when + you and your + counterparty DM each + other.

+

If you want to + improve your privacy, + consider utilizing a + service that masks your + IP address (e.g. a VPN + or Tor) from trackers + online.

+

The relay also + learns which public keys + you are requesting, + meaning your public key + will be tied to your IP + address.

+

Information + shared on Nostr can be + re-broadcasted to other + servers and should be + assumed permanent for + privacy purposes. There + is no way to guarantee + the deletion of any + content once posted.

+

+ Development + Overview

+ +
+

This + repository is split + between Amethyst and + Quartz:

+
    +
  • Amethyst is a native + Android app made + with Kotlin and + Jetpack Compose. +
  • +
  • Quartz is our own + Nostr-commons + library to host + classes that are of + interest to other + Nostr Clients. +
  • +
+

The app + architecture consists of + the UI, which uses the + usual + State/ViewModel/Composition, + the service layer that + connects with Nostr + relays, + and the model/repository + layer, which keeps all + Nostr objects in memory, + in a full OO graph.

+

The repository + layer stores Nostr + Events as Notes and + Users separately. Those + classes use LiveData and + Flow objects to + allow the UI and other + parts of the app to + subscribe to each + Note/User and receive + updates when they + happen. + They are also + responsible for updating + viewModels when needed. + As the user scrolls + through Events, the + Datasource classes + are updated to receive + more information about + those particular + Events.

+

Most of the UI + is reactive to changes + in the repository + classes. The service + layer assembles Nostr + filters for each need of + the app, + receives the data from + the Relay, and sends it + to the repository. + Connection with relays + is never closed during + the use of the app. + The UI receives a + notification that + objects have been + updated. Instances of + User and Notes are + mutable directly. + There will never be two + Notes with the same ID + or two User instances + with the same + pubkey.

+

Lastly, the + user's account + information (private + key/pub key) is stored + in the Android KeyStore + for security.

+

+ Setup

+ +
+

Make sure to + have the following + pre-requisites + installed:

+
    +
  1. Java 17+
  2. +
  3. Android Studio
  4. +
  5. Android 8.0+ Phone + or Emulation setup +
  6. +
+

Fork and clone + this repository and + import it into Android + Studio

+
+
git clone https://github.com/vitorpamplona/amethyst.git
+
+

Use an Android + Studio build action to + install and run the app + on your device or a + simulator.

+

+ Building

+ +
+

Build the + app:

+
+
./gradlew assembleDebug
+
+

+ Testing

+ +
+
./gradlew test
+./gradlew connectedAndroidTest
+
+

+ Linting

+ +
+
./gradlew spotlessCheck
+./gradlew spotlessApply
+
+

+ Installing on + device

+ +
+

For the + F-Droid build:

+
+
./gradlew installFdroidDebug
+
+

For the Play + build:

+
+
./gradlew installPlayDebug
+
+

+ Deploying

+ +
+
    +
  1. Generate a new + signing key +
  2. +
+
keytool -genkey -v -keystore <my-release-key.keystore> -alias <alias_name> -keyalg RSA -keysize 2048 -validity 10000
+openssl base64 < <my-release-key.keystore> | tr -d '\n' | tee some_signing_key.jks.base64.txt
+
+
+
    +
  1. Create four Secret + Key variables on + your GitHub + repository and fill + in the signing key + information +
      +
    • KEY_ALIAS + <- <alias_name> +
    • +
    • KEY_PASSWORD + <- <your + password> +
    • +
    • KEY_STORE_PASSWORD + <- <your + key + store + password> +
    • +
    • SIGNING_KEY + <- the + data from + <my-release-key.keystore> +
    • +
    +
  2. +
  3. Change the versionCode + and versionName + on app/build.gradle +
  4. +
  5. Commit and push. +
  6. +
  7. Tag the commit with + v{x.x.x} +
  8. +
  9. Let the Create + Release GitHub + Action build a + new aab + file. +
  10. +
  11. Add your CHANGE LOG + to the description + of the new release +
  12. +
  13. Download the aab + file and upload it + to the PlayStore. +
  14. +
+

Using + the + Quartz library

+ +
+

Setup JitPack.io + to your build file

+

Add maven + { url + 'https://jitpack.io' + } to + settings.gradle at the + end of repositories:

+
dependencyResolutionManagement {
+  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+  repositories {
+    mavenCentral()
+    maven { url 'https://jitpack.io' }
+  }
+}
+
+

Add the + dependency

+
+
implementation('com.github.vitorpamplona.amethyst:quartz:v0.85.1')
+
+

+ Contributing

+ +
+

Issues can be + logged on: https://gitworkshop.dev/repo/amethyst +

+

GitHub + issues and pull + requests here are + also welcome. + Translations can be + provided via Crowdin +

+

You can also + send patches through + Nostr using GitStr + to + this + nostr address +

+

By + contributing to this + repository, you agree to + license your work under + the MIT license. Any + work contributed where + you are not the original + author must contain its + license header with the + original author(s) and + source.

+

+ Screenshots

+ +
+ + + + + + + + + + + + + + + + + +
FollowFeedsChatsGroupLiveStreamsNotifications +
+ Home Feed + + Messages + + Live Streams + + Notifications +
+

+ Contributors

+ +
+ + + +

MIT + License

+ +
+
Copyright (c) 2023 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.
+
+
+
+
+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+

About

+ +

+ Nostr client for Android +

+ +

Topics

+ + +

Resources

+ + + +

License

+ + + + + + + + + +

Stars

+ + +

Watchers

+ + +

Forks

+ + + +
+ +
+
+ + + + + +
+
+

+ + Packages + +

+ + +
+ No packages published
+
+ + +
+
+ + + + + +
+
+

+ + Contributors + 58 +

+ + + + + + +
+
+ + +
+
+

Languages

+
+ + + +
+ + +
+
+ +
+
+ +
+
+ +
+ + +
+ +
+ + +
+
+ +
+ +
+

Footer

+ + +
+
+ + + + + © 2024 GitHub, Inc. + +
+ + +
+
+ + + + + + + + + + + + + + + +
+ +
+
+ + From 6eb2fbfa2fd8486b3fb7b92fbe421449384d18fd Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Wed, 27 Mar 2024 22:54:06 +0900 Subject: [PATCH 3/3] reduce creation of StringBuffers in meta tags parsing --- .../commons/preview/MetaTagsParser.kt | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt b/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt index 093ba98e6..75b9e7325 100644 --- a/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt +++ b/commons/src/main/java/com/vitorpamplona/amethyst/commons/preview/MetaTagsParser.kt @@ -21,7 +21,6 @@ package com.vitorpamplona.amethyst.commons.preview import kotlinx.collections.immutable.toImmutableMap -import java.lang.StringBuilder data class MetaTag(private val attrs: Map) { /** @@ -42,7 +41,7 @@ object MetaTagsParser { val s = TagScanner(input) while (!s.exhausted()) { val t = s.nextTag() ?: continue - if (t.name == "/head") { + if (t.name == "head" && t.isEnd) { break } if (t.name == "meta") { @@ -52,61 +51,45 @@ object MetaTagsParser { } } - private data class RawTag(val name: String, val attrPart: String) + private data class RawTag(val isEnd: Boolean, val name: String, val attrPart: String) private class TagScanner(private val input: String) { - var p = 0 + private var p = 0 fun exhausted(): Boolean = p >= input.length private fun peek(): Char = input[p] - private fun consume(): Char { - return input[p++] - } + private fun consume(): Char = input[p++] - private fun consumeChar(c: Char): Boolean { - if (this.peek() == c) { + private fun skipWhile(pred: (Char) -> Boolean) { + while (!this.exhausted() && pred(this.peek())) { this.consume() - return true } - return false } private fun skipSpaces() { - while (!this.exhausted() && this.peek().isWhitespace()) { - this.consume() - } - } - - private fun skipUntil(c: Char) { - while (!this.exhausted() && this.peek() != c) { - this.consume() - } - } - - private fun readWhile(pred: (Char) -> Boolean): String { - val sb = StringBuilder() - while (!this.exhausted() && pred(this.peek())) { - sb.append(this.consume()) - } - return sb.toString() + this.skipWhile { it.isWhitespace() } } fun nextTag(): RawTag? { - skipUntil('<') + skipWhile { it != '<' } consume() // read tag name - val name = StringBuilder() - if (consumeChar('/')) { - name.append('/') + val isEnd = peek() == '/' + if (isEnd) { + consume() } - val n = readWhile { !it.isWhitespace() && it != '>' } - skipSpaces() + val nameStart = p + skipWhile { !it.isWhitespace() && it != '>' } + val nameEnd = p - // read until end of tag - val attrsPart = StringBuilder() + // seek to start of attrs part + skipSpaces() + val attrsStart = p + + // skip until end of tag var quote: Char? = null while (!exhausted()) { val c = consume() @@ -129,13 +112,15 @@ object MetaTagsParser { quote = null } } - attrsPart.append(c) } + val attrsEnd = p - 1 - if (!n.matches(Regex("""[0-9a-zA-Z]+"""))) { + val name = input.slice(nameStart..