mirror of
https://github.com/greenart7c3/Amber.git
synced 2026-10-05 19:08:23 +00:00
code cleanup
This commit is contained in:
+9
@@ -3,30 +3,39 @@
|
|||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".nostrsigner"
|
android:name=".Nostrsigner"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:configChanges="orientation|screenSize|screenLayout"
|
android:configChanges="orientation|screenSize|screenLayout"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import androidx.security.crypto.MasterKey
|
|||||||
object EncryptedStorage {
|
object EncryptedStorage {
|
||||||
private const val PREFERENCES_NAME = "secret_keeper"
|
private const val PREFERENCES_NAME = "secret_keeper"
|
||||||
|
|
||||||
fun prefsFileName(npub: String? = null): String {
|
private fun prefsFileName(npub: String? = null): String {
|
||||||
return if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
|
return if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun preferences(npub: String? = null): EncryptedSharedPreferences {
|
fun preferences(npub: String? = null): EncryptedSharedPreferences {
|
||||||
val context = nostrsigner.instance
|
val context = Nostrsigner.instance
|
||||||
val masterKey: MasterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
val masterKey: MasterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
||||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ object LocalPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val prefsDirPath: String
|
private val prefsDirPath: String
|
||||||
get() = "${nostrsigner.instance.filesDir.parent}/shared_prefs/"
|
get() = "${Nostrsigner.instance.filesDir.parent}/shared_prefs/"
|
||||||
|
|
||||||
private fun addAccount(npub: String) {
|
private fun addAccount(npub: String) {
|
||||||
val accounts = savedAccounts().toMutableList()
|
val accounts = savedAccounts().toMutableList()
|
||||||
@@ -135,7 +135,7 @@ object LocalPreferences {
|
|||||||
private fun encryptedPreferences(npub: String? = null): SharedPreferences {
|
private fun encryptedPreferences(npub: String? = null): SharedPreferences {
|
||||||
return if (BuildConfig.DEBUG && DEBUG_PLAINTEXT_PREFERENCES) {
|
return if (BuildConfig.DEBUG && DEBUG_PLAINTEXT_PREFERENCES) {
|
||||||
val preferenceFile = if (npub == null) DEBUG_PREFERENCES_NAME else "${DEBUG_PREFERENCES_NAME}_$npub"
|
val preferenceFile = if (npub == null) DEBUG_PREFERENCES_NAME else "${DEBUG_PREFERENCES_NAME}_$npub"
|
||||||
nostrsigner.instance.getSharedPreferences(preferenceFile, Context.MODE_PRIVATE)
|
Nostrsigner.instance.getSharedPreferences(preferenceFile, Context.MODE_PRIVATE)
|
||||||
} else {
|
} else {
|
||||||
return EncryptedStorage.preferences(npub)
|
return EncryptedStorage.preferences(npub)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -2,14 +2,14 @@ package com.greenart7c3.nostrsigner
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
|
||||||
class nostrsigner : Application() {
|
class Nostrsigner : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: nostrsigner
|
lateinit var instance: Nostrsigner
|
||||||
private set
|
private set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,6 @@ import com.vitorpamplona.quartz.crypto.KeyPair
|
|||||||
import com.vitorpamplona.quartz.signers.NostrSigner
|
import com.vitorpamplona.quartz.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
||||||
|
|
||||||
class History(
|
|
||||||
val appName: String,
|
|
||||||
val type: String,
|
|
||||||
val time: Long,
|
|
||||||
val kind: Int?
|
|
||||||
)
|
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class Account(
|
class Account(
|
||||||
val keyPair: KeyPair,
|
val keyPair: KeyPair,
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.models
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
data class ImmutableListOfLists<T>(val lists: List<List<T>> = emptyList())
|
|
||||||
|
|
||||||
fun List<List<String>>.toImmutableListOfLists(): ImmutableListOfLists<String> {
|
|
||||||
return ImmutableListOfLists(this)
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,5 @@
|
|||||||
package com.greenart7c3.nostrsigner.models
|
package com.greenart7c3.nostrsigner.models
|
||||||
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Date
|
|
||||||
import java.util.Locale
|
|
||||||
import java.util.TimeZone
|
|
||||||
|
|
||||||
object TimeUtils {
|
object TimeUtils {
|
||||||
fun now() = System.currentTimeMillis() / 1000
|
fun now() = System.currentTimeMillis() / 1000
|
||||||
fun format(date: Long): String {
|
|
||||||
val sdf = SimpleDateFormat(
|
|
||||||
"dd/MM/yyyy HH:mm:ss",
|
|
||||||
Locale.getDefault()
|
|
||||||
)
|
|
||||||
sdf.timeZone = TimeZone.getDefault()
|
|
||||||
return sdf.format(Date(date * 1000))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,12 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
|
|||||||
import com.vitorpamplona.quartz.crypto.Nip44Version
|
import com.vitorpamplona.quartz.crypto.Nip44Version
|
||||||
import com.vitorpamplona.quartz.crypto.decodeNIP44
|
import com.vitorpamplona.quartz.crypto.decodeNIP44
|
||||||
import com.vitorpamplona.quartz.crypto.encodeNIP44
|
import com.vitorpamplona.quartz.crypto.encodeNIP44
|
||||||
import com.vitorpamplona.quartz.encoders.Bech32
|
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||||
import fr.acinq.secp256k1.Hex
|
import fr.acinq.secp256k1.Hex
|
||||||
import java.nio.charset.Charset
|
|
||||||
import java.security.SecureRandom
|
|
||||||
import javax.crypto.Cipher
|
|
||||||
import javax.crypto.spec.IvParameterSpec
|
|
||||||
import javax.crypto.spec.SecretKeySpec
|
|
||||||
|
|
||||||
object AmberUtils {
|
object AmberUtils {
|
||||||
fun encryptOrDecryptData(data: String, type: SignerType, account: Account, pubKey: HexKey): String? {
|
fun encryptOrDecryptData(data: String, type: SignerType, account: Account, pubKey: HexKey): String? {
|
||||||
@@ -124,25 +118,6 @@ object AmberUtils {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
|
||||||
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
|
||||||
val iv = ByteArray(16)
|
|
||||||
SecureRandom().nextBytes(iv)
|
|
||||||
|
|
||||||
val keySpec = SecretKeySpec(sharedSecret, "AES")
|
|
||||||
val ivSpec = IvParameterSpec(iv)
|
|
||||||
|
|
||||||
val utf8message = msg.toByteArray(Charset.forName("utf-8"))
|
|
||||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
|
|
||||||
val encryptedMsg = cipher.doFinal(utf8message)
|
|
||||||
|
|
||||||
val encryptedMsgBech32 = Bech32.encode("pzap", Bech32.eight2five(encryptedMsg), Bech32.Encoding.Bech32)
|
|
||||||
val ivBech32 = Bech32.encode("iv", Bech32.eight2five(iv), Bech32.Encoding.Bech32)
|
|
||||||
|
|
||||||
return encryptedMsgBech32 + "_" + ivBech32
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.toShortenHex(): String {
|
fun String.toShortenHex(): String {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ open class AmberEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val gson: Gson = GsonBuilder()
|
private val gson: Gson = GsonBuilder()
|
||||||
.disableHtmlEscaping()
|
.disableHtmlEscaping()
|
||||||
.registerTypeAdapter(AmberEvent::class.java, EventSerializer())
|
.registerTypeAdapter(AmberEvent::class.java, EventSerializer())
|
||||||
.registerTypeAdapter(AmberEvent::class.java, EventDeserializer())
|
.registerTypeAdapter(AmberEvent::class.java, EventDeserializer())
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.service.model
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.encoders.ATag
|
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
|
||||||
import com.vitorpamplona.quartz.events.EmojiUrl
|
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
interface AmberEventInterface {
|
|
||||||
fun id(): HexKey
|
|
||||||
|
|
||||||
fun pubKey(): HexKey
|
|
||||||
|
|
||||||
fun createdAt(): Long
|
|
||||||
|
|
||||||
fun kind(): Int
|
|
||||||
|
|
||||||
fun description(): String
|
|
||||||
|
|
||||||
fun tags(): Array<Array<String>>
|
|
||||||
|
|
||||||
fun content(): String
|
|
||||||
|
|
||||||
fun sig(): HexKey
|
|
||||||
|
|
||||||
fun toJson(): String
|
|
||||||
|
|
||||||
fun checkSignature()
|
|
||||||
|
|
||||||
fun hasValidSignature(): Boolean
|
|
||||||
|
|
||||||
fun isTaggedUser(idHex: String): Boolean
|
|
||||||
|
|
||||||
fun isTaggedEvent(idHex: String): Boolean
|
|
||||||
|
|
||||||
fun isTaggedAddressableNote(idHex: String): Boolean
|
|
||||||
fun isTaggedAddressableNotes(idHexes: Set<String>): Boolean
|
|
||||||
|
|
||||||
fun isTaggedHash(hashtag: String): Boolean
|
|
||||||
fun isTaggedGeoHash(hashtag: String): Boolean
|
|
||||||
|
|
||||||
fun isTaggedHashes(hashtags: Set<String>): Boolean
|
|
||||||
fun isTaggedGeoHashes(hashtags: Set<String>): Boolean
|
|
||||||
|
|
||||||
fun firstIsTaggedHashes(hashtags: Set<String>): String?
|
|
||||||
fun firstIsTaggedAddressableNote(addressableNotes: Set<String>): String?
|
|
||||||
|
|
||||||
fun isTaggedAddressableKind(kind: Int): Boolean
|
|
||||||
fun getTagOfAddressableKind(kind: Int): ATag?
|
|
||||||
|
|
||||||
fun hashtags(): List<String>
|
|
||||||
fun geohashes(): List<String>
|
|
||||||
|
|
||||||
fun getReward(): BigDecimal?
|
|
||||||
fun getPoWRank(): Int
|
|
||||||
fun getGeoHash(): String?
|
|
||||||
|
|
||||||
fun zapAddress(): String?
|
|
||||||
fun isSensitive(): Boolean
|
|
||||||
fun zapraiserAmount(): Long?
|
|
||||||
|
|
||||||
fun taggedAddresses(): List<ATag>
|
|
||||||
fun taggedUsers(): List<HexKey>
|
|
||||||
fun taggedEvents(): List<HexKey>
|
|
||||||
fun taggedUrls(): List<String>
|
|
||||||
|
|
||||||
fun taggedEmojis(): List<EmojiUrl>
|
|
||||||
fun matchTag1With(text: String): Boolean
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,6 @@ package com.greenart7c3.nostrsigner.ui
|
|||||||
import com.greenart7c3.nostrsigner.models.Account
|
import com.greenart7c3.nostrsigner.models.Account
|
||||||
|
|
||||||
sealed class AccountState {
|
sealed class AccountState {
|
||||||
object LoggedOff : AccountState()
|
data object LoggedOff : AccountState()
|
||||||
class LoggedIn(val account: Account, val route: String?) : AccountState()
|
class LoggedIn(val account: Account, val route: String?) : AccountState()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.app.Activity.RESULT_OK
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.format.DateUtils
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@@ -357,19 +356,3 @@ fun MainScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun timeAgoShort(mills: Long?, stringForNow: String): String {
|
|
||||||
if (mills == null) return " "
|
|
||||||
|
|
||||||
var humanReadable = DateUtils.getRelativeTimeSpanString(
|
|
||||||
mills * 1000,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
DateUtils.FORMAT_ABBREV_ALL
|
|
||||||
).toString()
|
|
||||||
if (humanReadable.startsWith("In") || humanReadable.startsWith("0")) {
|
|
||||||
humanReadable = stringForNow
|
|
||||||
}
|
|
||||||
|
|
||||||
return humanReadable
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.ui
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import com.greenart7c3.nostrsigner.models.Account
|
|
||||||
import com.greenart7c3.nostrsigner.ui.components.CloseButton
|
|
||||||
import com.greenart7c3.nostrsigner.ui.theme.Size35dp
|
|
||||||
import com.vitorpamplona.quartz.encoders.toNpub
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ShowQRDialog(account: Account, onClose: () -> Unit) {
|
|
||||||
Dialog(
|
|
||||||
onDismissRequest = onClose,
|
|
||||||
properties = DialogProperties(usePlatformDefaultWidth = false)
|
|
||||||
) {
|
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.background(MaterialTheme.colorScheme.background)
|
|
||||||
.fillMaxSize()
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(10.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
CloseButton(onCancel = onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(horizontal = 10.dp),
|
|
||||||
verticalArrangement = Arrangement.SpaceAround
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = Size35dp)
|
|
||||||
) {
|
|
||||||
QrCodeDrawer("nostr:${account.keyPair.pubKey.toNpub()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.ui.components
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AppTitle(appName: String) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
text = appName,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.ui.components
|
|
||||||
|
|
||||||
import androidx.activity.OnBackPressedCallback
|
|
||||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.SideEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
|
||||||
|
|
||||||
@SuppressWarnings("MissingJvmstatic")
|
|
||||||
@Composable
|
|
||||||
fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) {
|
|
||||||
// Safely update the current `onBack` lambda when a new one is provided
|
|
||||||
val currentOnBack by rememberUpdatedState(onBack)
|
|
||||||
// Remember in Composition a back callback that calls the `onBack` lambda
|
|
||||||
val backCallback = remember {
|
|
||||||
object : OnBackPressedCallback(enabled) {
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
currentOnBack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// On every successful composition, update the callback with the `enabled` value
|
|
||||||
SideEffect {
|
|
||||||
backCallback.isEnabled = enabled
|
|
||||||
}
|
|
||||||
val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) {
|
|
||||||
"No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner"
|
|
||||||
}.onBackPressedDispatcher
|
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
|
||||||
DisposableEffect(lifecycleOwner, backDispatcher) {
|
|
||||||
// Add callback to the backDispatcher
|
|
||||||
backDispatcher.addCallback(lifecycleOwner, backCallback)
|
|
||||||
// When the effect leaves the Composition, remove the callback
|
|
||||||
onDispose {
|
|
||||||
backCallback.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.ui.components
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.ClearAll
|
|
||||||
import androidx.compose.material.icons.filled.Key
|
|
||||||
import androidx.compose.material.icons.filled.QrCode
|
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.DrawerState
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import com.greenart7c3.nostrsigner.BuildConfig
|
|
||||||
import com.greenart7c3.nostrsigner.LocalPreferences
|
|
||||||
import com.greenart7c3.nostrsigner.R
|
|
||||||
import com.greenart7c3.nostrsigner.models.Account
|
|
||||||
import com.greenart7c3.nostrsigner.ui.AccountStateViewModel
|
|
||||||
import com.greenart7c3.nostrsigner.ui.ShowQRDialog
|
|
||||||
import com.greenart7c3.nostrsigner.ui.actions.AccountBackupDialog
|
|
||||||
import com.vitorpamplona.quartz.encoders.toNpub
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Drawer(
|
|
||||||
accountStateViewModel: AccountStateViewModel,
|
|
||||||
account: Account,
|
|
||||||
drawerState: DrawerState
|
|
||||||
) {
|
|
||||||
var logoutDialog by remember { mutableStateOf(false) }
|
|
||||||
var resetPermissions by remember { mutableStateOf(false) }
|
|
||||||
var backupDialogOpen by remember { mutableStateOf(false) }
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
if (logoutDialog) {
|
|
||||||
AlertDialog(
|
|
||||||
title = {
|
|
||||||
Text(text = stringResource(R.string.logout))
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(R.string.logging_out_deletes_all_your_local_information_make_sure_to_have_your_private_keys_backed_up_to_avoid_losing_your_account_do_you_want_to_continue))
|
|
||||||
},
|
|
||||||
onDismissRequest = {
|
|
||||||
logoutDialog = false
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
logoutDialog = false
|
|
||||||
accountStateViewModel.logOff(account.keyPair.pubKey.toNpub())
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.logout))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
logoutDialog = false
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.cancel))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetPermissions) {
|
|
||||||
AlertDialog(
|
|
||||||
title = {
|
|
||||||
Text(text = stringResource(R.string.reset_permissions))
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(R.string.do_you_want_to_reset_all_permissions))
|
|
||||||
},
|
|
||||||
onDismissRequest = {
|
|
||||||
resetPermissions = false
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
resetPermissions = false
|
|
||||||
scope.launch {
|
|
||||||
drawerState.close()
|
|
||||||
LocalPreferences.deleteSavedApps(account)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.confirm))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
resetPermissions = false
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.cancel))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
color = MaterialTheme.colorScheme.background
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
IconRow(
|
|
||||||
title = "Reset permissions",
|
|
||||||
icon = Icons.Default.ClearAll,
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground,
|
|
||||||
onClick = {
|
|
||||||
resetPermissions = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
IconRow(
|
|
||||||
title = "Backup Keys",
|
|
||||||
icon = Icons.Default.Key,
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground,
|
|
||||||
onClick = {
|
|
||||||
scope.launch {
|
|
||||||
drawerState.close()
|
|
||||||
}
|
|
||||||
backupDialogOpen = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
BottomContent(account, drawerState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (backupDialogOpen) {
|
|
||||||
AccountBackupDialog(account, onClose = { backupDialogOpen = false })
|
|
||||||
}
|
|
||||||
BackHandler(enabled = drawerState.isOpen) {
|
|
||||||
scope.launch { drawerState.close() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BottomContent(account: Account, drawerState: DrawerState) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
// store the dialog open or close state
|
|
||||||
var dialogOpen by remember {
|
|
||||||
mutableStateOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(modifier = Modifier) {
|
|
||||||
Divider(
|
|
||||||
modifier = Modifier.padding(top = 15.dp),
|
|
||||||
thickness = 0.25.dp
|
|
||||||
)
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 15.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(start = 16.dp),
|
|
||||||
text = "v" + BuildConfig.VERSION_NAME,
|
|
||||||
fontSize = 12.sp,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
|
|
||||||
Box(modifier = Modifier.weight(1F))
|
|
||||||
IconButton(onClick = {
|
|
||||||
dialogOpen = true
|
|
||||||
coroutineScope.launch {
|
|
||||||
drawerState.close()
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.QrCode,
|
|
||||||
null,
|
|
||||||
modifier = Modifier.size(24.dp),
|
|
||||||
tint = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dialogOpen) {
|
|
||||||
ShowQRDialog(
|
|
||||||
account,
|
|
||||||
onClose = { dialogOpen = false }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package com.greenart7c3.nostrsigner.ui.components
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun EventRow(
|
|
||||||
icon: ImageVector,
|
|
||||||
title: String,
|
|
||||||
content: String
|
|
||||||
) {
|
|
||||||
Divider(
|
|
||||||
modifier = Modifier.padding(vertical = 15.dp),
|
|
||||||
thickness = Dp.Hairline
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
|
||||||
Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.Start
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
null,
|
|
||||||
tint = MaterialTheme.colorScheme.onBackground
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.padding(start = 8.dp),
|
|
||||||
text = "$title ",
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
content,
|
|
||||||
color = Color.Gray,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -209,10 +209,10 @@ fun MultiEventHomeScreen(
|
|||||||
{ }
|
{ }
|
||||||
) {
|
) {
|
||||||
intentData.rememberMyChoice.value = !intentData.rememberMyChoice.value
|
intentData.rememberMyChoice.value = !intentData.rememberMyChoice.value
|
||||||
intents.filter { it ->
|
intents.filter { item ->
|
||||||
intentData.type == it.type
|
intentData.type == item.type
|
||||||
}.forEach { it ->
|
}.forEach { item ->
|
||||||
it.rememberMyChoice.value = intentData.rememberMyChoice.value
|
item.rememberMyChoice.value = intentData.rememberMyChoice.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package com.greenart7c3.nostrsigner.ui.navigation
|
package com.greenart7c3.nostrsigner.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.History
|
|
||||||
import androidx.compose.material.icons.filled.Home
|
import androidx.compose.material.icons.filled.Home
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material.icons.filled.ViewList
|
import androidx.compose.material.icons.filled.ViewList
|
||||||
import androidx.compose.material.icons.outlined.History
|
|
||||||
import androidx.compose.material.icons.outlined.Home
|
import androidx.compose.material.icons.outlined.Home
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material.icons.outlined.ViewList
|
import androidx.compose.material.icons.outlined.ViewList
|
||||||
@@ -41,10 +39,4 @@ sealed class Route(
|
|||||||
icon = Icons.Outlined.ViewList,
|
icon = Icons.Outlined.ViewList,
|
||||||
selectedIcon = Icons.Default.ViewList
|
selectedIcon = Icons.Default.ViewList
|
||||||
)
|
)
|
||||||
|
|
||||||
data object History : Route(
|
|
||||||
route = "History",
|
|
||||||
icon = Icons.Outlined.History,
|
|
||||||
selectedIcon = Icons.Default.History
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="purple_200">#FFBB86FC</color>
|
|
||||||
<color name="purple_500">#FF6200EE</color>
|
|
||||||
<color name="purple_700">#FF3700B3</color>
|
<color name="purple_700">#FF3700B3</color>
|
||||||
<color name="teal_200">#FF03DAC5</color>
|
|
||||||
<color name="teal_700">#FF018786</color>
|
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
|
||||||
<color name="amber">#FFBF00</color>
|
<color name="amber">#FFBF00</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -17,17 +17,13 @@
|
|||||||
<string name="copies_the_npub_id_your_user_to_the_clipboard_for_backup">Copies the Npub ID (your user) to the clipboard for backup</string>
|
<string name="copies_the_npub_id_your_user_to_the_clipboard_for_backup">Copies the Npub ID (your user) to the clipboard for backup</string>
|
||||||
<string name="copy_my_public_key">Copy my public key</string>
|
<string name="copy_my_public_key">Copy my public key</string>
|
||||||
<string name="event_pubkey_is_not_equal_to_current_logged_in_user">Event pubkey is not equal to current logged in user</string>
|
<string name="event_pubkey_is_not_equal_to_current_logged_in_user">Event pubkey is not equal to current logged in user</string>
|
||||||
<string name="scan_qr">Scan QR</string>
|
|
||||||
<string name="signature_copied_to_the_clipboard">Signature copied to the clipboard</string>
|
<string name="signature_copied_to_the_clipboard">Signature copied to the clipboard</string>
|
||||||
<string name="sign">Sign</string>
|
|
||||||
<string name="raw_json_copied_to_the_clipboard"> copied to the clipboard</string>
|
|
||||||
<string name="reset_permissions">Reset permissions</string>
|
<string name="reset_permissions">Reset permissions</string>
|
||||||
<string name="logout">Logout</string>
|
<string name="logout">Logout</string>
|
||||||
<string name="logging_out_deletes_all_your_local_information_make_sure_to_have_your_private_keys_backed_up_to_avoid_losing_your_account_do_you_want_to_continue">Logging out deletes all your local information. Make sure to have your private keys backed up to avoid losing your account. Do you want to continue?</string>
|
<string name="logging_out_deletes_all_your_local_information_make_sure_to_have_your_private_keys_backed_up_to_avoid_losing_your_account_do_you_want_to_continue">Logging out deletes all your local information. Make sure to have your private keys backed up to avoid losing your account. Do you want to continue?</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="do_you_want_to_reset_all_permissions">Do you want to reset all permissions?</string>
|
<string name="do_you_want_to_reset_all_permissions">Do you want to reset all permissions?</string>
|
||||||
<string name="confirm">Confirm</string>
|
<string name="confirm">Confirm</string>
|
||||||
<string name="could_not_decrypt_the_message">Could not decrypt the message</string>
|
|
||||||
<string name="nsec">nsec..</string>
|
<string name="nsec">nsec..</string>
|
||||||
<string name="show_password">Show password</string>
|
<string name="show_password">Show password</string>
|
||||||
<string name="hide_password">Hide password</string>
|
<string name="hide_password">Hide password</string>
|
||||||
@@ -44,7 +40,6 @@
|
|||||||
<string name="read_your_public_key">Read your public key</string>
|
<string name="read_your_public_key">Read your public key</string>
|
||||||
<string name="no_permissions_granted">No permissions granted</string>
|
<string name="no_permissions_granted">No permissions granted</string>
|
||||||
<string name="no_event_to_sign">No event to sign</string>
|
<string name="no_event_to_sign">No event to sign</string>
|
||||||
<string name="wants_to_read_your_public_key">wants to read your public key</string>
|
|
||||||
<string name="remember_my_choice_and_don_t_ask_again">Remember my choice and don\'t ask again</string>
|
<string name="remember_my_choice_and_don_t_ask_again">Remember my choice and don\'t ask again</string>
|
||||||
<string name="reject">Reject</string>
|
<string name="reject">Reject</string>
|
||||||
<string name="accept">Accept</string>
|
<string name="accept">Accept</string>
|
||||||
@@ -54,17 +49,9 @@
|
|||||||
<string name="decrypt_nip04">decrypt nip04</string>
|
<string name="decrypt_nip04">decrypt nip04</string>
|
||||||
<string name="decrypt_zap_event">decrypt zap event</string>
|
<string name="decrypt_zap_event">decrypt zap event</string>
|
||||||
<string name="encrypt_decrypt">encrypt/decrypt</string>
|
<string name="encrypt_decrypt">encrypt/decrypt</string>
|
||||||
<string name="wants_you_to_data">wants you to %1$s data</string>
|
|
||||||
<string name="encrypt">encrypt</string>
|
<string name="encrypt">encrypt</string>
|
||||||
<string name="decrypt">decrypt</string>
|
<string name="decrypt">decrypt</string>
|
||||||
<string name="error_to_data">Error to %1$s data</string>
|
<string name="error_to_data">Error to %1$s data</string>
|
||||||
<string name="client_authentication">client authentication</string>
|
|
||||||
<string name="event">event</string>
|
|
||||||
<string name="wants_you_to_sign_a">wants you to sign a %1$s</string>
|
|
||||||
<string name="kind">"Kind "</string>
|
|
||||||
<string name="created_at">"Created at "</string>
|
|
||||||
<string name="signed_by">"Signed by "</string>
|
|
||||||
<string name="content">"Content "</string>
|
|
||||||
<string name="show_details">Show Details</string>
|
<string name="show_details">Show Details</string>
|
||||||
<string name="hide_details">Hide Details</string>
|
<string name="hide_details">Hide Details</string>
|
||||||
<string name="copy_raw_json">Raw json</string>
|
<string name="copy_raw_json">Raw json</string>
|
||||||
@@ -72,13 +59,10 @@
|
|||||||
<string name="select_account">Select Account</string>
|
<string name="select_account">Select Account</string>
|
||||||
<string name="active_account">Active Account</string>
|
<string name="active_account">Active Account</string>
|
||||||
<string name="edit_name">Edit name</string>
|
<string name="edit_name">Edit name</string>
|
||||||
<string name="copy_to_clipboard">Copy to clipboard</string>
|
|
||||||
<string name="copy">"Copy "</string>
|
<string name="copy">"Copy "</string>
|
||||||
<string name="my_public_key">my public key</string>
|
|
||||||
<string name="encrypted_decrypted_data">Encrypted/Decrypted data</string>
|
<string name="encrypted_decrypted_data">Encrypted/Decrypted data</string>
|
||||||
<string name="data_copied_to_the_clipboard">Data copied to the clipboard</string>
|
<string name="data_copied_to_the_clipboard">Data copied to the clipboard</string>
|
||||||
<string name="message_already_encrypted">Message already encrypted</string>
|
<string name="message_already_encrypted">Message already encrypted</string>
|
||||||
<string name="relay">Relay</string>
|
|
||||||
<string name="go_to_top">go to top</string>
|
<string name="go_to_top">go to top</string>
|
||||||
<string name="event_copied_to_the_clipboard">Event copied to the clipboard</string>
|
<string name="event_copied_to_the_clipboard">Event copied to the clipboard</string>
|
||||||
<string name="select_deselect_all">Select/Deselect all</string>
|
<string name="select_deselect_all">Select/Deselect all</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user