mobile: simplex links in UI, core: trusted uri for simplex links (#1410)

This commit is contained in:
Evgeny Poberezkin
2022-11-24 17:14:56 +00:00
committed by GitHub
parent a7345ee4d9
commit 4485d46307
13 changed files with 158 additions and 14 deletions
@@ -1491,12 +1491,21 @@ object MsgContentSerializer : KSerializer<MsgContent> {
@Serializable
class FormattedText(val text: String, val format: Format? = null) {
// TODO make it dependent on simplexLinkMode preference
val link: String? = when (format) {
is Format.Uri -> text
is Format.SimplexLink -> format.simplexUri
is Format.Email -> "mailto:$text"
is Format.Phone -> "tel:$text"
else -> null
}
// TODO make it dependent on simplexLinkMode preference
val viewText: String =
if (format is Format.SimplexLink) simplexLinkText(format.linkType, format.smpHosts) else text
fun simplexLinkText(linkType: SimplexLinkType, smpHosts: List<String>): String =
"${linkType.description} (${String.format(generalGetString(R.string.simplex_link_connection), smpHosts.firstOrNull() ?: "?")})"
}
@Serializable
@@ -1508,6 +1517,8 @@ sealed class Format {
@Serializable @SerialName("secret") class Secret: Format()
@Serializable @SerialName("colored") class Colored(val color: FormatColor): Format()
@Serializable @SerialName("uri") class Uri: Format()
// TODO trustedUri: Boolean
@Serializable @SerialName("simplexLink") class SimplexLink(val linkType: SimplexLinkType, val simplexUri: String, val smpHosts: List<String>): Format()
@Serializable @SerialName("email") class Email: Format()
@Serializable @SerialName("phone") class Phone: Format()
@@ -1519,6 +1530,7 @@ sealed class Format {
is Secret -> SpanStyle(color = Color.Transparent, background = SecretColor)
is Colored -> SpanStyle(color = this.color.uiColor)
is Uri -> linkStyle
is SimplexLink -> linkStyle
is Email -> linkStyle
is Phone -> linkStyle
}
@@ -1528,6 +1540,19 @@ sealed class Format {
}
}
@Serializable
enum class SimplexLinkType(val linkType: String) {
contact("contact"),
invitation("invitation"),
group("group");
val description: String get() = generalGetString(when (this) {
contact -> R.string.simplex_link_contact
invitation -> R.string.simplex_link_invitation
group -> R.string.simplex_link_group
})
}
@Serializable
enum class FormatColor(val color: String) {
red("red"),
@@ -60,6 +60,16 @@ enum class CallOnLockScreen {
}
}
enum class SimplexLinkMode {
DESCRIPTION,
FULL,
BROWSER;
companion object {
val default = SimplexLinkMode.DESCRIPTION
}
}
class AppPreferences(val context: Context) {
private val sharedPreferences: SharedPreferences = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE)
@@ -91,6 +101,18 @@ class AppPreferences(val context: Context) {
val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true)
val privacyTransferImagesInline = mkBoolPreference(SHARED_PREFS_PRIVACY_TRANSFER_IMAGES_INLINE, false)
val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true)
private val _simplexLinkMode = mkStrPreference(SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE, SimplexLinkMode.default.name)
val simplexLinkMode: Preference<SimplexLinkMode> = Preference(
get = fun(): SimplexLinkMode {
val value = _simplexLinkMode.get() ?: return SimplexLinkMode.default
return try {
SimplexLinkMode.valueOf(value)
} catch (e: Error) {
SimplexLinkMode.default
}
},
set = fun(mode: SimplexLinkMode) { _simplexLinkMode.set(mode.name) }
)
val experimentalCalls = mkBoolPreference(SHARED_PREFS_EXPERIMENTAL_CALLS, false)
val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null)
val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null)
@@ -181,6 +203,7 @@ class AppPreferences(val context: Context) {
private const val SHARED_PREFS_PRIVACY_ACCEPT_IMAGES = "PrivacyAcceptImages"
private const val SHARED_PREFS_PRIVACY_TRANSFER_IMAGES_INLINE = "PrivacyTransferImagesInline"
private const val SHARED_PREFS_PRIVACY_LINK_PREVIEWS = "PrivacyLinkPreviews"
private const val SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE = "PrivacySimplexLinkMode"
private const val SHARED_PREFS_EXPERIMENTAL_CALLS = "ExperimentalCalls"
private const val SHARED_PREFS_CHAT_ARCHIVE_NAME = "ChatArchiveName"
private const val SHARED_PREFS_CHAT_ARCHIVE_TIME = "ChatArchiveTime"
@@ -84,7 +84,7 @@ fun MarkdownText (
hasLinks = true
val ftStyle = ft.format.style
withAnnotation(tag = "URL", annotation = link) {
withStyle(ftStyle) { append(ft.text) }
withStyle(ftStyle) { append(ft.viewText) }
}
} else {
withStyle(ft.format.style) { append(ft.text) }
@@ -41,6 +41,12 @@
<string name="description_via_one_time_link">über einen Einmal-Link</string>
<string name="description_via_one_time_link_incognito">Inkognito über einen Einmal-Link</string>
<!-- FormattedText, SimpleX links - ChatModel.kt -->
<string name="simplex_link_contact">SimpleX Kontaktadressen-Link</string>
<string name="simplex_link_invitation">SimpleX Einmal-Link</string>
<string name="simplex_link_group">SimpleX Gruppen-Link</string>
<string name="simplex_link_connection">über <xliff:g id="serverHost" example="smp.simplex.im">%1$s</xliff:g></string>
<!-- SimpleXAPI.kt -->
<string name="error_saving_smp_servers">Fehler beim Speichern der SMP-Server</string>
<string name="ensure_smp_server_address_are_correct_format_and_unique">Stellen Sie sicher, dass die SMP-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind.</string>
@@ -41,6 +41,12 @@
<string name="description_via_one_time_link">через одноразовую ссылку</string>
<string name="description_via_one_time_link_incognito">инкогнито через одноразовую ссылку</string>
<!-- FormattedText, SimpleX links - ChatModel.kt -->
<string name="simplex_link_contact">SimpleX ссылка-контакт</string>
<string name="simplex_link_invitation">SimpleX одноразовая ссылка</string>
<string name="simplex_link_group">SimpleX ссылка группы</string>
<string name="simplex_link_connection">через <xliff:g id="serverHost" example="smp.simplex.im">%1$s</xliff:g></string>
<!-- SimpleXAPI.kt -->
<string name="error_saving_smp_servers">Ошибка при сохранении SMP серверов</string>
<string name="ensure_smp_server_address_are_correct_format_and_unique">Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.</string>
@@ -41,6 +41,12 @@
<string name="description_via_one_time_link">via one-time link</string>
<string name="description_via_one_time_link_incognito">incognito via one-time link</string>
<!-- FormattedText, SimpleX links - ChatModel.kt -->
<string name="simplex_link_contact">SimpleX contact address</string>
<string name="simplex_link_invitation">SimpleX 1-time invitation</string>
<string name="simplex_link_group">SimpleX group link</string>
<string name="simplex_link_connection">via <xliff:g id="serverHost" example="smp.simplex.im">%1$s</xliff:g></string>
<!-- SimpleXAPI.kt -->
<string name="error_saving_smp_servers">Error saving SMP servers</string>
<string name="ensure_smp_server_address_are_correct_format_and_unique">Make sure SMP server addresses are in correct format, line separated and are not duplicated.</string>
@@ -70,6 +70,12 @@ private func formatText(_ ft: FormattedText, _ preview: Bool) -> Text {
case .secret: return Text(t).foregroundColor(.clear).underline(color: .primary)
case let .colored(color): return Text(t).foregroundColor(color.uiColor)
case .uri: return linkText(t, t, preview, prefix: "")
case let .simplexLink(linkType, simplexUri, smpHosts):
switch privacySimplexLinkModeDefault.get() {
case .description: return linkText(simplexLinkText(linkType, smpHosts), simplexUri, preview, prefix: "")
case .full: return linkText(t, simplexUri, preview, prefix: "")
case .browser: return linkText(t, t, preview, prefix: "")
}
case .email: return linkText(t, t, preview, prefix: "mailto:")
case .phone: return linkText(t, t.replacingOccurrences(of: " ", with: ""), preview, prefix: "tel:")
}
@@ -88,6 +94,10 @@ private func linkText(_ s: String, _ link: String,
]))).underline()
}
private func simplexLinkText(_ linkType: SimplexLinkType, _ smpHosts: [String]) -> String {
linkType.description + " " + "(via \(smpHosts.first ?? "?"))"
}
struct MsgContentView_Previews: PreviewProvider {
static var previews: some View {
let chatItem = ChatItem.getSample(1, .directSnd, .now, "hello")
@@ -14,6 +14,7 @@ struct PrivacySettings: View {
@AppStorage(DEFAULT_PRIVACY_LINK_PREVIEWS) private var useLinkPreviews = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE, store: groupDefaults) private var transferImagesInline = false
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
var body: some View {
VStack {
@@ -21,7 +22,8 @@ struct PrivacySettings: View {
Section("Device") {
SimplexLockSetting()
}
Section("Chats") {
Section {
settingsRow("photo") {
Toggle("Auto-accept images", isOn: $autoAcceptImages)
.onChange(of: autoAcceptImages) {
@@ -36,6 +38,23 @@ struct PrivacySettings: View {
settingsRow("network") {
Toggle("Send link previews", isOn: $useLinkPreviews)
}
settingsRow("link") {
Picker("SimpleX links", selection: $simplexLinkMode) {
ForEach(SimpleXLinkMode.values) { mode in
Text(mode.text)
}
}
}
.frame(height: 36)
.onChange(of: simplexLinkMode) { mode in
privacySimplexLinkModeDefault.set(mode)
}
} header: {
Text("Chats")
} footer: {
if case .browser = simplexLinkMode {
Text("Opening the link in the browser may reduce connection privacy and security.")
}
}
}
}
@@ -23,6 +23,7 @@ let DEFAULT_WEBRTC_POLICY_RELAY = "webrtcPolicyRelay"
let DEFAULT_WEBRTC_ICE_SERVERS = "webrtcICEServers"
let DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
let DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews"
let DEFAULT_PRIVACY_SIMPLEX_LINK_MODE = "privacySimplexLinkMode"
let DEFAULT_EXPERIMENTAL_CALLS = "experimentalCalls"
let DEFAULT_CHAT_ARCHIVE_NAME = "chatArchiveName"
let DEFAULT_CHAT_ARCHIVE_TIME = "chatArchiveTime"
@@ -43,6 +44,7 @@ let appDefaults: [String: Any] = [
DEFAULT_WEBRTC_POLICY_RELAY: true,
DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
DEFAULT_PRIVACY_LINK_PREVIEWS: true,
DEFAULT_PRIVACY_SIMPLEX_LINK_MODE: "description",
DEFAULT_EXPERIMENTAL_CALLS: false,
DEFAULT_CHAT_V3_DB_MIGRATION: "offer",
DEFAULT_DEVELOPER_TOOLS: false,
@@ -54,6 +56,24 @@ let appDefaults: [String: Any] = [
DEFAULT_CONNECT_VIA_LINK_TAB: "scan"
]
enum SimpleXLinkMode: String, Identifiable {
case description
case full
case browser
static var values: [SimpleXLinkMode] = [.description, .full, .browser]
public var id: Self { self }
var text: LocalizedStringKey {
switch self {
case .description: return "Description"
case .full: return "Full link"
case .browser: return "Via browser"
}
}
}
private var indent: CGFloat = 36
let chatArchiveTimeDefault = DateDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CHAT_ARCHIVE_TIME)
@@ -64,6 +84,8 @@ let encryptionStartedAtDefault = DateDefault(defaults: UserDefaults.standard, fo
let connectViaLinkTabDefault = EnumDefault<ConnectViaLinkTab>(defaults: UserDefaults.standard, forKey: DEFAULT_CONNECT_VIA_LINK_TAB, withDefault: .scan)
let privacySimplexLinkModeDefault = EnumDefault<SimpleXLinkMode>(defaults: UserDefaults.standard, forKey: DEFAULT_PRIVACY_SIMPLEX_LINK_MODE, withDefault: .description)
func setGroupDefaults() {
privacyAcceptImagesGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES))
}
+16
View File
@@ -1713,10 +1713,26 @@ public enum Format: Decodable, Equatable {
case secret
case colored(color: FormatColor)
case uri
// TODO trustedUri: Bool
case simplexLink(linkType: SimplexLinkType, simplexUri: String, smpHosts: [String])
case email
case phone
}
public enum SimplexLinkType: String, Decodable {
case contact
case invitation
case group
public var description: String {
switch self {
case .contact: return NSLocalizedString("SimpleX contact address", comment: "simplex link type")
case .invitation: return NSLocalizedString("SimpleX 1-time invitation", comment: "simplex link type")
case .group: return NSLocalizedString("SimpleX group link", comment: "simplex link type")
}
}
}
public enum FormatColor: String, Decodable {
case red = "red"
case green = "green"