From 0c8360653928f94e3a391ed851a63b309fda7e3d Mon Sep 17 00:00:00 2001 From: alansley Date: Tue, 13 Aug 2024 15:50:35 +1000 Subject: [PATCH] SS-75 Open URL modal change --- .../securesms/SessionDialogBuilder.kt | 184 +++++++++++++----- .../v2/messages/LinkPreviewView.kt | 22 +-- .../v2/messages/VisibleMessageContentView.kt | 3 +- app/src/main/res/values/styles.xml | 2 + 4 files changed, 146 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt index 15b19956f9..cb97b3787f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -2,15 +2,21 @@ package org.thoughtcrime.securesms import android.content.Context import android.content.Intent +import android.graphics.Typeface import android.net.Uri -import android.util.AttributeSet +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 @@ -22,7 +28,9 @@ 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.conversation.v2.Util.writeTextToClipboard import org.thoughtcrime.securesms.util.toPx @@ -33,20 +41,22 @@ annotation class DialogDsl @DialogDsl class SessionDialogBuilder(val context: Context) { - private val dp20 = toPx(20, context.resources) - private val dp40 = toPx(40, context.resources) - private val dp60 = toPx(60, context.resources) + val dp20 = toPx(20, context.resources) + val dp40 = toPx(40, context.resources) + val dp60 = toPx(60, context.resources) private val dialogBuilder: AlertDialog.Builder = AlertDialog.Builder(context) private var dialog: AlertDialog? = null - private fun dismiss() = dialog?.dismiss() + fun dismiss() = dialog?.dismiss() private val topView = LinearLayout(context) .apply { setPadding(0, dp20, 0, 0) } .apply { orientation = VERTICAL } .also(dialogBuilder::setCustomTitle) - private val contentView = LinearLayout(context).apply { orientation = VERTICAL } + + val contentView = LinearLayout(context).apply { orientation = VERTICAL } + private val buttonLayout = LinearLayout(context) private val root = LinearLayout(context).apply { orientation = VERTICAL } @@ -56,39 +66,24 @@ class SessionDialogBuilder(val context: Context) { addView(buttonLayout) } - fun title(@StringRes id: Int) = title(context.getString(id)) - - fun title(text: CharSequence?) = title(text?.toString()) + // Main title entry point fun title(text: String?) { text(text, R.style.TextAppearance_AppCompat_Title) { setPadding(dp20, 0, dp20, 0) } } + // Convenience assessor for title that takes a string resource + fun title(@StringRes id: Int) = title(context.getString(id)) + + // Convenience accessor for title that takes a CharSequence + fun title(text: CharSequence?) = title(text?.toString()) + fun text(@StringRes id: Int, style: Int = 0) = text(context.getString(id), style) + fun text(text: CharSequence?, @StyleRes style: Int = 0) { text(text, style) { layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) .apply { updateMargins(dp40, 0, dp40, 0) } } - } - - fun textWithMaxOfFiveLines(text: CharSequence?, @StyleRes style: Int = 0) { - - ScrollView(context, null, 0).apply { - layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) - - // fix this so it adds and shows a scrollbar if it exceeds 5 lines - here! - text(text, style) { - maxLines = 5 - layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) - .apply { updateMargins(dp40, 0, dp40, 0) } - }.apply { refreshDrawableState() } - - } - - - - } private fun text(text: CharSequence?, @StyleRes style: Int, modify: TextView.() -> Unit) { @@ -148,11 +143,11 @@ class SessionDialogBuilder(val context: Context) { ) { listener() } fun okButton(listener: (() -> Unit) = {}) = button(android.R.string.ok) { listener() } + fun cancelButton(listener: (() -> Unit) = {}) = button(android.R.string.cancel, R.string.AccessibilityId_cancel) { listener() } fun copyUrlButton(listener: (() -> Unit) = {}) = button(android.R.string.copyUrl, R.string.AccessibilityId_copy) { listener() } - fun button( @StringRes text: Int, @StringRes contentDescriptionRes: Int = text, @@ -176,32 +171,125 @@ class SessionDialogBuilder(val context: Context) { fun Context.showSessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog = SessionDialogBuilder(this).apply { build() }.show() -private fun Context.showOpenUrlDialogInner(build: SessionDialogBuilder.() -> Unit): AlertDialog = - SessionDialogBuilder(this).apply { - title(R.string.urlOpen) - text(R.string.urlOpenBrowser) - build() - }.show() - -fun Context.showOpenUrlDialogPublicFacing(url: String): AlertDialog { +// 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 { - title(R.string.urlOpen) - val txt = getString(R.string.urlOpenBrowser) + "\n\n" + "https://github.com/oxen-io/session-android/theworldslongrsturlisverylongindeedandshouldgoover5linesbecausethatswhatIneedtotestbecausemaryhadalittlelamb" //url - textWithMaxOfFiveLines(txt) - dangerButton(R.string.open) { openUrl(url) } + // 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 + scrollView.setScrollbarFadingEnabled(false) + 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 { writeTextToClipboard(context, url) } }.show() - - } -// showOpenUrlDialogInner { -// okButton { openUrl(url) } -// cancelButton() -// } +// 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) fun Fragment.showSessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog = SessionDialogBuilder(requireContext()).apply { build() }.show() + fun Fragment.createSessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog = SessionDialogBuilder(requireContext()).apply { build() }.create() 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 3993b61a71..68da078e55 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 @@ -8,20 +8,16 @@ 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 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.ModalUrlBottomSheet import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities import org.thoughtcrime.securesms.database.model.MmsMessageRecord -import com.bumptech.glide.RequestManager -import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.SessionDialogBuilder import org.thoughtcrime.securesms.mms.ImageSlide - -import org.thoughtcrime.securesms.showOpenUrlDialogPublicFacing - +import org.thoughtcrime.securesms.showOpenUrlDialog class LinkPreviewView : LinearLayout { private val binding: ViewLinkPreviewBinding by lazy { ViewLinkPreviewBinding.bind(this) } @@ -89,17 +85,11 @@ class LinkPreviewView : LinearLayout { } } - fun openURL() { + // 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 - - // OLDE! - //ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog") - - activity.showOpenUrlDialogPublicFacing(url) - - - + 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 0f81aff376..1d193bd745 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 @@ -40,6 +40,7 @@ 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.showOpenUrlDialog import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.SearchUtil import org.thoughtcrime.securesms.util.getAccentColor @@ -293,7 +294,7 @@ class VisibleMessageContentView : ConstraintLayout { val updatedUrl = urlSpan.url.let { it.toHttpUrlOrNull().toString() } val replacementSpan = ModalURLSpan(updatedUrl) { url -> val activity = context as AppCompatActivity - ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog") + activity.showOpenUrlDialog(url) } val start = body.getSpanStart(urlSpan) val end = body.getSpanEnd(urlSpan) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 60fd7087d2..b336d5ae02 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -122,11 +122,13 @@