mirror of
https://github.com/oxen-io/session-android.git
synced 2025-05-01 05:10:47 +00:00
Scroll to message upon tapping quote & fix various bugs
This commit is contained in:
parent
7ce124118f
commit
bef7413055
@ -3,15 +3,14 @@ package org.thoughtcrime.securesms.conversation.v2
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.animation.FloatEvaluator
|
import android.animation.FloatEvaluator
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.content.Context
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.os.Bundle
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.*
|
import android.os.*
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
@ -72,6 +71,7 @@ import org.thoughtcrime.securesms.conversation.v2.input_bar.mentions.MentionCand
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
|
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
|
||||||
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate
|
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate
|
||||||
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
|
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
|
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
@ -84,11 +84,11 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
|
||||||
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
|
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
|
||||||
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity.Companion.selectedContactsKey
|
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity.Companion.selectedContactsKey
|
||||||
|
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities
|
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities
|
||||||
|
import org.thoughtcrime.securesms.loki.utilities.push
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.loki.utilities.toPx
|
||||||
import org.thoughtcrime.securesms.mediasend.Media
|
import org.thoughtcrime.securesms.mediasend.Media
|
||||||
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
|
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
|
||||||
@ -108,7 +108,7 @@ import kotlin.math.*
|
|||||||
|
|
||||||
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
|
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
|
||||||
InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
|
InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
|
||||||
ConversationActionModeCallbackDelegate {
|
ConversationActionModeCallbackDelegate, VisibleMessageContentViewDelegate {
|
||||||
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
||||||
private var linkPreviewViewModel: LinkPreviewViewModel? = null
|
private var linkPreviewViewModel: LinkPreviewViewModel? = null
|
||||||
private var threadID: Long = -1
|
private var threadID: Long = -1
|
||||||
@ -147,6 +147,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
},
|
},
|
||||||
glide
|
glide
|
||||||
)
|
)
|
||||||
|
adapter.visibleMessageContentViewDelegate = this
|
||||||
adapter
|
adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,6 +738,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
this.previousText = newText
|
this.previousText = newText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun scrollToMessageIfPossible(timestamp: Long) {
|
||||||
|
val lastSeenItemPosition = adapter.getItemPositionForTimestamp(timestamp) ?: return
|
||||||
|
conversationRecyclerView.scrollToPosition(lastSeenItemPosition)
|
||||||
|
}
|
||||||
|
|
||||||
override fun sendMessage() {
|
override fun sendMessage() {
|
||||||
if (thread.isContactRecipient && thread.isBlocked) {
|
if (thread.isContactRecipient && thread.isBlocked) {
|
||||||
BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog")
|
BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog")
|
||||||
@ -905,10 +911,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun startRecordingVoiceMessage() {
|
override fun startRecordingVoiceMessage() {
|
||||||
|
if (Permissions.hasAll(this, Manifest.permission.RECORD_AUDIO)) {
|
||||||
showVoiceMessageUI()
|
showVoiceMessageUI()
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
audioRecorder.startRecording()
|
audioRecorder.startRecording()
|
||||||
stopAudioHandler.postDelayed(stopVoiceMessageRecordingTask, 60000) // Limit voice messages to 1 minute each
|
stopAudioHandler.postDelayed(stopVoiceMessageRecordingTask, 60000) // Limit voice messages to 1 minute each
|
||||||
|
} else {
|
||||||
|
Permissions.with(this)
|
||||||
|
.request(Manifest.permission.RECORD_AUDIO)
|
||||||
|
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
|
||||||
|
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendVoiceMessage() {
|
override fun sendVoiceMessage() {
|
||||||
|
@ -8,6 +8,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
import kotlinx.android.synthetic.main.view_visible_message.view.*
|
import kotlinx.android.synthetic.main.view_visible_message.view.*
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
@ -20,6 +21,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
|
|||||||
: CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
: CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
|
||||||
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
|
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
|
||||||
var selectedItems = mutableSetOf<MessageRecord>()
|
var selectedItems = mutableSetOf<MessageRecord>()
|
||||||
|
var visibleMessageContentViewDelegate: VisibleMessageContentViewDelegate? = null
|
||||||
|
|
||||||
sealed class ViewType(val rawValue: Int) {
|
sealed class ViewType(val rawValue: Int) {
|
||||||
object Visible : ViewType(0)
|
object Visible : ViewType(0)
|
||||||
@ -72,6 +74,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
|
|||||||
view.onPress = { rawX, rawY -> onItemPress(message, viewHolder.adapterPosition, view, Rect(rawX, rawY, rawX, rawY)) }
|
view.onPress = { rawX, rawY -> onItemPress(message, viewHolder.adapterPosition, view, Rect(rawX, rawY, rawX, rawY)) }
|
||||||
view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
|
view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
|
||||||
view.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition) }
|
view.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition) }
|
||||||
|
view.contentViewDelegate = visibleMessageContentViewDelegate
|
||||||
}
|
}
|
||||||
is ControlMessageViewHolder -> viewHolder.view.bind(message)
|
is ControlMessageViewHolder -> viewHolder.view.bind(message)
|
||||||
}
|
}
|
||||||
@ -113,8 +116,19 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
|
|||||||
if (lastSeenTimestamp <= 0L || cursor == null || !isActiveCursor) return null
|
if (lastSeenTimestamp <= 0L || cursor == null || !isActiveCursor) return null
|
||||||
for (i in 0 until itemCount) {
|
for (i in 0 until itemCount) {
|
||||||
cursor.moveToPosition(i)
|
cursor.moveToPosition(i)
|
||||||
val messageRecord = messageDB.readerFor(cursor).current
|
val message = messageDB.readerFor(cursor).current
|
||||||
if (messageRecord.isOutgoing || messageRecord.dateReceived <= lastSeenTimestamp) { return i }
|
if (message.isOutgoing || message.dateReceived <= lastSeenTimestamp) { return i }
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItemPositionForTimestamp(timestamp: Long): Int? {
|
||||||
|
val cursor = this.cursor
|
||||||
|
if (timestamp <= 0L || cursor == null || !isActiveCursor) return null
|
||||||
|
for (i in 0 until itemCount) {
|
||||||
|
cursor.moveToPosition(i)
|
||||||
|
val message = messageDB.readerFor(cursor).current
|
||||||
|
if (message.dateSent == timestamp) { return i }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,12 @@ import android.text.SpannableStringBuilder
|
|||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
|
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
import org.session.libsession.messaging.open_groups.OpenGroupV2
|
||||||
import org.session.libsession.utilities.OpenGroupUrlParser
|
import org.session.libsession.utilities.OpenGroupUrlParser
|
||||||
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
||||||
@ -33,8 +35,11 @@ class JoinOpenGroupDialog(private val name: String, private val url: String) : B
|
|||||||
|
|
||||||
private fun join() {
|
private fun join() {
|
||||||
val openGroup = OpenGroupUrlParser.parseUrl(url)
|
val openGroup = OpenGroupUrlParser.parseUrl(url)
|
||||||
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, requireContext())
|
val activity = requireContext() as AppCompatActivity
|
||||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(requireContext())
|
ThreadUtils.queue {
|
||||||
|
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity)
|
||||||
|
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(activity)
|
||||||
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,6 +32,9 @@ class ControlMessageView : LinearLayout {
|
|||||||
if (message.isExpirationTimerUpdate) {
|
if (message.isExpirationTimerUpdate) {
|
||||||
iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_timer, context.theme))
|
iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_timer, context.theme))
|
||||||
iconImageView.visibility = View.VISIBLE
|
iconImageView.visibility = View.VISIBLE
|
||||||
|
} else if (message.isMediaSavedNotification) {
|
||||||
|
iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_file_download_white_36dp, context.theme))
|
||||||
|
iconImageView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
textView.text = message.getDisplayBody(context)
|
textView.text = message.getDisplayBody(context)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import androidx.core.graphics.BlendModeCompat
|
|||||||
import androidx.core.text.getSpans
|
import androidx.core.text.getSpans
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.text.util.LinkifyCompat
|
import androidx.core.text.util.LinkifyCompat
|
||||||
|
import kotlinx.android.synthetic.main.view_link_preview.view.*
|
||||||
import kotlinx.android.synthetic.main.view_visible_message_content.view.*
|
import kotlinx.android.synthetic.main.view_visible_message_content.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.utilities.ThemeUtil
|
import org.session.libsession.utilities.ThemeUtil
|
||||||
@ -41,6 +42,7 @@ import kotlin.math.roundToInt
|
|||||||
class VisibleMessageContentView : LinearLayout {
|
class VisibleMessageContentView : LinearLayout {
|
||||||
var onContentClick: ((rawRect: Rect) -> Unit)? = null
|
var onContentClick: ((rawRect: Rect) -> Unit)? = null
|
||||||
var onContentDoubleTap: (() -> Unit)? = null
|
var onContentDoubleTap: (() -> Unit)? = null
|
||||||
|
var delegate: VisibleMessageContentViewDelegate? = null
|
||||||
|
|
||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
constructor(context: Context) : super(context) { initialize() }
|
constructor(context: Context) : super(context) { initialize() }
|
||||||
@ -87,6 +89,13 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message)
|
val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message)
|
||||||
ViewUtil.setPaddingTop(bodyTextView, 0)
|
ViewUtil.setPaddingTop(bodyTextView, 0)
|
||||||
mainContainer.addView(bodyTextView)
|
mainContainer.addView(bodyTextView)
|
||||||
|
onContentClick = { rect ->
|
||||||
|
val r = Rect()
|
||||||
|
quoteView.getGlobalVisibleRect(r)
|
||||||
|
if (r.contains(rect)) {
|
||||||
|
delegate?.scrollToMessageIfPossible(quote.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
|
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
|
||||||
val voiceMessageView = VoiceMessageView(context)
|
val voiceMessageView = VoiceMessageView(context)
|
||||||
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||||
@ -189,3 +198,8 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VisibleMessageContentViewDelegate {
|
||||||
|
|
||||||
|
fun scrollToMessageIfPossible(timestamp: Long)
|
||||||
|
}
|
@ -48,6 +48,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
var onPress: ((rawX: Int, rawY: Int) -> Unit)? = null
|
var onPress: ((rawX: Int, rawY: Int) -> Unit)? = null
|
||||||
var onSwipeToReply: (() -> Unit)? = null
|
var onSwipeToReply: (() -> Unit)? = null
|
||||||
var onLongPress: (() -> Unit)? = null
|
var onLongPress: (() -> Unit)? = null
|
||||||
|
var contentViewDelegate: VisibleMessageContentViewDelegate? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val swipeToReplyThreshold = 80.0f // dp
|
const val swipeToReplyThreshold = 80.0f // dp
|
||||||
@ -139,6 +140,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
|
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
|
||||||
// Populate content view
|
// Populate content view
|
||||||
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread)
|
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread)
|
||||||
|
messageContentView.delegate = contentViewDelegate
|
||||||
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
|
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +241,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
} else {
|
} else {
|
||||||
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
|
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
|
||||||
}
|
}
|
||||||
|
if (translationX > 0) { return } // Only allow swipes to the left
|
||||||
// The idea here is to asymptotically approach a maximum drag distance
|
// The idea here is to asymptotically approach a maximum drag distance
|
||||||
val damping = 50.0f
|
val damping = 50.0f
|
||||||
val sign = -1.0f
|
val sign = -1.0f
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/menu_badge_icon"
|
android:id="@+id/menu_badge_icon"
|
||||||
android:layout_width="20dp"
|
android:layout_width="16dp"
|
||||||
android:layout_height="20dp"
|
android:layout_height="16dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:src="@drawable/ic_timer"
|
android:src="@drawable/ic_timer"
|
||||||
android:background="@color/transparent"
|
android:background="@color/transparent"
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingVertical="@dimen/medium_spacing"
|
android:paddingVertical="@dimen/medium_spacing"
|
||||||
android:paddingHorizontal="@dimen/massive_spacing"
|
android:paddingHorizontal="@dimen/massive_spacing"
|
||||||
@ -12,7 +13,8 @@
|
|||||||
android:id="@+id/iconImageView"
|
android:id="@+id/iconImageView"
|
||||||
android:layout_width="12dp"
|
android:layout_width="12dp"
|
||||||
android:layout_height="12dp"
|
android:layout_height="12dp"
|
||||||
android:layout_marginBottom="@dimen/small_spacing" />
|
android:layout_marginBottom="@dimen/small_spacing"
|
||||||
|
app:tint="@color/text" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView"
|
android:id="@+id/textView"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user