diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml
index bdecdca907..1d9c766d4c 100644
--- a/app/lint-baseline.xml
+++ b/app/lint-baseline.xml
@@ -36942,4 +36942,500 @@
column="1"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java
index 39929e5477..45c50572da 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java
@@ -600,7 +600,18 @@ public final class ContactSelectionListFragment extends LoggingFragment {
boolean isUnknown = contact instanceof ContactSearchKey.UnknownRecipientKey;
SelectedContact selectedContact = contact.requireSelectedContact();
- if (!canSelectSelf && !selectedContact.hasUsername() && Recipient.self().getId().equals(selectedContact.getOrCreateRecipientId())) {
+ boolean needsSelfCheck = !canSelectSelf && !selectedContact.hasUsername();
+
+ if (needsSelfCheck) {
+ lifecycleDisposable.add(contactChipViewModel.isSelf(selectedContact)
+ .subscribe(isSelf -> onItemClickResolved(contact, selectedContact, isUnknown, isSelf)));
+ } else {
+ onItemClickResolved(contact, selectedContact, isUnknown, false);
+ }
+ }
+
+ private void onItemClickResolved(ContactSearchKey contact, SelectedContact selectedContact, boolean isUnknown, boolean isSelf) {
+ if (isSelf) {
Toast.makeText(requireContext(), R.string.ContactSelectionListFragment_you_do_not_need_to_add_yourself_to_the_group, Toast.LENGTH_SHORT).show();
return;
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointActivity.java b/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointActivity.java
index 7b2a77fa5f..c4466bfa64 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointActivity.java
@@ -1,107 +1,27 @@
package org.thoughtcrime.securesms;
-import android.app.Activity;
-import android.content.Intent;
-import android.database.Cursor;
import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.text.TextUtils;
import android.widget.Toast;
-import androidx.annotation.NonNull;
+import androidx.activity.ComponentActivity;
+import androidx.lifecycle.ViewModelProvider;
-import org.signal.core.util.logging.Log;
-import org.thoughtcrime.securesms.conversation.ConversationIntents;
-import org.thoughtcrime.securesms.conversation.NewConversationActivity;
-import org.thoughtcrime.securesms.database.SignalDatabase;
-import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.util.Rfc5724Uri;
-
-import java.net.URISyntaxException;
-
-public class SystemContactsEntrypointActivity extends Activity {
-
- private static final String TAG = Log.tag(SystemContactsEntrypointActivity.class);
+public class SystemContactsEntrypointActivity extends ComponentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- startActivity(getNextIntent(getIntent()));
- finish();
super.onCreate(savedInstanceState);
- }
- private Intent getNextIntent(Intent original) {
- DestinationAndBody destination;
+ SystemContactsEntrypointViewModel viewModel = new ViewModelProvider(this).get(SystemContactsEntrypointViewModel.class);
- if (original.getData() != null && "content".equals(original.getData().getScheme())) {
- destination = getDestinationForSyncAdapter(original);
- } else {
- destination = getDestinationForView(original);
- }
-
- final Intent nextIntent;
-
- if (TextUtils.isEmpty(destination.destination)) {
- nextIntent = NewConversationActivity.createIntent(this, destination.getBody());
- Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
- } else {
- Recipient recipient = Recipient.external(destination.getDestination());
-
- if (recipient != null) {
- long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
-
- nextIntent = ConversationIntents.createBuilderSync(this, recipient.getId(), threadId)
- .withDraftText(destination.getBody())
- .build();
- } else {
- nextIntent = NewConversationActivity.createIntent(this, destination.getBody());
+ viewModel.getContactAction().observe(this, nextStep -> {
+ if (nextStep.getShowSpecifyRecipientToast()) {
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
}
- }
- return nextIntent;
- }
+ startActivity(nextStep.getIntent());
+ finish();
+ });
- private @NonNull DestinationAndBody getDestinationForView(Intent intent) {
- try {
- Rfc5724Uri smsUri = new Rfc5724Uri(intent.getData().toString());
- return new DestinationAndBody(smsUri.getPath(), smsUri.getQueryParams().get("body"));
- } catch (URISyntaxException e) {
- Log.w(TAG, "unable to parse RFC5724 URI from intent", e);
- return new DestinationAndBody("", "");
- }
- }
-
- private @NonNull DestinationAndBody getDestinationForSyncAdapter(Intent intent) {
- Cursor cursor = null;
-
- try {
- cursor = getContentResolver().query(intent.getData(), null, null, null, null);
-
- if (cursor != null && cursor.moveToNext()) {
- return new DestinationAndBody(cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.RawContacts.Data.DATA1)), "");
- }
-
- return new DestinationAndBody("", "");
- } finally {
- if (cursor != null) cursor.close();
- }
- }
-
- private static class DestinationAndBody {
- private final String destination;
- private final String body;
-
- private DestinationAndBody(String destination, String body) {
- this.destination = destination;
- this.body = body;
- }
-
- public String getDestination() {
- return destination;
- }
-
- public String getBody() {
- return body;
- }
+ viewModel.resolveNextStep(getIntent());
}
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointViewModel.kt
new file mode 100644
index 0000000000..0edcf0b2ea
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointViewModel.kt
@@ -0,0 +1,100 @@
+package org.thoughtcrime.securesms
+
+import android.content.Context
+import android.content.Intent
+import android.provider.ContactsContract
+import android.text.TextUtils
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.signal.core.util.logging.Log
+import org.thoughtcrime.securesms.conversation.ConversationIntents
+import org.thoughtcrime.securesms.conversation.NewConversationActivity
+import org.thoughtcrime.securesms.database.SignalDatabase
+import org.thoughtcrime.securesms.dependencies.AppDependencies
+import org.thoughtcrime.securesms.recipients.Recipient
+import org.thoughtcrime.securesms.util.Rfc5724Uri
+import java.net.URISyntaxException
+
+class SystemContactsEntrypointViewModel : ViewModel() {
+
+ companion object {
+ private val TAG = Log.tag(SystemContactsEntrypointViewModel::class.java)
+ }
+
+ private val internalContactAction = MutableLiveData()
+ val contactAction: LiveData = internalContactAction
+
+ fun resolveNextStep(original: Intent) {
+ viewModelScope.launch {
+ val result = withContext(Dispatchers.IO) {
+ getContactAction(AppDependencies.application, original)
+ }
+
+ internalContactAction.value = result
+ }
+ }
+
+ @WorkerThread
+ private fun getContactAction(context: Context, original: Intent): ContactAction {
+ val destination = if (original.data != null && "content" == original.data?.scheme) {
+ getDestinationForSyncAdapter(context, original)
+ } else {
+ getDestinationForView(original)
+ }
+
+ val destinationAddress = destination.destination
+ if (TextUtils.isEmpty(destinationAddress)) {
+ return ContactAction(NewConversationActivity.createIntent(context, destination.body), true)
+ }
+
+ val recipient = Recipient.external(destinationAddress!!)
+
+ if (recipient != null) {
+ val threadId = SignalDatabase.threads.getOrCreateThreadIdFor(recipient)
+
+ val nextIntent = ConversationIntents.createBuilderSync(context, recipient.id, threadId)
+ .withDraftText(destination.body)
+ .build()
+
+ return ContactAction(nextIntent, false)
+ }
+
+ return ContactAction(NewConversationActivity.createIntent(context, destination.body), true)
+ }
+
+ private fun getDestinationForView(intent: Intent): DestinationAndBody {
+ return try {
+ val smsUri = Rfc5724Uri(intent.data.toString())
+ DestinationAndBody(smsUri.path, smsUri.queryParams["body"])
+ } catch (e: URISyntaxException) {
+ Log.w(TAG, "unable to parse RFC5724 URI from intent", e)
+ DestinationAndBody("", "")
+ }
+ }
+
+ private fun getDestinationForSyncAdapter(context: Context, intent: Intent): DestinationAndBody {
+ context.contentResolver.query(intent.data!!, null, null, null, null).use { cursor ->
+ if (cursor != null && cursor.moveToNext()) {
+ return DestinationAndBody(cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.RawContacts.Data.DATA1)), "")
+ }
+
+ return DestinationAndBody("", "")
+ }
+ }
+
+ data class ContactAction(
+ val intent: Intent,
+ val showSpecifyRecipientToast: Boolean
+ )
+
+ private data class DestinationAndBody(
+ val destination: String?,
+ val body: String?
+ )
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactChipViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactChipViewModel.kt
index 74f5934be5..aaee54b0a4 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactChipViewModel.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/ContactChipViewModel.kt
@@ -72,6 +72,12 @@ class ContactChipViewModel : ViewModel() {
}
}
+ fun isSelf(selectedContact: SelectedContact): Single {
+ return Single.fromCallable { Recipient.self().id == selectedContact.getOrCreateRecipientId() }
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ }
+
private fun getOrCreateRecipientId(selectedContact: SelectedContact): Single {
return Single.fromCallable {
selectedContact.getOrCreateRecipientId()
diff --git a/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt b/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt
index 0a43bc6606..16af8a02bd 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/emoji/JumboEmoji.kt
@@ -45,7 +45,6 @@ object JumboEmoji {
private var currentVersion: Int = -1
@JvmStatic
- @MainThread
fun updateCurrentVersion(context: Context) {
SignalExecutors.BOUNDED.execute {
val version: EmojiFiles.Version = EmojiFiles.Version.readVersion(context, true) ?: return@execute
diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java
index 3671396f34..5f82c2f3cd 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessage.java
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.longmessage;
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import org.thoughtcrime.securesms.conversation.ConversationMessage;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -14,16 +15,19 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
class LongMessage {
private final ConversationMessage conversationMessage;
+ private final CharSequence fullBody;
- LongMessage(@NonNull ConversationMessage conversationMessage) {
+ @WorkerThread
+ LongMessage(@NonNull ConversationMessage conversationMessage, @NonNull Context context) {
this.conversationMessage = conversationMessage;
+ this.fullBody = conversationMessage.getDisplayBody(context);
}
@NonNull MessageRecord getMessageRecord() {
return conversationMessage.getMessageRecord();
}
- @NonNull CharSequence getFullBody(@NonNull Context context) {
- return conversationMessage.getDisplayBody(context);
+ @NonNull CharSequence getFullBody() {
+ return fullBody;
}
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageFragment.java
index 833635dea6..7604e3cd9c 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageFragment.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageFragment.java
@@ -122,7 +122,7 @@ public class LongMessageFragment extends FullScreenDialogFragment {
EmojiTextView text = bubble.findViewById(R.id.longmessage_text);
ConversationItemFooter footer = bubble.findViewById(R.id.longmessage_footer);
- SpannableString body = new SpannableString(getTrimmedBody(message.get().getFullBody(requireContext())));
+ SpannableString body = new SpannableString(getTrimmedBody(message.get().getFullBody()));
V2ConversationItemUtils.linkifyUrlLinks(body,
true,
url -> CommunicationActions.handlePotentialGroupLinkUrl(requireActivity(), url) ||
diff --git a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java
index 51192a9752..f1d1fd9ec3 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/longmessage/LongMessageRepository.java
@@ -37,7 +37,7 @@ class LongMessageRepository {
Optional record = getMmsMessage(mmsDatabase, messageId);
if (record.isPresent()) {
final ConversationMessage resolvedMessage = LongMessageResolveerKt.resolveBody(record.get(), context);
- return Optional.of(new LongMessage(resolvedMessage));
+ return Optional.of(new LongMessage(resolvedMessage, context));
} else {
return Optional.empty();
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt
index bed22e3ae7..c3c50661de 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentFragment.kt
@@ -1,22 +1,18 @@
package org.thoughtcrime.securesms.mediasend
-import android.database.Cursor
import android.net.Uri
import android.os.Bundle
-import android.provider.OpenableColumns
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
import org.signal.core.models.media.Media
import org.signal.core.util.bytes
import org.signal.core.util.getParcelableCompat
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
-import org.thoughtcrime.securesms.mms.PartAuthority
-import org.thoughtcrime.securesms.util.MediaUtil
-import java.io.IOException
-import java.util.Optional
+import org.thoughtcrime.securesms.mediasend.MediaSendDocumentViewModel.DocumentInfo
import org.signal.core.ui.R as CoreUiR
/**
@@ -43,6 +39,10 @@ class MediaSendDocumentFragment : Fragment(R.layout.mediasend_document_fragment)
private lateinit var uri: Uri
private lateinit var media: Media
+ private val viewModel: MediaSendDocumentViewModel by viewModels {
+ MediaSendDocumentViewModel.Factory(media)
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -52,24 +52,28 @@ class MediaSendDocumentFragment : Fragment(R.layout.mediasend_document_fragment)
this.media = requireNotNull(requireArguments().getParcelableCompat(KEY_MEDIA, Media::class.java))
- val fileInfo: Pair? = getFileInfo()
- if (fileInfo != null) {
- media.fileName = fileInfo.first
- name.text = fileInfo.first ?: getString(R.string.DocumentView_unnamed_file)
- size.text = fileInfo.second.bytes.toUnitString()
+ viewModel.documentInfo.observe(viewLifecycleOwner) { documentInfoOptional ->
+ val documentInfo: DocumentInfo? = documentInfoOptional.orElse(null)
+ if (documentInfo != null) {
+ media.fileName = documentInfo.fileName
- val extensionText: String = MediaUtil.getFileType(requireContext(), Optional.ofNullable(fileInfo.first), media.uri).orElse("")
- if (extensionText.length <= 3) {
- extension.text = extensionText
- extension.setTextAppearance(requireContext(), CoreUiR.style.Signal_Text_BodySmall)
- } else if (extensionText.length == 4) {
- extension.text = extensionText
- extension.setTextAppearance(requireContext(), CoreUiR.style.Signal_Text_Caption)
+ name.text = documentInfo.fileName ?: getString(R.string.DocumentView_unnamed_file)
+ size.text = documentInfo.fileSize.bytes.toUnitString()
+
+ if (documentInfo.extension.length <= 3) {
+ extension.text = documentInfo.extension
+ extension.setTextAppearance(requireContext(), CoreUiR.style.Signal_Text_BodySmall)
+ } else if (documentInfo.extension.length == 4) {
+ extension.text = documentInfo.extension
+ extension.setTextAppearance(requireContext(), CoreUiR.style.Signal_Text_Caption)
+ }
+ } else {
+ Toast.makeText(requireContext(), R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show()
+ requireActivity().finishAfterTransition()
}
- } else {
- Toast.makeText(requireContext(), R.string.ConversationActivity_sorry_there_was_an_error_setting_your_attachment, Toast.LENGTH_SHORT).show()
- requireActivity().finishAfterTransition()
}
+
+ viewModel.loadDocumentInfo()
}
override fun getUri(): Uri {
@@ -85,57 +89,4 @@ class MediaSendDocumentFragment : Fragment(R.layout.mediasend_document_fragment)
override fun restoreState(state: Any) = Unit
override fun notifyHidden() = Unit
-
- private fun getFileInfo(): Pair? {
- val fileInfo: Pair
- try {
- if (PartAuthority.isLocalUri(uri)) {
- fileInfo = getManuallyCalculatedFileInfo(uri)
- } else {
- val result = getContentResolverFileInfo(uri)
- fileInfo = if ((result == null)) getManuallyCalculatedFileInfo(uri) else result
- }
- } catch (e: IOException) {
- Log.w(TAG, e)
- return null
- }
-
- return fileInfo
- }
-
- @Throws(IOException::class)
- private fun getManuallyCalculatedFileInfo(uri: Uri): Pair {
- var fileName: String? = null
- var fileSize: Long? = null
-
- if (PartAuthority.isLocalUri(uri)) {
- fileSize = PartAuthority.getAttachmentSize(requireContext(), uri)
- fileName = PartAuthority.getAttachmentFileName(requireContext(), uri)
- }
- if (fileSize == null) {
- fileSize = MediaUtil.getMediaSize(context, uri)
- }
-
- return Pair(fileName, fileSize)
- }
-
- private fun getContentResolverFileInfo(uri: Uri): Pair? {
- var cursor: Cursor? = null
-
- try {
- cursor = requireContext().contentResolver.query(uri, null, null, null, null)
-
- if (cursor != null && cursor.moveToFirst()) {
- val fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
- val fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE))
- media.fileName = fileName
-
- return Pair(fileName, fileSize)
- }
- } finally {
- cursor?.close()
- }
-
- return null
- }
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentViewModel.kt
new file mode 100644
index 0000000000..6813c7235f
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentViewModel.kt
@@ -0,0 +1,105 @@
+package org.thoughtcrime.securesms.mediasend
+
+import android.content.Context
+import android.database.Cursor
+import android.net.Uri
+import android.provider.OpenableColumns
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.signal.core.models.media.Media
+import org.signal.core.util.logging.Log
+import org.thoughtcrime.securesms.dependencies.AppDependencies
+import org.thoughtcrime.securesms.mms.PartAuthority
+import org.thoughtcrime.securesms.util.MediaUtil
+import java.io.IOException
+import java.util.Optional
+
+class MediaSendDocumentViewModel(private val media: Media) : ViewModel() {
+
+ private val internalDocumentInfo = MutableLiveData>()
+ val documentInfo: LiveData> = internalDocumentInfo
+
+ fun loadDocumentInfo() {
+ viewModelScope.launch {
+ internalDocumentInfo.value = withContext(Dispatchers.IO) {
+ Optional.ofNullable(computeDocumentInfo())
+ }
+ }
+ }
+
+ private fun computeDocumentInfo(): DocumentInfo? {
+ val context = AppDependencies.application
+ val fileInfo: Pair = getFileInfo(context) ?: return null
+
+ val extensionText: String = MediaUtil.getFileType(context, Optional.ofNullable(fileInfo.first), media.uri).orElse("")
+
+ return DocumentInfo(fileInfo.first, fileInfo.second, extensionText)
+ }
+
+ private fun getFileInfo(context: Context): Pair? {
+ val uri = media.uri
+ return try {
+ if (PartAuthority.isLocalUri(uri)) {
+ getManuallyCalculatedFileInfo(context, uri)
+ } else {
+ getContentResolverFileInfo(context, uri) ?: getManuallyCalculatedFileInfo(context, uri)
+ }
+ } catch (e: IOException) {
+ Log.w(TAG, e)
+ null
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun getManuallyCalculatedFileInfo(context: Context, uri: Uri): Pair {
+ var fileName: String? = null
+ var fileSize: Long? = null
+
+ if (PartAuthority.isLocalUri(uri)) {
+ fileSize = PartAuthority.getAttachmentSize(context, uri)
+ fileName = PartAuthority.getAttachmentFileName(context, uri)
+ }
+ if (fileSize == null) {
+ fileSize = MediaUtil.getMediaSize(context, uri)
+ }
+
+ return Pair(fileName, fileSize)
+ }
+
+ private fun getContentResolverFileInfo(context: Context, uri: Uri): Pair? {
+ var cursor: Cursor? = null
+
+ try {
+ cursor = context.contentResolver.query(uri, null, null, null, null)
+
+ if (cursor != null && cursor.moveToFirst()) {
+ val fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
+ val fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE))
+
+ return Pair(fileName, fileSize)
+ }
+ } finally {
+ cursor?.close()
+ }
+
+ return null
+ }
+
+ data class DocumentInfo(val fileName: String?, val fileSize: Long, val extension: String)
+
+ class Factory(private val media: Media) : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ return requireNotNull(modelClass.cast(MediaSendDocumentViewModel(media)))
+ }
+ }
+
+ companion object {
+ private val TAG = Log.tag(MediaSendDocumentViewModel::class.java)
+ }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java b/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java
index b02de3ab01..e9139441c6 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/payments/Wallet.java
@@ -96,7 +96,7 @@ public final class Wallet {
return SignalStore.payments().mobileCoinLatestBalance();
}
- @AnyThread
+ @WorkerThread
public @NonNull MobileCoinLedgerWrapper getCachedLedger() {
return SignalStore.payments().mobileCoinLatestFullLedger();
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileViewModel.java
index 7ac10931b4..dd4829c065 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileViewModel.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/edit/EditProfileViewModel.java
@@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModelProvider;
import org.signal.core.util.BidiUtil;
import org.signal.core.util.StringUtil;
+import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.groups.GroupId;
@@ -143,24 +144,27 @@ class EditProfileViewModel extends ViewModel {
return;
}
- byte[] oldAvatar = originalAvatar.getValue();
- byte[] newAvatar = internalAvatar.getValue();
- String oldDisplayName = isGroup() ? originalDisplayName.getValue() : null;
- String oldDescription = isGroup() ? originalDescription : null;
+ byte[] oldAvatar = originalAvatar.getValue();
+ byte[] newAvatar = internalAvatar.getValue();
+ String oldDisplayName = isGroup() ? originalDisplayName.getValue() : null;
+ String oldDescription = isGroup() ? originalDescription : null;
+ boolean isGroup = isGroup();
- if (!isGroup() && SignalStore.phoneNumberPrivacy().getPhoneNumberDiscoverabilityMode() == PhoneNumberDiscoverabilityMode.UNDECIDED) {
- Log.i(TAG, "Phone number discoverability mode is still UNDECIDED. Setting to DISCOVERABLE.");
- SignalStore.phoneNumberPrivacy().setPhoneNumberDiscoverabilityMode(PhoneNumberDiscoverabilityMode.DISCOVERABLE);
- }
+ SignalExecutors.BOUNDED.execute(() -> {
+ if (!isGroup && SignalStore.phoneNumberPrivacy().getPhoneNumberDiscoverabilityMode() == PhoneNumberDiscoverabilityMode.UNDECIDED) {
+ Log.i(TAG, "Phone number discoverability mode is still UNDECIDED. Setting to DISCOVERABLE.");
+ SignalStore.phoneNumberPrivacy().setPhoneNumberDiscoverabilityMode(PhoneNumberDiscoverabilityMode.DISCOVERABLE);
+ }
- repository.uploadProfile(profileName,
- displayName,
- !Objects.equals(BidiUtil.stripBidiProtection(oldDisplayName), displayName),
- description,
- !Objects.equals(BidiUtil.stripBidiProtection(oldDescription), description),
- newAvatar,
- !Arrays.equals(oldAvatar, newAvatar),
- uploadResult::postValue);
+ repository.uploadProfile(profileName,
+ displayName,
+ !Objects.equals(BidiUtil.stripBidiProtection(oldDisplayName), displayName),
+ description,
+ !Objects.equals(BidiUtil.stripBidiProtection(oldDescription), description),
+ newAvatar,
+ !Arrays.equals(oldAvatar, newAvatar),
+ uploadResult::postValue);
+ });
}
static class Factory implements ViewModelProvider.Factory {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/ShareableGroupLinkRepository.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/ShareableGroupLinkRepository.java
index 791e1b31b1..fbdb008498 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/ShareableGroupLinkRepository.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/sharablegrouplink/ShareableGroupLinkRepository.java
@@ -41,19 +41,20 @@ final class ShareableGroupLinkRepository {
}
void toggleGroupLinkEnabled(@NonNull AsynchronousCallback.WorkerThread callback) {
- setGroupLinkEnabledState(toggleGroupLinkState(true, false), callback);
+ setGroupLinkEnabledState(true, false, callback);
}
void toggleGroupLinkApprovalRequired(@NonNull AsynchronousCallback.WorkerThread callback) {
- setGroupLinkEnabledState(toggleGroupLinkState(false, true), callback);
+ setGroupLinkEnabledState(false, true, callback);
}
- private void setGroupLinkEnabledState(@NonNull GroupManager.GroupLinkState state,
+ private void setGroupLinkEnabledState(boolean toggleEnabled,
+ boolean toggleApprovalNeeded,
@NonNull AsynchronousCallback.WorkerThread callback)
{
SignalExecutors.UNBOUNDED.execute(() -> {
try {
- GroupManager.setGroupLinkEnabledState(context, groupId, state);
+ GroupManager.setGroupLinkEnabledState(context, groupId, toggleGroupLinkState(toggleEnabled, toggleApprovalNeeded));
callback.onComplete(null);
} catch (GroupNotAMemberException | GroupChangeFailedException | GroupInsufficientRightsException | IOException | GroupChangeBusyException e) {
callback.onError(GroupChangeFailureReason.fromException(e));
diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java
index 665af436e3..28f5248671 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/service/TimedEventManager.java
@@ -10,7 +10,6 @@ import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
-import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -84,7 +83,7 @@ public abstract class TimedEventManager {
* Schedules an alarm to call {@link #scheduleIfNecessary()} after the specified delay. You can
* use {@link #setAlarm(Context, long, Class)} as a helper method.
*/
- @AnyThread
+ @WorkerThread
protected abstract void scheduleAlarm(@NonNull Application application, E event, long delay);
/**