From b16481616aed8165943546ed0a5ddd65184a27e6 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 15 Jun 2026 11:29:08 -0400 Subject: [PATCH] Fix a bunch of lint issues. --- app/lint-baseline.xml | 496 ++++++++++++++++++ .../ContactSelectionListFragment.java | 13 +- .../SystemContactsEntrypointActivity.java | 100 +--- .../SystemContactsEntrypointViewModel.kt | 100 ++++ .../contacts/ContactChipViewModel.kt | 6 + .../securesms/emoji/JumboEmoji.kt | 1 - .../securesms/longmessage/LongMessage.java | 10 +- .../longmessage/LongMessageFragment.java | 2 +- .../longmessage/LongMessageRepository.java | 2 +- .../mediasend/MediaSendDocumentFragment.kt | 99 +--- .../mediasend/MediaSendDocumentViewModel.kt | 105 ++++ .../securesms/payments/Wallet.java | 2 +- .../profiles/edit/EditProfileViewModel.java | 36 +- .../ShareableGroupLinkRepository.java | 9 +- .../securesms/service/TimedEventManager.java | 3 +- 15 files changed, 790 insertions(+), 194 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/SystemContactsEntrypointViewModel.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendDocumentViewModel.kt 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); /**