Merge remote-tracking branch 'upstream/ui' into ui

# Conflicts:
#	app/src/main/java/org/thoughtcrime/securesms/components/AlbumThumbnailView.java
#	app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
#	app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt
This commit is contained in:
jubb 2021-06-24 16:26:45 +10:00
commit b5f41b4383
61 changed files with 1088 additions and 159 deletions

View File

@ -223,7 +223,7 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Theme.Session.DayNight.FlatActionBar" /> android:theme="@style/Theme.Session.DayNight.FlatActionBar" />
<activity <activity
android:name="org.thoughtcrime.securesms.conversation.v2.OpenGroupGuidelinesActivity" android:name="org.thoughtcrime.securesms.loki.activities.OpenGroupGuidelinesActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Theme.TextSecure.DayNight" /> android:theme="@style/Theme.TextSecure.DayNight" />
<activity <activity

View File

@ -26,7 +26,7 @@ import android.widget.TextView;
import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter; import com.codewaves.stickyheadergrid.StickyHeaderGridAdapter;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord; import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia; import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.BucketedThreadMedia;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;

View File

@ -12,7 +12,7 @@ import android.widget.TextView;
import network.loki.messenger.R; import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.v2.ThumbnailView; import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;

View File

@ -12,6 +12,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;

View File

@ -8,6 +8,7 @@ import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorView;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.ThemeUtil; import org.session.libsession.utilities.ThemeUtil;

View File

@ -5,6 +5,7 @@ import android.graphics.Canvas;
import android.util.AttributeSet; import android.util.AttributeSet;
import org.session.libsession.utilities.ThemeUtil; import org.session.libsession.utilities.ThemeUtil;
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import network.loki.messenger.R; import network.loki.messenger.R;

View File

@ -7,6 +7,7 @@ import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.Slide;

View File

@ -11,7 +11,6 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue; import android.util.TypedValue;
import network.loki.messenger.R; import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable; import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser; import org.thoughtcrime.securesms.components.emoji.parsing.EmojiParser;
@ -19,9 +18,7 @@ import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.Util; import org.session.libsession.utilities.Util;
import org.session.libsignal.utilities.guava.Optional; import org.session.libsignal.utilities.guava.Optional;
public class EmojiTextView extends AppCompatTextView { public class EmojiTextView extends AppCompatTextView {
private final boolean scaleEmojis; private final boolean scaleEmojis;
private static final char ELLIPSIS = '…'; private static final char ELLIPSIS = '…';
@ -46,14 +43,9 @@ public class EmojiTextView extends AppCompatTextView {
public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) { public EmojiTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EmojiTextView, 0, 0); scaleEmojis = true;
scaleEmojis = a.getBoolean(R.styleable.EmojiTextView_scaleEmojis, false); maxLength = 1000;
maxLength = a.getInteger(R.styleable.EmojiTextView_emoji_maxLength, -1); originalFontSize = getResources().getDimension(R.dimen.small_font_size);
a.recycle();
a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.textSize});
originalFontSize = a.getDimensionPixelSize(0, 0);
a.recycle();
} }
@Override public void setText(@Nullable CharSequence text, BufferType type) { @Override public void setText(@Nullable CharSequence text, BufferType type) {
@ -182,8 +174,11 @@ public class EmojiTextView extends AppCompatTextView {
@Override @Override
public void invalidateDrawable(@NonNull Drawable drawable) { public void invalidateDrawable(@NonNull Drawable drawable) {
if (drawable instanceof EmojiDrawable) invalidate(); if (drawable instanceof EmojiDrawable) {
else super.invalidateDrawable(drawable); invalidate();
} else {
super.invalidateDrawable(drawable);
}
} }
@Override @Override

View File

@ -5,25 +5,35 @@ import android.animation.ValueAnimator
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.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.util.TypedValue
import android.view.* import android.view.*
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProviders
import androidx.loader.app.LoaderManager import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader import androidx.loader.content.Loader
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_conversation_v2.* import kotlinx.android.synthetic.main.activity_conversation_v2.*
import kotlinx.android.synthetic.main.activity_conversation_v2.view.* import kotlinx.android.synthetic.main.activity_conversation_v2.view.*
import kotlinx.android.synthetic.main.activity_conversation_v2_action_bar.* import kotlinx.android.synthetic.main.activity_conversation_v2_action_bar.*
import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.view_conversation.view.*
import kotlinx.android.synthetic.main.view_input_bar.view.* import kotlinx.android.synthetic.main.view_input_bar.view.*
import kotlinx.android.synthetic.main.view_input_bar_recording.* import kotlinx.android.synthetic.main.view_input_bar_recording.*
import kotlinx.android.synthetic.main.view_input_bar_recording.view.* import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.ui.successUi
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.mentions.MentionsManager import org.session.libsession.messaging.mentions.MentionsManager
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.dialogs.*
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarRecordingViewDelegate import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarRecordingViewDelegate
@ -35,8 +45,13 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.DraftDatabase import org.thoughtcrime.securesms.database.DraftDatabase
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts import org.thoughtcrime.securesms.database.DraftDatabase.Drafts
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.util.DateUtils
import java.util.*
import kotlin.math.* import kotlin.math.*
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate, class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
@ -44,6 +59,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private val scrollButtonFullVisibilityThreshold by lazy { toPx(120.0f, resources) } private val scrollButtonFullVisibilityThreshold by lazy { toPx(120.0f, resources) }
private val scrollButtonNoVisibilityThreshold by lazy { toPx(20.0f, resources) } private val scrollButtonNoVisibilityThreshold by lazy { toPx(20.0f, resources) }
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private var linkPreviewViewModel: LinkPreviewViewModel? = null
private var threadID: Long = -1 private var threadID: Long = -1
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private var isLockViewExpanded = false private var isLockViewExpanded = false
@ -52,7 +68,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// TODO: Selected message background color // TODO: Selected message background color
// TODO: Overflow menu background + text color // TODO: Overflow menu background + text color
// TODO: Typing indicators
private val adapter by lazy { private val adapter by lazy {
val cursor = DatabaseFactory.getMmsSmsDatabase(this).getConversation(threadID) val cursor = DatabaseFactory.getMmsSmsDatabase(this).getConversation(threadID)
@ -101,6 +116,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
restoreDraftIfNeeded() restoreDraftIfNeeded()
addOpenGroupGuidelinesIfNeeded() addOpenGroupGuidelinesIfNeeded()
scrollToBottomButton.setOnClickListener { conversationRecyclerView.smoothScrollToPosition(0) } scrollToBottomButton.setOnClickListener { conversationRecyclerView.smoothScrollToPosition(0) }
updateUnreadCount()
setUpTypingObserver()
updateSubtitle()
getLatestOpenGroupInfoIfNeeded()
setUpBlockedBanner()
setUpLinkPreviewObserver()
} }
override fun onResume() { override fun onResume() {
@ -180,6 +201,47 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
conversationRecyclerView.layoutParams = recyclerViewLayoutParams conversationRecyclerView.layoutParams = recyclerViewLayoutParams
} }
private fun setUpTypingObserver() {
ApplicationContext.getInstance(this).typingStatusRepository.getTypists(threadID).observe(this) { state ->
val recipients = if (state != null) state.typists else listOf()
typingIndicatorViewContainer.isVisible = recipients.isNotEmpty()
typingIndicatorViewContainer.setTypists(recipients)
inputBarHeightChanged(inputBar.height)
}
}
private fun getLatestOpenGroupInfoIfNeeded() {
val openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID) ?: return
OpenGroupAPIV2.getMemberCount(openGroup.room, openGroup.server).successUi { updateSubtitle() }
}
private fun setUpBlockedBanner() {
if (thread.isGroupRecipient) { return }
val contactDB = DatabaseFactory.getSessionContactDatabase(this)
val sessionID = thread.address.toString()
val contact = contactDB.getContactWithSessionID(sessionID)
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID
blockedBannerTextView.text = resources.getString(R.string.activity_conversation_blocked_banner_text, name)
blockedBanner.isVisible = thread.isBlocked
blockedBanner.setOnClickListener { unblock() }
}
private fun setUpLinkPreviewObserver() {
val linkPreviewViewModel = ViewModelProviders.of(this, LinkPreviewViewModel.Factory(LinkPreviewRepository(this)))[LinkPreviewViewModel::class.java]
this.linkPreviewViewModel = linkPreviewViewModel
if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) {
linkPreviewViewModel.onUserCancel(); return
}
linkPreviewViewModel.linkPreviewState.observe(this, { previewState: LinkPreviewState? ->
if (previewState == null) return@observe
if (previewState.isLoading) {
inputBar.draftLinkPreview()
} else {
inputBar.updateLinkPreviewDraft(glide, previewState.linkPreview.get())
}
})
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean { override fun onPrepareOptionsMenu(menu: Menu): Boolean {
ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, this) { onOptionsItemSelected(it) } ConversationMenuHelper.onPrepareOptionsMenu(menu, menuInflater, thread, this) { onOptionsItemSelected(it) }
super.onPrepareOptionsMenu(menu) super.onPrepareOptionsMenu(menu)
@ -194,9 +256,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// region Updating & Animation // region Updating & Animation
override fun inputBarHeightChanged(newValue: Int) { override fun inputBarHeightChanged(newValue: Int) {
@Suppress("NAME_SHADOWING") val newValue = max(newValue, resources.getDimension(R.dimen.input_bar_height).roundToInt())
// 36 DP is the exact height of the typing indicator view. It's also exactly 18 * 2, and 18 is the large message
// corner radius. This makes 36 DP look "correct" in the context of other messages on the screen.
val typingIndicatorHeight = if (typingIndicatorViewContainer.isVisible) toPx(36, resources) else 0
// Recycler view // Recycler view
val recyclerViewLayoutParams = conversationRecyclerView.layoutParams as RelativeLayout.LayoutParams val recyclerViewLayoutParams = conversationRecyclerView.layoutParams as RelativeLayout.LayoutParams
recyclerViewLayoutParams.bottomMargin = newValue + additionalContentContainer.height recyclerViewLayoutParams.bottomMargin = newValue + additionalContentContainer.height + typingIndicatorHeight
conversationRecyclerView.layoutParams = recyclerViewLayoutParams conversationRecyclerView.layoutParams = recyclerViewLayoutParams
// Additional content container // Additional content container
val additionalContentContainerLayoutParams = additionalContentContainer.layoutParams as RelativeLayout.LayoutParams val additionalContentContainerLayoutParams = additionalContentContainer.layoutParams as RelativeLayout.LayoutParams
@ -216,6 +282,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
override fun inputBarEditTextContentChanged(newContent: CharSequence) { override fun inputBarEditTextContentChanged(newContent: CharSequence) {
linkPreviewViewModel?.onTextChanged(this, inputBar.text, 0, 0)
// TODO: Implement the full mention show/hide logic // TODO: Implement the full mention show/hide logic
if (newContent.contains("@")) { if (newContent.contains("@")) {
showMentionCandidates() showMentionCandidates()
@ -330,6 +397,35 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
(scrollButtonFullVisibilityThreshold - scrollButtonNoVisibilityThreshold) (scrollButtonFullVisibilityThreshold - scrollButtonNoVisibilityThreshold)
val alpha = max(min(rawAlpha, 1.0f), 0.0f) val alpha = max(min(rawAlpha, 1.0f), 0.0f)
scrollToBottomButton.alpha = alpha scrollToBottomButton.alpha = alpha
updateUnreadCount()
}
private fun updateUnreadCount() {
val unreadCount = DatabaseFactory.getMmsSmsDatabase(this).getUnreadCount(threadID)
val formattedUnreadCount = if (unreadCount < 100) unreadCount.toString() else "99+"
unreadCountTextView.text = formattedUnreadCount
val textSize = if (unreadCount < 100) 12.0f else 9.0f
unreadCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize)
unreadCountTextView.setTypeface(Typeface.DEFAULT, if (unreadCount < 100) Typeface.BOLD else Typeface.NORMAL)
unreadCountIndicator.isVisible = (unreadCount != 0)
}
private fun updateSubtitle() {
muteIconImageView.isVisible = thread.isMuted
conversationSubtitleView.isVisible = true
if (thread.isMuted) {
conversationSubtitleView.text = getString(R.string.ConversationActivity_muted_until_date, DateUtils.getFormattedDateTime(thread.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault()))
} else if (thread.isGroupRecipient) {
val openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID)
if (openGroup != null) {
val userCount = DatabaseFactory.getLokiAPIDatabase(this).getUserCount(openGroup.room, openGroup.server) ?: 0
conversationSubtitleView.text = getString(R.string.ConversationActivity_member_count, userCount)
} else {
conversationSubtitleView.isVisible = false
}
} else {
conversationSubtitleView.isVisible = false
}
} }
// endregion // endregion
@ -351,6 +447,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
this.actionMode = null this.actionMode = null
} }
} else { } else {
// NOTE:
// We have to use onContentClick (rather than a click listener directly on
// the view) so as to not interfere with all the other gestures. Do not add
// onClickListeners directly to message content views.
view.onContentClick() view.onContentClick()
} }
} }
@ -432,6 +532,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
lockViewLocation[0] + lockView.width + lockViewHitMargin, lockViewLocation[1] + lockView.height) lockViewLocation[0] + lockView.width + lockViewHitMargin, lockViewLocation[1] + lockView.height)
return hitRect.contains(x, y) return hitRect.contains(x, y)
} }
private fun unblock() {
// TODO: Implement
}
// endregion // endregion
// region General // region General

View File

@ -5,6 +5,7 @@ import android.util.AttributeSet
import android.util.Log import android.util.Log
import android.view.MotionEvent import android.view.MotionEvent
import android.view.VelocityTracker import android.view.VelocityTracker
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_conversation_v2.* import kotlinx.android.synthetic.main.activity_conversation_v2.*
import org.thoughtcrime.securesms.loki.utilities.disableClipping import org.thoughtcrime.securesms.loki.utilities.disableClipping

View File

@ -0,0 +1,38 @@
package org.thoughtcrime.securesms.conversation.v2.components
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_link_preview_draft.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.ImageSlide
class LinkPreviewDraftView : LinearLayout {
// region Lifecycle
constructor(context: Context) : super(context) { initialize() }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
private fun initialize() {
LayoutInflater.from(context).inflate(R.layout.view_link_preview_draft, this)
linkPreviewDraftContainer.isVisible = false
thumbnailImageView.clipToOutline = true
}
// endregion
// region Updating
fun update(glide: GlideRequests, linkPreview: LinkPreview) {
linkPreviewDraftContainer.isVisible = true
linkPreviewDraftLoader.isVisible = false
if (linkPreview.getThumbnail().isPresent) {
thumbnailImageView.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), false, false)
}
linkPreviewDraftTitleTextView.text = linkPreview.title
}
// endregion
}

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.conversation.v2 package org.thoughtcrime.securesms.conversation.v2.components
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -7,6 +7,8 @@ import android.view.LayoutInflater
import android.widget.FrameLayout import android.widget.FrameLayout
import kotlinx.android.synthetic.main.view_open_group_guidelines.view.* import kotlinx.android.synthetic.main.view_open_group_guidelines.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.loki.activities.OpenGroupGuidelinesActivity
import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.push
class OpenGroupGuidelinesView : FrameLayout { class OpenGroupGuidelinesView : FrameLayout {

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.components; package org.thoughtcrime.securesms.conversation.v2.components;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@ -13,18 +13,14 @@ import android.widget.LinearLayout;
import network.loki.messenger.R; import network.loki.messenger.R;
public class TypingIndicatorView extends LinearLayout { public class TypingIndicatorView extends LinearLayout {
private boolean isActive;
private long startTime;
private static final long DURATION = 300;
private static final long PRE_DELAY = 500;
private static final long POST_DELAY = 500;
private static final long CYCLE_DURATION = 1500; private static final long CYCLE_DURATION = 1500;
private static final long DOT_DURATION = 600; private static final long DOT_DURATION = 600;
private static final float MIN_ALPHA = 0.4f; private static final float MIN_ALPHA = 0.4f;
private static final float MIN_SCALE = 0.75f; private static final float MIN_SCALE = 0.75f;
private boolean isActive;
private long startTime;
private View dot1; private View dot1;
private View dot2; private View dot2;
private View dot3; private View dot3;
@ -40,7 +36,7 @@ public class TypingIndicatorView extends LinearLayout {
} }
private void initialize(@Nullable AttributeSet attrs) { private void initialize(@Nullable AttributeSet attrs) {
inflate(getContext(), R.layout.typing_indicator_view, this); inflate(getContext(), R.layout.view_typing_indicator, this);
setWillNotDraw(false); setWillNotDraw(false);

View File

@ -0,0 +1,25 @@
package org.thoughtcrime.securesms.conversation.v2.components
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_conversation_typing_container.view.*
import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient
class TypingIndicatorViewContainer : LinearLayout {
constructor(context: Context) : super(context) { initialize() }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
private fun initialize() {
LayoutInflater.from(context).inflate(R.layout.view_conversation_typing_container, this)
}
fun setTypists(typists: List<Recipient>) {
if (typists.isEmpty()) { typingIndicator.stopAnimation(); return }
typingIndicator.startAnimation()
}
}

View File

@ -0,0 +1,40 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.graphics.Typeface
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_blocked.view.*
import kotlinx.android.synthetic.main.dialog_blocked.view.cancelButton
import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.database.DatabaseFactory
class BlockedDialog(private val recipient: Recipient) : BaseDialog() {
override fun setContentView(builder: AlertDialog.Builder) {
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_blocked, null)
val contactDB = DatabaseFactory.getSessionContactDatabase(requireContext())
val sessionID = recipient.address.toString()
val contact = contactDB.getContactWithSessionID(sessionID)
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID
val title = resources.getString(R.string.dialog_blocked_title, name)
contentView.blockedTitleTextView.text = title
val explanation = resources.getString(R.string.dialog_blocked_explanation, name)
val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
contentView.blockedExplanationTextView.text = spannable
contentView.cancelButton.setOnClickListener { dismiss() }
contentView.unblockButton.setOnClickListener { unblock() }
builder.setView(contentView)
}
private fun unblock() {
// TODO: Implement
}
}

View File

@ -0,0 +1,39 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.graphics.Typeface
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_download.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.database.DatabaseFactory
class DownloadDialog(private val recipient: Recipient) : BaseDialog() {
override fun setContentView(builder: AlertDialog.Builder) {
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_download, null)
val contactDB = DatabaseFactory.getSessionContactDatabase(requireContext())
val sessionID = recipient.address.toString()
val contact = contactDB.getContactWithSessionID(sessionID)
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID
val title = resources.getString(R.string.dialog_download_title, name)
contentView.downloadTitleTextView.text = title
val explanation = resources.getString(R.string.dialog_download_explanation, name)
val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
contentView.downloadExplanationTextView.text = spannable
contentView.cancelButton.setOnClickListener { dismiss() }
contentView.downloadButton.setOnClickListener { trust() }
builder.setView(contentView)
}
private fun trust() {
// TODO: Implement
}
}

View File

@ -0,0 +1,34 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.graphics.Typeface
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.open_groups.OpenGroupV2
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
class JoinOpenGroupDialog(private val openGroup: OpenGroupV2) : BaseDialog() {
override fun setContentView(builder: AlertDialog.Builder) {
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_join_open_group, null)
val name = openGroup.name
val title = resources.getString(R.string.dialog_join_open_group_title, name)
contentView.joinOpenGroupTitleTextView.text = title
val explanation = resources.getString(R.string.dialog_join_open_group_explanation, name)
val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
contentView.joinOpenGroupExplanationTextView.text = spannable
contentView.cancelButton.setOnClickListener { dismiss() }
contentView.joinButton.setOnClickListener { join() }
builder.setView(contentView)
}
private fun join() {
// TODO: Implement
}
}

View File

@ -0,0 +1,21 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_link_preview.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
class LinkPreviewDialog() : BaseDialog() {
override fun setContentView(builder: AlertDialog.Builder) {
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_link_preview, null)
contentView.cancelButton.setOnClickListener { dismiss() }
contentView.enableLinkPreviewsButton.setOnClickListener { enable() }
builder.setView(contentView)
}
private fun enable() {
// TODO: Implement
}
}

View File

@ -0,0 +1,30 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.graphics.Typeface
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_open_url.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
class OpenURLDialog(private val url: String) : BaseDialog() {
override fun setContentView(builder: AlertDialog.Builder) {
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_open_url, null)
val explanation = resources.getString(R.string.dialog_open_url_explanation, url)
val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(url)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + url.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
contentView.openURLExplanationTextView.text = spannable
contentView.cancelButton.setOnClickListener { dismiss() }
contentView.openURLButton.setOnClickListener { open() }
builder.setView(contentView)
}
private fun open() {
// TODO: Implement
}
}

View File

@ -2,24 +2,23 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.text.Editable
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.widget.LinearLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_input_bar.view.* import kotlinx.android.synthetic.main.view_input_bar.view.*
import kotlinx.android.synthetic.main.view_quote.view.* import kotlinx.android.synthetic.main.view_quote.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
import org.thoughtcrime.securesms.conversation.v2.components.LinkPreviewDraftView
import org.thoughtcrime.securesms.conversation.v2.messages.QuoteView import org.thoughtcrime.securesms.conversation.v2.messages.QuoteView
import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate import org.thoughtcrime.securesms.conversation.v2.messages.QuoteViewDelegate
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.loki.utilities.toDp import org.thoughtcrime.securesms.loki.utilities.toDp
import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.mms.GlideRequests
import kotlin.math.max import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -27,6 +26,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate {
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private val vMargin by lazy { toDp(4, resources) } private val vMargin by lazy { toDp(4, resources) }
private val minHeight by lazy { toPx(56, resources) } private val minHeight by lazy { toPx(56, resources) }
private var linkPreviewDraftView: LinkPreviewDraftView? = null
var delegate: InputBarDelegate? = null var delegate: InputBarDelegate? = null
var additionalContentHeight = 0 var additionalContentHeight = 0
@ -96,6 +96,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate {
} }
fun draftQuote(message: MessageRecord) { fun draftQuote(message: MessageRecord) {
linkPreviewDraftView = null
inputBarAdditionalContentContainer.removeAllViews() inputBarAdditionalContentContainer.removeAllViews()
val quoteView = QuoteView(context, QuoteView.Mode.Draft) val quoteView = QuoteView(context, QuoteView.Mode.Draft)
quoteView.delegate = this quoteView.delegate = this
@ -121,6 +122,22 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate {
additionalContentHeight = 0 additionalContentHeight = 0
setHeight(newHeight) setHeight(newHeight)
} }
fun draftLinkPreview() {
val linkPreviewDraftHeight = toPx(88, resources)
inputBarAdditionalContentContainer.removeAllViews()
val linkPreviewDraftView = LinkPreviewDraftView(context)
this.linkPreviewDraftView = linkPreviewDraftView
inputBarAdditionalContentContainer.addView(linkPreviewDraftView)
val newHeight = max(inputBarEditText.height + 2 * vMargin, minHeight) + linkPreviewDraftHeight
additionalContentHeight = linkPreviewDraftHeight
setHeight(newHeight)
}
fun updateLinkPreviewDraft(glide: GlideRequests, linkPreview: LinkPreview) {
val linkPreviewDraftView = this.linkPreviewDraftView ?: return
linkPreviewDraftView.update(glide, linkPreview)
}
// endregion // endregion
} }

View File

@ -1,16 +1,9 @@
package org.thoughtcrime.securesms.conversation.v2.messages package org.thoughtcrime.securesms.conversation.v2.messages
import android.content.Context import android.content.Context
import android.graphics.Canvas
import android.graphics.Outline
import android.graphics.Path
import android.graphics.RectF
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewOutlineProvider import android.view.ViewOutlineProvider
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@ -19,6 +12,7 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.ImageSlide
class LinkPreviewView : LinearLayout { class LinkPreviewView : LinearLayout {
@ -39,10 +33,9 @@ class LinkPreviewView : LinearLayout {
mainLinkPreviewContainer.clipToOutline = true mainLinkPreviewContainer.clipToOutline = true
// Thumbnail // Thumbnail
val linkPreview = message.linkPreviews.first() val linkPreview = message.linkPreviews.first()
// TODO: Handle downloading state if (linkPreview.getThumbnail().isPresent) {
val uri = linkPreview.thumbnail.get().dataUri ?: return thumbnailImageView.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), false, false)
glide.load(uri).into(thumbnailImageView) }
// TODO: Properly use glide and the actual thumbnail
// Title // Title
titleTextView.text = linkPreview.title titleTextView.text = linkPreview.title
val textColorID = if (message.isOutgoing && UiModeUtilities.isDayUiMode(context)) { val textColorID = if (message.isOutgoing && UiModeUtilities.isDayUiMode(context)) {

View File

@ -13,26 +13,24 @@ import androidx.annotation.DrawableRes
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat import androidx.core.graphics.BlendModeCompat
import androidx.core.text.toSpannable
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
import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.ViewUtil
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.components.v2.AlbumThumbnailView import org.thoughtcrime.securesms.components.v2.AlbumThumbnailView
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.loki.utilities.UiMode import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities import org.thoughtcrime.securesms.loki.utilities.MentionUtilities.highlightMentions
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import kotlin.math.roundToInt import kotlin.math.roundToInt
class VisibleMessageContentView : LinearLayout { class VisibleMessageContentView : LinearLayout {
var onContentClick: (() -> Unit)? = null var onContentClick: (() -> Unit)? = null
// TODO: Large emojis
// region Lifecycle // region Lifecycle
constructor(context: Context) : super(context) { initialize() } constructor(context: Context) : super(context) { initialize() }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
@ -124,16 +122,18 @@ class VisibleMessageContentView : LinearLayout {
companion object { companion object {
fun getBodyTextView(context: Context, message: MessageRecord): TextView { fun getBodyTextView(context: Context, message: MessageRecord): TextView {
val result = TextView(context) val result = EmojiTextView(context)
val vPadding = context.resources.getDimension(R.dimen.small_spacing).toInt() val vPadding = context.resources.getDimension(R.dimen.small_spacing).toInt()
val hPadding = toPx(12, context.resources) val hPadding = toPx(12, context.resources)
result.setPadding(hPadding, vPadding, hPadding, vPadding) result.setPadding(hPadding, vPadding, hPadding, vPadding)
result.text = message.body
result.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(R.dimen.small_font_size)) result.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.resources.getDimension(R.dimen.small_font_size))
val color = getTextColor(context, message) val color = getTextColor(context, message)
result.setTextColor(color) result.setTextColor(color)
result.setLinkTextColor(color) result.setLinkTextColor(color)
Linkify.addLinks(result, Linkify.WEB_URLS) var body = message.body.toSpannable()
Linkify.addLinks(body, Linkify.WEB_URLS)
body = MentionUtilities.highlightMentions(body, message.isOutgoing, message.threadId, context);
result.text = body
return result return result
} }

View File

@ -0,0 +1,26 @@
package org.thoughtcrime.securesms.conversation.v2.utilities
import android.app.Dialog
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
open class BaseDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(requireContext())
setContentView(builder)
val result = builder.create()
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
result.window?.setDimAmount(if (isLightMode) 0.1f else 0.75f)
return result
}
open fun setContentView(builder: AlertDialog.Builder) {
// To be overridden by subclasses
}
}

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.components; package org.thoughtcrime.securesms.conversation.v2.utilities;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@ -24,6 +24,9 @@ import com.bumptech.glide.request.RequestOptions;
import network.loki.messenger.R; import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget;
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget;
import org.thoughtcrime.securesms.components.TransferControlView;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequest; import org.thoughtcrime.securesms.mms.GlideRequest;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
@ -94,10 +97,9 @@ public class ThumbnailView extends FrameLayout {
bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0); bounds[MAX_WIDTH] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxWidth, 0);
bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0); bounds[MIN_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_minHeight, 0);
bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0); bounds[MAX_HEIGHT] = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_maxHeight, 0);
radius = typedArray.getDimensionPixelSize(R.styleable.ThumbnailView_thumbnail_radius, getResources().getDimensionPixelSize(R.dimen.thumbnail_default_radius));
typedArray.recycle(); typedArray.recycle();
} else { } else {
radius = getResources().getDimensionPixelSize(R.dimen.message_corner_collapse_radius); radius = 0;
} }
} }
@ -275,7 +277,7 @@ public class ThumbnailView extends FrameLayout {
this.slide = slide; this.slide = slide;
this.captionIcon.setVisibility(slide.getCaption().isPresent() ? VISIBLE : GONE); this.captionIcon.setVisibility(GONE);
dimens[WIDTH] = naturalWidth; dimens[WIDTH] = naturalWidth;
dimens[HEIGHT] = naturalHeight; dimens[HEIGHT] = naturalHeight;
@ -398,6 +400,7 @@ public class ThumbnailView extends FrameLayout {
} }
private class ThumbnailClickDispatcher implements View.OnClickListener { private class ThumbnailClickDispatcher implements View.OnClickListener {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (thumbnailClickListener != null && if (thumbnailClickListener != null &&
@ -413,9 +416,9 @@ public class ThumbnailView extends FrameLayout {
} }
private class DownloadClickDispatcher implements View.OnClickListener { private class DownloadClickDispatcher implements View.OnClickListener {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Log.i(TAG, "onClick() for download button");
if (downloadClickListener != null && slide != null) { if (downloadClickListener != null && slide != null) {
downloadClickListener.onClick(view, Collections.singletonList(slide)); downloadClickListener.onClick(view, Collections.singletonList(slide));
} else { } else {

View File

@ -74,7 +74,7 @@ public class LinkPreviewViewModel extends ViewModel {
activeRequest = null; activeRequest = null;
} }
if (!link.isPresent() || !isCursorPositionValid(text, link.get(), cursorStart, cursorEnd)) { if (!link.isPresent()) {
activeUrl = null; activeUrl = null;
linkPreviewState.setValue(LinkPreviewState.forEmpty()); linkPreviewState.setValue(LinkPreviewState.forEmpty());
return; return;

View File

@ -1,4 +1,4 @@
package org.thoughtcrime.securesms.conversation.v2 package org.thoughtcrime.securesms.loki.activities
import android.os.Bundle import android.os.Bundle
import kotlinx.android.synthetic.main.activity_open_group_guidelines.* import kotlinx.android.synthetic.main.activity_open_group_guidelines.*

View File

@ -12,18 +12,16 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
import org.session.libsession.utilities.KeyPairUtilities import org.session.libsession.utilities.KeyPairUtilities
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
class ClearAllDataDialog : DialogFragment() { class ClearAllDataDialog : BaseDialog() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun setContentView(builder: AlertDialog.Builder) {
val builder = AlertDialog.Builder(requireContext())
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_clear_all_data, null) val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_clear_all_data, null)
contentView.cancelButton.setOnClickListener { dismiss() } contentView.cancelButton.setOnClickListener { dismiss() }
contentView.clearAllDataButton.setOnClickListener { clearAllData() } contentView.clearAllDataButton.setOnClickListener { clearAllData() }
builder.setView(contentView) builder.setView(contentView)
val result = builder.create()
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
return result
} }
private fun clearAllData() { private fun clearAllData() {

View File

@ -1,24 +1,20 @@
package org.thoughtcrime.securesms.loki.dialogs package org.thoughtcrime.securesms.loki.dialogs
import android.app.Dialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import kotlinx.android.synthetic.main.dialog_seed.view.* import kotlinx.android.synthetic.main.dialog_seed.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.IdentityKeyUtil import org.session.libsession.utilities.IdentityKeyUtil
import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities
import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.crypto.MnemonicCodec
import org.session.libsignal.utilities.hexEncodedPrivateKey import org.session.libsignal.utilities.hexEncodedPrivateKey
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
class SeedDialog : DialogFragment() { class SeedDialog : BaseDialog() {
private val seed by lazy { private val seed by lazy {
var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED) var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED)
@ -31,16 +27,12 @@ class SeedDialog : DialogFragment() {
MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english)
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun setContentView(builder: AlertDialog.Builder) {
val builder = AlertDialog.Builder(requireContext())
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_seed, null) val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_seed, null)
contentView.seedTextView.text = seed contentView.seedTextView.text = seed
contentView.cancelButton.setOnClickListener { dismiss() } contentView.cancelButton.setOnClickListener { dismiss() }
contentView.copyButton.setOnClickListener { copySeed() } contentView.copyButton.setOnClickListener { copySeed() }
builder.setView(contentView) builder.setView(contentView)
val result = builder.create()
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
return result
} }
private fun copySeed() { private fun copySeed() {

View File

@ -7,6 +7,7 @@ import android.text.SpannableString
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
import android.util.Range import android.util.Range
import androidx.core.content.res.ResourcesCompat
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.combine.Tuple2 import nl.komponents.kovenant.combine.Tuple2
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
@ -23,7 +24,7 @@ object MentionUtilities {
@JvmStatic @JvmStatic
fun highlightMentions(text: CharSequence, isOutgoingMessage: Boolean, threadID: Long, context: Context): SpannableString { fun highlightMentions(text: CharSequence, isOutgoingMessage: Boolean, threadID: Long, context: Context): SpannableString {
var text = text @Suppress("NAME_SHADOWING") var text = text
val threadDB = DatabaseFactory.getThreadDatabase(context) val threadDB = DatabaseFactory.getThreadDatabase(context)
val isOpenGroup = threadDB.getRecipientForThreadId(threadID)?.isOpenGroupRecipient ?: false val isOpenGroup = threadDB.getRecipientForThreadId(threadID)?.isOpenGroupRecipient ?: false
val pattern = Pattern.compile("@[0-9a-fA-F]*") val pattern = Pattern.compile("@[0-9a-fA-F]*")
@ -38,7 +39,7 @@ object MentionUtilities {
TextSecurePreferences.getProfileName(context) TextSecurePreferences.getProfileName(context)
} else { } else {
val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey) val contact = DatabaseFactory.getSessionContactDatabase(context).getContactWithSessionID(publicKey)
val context = if (isOpenGroup) Contact.ContactContext.OPEN_GROUP else Contact.ContactContext.REGULAR @Suppress("NAME_SHADOWING") val context = if (isOpenGroup) Contact.ContactContext.OPEN_GROUP else Contact.ContactContext.REGULAR
contact?.displayName(context) contact?.displayName(context)
} }
if (userDisplayName != null) { if (userDisplayName != null) {
@ -54,10 +55,15 @@ object MentionUtilities {
} }
} }
val result = SpannableString(text) val result = SpannableString(text)
for (mention in mentions) {
val isLightMode = UiModeUtilities.isDayUiMode(context) val isLightMode = UiModeUtilities.isDayUiMode(context)
val colorID = if (isLightMode && isOutgoingMessage) R.color.black else R.color.accent for (mention in mentions) {
result.setSpan(ForegroundColorSpan(context.resources.getColorWithID(colorID, context.theme)), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) val colorID = if (isOutgoingMessage) {
if (isLightMode) R.color.white else R.color.black
} else {
R.color.accent
}
val color = ResourcesCompat.getColor(context.resources, colorID, context.theme)
result.setSpan(ForegroundColorSpan(color), 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) result.setSpan(StyleSpan(Typeface.BOLD), mention.first.lower, mention.first.upper, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} }
return result return result

View File

@ -4,9 +4,11 @@ import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Typeface import android.graphics.Typeface
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.view_conversation.view.* import kotlinx.android.synthetic.main.view_conversation.view.*
import network.loki.messenger.R import network.loki.messenger.R
@ -37,13 +39,20 @@ class ConversationView : LinearLayout {
fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) { fun bind(thread: ThreadRecord, isTyping: Boolean, glide: GlideRequests) {
this.thread = thread this.thread = thread
populateUserPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a bad place to do this populateUserPublicKeyCacheIfNeeded(thread.threadId, context) // FIXME: This is a bad place to do this
val unreadCount = thread.unreadCount
if (thread.recipient.isBlocked) { if (thread.recipient.isBlocked) {
accentView.setBackgroundResource(R.color.destructive) accentView.setBackgroundResource(R.color.destructive)
accentView.visibility = View.VISIBLE accentView.visibility = View.VISIBLE
} else { } else {
accentView.setBackgroundResource(R.color.accent) accentView.setBackgroundResource(R.color.accent)
accentView.visibility = if (thread.unreadCount > 0) View.VISIBLE else View.INVISIBLE accentView.visibility = if (unreadCount > 0) View.VISIBLE else View.INVISIBLE
} }
val formattedUnreadCount = if (unreadCount < 100) unreadCount.toString() else "99+"
unreadCountTextView.text = formattedUnreadCount
val textSize = if (unreadCount < 100) 12.0f else 9.0f
unreadCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize)
unreadCountTextView.setTypeface(Typeface.DEFAULT, if (unreadCount < 100) Typeface.BOLD else Typeface.NORMAL)
unreadCountIndicator.isVisible = (unreadCount != 0)
profilePictureView.glide = glide profilePictureView.glide = glide
profilePictureView.update(thread.recipient, thread.threadId) profilePictureView.update(thread.recipient, thread.threadId)
val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString() val senderDisplayName = getUserDisplayName(thread.recipient) ?: thread.recipient.address.toString()
@ -53,7 +62,7 @@ class ConversationView : LinearLayout {
val rawSnippet = thread.getDisplayBody(context) val rawSnippet = thread.getDisplayBody(context)
val snippet = highlightMentions(rawSnippet, thread.threadId, context) val snippet = highlightMentions(rawSnippet, thread.threadId, context)
snippetTextView.text = snippet snippetTextView.text = snippet
snippetTextView.typeface = if (thread.unreadCount > 0) Typeface.DEFAULT_BOLD else Typeface.DEFAULT snippetTextView.typeface = if (unreadCount > 0) Typeface.DEFAULT_BOLD else Typeface.DEFAULT
snippetTextView.visibility = if (isTyping) View.GONE else View.VISIBLE snippetTextView.visibility = if (isTyping) View.GONE else View.VISIBLE
if (isTyping) { if (isTyping) {
typingIndicatorView.startAnimation() typingIndicatorView.startAnimation()

View File

@ -10,7 +10,7 @@ import android.view.ViewGroup;
import network.loki.messenger.R; import network.loki.messenger.R;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import org.thoughtcrime.securesms.mediasend.Media; import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.StableIdGenerator; import org.thoughtcrime.securesms.util.StableIdGenerator;

View File

@ -41,7 +41,7 @@ import org.thoughtcrime.securesms.MediaPreviewActivity;
import org.thoughtcrime.securesms.loki.views.MessageAudioView; import org.thoughtcrime.securesms.loki.views.MessageAudioView;
import org.thoughtcrime.securesms.components.DocumentView; import org.thoughtcrime.securesms.components.DocumentView;
import org.thoughtcrime.securesms.components.RemovableEditableMediaView; import org.thoughtcrime.securesms.components.RemovableEditableMediaView;
import org.thoughtcrime.securesms.components.ThumbnailView; import org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView;
import org.session.libsignal.utilities.NoExternalStorageException; import org.session.libsignal.utilities.NoExternalStorageException;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity; import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.Log;

View File

@ -7,5 +7,4 @@
<corners android:radius="@dimen/dialog_corner_radius" /> <corners android:radius="@dimen/dialog_corner_radius" />
<!-- <stroke android:width="@dimen/border_thickness" android:color="@color/dialog_border" />-->
</shape> </shape>

View File

@ -2,6 +2,5 @@
<inset <inset
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/default_dialog_background" android:drawable="@drawable/default_dialog_background"
android:insetRight="@dimen/medium_spacing" android:inset="@dimen/medium_spacing">
android:insetLeft="@dimen/medium_spacing">
</inset> </inset>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M326.612,185.391c59.747,59.809 58.927,155.698 0.36,214.59 -0.11,0.12 -0.24,0.25 -0.36,0.37l-67.2,67.2c-59.27,59.27 -155.699,59.262 -214.96,0 -59.27,-59.26 -59.27,-155.7 0,-214.96l37.106,-37.106c9.84,-9.84 26.786,-3.3 27.294,10.606 0.648,17.722 3.826,35.527 9.69,52.721 1.986,5.822 0.567,12.262 -3.783,16.612l-13.087,13.087c-28.026,28.026 -28.905,73.66 -1.155,101.96 28.024,28.579 74.086,28.749 102.325,0.51l67.2,-67.19c28.191,-28.191 28.073,-73.757 0,-101.83 -3.701,-3.694 -7.429,-6.564 -10.341,-8.569a16.037,16.037 0,0 1,-6.947 -12.606c-0.396,-10.567 3.348,-21.456 11.698,-29.806l21.054,-21.055c5.521,-5.521 14.182,-6.199 20.584,-1.731a152.482,152.482 0,0 1,20.522 17.197zM467.547,44.449c-59.261,-59.262 -155.69,-59.27 -214.96,0l-67.2,67.2c-0.12,0.12 -0.25,0.25 -0.36,0.37 -58.566,58.892 -59.387,154.781 0.36,214.59a152.454,152.454 0,0 0,20.521 17.196c6.402,4.468 15.064,3.789 20.584,-1.731l21.054,-21.055c8.35,-8.35 12.094,-19.239 11.698,-29.806a16.037,16.037 0,0 0,-6.947 -12.606c-2.912,-2.005 -6.64,-4.875 -10.341,-8.569 -28.073,-28.073 -28.191,-73.639 0,-101.83l67.2,-67.19c28.239,-28.239 74.3,-28.069 102.325,0.51 27.75,28.3 26.872,73.934 -1.155,101.96l-13.087,13.087c-4.35,4.35 -5.769,10.79 -3.783,16.612 5.864,17.194 9.042,34.999 9.69,52.721 0.509,13.906 17.454,20.446 27.294,10.606l37.106,-37.106c59.271,-59.259 59.271,-155.699 0.001,-214.959z"
android:fillColor="@color/text"/>
</vector>

View File

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contentView" android:id="@+id/contentView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<org.thoughtcrime.securesms.conversation.v2.OpenGroupGuidelinesView <org.thoughtcrime.securesms.conversation.v2.components.OpenGroupGuidelinesView
android:id="@+id/openGroupGuidelinesView" android:id="@+id/openGroupGuidelinesView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -18,6 +20,14 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="@dimen/input_bar_height" /> android:layout_marginBottom="@dimen/input_bar_height" />
<org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorViewContainer
android:id="@+id/typingIndicatorViewContainer"
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_marginBottom="@dimen/input_bar_height"
android:visibility="gone"
android:layout_alignParentBottom="true" />
<org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar <org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
android:id="@+id/inputBar" android:id="@+id/inputBar"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -73,12 +83,18 @@
<RelativeLayout <RelativeLayout
android:id="@+id/scrollToBottomButton" android:id="@+id/scrollToBottomButton"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="50dp"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_marginRight="12dp" android:layout_marginRight="12dp"
android:layout_marginBottom="72dp" android:layout_marginBottom="72dp"
android:alpha="0" android:alpha="1">
<RelativeLayout
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:background="@drawable/view_scroll_to_bottom_button_background"> android:background="@drawable/view_scroll_to_bottom_button_background">
<ImageView <ImageView
@ -91,6 +107,28 @@
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/unreadCountIndicator"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
android:background="@drawable/circle_tintable"
android:backgroundTint="@color/conversation_unread_count_indicator_background">
<TextView
android:id="@+id/unreadCountTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text"
android:text="8" />
</RelativeLayout>
</RelativeLayout>
<org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarRecordingView <org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarRecordingView
android:id="@+id/inputBarRecordingView" android:id="@+id/inputBarRecordingView"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -99,4 +137,26 @@
android:visibility="gone" android:visibility="gone"
android:layout_alignParentBottom="true" /> android:layout_alignParentBottom="true" />
<RelativeLayout
android:id="@+id/blockedBanner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/destructive"
android:layout_alignParentTop="true"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/blockedBannerTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="@dimen/medium_spacing"
android:textSize="@dimen/small_font_size"
android:textColor="@color/white"
android:textStyle="bold"
tools:text="Elon is blocked. Unblock them?" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout 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="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical"> android:gravity="center_vertical">
@ -11,11 +12,18 @@
android:layout_width="@dimen/small_profile_picture_size" android:layout_width="@dimen/small_profile_picture_size"
android:layout_height="@dimen/small_profile_picture_size" /> android:layout_height="@dimen/small_profile_picture_size" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-2dp"
android:orientation="vertical">
<TextView <TextView
android:id="@+id/conversationTitleView" android:id="@+id/conversationTitleView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginBottom="-4dp"
android:text="Elon" android:text="Elon"
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
@ -23,4 +31,38 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" /> android:ellipsize="end" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/muteIconImageView"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginEnd="4dp"
android:layout_gravity="center"
android:src="@drawable/ic_outline_notifications_off_24"
app:tint="@color/text"
android:alpha="0.6"
android:visibility="gone"
tools:visibility="visible"/>
<TextView
android:id="@+id/conversationSubtitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Muted"
android:textColor="@color/text"
android:alpha="0.6"
android:textSize="@dimen/very_small_font_size"
android:maxLines="1"
android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -135,7 +135,7 @@
android:background="?android:dividerHorizontal" android:background="?android:dividerHorizontal"
android:elevation="1dp" /> android:elevation="1dp" />
<org.thoughtcrime.securesms.conversation.v2.OpenGroupGuidelinesView <org.thoughtcrime.securesms.conversation.v2.components.OpenGroupGuidelinesView
android:id="@+id/open_group_guidelines_view" android:id="@+id/open_group_guidelines_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -13,7 +13,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"> android:layout_gravity="center">
<org.thoughtcrime.securesms.components.ThumbnailView <org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/attachment_thumbnail" android:id="@+id/attachment_thumbnail"
android:layout_width="230dp" android:layout_width="230dp"
android:layout_height="150dp" android:layout_height="150dp"

View File

@ -4,7 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<org.thoughtcrime.securesms.components.ThumbnailView <org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/conversation_thumbnail_image" android:id="@+id/conversation_thumbnail_image"
android:layout_width="@dimen/media_bubble_default_dimens" android:layout_width="@dimen/media_bubble_default_dimens"
android:layout_height="@dimen/media_bubble_default_dimens" android:layout_height="@dimen/media_bubble_default_dimens"

View File

@ -24,7 +24,7 @@
android:padding="12dp" android:padding="12dp"
android:background="@drawable/message_bubble_background"> android:background="@drawable/message_bubble_background">
<org.thoughtcrime.securesms.components.TypingIndicatorView <org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorView
android:id="@+id/typing_indicator" android:id="@+id/typing_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="32dp">
<TextView
android:id="@+id/blockedTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_blocked_title"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/blockedExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_blocked_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/unblockButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/ConversationActivity_unblock" />
</LinearLayout>
</LinearLayout>

View File

@ -6,10 +6,8 @@
android:background="@drawable/default_dialog_background_inset" android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="32dp" android:elevation="4dp"
android:paddingTop="@dimen/medium_spacing" android:padding="32dp">
android:paddingRight="32dp"
android:paddingBottom="@dimen/medium_spacing">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="32dp">
<TextView
android:id="@+id/downloadTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_download_title"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/downloadExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_download_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/downloadButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/dialog_download_button_title" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="32dp">
<TextView
android:id="@+id/joinOpenGroupTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_join_open_group_title"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/joinOpenGroupExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_join_open_group_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/joinButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/open_group_invitation_view__join_accessibility_description" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="32dp">
<TextView
android:id="@+id/linkPreviewDialogTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_link_preview_title"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/linkPreviewDialogExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_link_preview_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/enableLinkPreviewsButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/dialog_link_preview_enable_button_title" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="32dp">
<TextView
android:id="@+id/openURLTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_open_url_title"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/openURLExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_open_url_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.Unimportant"
android:id="@+id/openURLButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/open" />
</LinearLayout>
</LinearLayout>

View File

@ -6,10 +6,7 @@
android:background="@drawable/default_dialog_background_inset" android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="32dp" android:padding="32dp">
android:paddingTop="@dimen/medium_spacing"
android:paddingRight="32dp"
android:paddingBottom="@dimen/medium_spacing">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -5,7 +5,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="2dp"> android:padding="2dp">
<org.thoughtcrime.securesms.components.ThumbnailView <org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -8,7 +8,7 @@
android:layout_margin="2dp" android:layout_margin="2dp"
android:animateLayoutChanges="true"> android:animateLayoutChanges="true">
<org.thoughtcrime.securesms.components.ThumbnailView <org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/rail_item_image" android:id="@+id/rail_item_image"
android:layout_width="56dp" android:layout_width="56dp"
android:layout_height="56dp" android:layout_height="56dp"

View File

@ -13,7 +13,7 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"/> tools:visibility="visible"/>
<org.thoughtcrime.securesms.components.ThumbnailView <org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/sticker_thumbnail" android:id="@+id/sticker_thumbnail"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -41,11 +41,17 @@
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center_vertical"> android:gravity="center_vertical">
<TextView <LinearLayout
android:id="@+id/conversationViewDisplayNameTextView"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/conversationViewDisplayNameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAlignment="viewStart" android:textAlignment="viewStart"
@ -54,6 +60,28 @@
android:textColor="@color/text" android:textColor="@color/text"
tools:text="I'm a very long display name. What are you going to do about it?" /> tools:text="I'm a very long display name. What are you going to do about it?" />
<RelativeLayout
android:id="@+id/unreadCountIndicator"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="4dp"
android:background="@drawable/circle_tintable"
android:backgroundTint="@color/conversation_unread_count_indicator_background">
<TextView
android:id="@+id/unreadCountTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="@dimen/very_small_font_size"
android:textColor="@color/text"
android:textStyle="bold"
android:text="8" />
</RelativeLayout>
</LinearLayout>
<TextView <TextView
android:id="@+id/timestampTextView" android:id="@+id/timestampTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -98,7 +126,7 @@
android:textColor="@color/text" android:textColor="@color/text"
tools:text="Sorry, gotta go fight crime again" /> tools:text="Sorry, gotta go fight crime again" />
<org.thoughtcrime.securesms.components.TypingIndicatorView <org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorView
android:id="@+id/typingIndicatorView" android:id="@+id/typingIndicatorView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/medium_spacing"
android:paddingBottom="@dimen/small_spacing"
android:gravity="center_vertical">
<FrameLayout
android:id="@+id/typingIndicatorBubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="7dp"
android:background="@drawable/message_bubble_background_received_alone"
android:backgroundTint="?message_received_background_color">
<org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorView
android:id="@+id/typingIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:typingIndicator_tint="@color/text" />
</FrameLayout>
</FrameLayout>

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout android:id="@+id/mainLinkPreviewContainer"
android:id="@+id/mainLinkPreviewContainer"
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:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"> android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -13,12 +13,25 @@
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center"> android:gravity="center">
<ImageView <RelativeLayout
android:id="@+id/thumbnailImageView"
android:layout_width="96dp" android:layout_width="96dp"
android:layout_height="96dp" android:layout_height="96dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_link"
app:tint="@color/text" />
<org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/thumbnailImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" /> android:scaleType="centerCrop" />
</RelativeLayout>
<TextView <TextView
android:id="@+id/titleTextView" android:id="@+id/titleTextView"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="88dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:paddingHorizontal="@dimen/medium_spacing">
<RelativeLayout
android:id="@+id/linkPreviewDraftContainer"
android:layout_width="match_parent"
android:layout_centerVertical="true"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_link"
app:tint="@color/text" />
<org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView
android:id="@+id/thumbnailImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</RelativeLayout>
<TextView
android:id="@+id/linkPreviewDraftTitleTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="80dp"
android:paddingEnd="30dp"
android:layout_centerInParent="true"
android:gravity="center_vertical"
android:textSize="@dimen/small_font_size"
android:textStyle="bold"
tools:text="The Day The Dinosaurs Died - Minute by Minute"
android:maxLines="3"
android:ellipsize="end"
android:textColor="@color/text" />
<ImageView
android:id="@+id/linkPreviewDraftCancelButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:padding="6dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_close_white_48dp"
app:tint="@color/text" />
</RelativeLayout>
<com.github.ybq.android.spinkit.SpinKitView
android:id="@+id/linkPreviewDraftLoader"
style="@style/SpinKitView.Large.ThreeBounce"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:SpinKit_Color="@color/text"
android:layout_centerInParent="true" />
</RelativeLayout>

View File

@ -5,7 +5,7 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="org.thoughtcrime.securesms.components.TypingIndicatorView"> tools:context="org.thoughtcrime.securesms.conversation.v2.components.TypingIndicatorView">
<View <View
android:id="@+id/typing_dot1" android:id="@+id/typing_dot1"

View File

@ -31,6 +31,7 @@
<color name="link_preview_background">#0F000000</color> <color name="link_preview_background">#0F000000</color>
<color name="scroll_to_bottom_button_background">#FCFCFC</color> <color name="scroll_to_bottom_button_background">#FCFCFC</color>
<color name="scroll_to_bottom_button_border">#99000000</color> <color name="scroll_to_bottom_button_border">#99000000</color>
<color name="conversation_unread_count_indicator_background">#E0E0E0</color>
<color name="default_background_start">#ffffff</color> <color name="default_background_start">#ffffff</color>
<color name="default_background_end">#fcfcfc</color> <color name="default_background_end">#fcfcfc</color>

View File

@ -38,6 +38,7 @@
<color name="link_preview_background">#000000</color> <color name="link_preview_background">#000000</color>
<color name="scroll_to_bottom_button_background">#171717</color> <color name="scroll_to_bottom_button_background">#171717</color>
<color name="scroll_to_bottom_button_border">#99FFFFFF</color> <color name="scroll_to_bottom_button_border">#99FFFFFF</color>
<color name="conversation_unread_count_indicator_background">#303030</color>
<array name="profile_picture_placeholder_colors"> <array name="profile_picture_placeholder_colors">
<item>#5ff8b0</item> <item>#5ff8b0</item>

View File

@ -309,7 +309,7 @@
<string name="MediaPreviewActivity_you">You</string> <string name="MediaPreviewActivity_you">You</string>
<string name="MediaPreviewActivity_unssuported_media_type">Unsupported media type</string> <string name="MediaPreviewActivity_unssuported_media_type">Unsupported media type</string>
<string name="MediaPreviewActivity_draft">Draft</string> <string name="MediaPreviewActivity_draft">Draft</string>
<string name="MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied">Session needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".</string> <string name="MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied">Session needs storage access in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".</string>
<string name="MediaPreviewActivity_unable_to_write_to_external_storage_without_permission">Unable to save to external storage without permissions</string> <string name="MediaPreviewActivity_unable_to_write_to_external_storage_without_permission">Unable to save to external storage without permissions</string>
<string name="MediaPreviewActivity_media_delete_confirmation_title">Delete message?</string> <string name="MediaPreviewActivity_media_delete_confirmation_title">Delete message?</string>
<string name="MediaPreviewActivity_media_delete_confirmation_message">This will permanently delete this message.</string> <string name="MediaPreviewActivity_media_delete_confirmation_message">This will permanently delete this message.</string>
@ -853,4 +853,23 @@
<string name="document">Document</string> <string name="document">Document</string>
<string name="dialog_blocked_title">Unblock %s?</string>
<string name="dialog_blocked_explanation">Are you sure you want to unblock %s?</string>
<string name="dialog_join_open_group_title">Join %s?</string>
<string name="dialog_join_open_group_explanation">Are you sure you want to join the %s open group?</string>
<string name="dialog_open_url_title">Open URL?</string>
<string name="dialog_open_url_explanation">Are you sure you want to open %s?</string>
<string name="open">Open</string>
<string name="dialog_link_preview_title">Enable Link Previews?</string>
<string name="dialog_link_preview_explanation">Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.</string>
<string name="dialog_link_preview_enable_button_title">Enable</string>
<string name="dialog_download_title">Trust %s?</string>
<string name="dialog_download_explanation">Are you sure you want to download media sent by %s?</string>
<string name="dialog_download_button_title">Download</string>
<string name="activity_conversation_blocked_banner_text">%s is blocked. Unblock them?</string>
</resources> </resources>

View File

@ -280,4 +280,9 @@
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
</style> </style>
<style name="Session.Dialog" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">#120000FF</item>
<item name="android:windowIsFloating">false</item>
</style>
</resources> </resources>