mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 16:57:50 +00:00
Merge branch 'dev' into fix-dialog-button
This commit is contained in:
commit
c28eba313a
@ -39,15 +39,20 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||
public int getDesiredTheme() {
|
||||
ThemeState themeState = ActivityUtilitiesKt.themeState(getPreferences());
|
||||
int userSelectedTheme = themeState.getTheme();
|
||||
|
||||
// If the user has configured Session to follow the system light/dark theme mode then do so..
|
||||
if (themeState.getFollowSystem()) {
|
||||
// do light or dark based on the selected theme
|
||||
|
||||
// Use light or dark versions of the user's theme based on light-mode / dark-mode settings
|
||||
boolean isDayUi = UiModeUtilities.isDayUiMode(this);
|
||||
if (userSelectedTheme == R.style.Ocean_Dark || userSelectedTheme == R.style.Ocean_Light) {
|
||||
return isDayUi ? R.style.Ocean_Light : R.style.Ocean_Dark;
|
||||
} else {
|
||||
return isDayUi ? R.style.Classic_Light : R.style.Classic_Dark;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else // ..otherwise just return their selected theme.
|
||||
{
|
||||
return userSelectedTheme;
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,14 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.LinearLayout.VERTICAL
|
||||
import android.widget.Space
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.core.view.updateMargins
|
||||
import androidx.fragment.app.Fragment
|
||||
import network.loki.messenger.R
|
||||
@ -36,7 +37,9 @@ class SessionDialogBuilder(val context: Context) {
|
||||
private var dialog: AlertDialog? = null
|
||||
private fun dismiss() = dialog?.dismiss()
|
||||
|
||||
private val topView = LinearLayout(context).apply { orientation = VERTICAL }
|
||||
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 }
|
||||
private val buttonLayout = LinearLayout(context)
|
||||
@ -52,14 +55,14 @@ class SessionDialogBuilder(val context: Context) {
|
||||
|
||||
fun title(text: CharSequence?) = title(text?.toString())
|
||||
fun title(text: String?) {
|
||||
text(text, R.style.TextAppearance_AppCompat_Title) { setPadding(dp20) }
|
||||
text(text, R.style.TextAppearance_AppCompat_Title) { setPadding(dp20, 0, dp20, 0) }
|
||||
}
|
||||
|
||||
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, dp20) }
|
||||
.apply { updateMargins(dp40, 0, dp40, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +74,10 @@ class SessionDialogBuilder(val context: Context) {
|
||||
textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
modify()
|
||||
}.let(topView::addView)
|
||||
|
||||
Space(context).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(0, dp20)
|
||||
}.let(topView::addView)
|
||||
}
|
||||
|
||||
fun view(view: View) = contentView.addView(view)
|
||||
@ -123,7 +130,8 @@ class SessionDialogBuilder(val context: Context) {
|
||||
) = Button(context, null, 0, style).apply {
|
||||
setText(text)
|
||||
contentDescription = resources.getString(contentDescriptionRes)
|
||||
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, dp60, 1f)
|
||||
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, dp60, 1f)
|
||||
.apply { setMargins(dp20) }
|
||||
setOnClickListener {
|
||||
listener.invoke()
|
||||
if (dismiss) dismiss()
|
||||
|
@ -17,6 +17,7 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.getColorFromAttr
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
|
||||
@ -84,7 +85,7 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) {
|
||||
context.theme.resolveAttribute(item.iconRes, typedValue, true)
|
||||
icon.setImageDrawable(ContextCompat.getDrawable(context, typedValue.resourceId))
|
||||
|
||||
icon.imageTintList = color?.let(ColorStateList::valueOf)
|
||||
icon.imageTintList = ColorStateList.valueOf(color ?: context.getColorFromAttr(android.R.attr.textColor))
|
||||
}
|
||||
item.contentDescription?.let(context.resources::getString)?.let { itemView.contentDescription = it }
|
||||
title.setText(item.title)
|
||||
|
@ -140,6 +140,8 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
|
||||
}
|
||||
|
||||
fun draftQuote(thread: Recipient, message: MessageRecord, glide: GlideRequests) {
|
||||
quoteView?.let(binding.inputBarAdditionalContentContainer::removeView)
|
||||
|
||||
quote = message
|
||||
|
||||
// If we already have a link preview View then clear the 'additional content' layout so that
|
||||
|
@ -4,18 +4,24 @@ import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.Range
|
||||
import androidx.appcompat.widget.ThemeUtils
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import network.loki.messenger.R
|
||||
import nl.komponents.kovenant.combine.Tuple2
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.ThemeUtil
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||
import org.thoughtcrime.securesms.util.getAccentColor
|
||||
import org.thoughtcrime.securesms.util.getColorResourceIdFromAttr
|
||||
import org.thoughtcrime.securesms.util.getMessageTextColourAttr
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object MentionUtilities {
|
||||
@ -58,15 +64,37 @@ object MentionUtilities {
|
||||
}
|
||||
}
|
||||
val result = SpannableString(text)
|
||||
val isLightMode = UiModeUtilities.isDayUiMode(context)
|
||||
val color = if (isOutgoingMessage) {
|
||||
ResourcesCompat.getColor(context.resources, if (isLightMode) R.color.white else R.color.black, context.theme)
|
||||
} else {
|
||||
context.getAccentColor()
|
||||
|
||||
var mentionTextColour: Int? = null
|
||||
// In dark themes..
|
||||
if (ThemeUtil.isDarkTheme(context)) {
|
||||
// ..we use the standard outgoing message colour for outgoing messages..
|
||||
if (isOutgoingMessage) {
|
||||
val mentionTextColourAttributeId = getMessageTextColourAttr(true)
|
||||
val mentionTextColourResourceId = getColorResourceIdFromAttr(context, mentionTextColourAttributeId)
|
||||
mentionTextColour = ResourcesCompat.getColor(context.resources, mentionTextColourResourceId, context.theme)
|
||||
}
|
||||
else // ..but we use the accent colour for incoming messages (i.e., someone mentioning us)..
|
||||
{
|
||||
mentionTextColour = context.getAccentColor()
|
||||
}
|
||||
}
|
||||
else // ..while in light themes we always just use the incoming or outgoing message text colour for mentions.
|
||||
{
|
||||
val mentionTextColourAttributeId = getMessageTextColourAttr(isOutgoingMessage)
|
||||
val mentionTextColourResourceId = getColorResourceIdFromAttr(context, mentionTextColourAttributeId)
|
||||
mentionTextColour = ResourcesCompat.getColor(context.resources, mentionTextColourResourceId, context.theme)
|
||||
}
|
||||
|
||||
for (mention in mentions) {
|
||||
result.setSpan(ForegroundColorSpan(color), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
result.setSpan(ForegroundColorSpan(mentionTextColour), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
result.setSpan(StyleSpan(Typeface.BOLD), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
// If we're using a light theme then we change the background colour of the mention to be the accent colour
|
||||
if (ThemeUtil.isLightTheme(context)) {
|
||||
val backgroundColour = context.getAccentColor();
|
||||
result.setSpan(BackgroundColorSpan(backgroundColour), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -320,6 +320,19 @@ public class MmsSmsDatabase extends Database {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public long getLastMessageTimestamp(long threadId) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
|
||||
|
||||
try (Cursor cursor = queryTables(PROJECTION, selection, order, "1")) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.NORMALIZED_DATE_SENT));
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Cursor getUnread() {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " ASC";
|
||||
String selection = "(" + MmsSmsColumns.READ + " = 0 OR " + MmsSmsColumns.REACTIONS_UNREAD + " = 1) AND " + MmsSmsColumns.NOTIFIED + " = 0";
|
||||
|
@ -934,7 +934,17 @@ public class ThreadDatabase extends Database {
|
||||
readReceiptCount = 0;
|
||||
}
|
||||
|
||||
return new ThreadRecord(body, snippetUri, recipient, date, count,
|
||||
MessageRecord lastMessage = null;
|
||||
|
||||
if (count > 0) {
|
||||
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
|
||||
long messageTimestamp = mmsSmsDatabase.getLastMessageTimestamp(threadId);
|
||||
if (messageTimestamp > 0) {
|
||||
lastMessage = mmsSmsDatabase.getMessageForTimestamp(messageTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
return new ThreadRecord(body, snippetUri, lastMessage, recipient, date, count,
|
||||
unreadCount, unreadMentionCount, threadId, deliveryReceiptCount, status, type,
|
||||
distributionType, archived, expiresIn, lastSeen, readReceiptCount, pinned);
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import network.loki.messenger.R;
|
||||
public class ThreadRecord extends DisplayRecord {
|
||||
|
||||
private @Nullable final Uri snippetUri;
|
||||
public @Nullable final MessageRecord lastMessage;
|
||||
private final long count;
|
||||
private final int unreadCount;
|
||||
private final int unreadMentionCount;
|
||||
@ -54,13 +55,14 @@ public class ThreadRecord extends DisplayRecord {
|
||||
private final int initialRecipientHash;
|
||||
|
||||
public ThreadRecord(@NonNull String body, @Nullable Uri snippetUri,
|
||||
@NonNull Recipient recipient, long date, long count, int unreadCount,
|
||||
@Nullable MessageRecord lastMessage, @NonNull Recipient recipient, long date, long count, int unreadCount,
|
||||
int unreadMentionCount, long threadId, int deliveryReceiptCount, int status,
|
||||
long snippetType, int distributionType, boolean archived, long expiresIn,
|
||||
long lastSeen, int readReceiptCount, boolean pinned)
|
||||
{
|
||||
super(body, recipient, date, date, threadId, status, deliveryReceiptCount, snippetType, readReceiptCount);
|
||||
this.snippetUri = snippetUri;
|
||||
this.lastMessage = lastMessage;
|
||||
this.count = count;
|
||||
this.unreadCount = unreadCount;
|
||||
this.unreadMentionCount = unreadMentionCount;
|
||||
|
@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.text.SpannableString
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
@ -89,7 +91,7 @@ class ConversationView : LinearLayout {
|
||||
|| (configFactory.convoVolatile?.getConversationUnread(thread) == true)
|
||||
binding.unreadMentionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize)
|
||||
binding.unreadMentionIndicator.isVisible = (thread.unreadMentionCount != 0 && thread.recipient.address.isGroup)
|
||||
val senderDisplayName = getUserDisplayName(thread.recipient)
|
||||
val senderDisplayName = getTitle(thread.recipient)
|
||||
?: thread.recipient.address.toString()
|
||||
binding.conversationViewDisplayNameTextView.text = senderDisplayName
|
||||
binding.timestampTextView.text = thread.date.takeIf { it != 0L }?.let { DateUtils.getDisplayFormattedTimeSpanString(context, Locale.getDefault(), it) }
|
||||
@ -101,9 +103,7 @@ class ConversationView : LinearLayout {
|
||||
R.drawable.ic_notifications_mentions
|
||||
}
|
||||
binding.muteIndicatorImageView.setImageResource(drawableRes)
|
||||
val rawSnippet = thread.getDisplayBody(context)
|
||||
val snippet = highlightMentions(rawSnippet, thread.threadId, context)
|
||||
binding.snippetTextView.text = snippet
|
||||
binding.snippetTextView.text = highlightMentions(thread.getSnippet(), thread.threadId, context)
|
||||
binding.snippetTextView.typeface = if (unreadCount > 0 && !thread.isRead) Typeface.DEFAULT_BOLD else Typeface.DEFAULT
|
||||
binding.snippetTextView.visibility = if (isTyping) View.GONE else View.VISIBLE
|
||||
if (isTyping) {
|
||||
@ -131,12 +131,21 @@ class ConversationView : LinearLayout {
|
||||
binding.profilePictureView.recycle()
|
||||
}
|
||||
|
||||
private fun getUserDisplayName(recipient: Recipient): String? {
|
||||
return if (recipient.isLocalNumber) {
|
||||
context.getString(R.string.note_to_self)
|
||||
} else {
|
||||
recipient.toShortString() // Internally uses the Contact API
|
||||
private fun getTitle(recipient: Recipient): String? = when {
|
||||
recipient.isLocalNumber -> context.getString(R.string.note_to_self)
|
||||
else -> recipient.toShortString() // Internally uses the Contact API
|
||||
}
|
||||
|
||||
private fun ThreadRecord.getSnippet(): CharSequence =
|
||||
concatSnippet(getSnippetPrefix(), getDisplayBody(context))
|
||||
|
||||
private fun concatSnippet(prefix: CharSequence?, body: CharSequence): CharSequence =
|
||||
prefix?.let { TextUtils.concat(it, ": ", body) } ?: body
|
||||
|
||||
private fun ThreadRecord.getSnippetPrefix(): CharSequence? = when {
|
||||
recipient.isLocalNumber || lastMessage?.isControlMessage == true -> null
|
||||
lastMessage?.isOutgoing == true -> resources.getString(R.string.MessageRecord_you)
|
||||
else -> lastMessage?.individualRecipient?.toShortString()
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -9,12 +9,15 @@ import android.graphics.Bitmap
|
||||
import android.graphics.PointF
|
||||
import android.graphics.Rect
|
||||
import android.util.Size
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.DimenRes
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.getColorFromAttr
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.graphics.applyCanvas
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -32,6 +35,20 @@ val View.hitRect: Rect
|
||||
@ColorInt
|
||||
fun Context.getAccentColor() = getColorFromAttr(R.attr.colorAccent)
|
||||
|
||||
// Method to grab the appropriate attribute for a message colour.
|
||||
// Note: This is an attribute, NOT a resource Id - see `getColorResourceIdFromAttr` for that.
|
||||
@AttrRes
|
||||
fun getMessageTextColourAttr(messageIsOutgoing: Boolean): Int =
|
||||
if (messageIsOutgoing) R.attr.message_sent_text_color else R.attr.message_received_text_color
|
||||
|
||||
// Method to get an actual R.id.<SOME_COLOUR> resource Id from an attribute such as R.attr.message_sent_text_color etc.
|
||||
@ColorRes
|
||||
fun getColorResourceIdFromAttr(context: Context, attr: Int): Int {
|
||||
val typedValue = TypedValue()
|
||||
context.theme.resolveAttribute(attr, typedValue, true)
|
||||
return typedValue.resourceId
|
||||
}
|
||||
|
||||
fun View.animateSizeChange(@DimenRes startSizeID: Int, @DimenRes endSizeID: Int, animationDuration: Long = 250) {
|
||||
val startSize = resources.getDimension(startSizeID)
|
||||
val endSize = resources.getDimension(endSizeID)
|
||||
@ -70,7 +87,6 @@ fun View.hideKeyboard() {
|
||||
imm.hideSoftInputFromWindow(this.windowToken, 0)
|
||||
}
|
||||
|
||||
|
||||
fun View.drawToBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888, longestWidth: Int = 2000): Bitmap {
|
||||
val size = Size(measuredWidth, measuredHeight).coerceAtMost(longestWidth)
|
||||
val scale = size.width / measuredWidth.toFloat()
|
||||
|
@ -2,7 +2,7 @@ package org.session.libsession.messaging.mentions
|
||||
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
object MentionsManager {
|
||||
var userPublicKeyCache = mutableMapOf<Long, Set<String>>() // Thread ID to set of user hex encoded public keys
|
||||
|
@ -27,6 +27,10 @@ public class ThemeUtil {
|
||||
return getAttributeText(context, R.attr.theme_type, "light").equals("dark");
|
||||
}
|
||||
|
||||
public static boolean isLightTheme(@NonNull Context context) {
|
||||
return getAttributeText(context, R.attr.theme_type, "light").equals("light");
|
||||
}
|
||||
|
||||
public static boolean getThemedBoolean(@NonNull Context context, @AttrRes int attr) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
|
Loading…
x
Reference in New Issue
Block a user