Proper set up for the Open URL dialog

This commit is contained in:
ThomasSession 2024-08-28 15:13:58 +10:00
parent 2aa58f4dd6
commit 25132c6342
6 changed files with 143 additions and 116 deletions

View File

@ -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))

View File

@ -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<URLSpan>(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

View File

@ -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<DialogButtonModel>? = 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<DialogButtonModel>? = 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<DialogButtonModel>? = 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 = {}
)
}

View File

@ -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

View File

@ -346,4 +346,9 @@
</LinearLayout>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/dialog_open_url"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -792,7 +792,7 @@ NOTE: Strings with blank lines have manually been replaced with '\n\n' - this wi
<string name="urlCopy">Copy URL</string>
<string name="urlOpen">Open URL</string>
<string name="urlOpenBrowser">This will open in your browser.</string>
<string name="urlOpenDescription">Are you sure you want to open this URL in your browser?\n\n{url}</string>
<string name="urlOpenDescription">Are you sure you want to open this URL in your browser?\n\n<b>{url}</b></string>
<string name="useFastMode">Use Fast Mode</string>
<string name="video">Video</string>
<string name="videoErrorPlay">Unable to play video.</string>