From 2aa58f4dd6c62ec712715a24cf86272c0990a7af Mon Sep 17 00:00:00 2001 From: alansley Date: Wed, 28 Aug 2024 08:27:03 +1000 Subject: [PATCH 1/5] WIP compose openURL dialog --- .../v2/messages/VisibleMessageContentView.kt | 52 ++++++++++- .../thoughtcrime/securesms/ui/AlertDialog.kt | 86 ++++++++++++++++--- 2 files changed, 126 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 1d193bd745..f5773b27db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -11,8 +11,12 @@ import android.text.util.Linkify import android.util.AttributeSet import android.view.MotionEvent import android.view.View +import android.widget.Toast import androidx.annotation.ColorInt import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.graphics.ColorUtils import androidx.core.text.getSpans @@ -40,7 +44,14 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.SmsMessageRecord import com.bumptech.glide.Glide import com.bumptech.glide.RequestManager +import org.thoughtcrime.securesms.copyURLToClipboard +import org.thoughtcrime.securesms.openUrl import org.thoughtcrime.securesms.showOpenUrlDialog +import org.thoughtcrime.securesms.ui.AlertDialog +import org.thoughtcrime.securesms.ui.DialogButtonModel +import org.thoughtcrime.securesms.ui.GetString +import org.thoughtcrime.securesms.ui.OpenURLAlertDialog +import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.SearchUtil import org.thoughtcrime.securesms.util.getAccentColor @@ -60,6 +71,7 @@ class VisibleMessageContentView : ConstraintLayout { // endregion // region Updating + @Composable fun bind( message: MessageRecord, isStartOfMessageCluster: Boolean = true, @@ -273,6 +285,7 @@ class VisibleMessageContentView : ConstraintLayout { // region Convenience companion object { + @Composable fun getBodySpans(context: Context, message: MessageRecord, searchQuery: String?): Spannable { var body = message.body.toSpannable() @@ -293,8 +306,14 @@ class VisibleMessageContentView : ConstraintLayout { body.getSpans(0, body.length).toList().forEach { urlSpan -> val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() } val replacementSpan = ModalURLSpan(updatedUrl) { url -> - val activity = context as AppCompatActivity - activity.showOpenUrlDialog(url) + + // @Thomas - PREVIOUS CODE WAS: + //val activity = context as AppCompatActivity + //activity.showOpenUrlDialog(url) + + // Now attempting to use compose for the dialog - but it's not happy =/ + OpenURLWarningDialog(url) + } val start = body.getSpanStart(urlSpan) val end = body.getSpanEnd(urlSpan) @@ -305,6 +324,35 @@ class VisibleMessageContentView : ConstraintLayout { return body } + @Composable + private fun OpenURLWarningDialog(url: String) { + val context = LocalContext.current + + OpenURLAlertDialog( + title = stringResource(R.string.urlOpen), + text = stringResource(R.string.urlOpenDescription), + showCloseButton = true, // display the 'x' button + buttons = listOf( + DialogButtonModel( + text = GetString(R.string.open), + contentDescription = GetString(R.string.AccessibilityId_urlOpenBrowser), + color = LocalColors.current.danger, + onClick = { context.openUrl(url) } + ), + DialogButtonModel( + text = GetString(android.R.string.copyUrl), + contentDescription = GetString(R.string.AccessibilityId_copy), + onClick = { + context.copyURLToClipboard(url) + Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() + } + ) + ), + url = url, + onDismissRequest = {} + ) + } + @ColorInt fun getTextColor(context: Context, message: MessageRecord): Int = context.getColorFromAttr( if (message.isOutgoing) R.attr.message_sent_text_color else R.attr.message_received_text_color diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt index 1dd1df46cb..9c16f7119b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -29,6 +28,7 @@ import androidx.compose.ui.graphics.takeOrElse import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import network.loki.messenger.R @@ -57,7 +57,8 @@ fun AlertDialog( text: String? = null, buttons: List? = null, showCloseButton: Boolean = false, - content: @Composable () -> Unit = {} + content: @Composable () -> Unit = {}, + optionalURL: String = "" ) { BasicAlertDialog( modifier = modifier, @@ -88,19 +89,32 @@ fun AlertDialog( ) { title?.let { Text( - it, + text = it, textAlign = TextAlign.Center, style = LocalType.current.h7, modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) ) } text?.let { - Text( - it, - textAlign = TextAlign.Center, - style = LocalType.current.large, - modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) - ) + if (optionalURL.isNotEmpty()) { + // If this is an open URL dialog it should have a maximum height of 5 lines and truncate long URLs with an ellipsis + Text( + text = it, + textAlign = TextAlign.Center, + style = LocalType.current.large, + modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing), + maxLines = 5, + overflow = TextOverflow.Ellipsis + ) + } else { + // Otherwise it should be a regular, non-open-URL dialog + Text( + text = it, + textAlign = TextAlign.Center, + style = LocalType.current.large, + modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) + ) + } } content() } @@ -127,6 +141,30 @@ fun AlertDialog( ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun OpenURLAlertDialog( + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + title: String? = null, + text: String? = null, + buttons: List? = null, + showCloseButton: Boolean = false, + content: @Composable () -> Unit = {}, + url: String = "" +) { + AlertDialog( + onDismissRequest = onDismissRequest, + modifier = modifier, + title = title, + text = text, + buttons = buttons, + showCloseButton = showCloseButton, + content = content, + optionalURL = url + ) +} + @Composable fun DialogButton( text: String, @@ -224,7 +262,8 @@ fun PreviewSimpleDialog() { DialogButtonModel( GetString(stringResource(R.string.cancel)) ) - ) + ), + optionalURL = "https://slashdot.org" ) } } @@ -254,6 +293,33 @@ fun PreviewXCloseDialog() { } } +@Preview +@Composable +fun PreviewOpenURLDialog() { + PreviewTheme { + OpenURLAlertDialog( + title = stringResource(R.string.urlOpen), + text = stringResource(R.string.urlOpenDescription), + showCloseButton = true, // display the 'x' button + buttons = listOf( + DialogButtonModel( + text = GetString(R.string.open), + contentDescription = GetString(R.string.AccessibilityId_urlOpenBrowser), + color = LocalColors.current.danger, + onClick = {} + ), + DialogButtonModel( + text = GetString(android.R.string.copyUrl), + contentDescription = GetString(R.string.AccessibilityId_copy), + onClick = {} + ) + ), + url = "http://slashdot.org", + onDismissRequest = {} + ) + } +} + @Preview @Composable fun PreviewLoadingDialog() { From 25132c6342f11613083b9cd3f7413b775faefc00 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Wed, 28 Aug 2024 15:13:58 +1000 Subject: [PATCH 2/5] Proper set up for the Open URL dialog --- .../conversation/v2/ConversationActivityV2.kt | 33 +++- .../v2/messages/VisibleMessageContentView.kt | 60 +------ .../thoughtcrime/securesms/ui/AlertDialog.kt | 147 +++++++++++------- .../securesms/ui/components/Html.kt | 12 ++ .../res/layout/activity_conversation_v2.xml | 5 + libsession/src/main/res/values/strings.xml | 2 +- 6 files changed, 143 insertions(+), 116 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 1481d8ff12..5902c0dfa9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -19,10 +19,7 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.provider.MediaStore -import android.text.SpannableString -import android.text.Spanned import android.text.TextUtils -import android.text.style.StyleSpan import android.util.Pair import android.util.TypedValue import android.view.ActionMode @@ -36,6 +33,10 @@ import android.widget.Toast import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.content.ContextCompat import androidx.core.view.drawToBitmap import androidx.core.view.isGone @@ -170,6 +171,8 @@ import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.reactions.ReactionsDialogFragment import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment import org.thoughtcrime.securesms.showSessionDialog +import org.thoughtcrime.securesms.ui.OpenURLAlertDialog +import org.thoughtcrime.securesms.ui.theme.SessionMaterialTheme import org.thoughtcrime.securesms.util.ActivityDispatcher import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.DateUtils @@ -195,6 +198,7 @@ import kotlin.math.roundToInt import kotlin.math.sqrt import kotlin.time.Duration.Companion.minutes + private const val TAG = "ConversationActivityV2" // Some things that seemingly belong to the input bar (e.g. the voice message recording UI) are actually @@ -237,6 +241,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe .get(LinkPreviewViewModel::class.java) } + private var openLinkDialogUrl: String? by mutableStateOf(null) + private val threadId: Long by lazy { var threadId = intent.getLongExtra(THREAD_ID, -1L) if (threadId == -1L) { @@ -400,12 +406,33 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } // endregion + fun showOpenUrlDialog(url: String){ + openLinkDialogUrl = url + } + // region Lifecycle override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { super.onCreate(savedInstanceState, isReady) binding = ActivityConversationV2Binding.inflate(layoutInflater) setContentView(binding.root) + // set the compose dialog content + binding.dialogOpenUrl.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + SessionMaterialTheme { + if(!openLinkDialogUrl.isNullOrEmpty()){ + OpenURLAlertDialog( + url = openLinkDialogUrl, + onDismissRequest = { + openLinkDialogUrl = null + } + ) + } + } + } + } + // messageIdToScroll messageToScrollTimestamp.set(intent.getLongExtra(SCROLL_MESSAGE_ID, -1)) messageToScrollAuthor.set(intent.getParcelableExtra(SCROLL_MESSAGE_AUTHOR)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index f5773b27db..157d459127 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -11,47 +11,32 @@ import android.text.util.Linkify import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import android.widget.Toast import androidx.annotation.ColorInt import androidx.appcompat.app.AppCompatActivity -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.graphics.ColorUtils import androidx.core.text.getSpans import androidx.core.text.toSpannable import androidx.core.view.children import androidx.core.view.isVisible +import com.bumptech.glide.Glide +import com.bumptech.glide.RequestManager import network.loki.messenger.R import network.loki.messenger.databinding.ViewVisibleMessageContentBinding -import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import org.session.libsession.messaging.MessagingModuleConfiguration -import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.modifyLayoutParams import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 -import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.SmsMessageRecord -import com.bumptech.glide.Glide -import com.bumptech.glide.RequestManager -import org.thoughtcrime.securesms.copyURLToClipboard -import org.thoughtcrime.securesms.openUrl import org.thoughtcrime.securesms.showOpenUrlDialog -import org.thoughtcrime.securesms.ui.AlertDialog -import org.thoughtcrime.securesms.ui.DialogButtonModel -import org.thoughtcrime.securesms.ui.GetString -import org.thoughtcrime.securesms.ui.OpenURLAlertDialog -import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.SearchUtil import org.thoughtcrime.securesms.util.getAccentColor @@ -71,7 +56,6 @@ class VisibleMessageContentView : ConstraintLayout { // endregion // region Updating - @Composable fun bind( message: MessageRecord, isStartOfMessageCluster: Boolean = true, @@ -285,7 +269,6 @@ class VisibleMessageContentView : ConstraintLayout { // region Convenience companion object { - @Composable fun getBodySpans(context: Context, message: MessageRecord, searchQuery: String?): Spannable { var body = message.body.toSpannable() @@ -306,14 +289,8 @@ class VisibleMessageContentView : ConstraintLayout { body.getSpans(0, body.length).toList().forEach { urlSpan -> val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() } val replacementSpan = ModalURLSpan(updatedUrl) { url -> - - // @Thomas - PREVIOUS CODE WAS: - //val activity = context as AppCompatActivity - //activity.showOpenUrlDialog(url) - - // Now attempting to use compose for the dialog - but it's not happy =/ - OpenURLWarningDialog(url) - + val activity = context as ConversationActivityV2 + activity.showOpenUrlDialog(url) } val start = body.getSpanStart(urlSpan) val end = body.getSpanEnd(urlSpan) @@ -324,35 +301,6 @@ class VisibleMessageContentView : ConstraintLayout { return body } - @Composable - private fun OpenURLWarningDialog(url: String) { - val context = LocalContext.current - - OpenURLAlertDialog( - title = stringResource(R.string.urlOpen), - text = stringResource(R.string.urlOpenDescription), - showCloseButton = true, // display the 'x' button - buttons = listOf( - DialogButtonModel( - text = GetString(R.string.open), - contentDescription = GetString(R.string.AccessibilityId_urlOpenBrowser), - color = LocalColors.current.danger, - onClick = { context.openUrl(url) } - ), - DialogButtonModel( - text = GetString(android.R.string.copyUrl), - contentDescription = GetString(R.string.AccessibilityId_copy), - onClick = { - context.copyURLToClipboard(url) - Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() - } - ) - ), - url = url, - onDismissRequest = {} - ) - } - @ColorInt fun getTextColor(context: Context, message: MessageRecord): Int = context.getColorFromAttr( if (message.isOutgoing) R.attr.message_sent_text_color else R.attr.message_received_text_color diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt index 9c16f7119b..fed01640e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.ui +import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box @@ -12,6 +13,8 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -25,14 +28,24 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.takeOrElse +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.max +import androidx.compose.ui.unit.times +import com.squareup.phrase.Phrase import network.loki.messenger.R +import org.session.libsession.utilities.StringSubstitutionConstants.URL_KEY +import org.thoughtcrime.securesms.copyURLToClipboard +import org.thoughtcrime.securesms.openUrl import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator +import org.thoughtcrime.securesms.ui.components.annotatedStringResource import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType @@ -48,17 +61,40 @@ class DialogButtonModel( val onClick: () -> Unit = {}, ) -@OptIn(ExperimentalMaterial3Api::class) @Composable fun AlertDialog( onDismissRequest: () -> Unit, modifier: Modifier = Modifier, title: String? = null, text: String? = null, + maxLines: Int? = null, buttons: List? = null, showCloseButton: Boolean = false, - content: @Composable () -> Unit = {}, - optionalURL: String = "" + content: @Composable () -> Unit = {} +) { + AlertDialog( + onDismissRequest = onDismissRequest, + modifier = modifier, + title = if(title != null) AnnotatedString(title) else null, + text = if(text != null) AnnotatedString(text) else null, + maxLines = maxLines, + buttons = buttons, + showCloseButton = showCloseButton, + content = content + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AlertDialog( + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + title: AnnotatedString? = null, + text: AnnotatedString? = null, + maxLines: Int? = null, + buttons: List? = null, + showCloseButton: Boolean = false, + content: @Composable () -> Unit = {} ) { BasicAlertDialog( modifier = modifier, @@ -96,25 +132,26 @@ fun AlertDialog( ) } text?.let { - if (optionalURL.isNotEmpty()) { - // If this is an open URL dialog it should have a maximum height of 5 lines and truncate long URLs with an ellipsis - Text( - text = it, - textAlign = TextAlign.Center, - style = LocalType.current.large, - modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing), - maxLines = 5, - overflow = TextOverflow.Ellipsis - ) - } else { - // Otherwise it should be a regular, non-open-URL dialog - Text( - text = it, - textAlign = TextAlign.Center, - style = LocalType.current.large, - modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) - ) + val textStyle = LocalType.current.large + var textModifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing) + + // if we have a maxLines, make the text scrollable + if(maxLines != null) { + val textHeight = with(LocalDensity.current) { + textStyle.lineHeight.toDp() + } * maxLines + + textModifier = textModifier + .height(textHeight) + .verticalScroll(rememberScrollState()) } + + Text( + text = it, + textAlign = TextAlign.Center, + style = textStyle, + modifier = textModifier + ) } content() } @@ -141,27 +178,42 @@ fun AlertDialog( ) } -@OptIn(ExperimentalMaterial3Api::class) @Composable fun OpenURLAlertDialog( onDismissRequest: () -> Unit, modifier: Modifier = Modifier, - title: String? = null, - text: String? = null, - buttons: List? = null, - showCloseButton: Boolean = false, - content: @Composable () -> Unit = {}, - url: String = "" + url: String, + content: @Composable () -> Unit = {} ) { + val context = LocalContext.current + val unformattedText = Phrase.from(context.getText(R.string.urlOpenDescription)) + .put(URL_KEY, url).format() + + AlertDialog( - onDismissRequest = onDismissRequest, modifier = modifier, - title = title, - text = text, - buttons = buttons, - showCloseButton = showCloseButton, - content = content, - optionalURL = url + title = AnnotatedString(stringResource(R.string.urlOpen)), + text = annotatedStringResource(text = unformattedText), + maxLines = 5, + showCloseButton = true, // display the 'x' button + buttons = listOf( + DialogButtonModel( + text = GetString(R.string.open), + contentDescription = GetString(R.string.AccessibilityId_urlOpenBrowser), + color = LocalColors.current.danger, + onClick = { context.openUrl(url) } + ), + DialogButtonModel( + text = GetString(android.R.string.copyUrl), + contentDescription = GetString(R.string.AccessibilityId_copy), + onClick = { + context.copyURLToClipboard(url) + Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() + } + ) + ), + onDismissRequest = onDismissRequest, + content = content ) } @@ -255,15 +307,14 @@ fun PreviewSimpleDialog() { text = stringResource(R.string.onboardingBackAccountCreation), buttons = listOf( DialogButtonModel( - GetString(stringResource(R.string.quit)), + GetString(stringResource(R.string.cancel)), color = LocalColors.current.danger, onClick = { } ), DialogButtonModel( - GetString(stringResource(R.string.cancel)) + GetString(stringResource(R.string.ok)) ) - ), - optionalURL = "https://slashdot.org" + ) ) } } @@ -298,23 +349,7 @@ fun PreviewXCloseDialog() { fun PreviewOpenURLDialog() { PreviewTheme { OpenURLAlertDialog( - title = stringResource(R.string.urlOpen), - text = stringResource(R.string.urlOpenDescription), - showCloseButton = true, // display the 'x' button - buttons = listOf( - DialogButtonModel( - text = GetString(R.string.open), - contentDescription = GetString(R.string.AccessibilityId_urlOpenBrowser), - color = LocalColors.current.danger, - onClick = {} - ), - DialogButtonModel( - text = GetString(android.R.string.copyUrl), - contentDescription = GetString(R.string.AccessibilityId_copy), - onClick = {} - ) - ), - url = "http://slashdot.org", + url = "https://getsession.org/", onDismissRequest = {} ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Html.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Html.kt index 951db1816e..15f0292853 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/Html.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/Html.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString @@ -26,6 +27,9 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.core.text.HtmlCompat +import com.squareup.phrase.Phrase +import network.loki.messenger.R +import org.session.libsession.utilities.StringSubstitutionConstants.URL_KEY // TODO Remove this file once we update to composeVersion=1.7.0-alpha06 fixes https://issuetracker.google.com/issues/139320238?pli=1 // which allows Stylized string in string resources @@ -71,6 +75,14 @@ fun annotatedStringResource(@StringRes id: Int): AnnotatedString { } } +@Composable +fun annotatedStringResource(text: CharSequence): AnnotatedString { + val density = LocalDensity.current + return remember(text.hashCode()) { + spannableStringToAnnotatedString(text, density) + } +} + private fun spannableStringToAnnotatedString( text: CharSequence, density: Density diff --git a/app/src/main/res/layout/activity_conversation_v2.xml b/app/src/main/res/layout/activity_conversation_v2.xml index 94250f1b0a..d8e6b1fe73 100644 --- a/app/src/main/res/layout/activity_conversation_v2.xml +++ b/app/src/main/res/layout/activity_conversation_v2.xml @@ -346,4 +346,9 @@ + + diff --git a/libsession/src/main/res/values/strings.xml b/libsession/src/main/res/values/strings.xml index 69c87854b2..5070f180ff 100644 --- a/libsession/src/main/res/values/strings.xml +++ b/libsession/src/main/res/values/strings.xml @@ -792,7 +792,7 @@ NOTE: Strings with blank lines have manually been replaced with '\n\n' - this wi Copy URL Open URL This will open in your browser. - Are you sure you want to open this URL in your browser?\n\n{url} + Are you sure you want to open this URL in your browser?\n\n{url} Use Fast Mode Video Unable to play video. From 20abbebf4ac8bc3a8fd46463f3be621b991c15d1 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Wed, 28 Aug 2024 15:19:46 +1000 Subject: [PATCH 3/5] Forgot !! --- .../securesms/conversation/v2/ConversationActivityV2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 5902c0dfa9..9b1a3ac68b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -423,7 +423,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe SessionMaterialTheme { if(!openLinkDialogUrl.isNullOrEmpty()){ OpenURLAlertDialog( - url = openLinkDialogUrl, + url = openLinkDialogUrl!!, onDismissRequest = { openLinkDialogUrl = null } From 0b1a71a5820901a010b633525f56988e8b5095cd Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Wed, 28 Aug 2024 15:23:02 +1000 Subject: [PATCH 4/5] Cleaning other use of old url dialog --- .../securesms/SessionDialogBuilder.kt | 132 ------------------ .../v2/messages/LinkPreviewView.kt | 5 +- .../v2/messages/VisibleMessageContentView.kt | 2 - 3 files changed, 2 insertions(+), 137 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt index 0dab6c6a04..8d40dd2785 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -4,25 +4,16 @@ import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.Intent -import android.graphics.Typeface import android.net.Uri -import android.text.Spannable -import android.text.SpannableString -import android.text.style.StyleSpan -import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.Button -import android.widget.ImageButton import android.widget.LinearLayout import android.widget.LinearLayout.VERTICAL -import android.widget.RelativeLayout -import android.widget.ScrollView import android.widget.Space import android.widget.TextView -import android.widget.Toast import androidx.annotation.AttrRes import androidx.annotation.LayoutRes import androidx.annotation.StringRes @@ -31,9 +22,7 @@ import androidx.appcompat.app.AlertDialog import androidx.core.text.HtmlCompat import androidx.core.view.updateMargins import androidx.fragment.app.Fragment -import com.squareup.phrase.Phrase import network.loki.messenger.R -import org.session.libsession.utilities.StringSubstitutionConstants.URL_KEY import org.thoughtcrime.securesms.util.toPx @DslMarker @@ -199,127 +188,6 @@ public fun Context.copyURLToClipboard(url: String) { clipboard.setPrimaryClip(clip) } -// Method to show a dialog used to open or copy a URL -fun Context.showOpenUrlDialog(url: String, showCloseButton: Boolean = true): AlertDialog { - - return SessionDialogBuilder(this).apply { - // If we're not showing a close button we can just use a simple title.. - if (!showCloseButton) { - title(R.string.urlOpen) - } else { - // ..otherwise we have to jump through some hoops to add a close button. - - // Create a RelativeLayout as the container for the custom title - val titleLayout = RelativeLayout(context).apply { - layoutParams = RelativeLayout.LayoutParams( - RelativeLayout.LayoutParams.MATCH_PARENT, - RelativeLayout.LayoutParams.WRAP_CONTENT - ) - } - - // Create a TextView for the title - val titleTextView = TextView(context).apply { - // Set the text and display it in the correct 'title' style - text = context.getString(R.string.urlOpen) - setTextAppearance(R.style.TextAppearance_AppCompat_Title) - - layoutParams = RelativeLayout.LayoutParams( - RelativeLayout.LayoutParams.WRAP_CONTENT, - RelativeLayout.LayoutParams.WRAP_CONTENT - ).apply { - addRule(RelativeLayout.CENTER_HORIZONTAL) - addRule(RelativeLayout.CENTER_VERTICAL) - } - } - - // Create an ImageButton for the close button - val closeButton = ImageButton(context).apply { - setImageResource(android.R.drawable.ic_menu_close_clear_cancel) // Use a standard Android close icon - background = null // Remove the background to make it look like an icon - layoutParams = RelativeLayout.LayoutParams( - RelativeLayout.LayoutParams.WRAP_CONTENT, - RelativeLayout.LayoutParams.WRAP_CONTENT - ).apply { - addRule(RelativeLayout.ALIGN_PARENT_END) // Place the close button on the "right" side - addRule(RelativeLayout.CENTER_VERTICAL) - } - contentDescription = context.getString(R.string.close) - } - - // // Close the dialog when the button is clicked - closeButton.setOnClickListener { dismiss() } - - // Add the TextView and ImageButton to the RelativeLayout.. - titleLayout.addView(titleTextView) - titleLayout.addView(closeButton) - - // ..and then add that layout to the contentView. - contentView.addView(titleLayout) - } - - // Create a TextView for the "Are you sure you want to open this URL?" - val txtView = TextView(context).apply { - layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) - .apply { updateMargins(dp40, 0, dp40, 0) } - - // Substitute the URL into the string then make it bold - val txt = Phrase.from(context, R.string.urlOpenDescription).put(URL_KEY, url).format().toString() - val txtWithBoldedURL = SpannableString(txt) - val urlStart = txt.indexOf(url) - if (urlStart >= 0) { - txtWithBoldedURL.setSpan( - StyleSpan(Typeface.BOLD), - urlStart, - urlStart + url.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } - text = txtWithBoldedURL - - gravity = Gravity.CENTER // Center the text - } - - // Create a ScrollView and add the TextView to it - val scrollView = ScrollView(context).apply { - addView(txtView) - - // Apply padding to the ScrollView so that the scroll bar isn't right up against the edge. - // We'll apply the same padding to both sides to keep the text centered. - setPadding(dp20, 0, dp20, 0) - - // Place the scroll bar inside the container. - // See the following for how different options look: https://stackoverflow.com/questions/3103132/android-listview-scrollbarstyle - scrollBarStyle = ScrollView.SCROLLBARS_INSIDE_INSET - } - - // If the textView takes up 5 lines or more then show the scroll bar, force it to remain visible, - // and set the ScrollView height accordingly. - txtView.viewTreeObserver.addOnGlobalLayoutListener { - // Only display the vertical scroll bar if the text takes up 5 lines or more - val maxLines = 5 - if (txtView.lineCount >= maxLines) { - scrollView.isVerticalScrollBarEnabled = true - // Note: `scrollView.isScrollbarFadingEnabled = false` does NOT - // work to prevent the scroll bar from fading away - so a hacky - // way to fix this is to allow it to fade out... after an hour, lol. - scrollView.scrollBarFadeDuration = 1000 * 60 * 60 // Value is in milliseconds - scrollView.isVerticalFadingEdgeEnabled = false - val lineHeight = txtView.lineHeight - scrollView.layoutParams.height = lineHeight * maxLines - } - } - - // Add the ScrollView to the contentView and then add the 'Open' and 'Copy URL' buttons. - // Note: The text and contentDescription are set on the `copyUrlButton` by the function. - contentView.addView(scrollView) - dangerButton(R.string.open, R.string.AccessibilityId_urlOpenBrowser) { openUrl(url) } - copyUrlButton { - context.copyURLToClipboard(url) - Toast.makeText(context, R.string.copied, Toast.LENGTH_SHORT).show() - } - }.show() -} - // Method to actually open a given URL via an Intent that will use the default browser fun Context.openUrl(url: String) = Intent(Intent.ACTION_VIEW, Uri.parse(url)).let(::startActivity) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt index 68da078e55..b35a1650b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt @@ -6,7 +6,6 @@ import android.graphics.Rect import android.util.AttributeSet import android.view.MotionEvent import android.widget.LinearLayout -import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isVisible import com.bumptech.glide.RequestManager import network.loki.messenger.R @@ -14,10 +13,10 @@ import network.loki.messenger.databinding.ViewLinkPreviewBinding import org.session.libsession.utilities.getColorFromAttr import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.components.CornerMask +import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.mms.ImageSlide -import org.thoughtcrime.securesms.showOpenUrlDialog class LinkPreviewView : LinearLayout { private val binding: ViewLinkPreviewBinding by lazy { ViewLinkPreviewBinding.bind(this) } @@ -88,7 +87,7 @@ class LinkPreviewView : LinearLayout { // Method to show the open or copy URL dialog private fun openURL() { val url = this.url ?: return Log.w("LinkPreviewView", "Cannot open a null URL") - val activity = context as AppCompatActivity + val activity = context as ConversationActivityV2 activity.showOpenUrlDialog(url) } // endregion diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 157d459127..48fa7a6093 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -12,7 +12,6 @@ import android.util.AttributeSet import android.view.MotionEvent import android.view.View import androidx.annotation.ColorInt -import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.graphics.ColorUtils import androidx.core.text.getSpans @@ -36,7 +35,6 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getInt import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.SmsMessageRecord -import org.thoughtcrime.securesms.showOpenUrlDialog import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.SearchUtil import org.thoughtcrime.securesms.util.getAccentColor From 116bef3c7110a38b9f8198dbdb85e8bc7eafffed Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Wed, 28 Aug 2024 15:25:03 +1000 Subject: [PATCH 5/5] For safety --- .../securesms/conversation/v2/messages/LinkPreviewView.kt | 4 ++-- .../conversation/v2/messages/VisibleMessageContentView.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt index b35a1650b7..d064d02872 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt @@ -87,8 +87,8 @@ class LinkPreviewView : LinearLayout { // Method to show the open or copy URL dialog private fun openURL() { val url = this.url ?: return Log.w("LinkPreviewView", "Cannot open a null URL") - val activity = context as ConversationActivityV2 - activity.showOpenUrlDialog(url) + val activity = context as? ConversationActivityV2 + activity?.showOpenUrlDialog(url) } // endregion } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 48fa7a6093..d62cc532c4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -287,8 +287,8 @@ class VisibleMessageContentView : ConstraintLayout { body.getSpans(0, body.length).toList().forEach { urlSpan -> val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() } val replacementSpan = ModalURLSpan(updatedUrl) { url -> - val activity = context as ConversationActivityV2 - activity.showOpenUrlDialog(url) + val activity = context as? ConversationActivityV2 + activity?.showOpenUrlDialog(url) } val start = body.getSpanStart(urlSpan) val end = body.getSpanEnd(urlSpan)