From b8e4ffb5aeeb56a434a73e342ba99727418651fb Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Wed, 1 Oct 2025 12:46:37 -0400 Subject: [PATCH] Release polls behind feature flag. --- .../v2/items/V2ConversationItemShapeTest.kt | 8 + .../securesms/database/PollTablesTest.kt | 131 ++++ .../DataMessageProcessorTest_polls.kt | 322 +++++++++ .../test/ConversationElementGenerator.kt | 1 + .../test/InternalConversationTestFragment.kt | 14 + .../assets/fonts/SignalSymbols-Regular.otf | Bin 25376 -> 56628 bytes .../securesms/BindableConversationItem.java | 5 + .../components/compose/RoundCheckbox.kt | 21 +- .../components/emoji/EmojiStrings.java | 1 + .../conversation/AttachmentKeyboard.java | 1 + .../AttachmentKeyboardButton.java | 3 +- .../conversation/ConversationItem.java | 40 +- .../conversation/ConversationMessage.java | 4 + .../ConversationReactionOverlay.java | 5 + .../conversation/ConversationUpdateItem.java | 88 ++- .../securesms/conversation/ExpirationTimer.kt | 55 ++ .../securesms/conversation/MenuState.java | 63 +- .../securesms/conversation/PollComponent.kt | 346 +++++++++ .../clicklisteners/PollVotesFragment.kt | 234 ++++++ .../clicklisteners/PollVotesViewModel.kt | 61 ++ .../quotes/MessageQuotesBottomSheet.kt | 4 + .../conversation/v2/ConversationFragment.kt | 63 ++ .../conversation/v2/ConversationRepository.kt | 70 ++ .../conversation/v2/ConversationViewModel.kt | 48 ++ .../conversation/v2/CreatePollFragment.kt | 291 ++++++++ .../v2/data/MessageDataFetcher.kt | 13 + .../v2/keyboard/AttachmentKeyboardFragment.kt | 25 +- .../ConversationListItem.java | 4 + .../securesms/database/MessageTable.kt | 174 ++++- .../securesms/database/MessageType.kt | 5 +- .../securesms/database/MessageTypes.java | 5 + .../securesms/database/PollTables.kt | 670 ++++++++++++++++++ .../securesms/database/SignalDatabase.kt | 8 + .../securesms/database/ThreadBodyUtil.java | 13 + .../securesms/database/ThreadTable.kt | 19 +- .../helpers/SignalDatabaseMigrations.kt | 6 +- .../helpers/migration/V292_AddPollTables.kt | 69 ++ .../database/model/DisplayRecord.java | 4 + .../database/model/MessageRecord.java | 10 +- .../database/model/MessageRecordExtensions.kt | 9 + .../database/model/MmsMessageRecord.java | 29 +- .../database/model/ThreadRecord.java | 5 + .../database/model/UpdateDescription.java | 25 +- .../securesms/fonts/SignalSymbols.kt | 1 + .../securesms/jobs/JobManagerFactories.java | 1 + .../securesms/jobs/PollVoteJob.kt | 242 +++++++ .../securesms/jobs/PushGroupSendJob.java | 42 +- .../messagedetails/MessageDetailsFragment.kt | 14 + .../messages/DataMessageProcessor.kt | 225 ++++++ .../messages/SignalServiceProtoUtil.kt | 5 +- .../messages/SyncMessageProcessor.kt | 123 ++++ .../securesms/mms/IncomingMessage.kt | 4 +- .../securesms/mms/OutgoingMessage.kt | 27 + .../notifications/v2/NotificationItem.kt | 38 + .../v2/NotificationStateProvider.kt | 35 + .../org/thoughtcrime/securesms/polls/Poll.kt | 41 ++ .../securesms/polls/PollOption.kt | 16 + .../securesms/polls/PollRecord.kt | 18 + .../thoughtcrime/securesms/polls/PollVote.kt | 18 + .../securesms/sms/MessageSender.java | 34 +- .../stickers/StickerPackListItems.kt | 2 +- .../securesms/util/MessageConstraintsUtil.kt | 3 +- .../securesms/util/MessageRecordUtil.kt | 14 +- .../securesms/util/RemoteConfig.kt | 8 + app/src/main/protowire/Database.proto | 7 + app/src/main/protowire/JobData.proto | 8 + app/src/main/res/drawable/drag_handle.xml | 24 + app/src/main/res/drawable/symbol_poll_16.xml | 11 + app/src/main/res/drawable/symbol_poll_24.xml | 9 + app/src/main/res/drawable/symbol_stop_24.xml | 2 +- .../res/layout/conversation_item_poll.xml | 5 + .../conversation_item_received_multimedia.xml | 6 + .../conversation_item_sent_multimedia.xml | 6 + .../res/layout/v2_conversation_fragment.xml | 8 + app/src/main/res/values/signal_styles.xml | 4 + app/src/main/res/values/strings.xml | 67 ++ app/src/main/res/values/styles.xml | 5 + .../MessageBitmaskColumnTransformer.kt | 2 + .../securesms/database/FakeMessageRecords.kt | 1 + .../api/SignalServiceMessageSender.java | 26 + .../api/messages/EnvelopeContentValidator.kt | 28 + .../api/messages/SignalServiceDataMessage.kt | 36 +- .../src/main/protowire/SignalService.proto | 23 +- .../messages/EnvelopeContentValidatorTest.kt | 105 +++ 84 files changed, 4164 insertions(+), 102 deletions(-) create mode 100644 app/src/androidTest/java/org/thoughtcrime/securesms/database/PollTablesTest.kt create mode 100644 app/src/androidTest/java/org/thoughtcrime/securesms/messages/DataMessageProcessorTest_polls.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/ExpirationTimer.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/PollComponent.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/PollVotesFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/clicklisteners/PollVotesViewModel.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/CreatePollFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/PollTables.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V292_AddPollTables.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/PollVoteJob.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/polls/Poll.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/polls/PollOption.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/polls/PollRecord.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/polls/PollVote.kt create mode 100644 app/src/main/res/drawable/drag_handle.xml create mode 100644 app/src/main/res/drawable/symbol_poll_16.xml create mode 100644 app/src/main/res/drawable/symbol_poll_24.xml create mode 100644 app/src/main/res/layout/conversation_item_poll.xml diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt index 71bcc68db6..f677b42e06 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/conversation/v2/items/V2ConversationItemShapeTest.kt @@ -36,6 +36,8 @@ import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory +import org.thoughtcrime.securesms.polls.PollOption +import org.thoughtcrime.securesms.polls.PollRecord import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stickers.StickerLocator @@ -348,5 +350,11 @@ class V2ConversationItemShapeTest { override fun onShowUnverifiedProfileSheet(forGroup: Boolean) = Unit override fun onUpdateSignalClicked() = Unit + + override fun onViewResultsClicked(pollId: Long) = Unit + + override fun onViewPollClicked(messageId: Long) = Unit + + override fun onToggleVote(poll: PollRecord, pollOption: PollOption, isChecked: Boolean) = Unit } } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/PollTablesTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/PollTablesTest.kt new file mode 100644 index 0000000000..85f7d2b870 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/PollTablesTest.kt @@ -0,0 +1,131 @@ +package org.thoughtcrime.securesms.database + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.signal.core.util.deleteAll +import org.thoughtcrime.securesms.database.model.MessageId +import org.thoughtcrime.securesms.mms.IncomingMessage +import org.thoughtcrime.securesms.polls.PollOption +import org.thoughtcrime.securesms.polls.PollRecord +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testing.SignalActivityRule + +@RunWith(AndroidJUnit4::class) +class PollTablesTest { + + @get:Rule + val harness = SignalActivityRule() + + private lateinit var poll1: PollRecord + + @Before + fun setUp() { + poll1 = PollRecord( + id = 1, + question = "how do you feel about unit testing?", + pollOptions = listOf( + PollOption(1, "yay", listOf(1)), + PollOption(2, "ok", emptyList()), + PollOption(3, "nay", emptyList()) + ), + allowMultipleVotes = false, + hasEnded = false, + authorId = 1, + messageId = 1 + ) + + SignalDatabase.polls.writableDatabase.deleteAll(PollTables.PollTable.TABLE_NAME) + SignalDatabase.polls.writableDatabase.deleteAll(PollTables.PollOptionTable.TABLE_NAME) + SignalDatabase.polls.writableDatabase.deleteAll(PollTables.PollVoteTable.TABLE_NAME) + + val message = IncomingMessage(type = MessageType.NORMAL, from = harness.others[0], sentTimeMillis = 100, serverTimeMillis = 100, receivedTimeMillis = 100) + SignalDatabase.messages.insertMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(harness.others[0], isGroup = false)) + } + + @Test + fun givenAPollWithVoting_whenIGetPoll_thenIExpectThatPoll() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(1), voterId = 1, voteCount = 1, messageId = MessageId(1)) + + assertEquals(poll1, SignalDatabase.polls.getPoll(1)) + } + + @Test + fun givenAPoll_whenIGetItsOptionIds_thenIExpectAllOptionsIds() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + assertEquals(poll1.pollOptions.map { it.id }, SignalDatabase.polls.getPollOptionIds(1)) + } + + @Test + fun givenAPollAndVoter_whenIGetItsVoteCount_thenIExpectTheCorrectVoterCount() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(1), voterId = 1, voteCount = 1, messageId = MessageId(1)) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(2), voterId = 2, voteCount = 2, messageId = MessageId(1)) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(3), voterId = 3, voteCount = 3, messageId = MessageId(1)) + + assertEquals(1, SignalDatabase.polls.getCurrentPollVoteCount(1, 1)) + assertEquals(2, SignalDatabase.polls.getCurrentPollVoteCount(1, 2)) + assertEquals(3, SignalDatabase.polls.getCurrentPollVoteCount(1, 3)) + } + + @Test + fun givenMultipleRoundsOfVoting_whenIGetItsCount_thenIExpectTheMostRecentResults() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(2), voterId = 1, voteCount = 1, messageId = MessageId(1)) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(3), voterId = 1, voteCount = 2, messageId = MessageId(1)) + SignalDatabase.polls.insertVotes(pollId = 1, pollOptionIds = listOf(1), voterId = 1, voteCount = 3, messageId = MessageId(1)) + + assertEquals(poll1, SignalDatabase.polls.getPoll(1)) + } + + @Test + fun givenAPoll_whenITerminateIt_thenIExpectItToEnd() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + SignalDatabase.polls.endPoll(1, System.currentTimeMillis()) + + assertEquals(true, SignalDatabase.polls.getPoll(1)!!.hasEnded) + } + + @Test + fun givenAPoll_whenIIVote_thenIExpectThatVote() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + val poll = SignalDatabase.polls.getPoll(1)!! + val pollOption = poll.pollOptions.first() + + val voteCount = SignalDatabase.polls.insertVote(poll, pollOption) + + assertEquals(1, voteCount) + assertEquals(listOf(0), SignalDatabase.polls.getVotes(poll.id, false)) + } + + @Test + fun givenAPoll_whenIRemoveVote_thenVoteIsCleared() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + val poll = SignalDatabase.polls.getPoll(1)!! + val pollOption = poll.pollOptions.first() + + val voteCount = SignalDatabase.polls.removeVote(poll, pollOption) + SignalDatabase.polls.markPendingAsRemoved(poll.id, Recipient.self().id.toLong(), voteCount, 1) + + assertEquals(1, voteCount) + val status = SignalDatabase.polls.getPollVoteStateForGivenVote(poll.id, voteCount) + assertEquals(PollTables.VoteState.REMOVED, status) + } + + @Test + fun givenAVote_whenISetPollOptionId_thenOptionIdIsUpdated() { + SignalDatabase.polls.insertPoll("how do you feel about unit testing?", false, listOf("yay", "ok", "nay"), 1, 1) + val poll = SignalDatabase.polls.getPoll(1)!! + val option = poll.pollOptions.first() + + SignalDatabase.polls.insertVotes(poll.id, listOf(option.id), Recipient.self().id.toLong(), 5, MessageId(1)) + SignalDatabase.polls.setPollVoteStateForGivenVote(poll.id, Recipient.self().id.toLong(), 5, 1, true) + val status = SignalDatabase.polls.getPollVoteStateForGivenVote(poll.id, 5) + + assertEquals(PollTables.VoteState.ADDED, status) + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/DataMessageProcessorTest_polls.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/DataMessageProcessorTest_polls.kt new file mode 100644 index 0000000000..d72c78e1b8 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/DataMessageProcessorTest_polls.kt @@ -0,0 +1,322 @@ +package org.thoughtcrime.securesms.messages + +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.thoughtcrime.securesms.database.MessageTable +import org.thoughtcrime.securesms.database.MessageType +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.MessageId +import org.thoughtcrime.securesms.groups.GroupId +import org.thoughtcrime.securesms.mms.IncomingMessage +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.testing.GroupTestingUtils +import org.thoughtcrime.securesms.testing.GroupTestingUtils.asMember +import org.thoughtcrime.securesms.testing.MessageContentFuzzer +import org.thoughtcrime.securesms.testing.SignalActivityRule +import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata +import org.whispersystems.signalservice.internal.push.DataMessage + +@Suppress("ClassName") +@RunWith(AndroidJUnit4::class) +class DataMessageProcessorTest_polls { + + @get:Rule + val harness = SignalActivityRule(createGroup = true) + + private lateinit var alice: Recipient + private lateinit var bob: Recipient + private lateinit var charlie: Recipient + private lateinit var groupId: GroupId.V2 + private lateinit var groupRecipientId: RecipientId + + @Before + fun setUp() { + alice = Recipient.resolved(harness.others[0]) + bob = Recipient.resolved(harness.others[1]) + charlie = Recipient.resolved(harness.others[2]) + + val groupInfo = GroupTestingUtils.insertGroup(revision = 0, harness.self.asMember(), alice.asMember(), bob.asMember()) + groupId = groupInfo.groupId + groupRecipientId = groupInfo.recipientId + } + + @Test + fun handlePollCreate_whenIHaveAValidPollProto_createPoll() { + val insertResult = handlePollCreate( + pollCreate = DataMessage.PollCreate(question = "question?", options = listOf("a", "b", "c"), allowMultiple = false), + senderRecipient = alice, + threadRecipient = Recipient.resolved(groupRecipientId), + groupId = groupId + ) + + assert(insertResult != null) + val poll = SignalDatabase.polls.getPoll(insertResult!!.messageId) + assert(poll != null) + assertThat(poll!!.question).isEqualTo("question?") + assertThat(poll.pollOptions.size).isEqualTo(3) + assertThat(poll.allowMultipleVotes).isEqualTo(false) + assertThat(poll.hasEnded).isEqualTo(false) + } + + @Test + fun handlePollCreate_whenSenderIsNotInGroup_dropMessage() { + val insertResult = handlePollCreate( + pollCreate = DataMessage.PollCreate(question = "question?", options = listOf("a", "b", "c"), allowMultiple = false), + senderRecipient = charlie, + threadRecipient = Recipient.resolved(groupRecipientId), + groupId = groupId + ) + + assert(insertResult == null) + } + + @Test + fun handlePollCreate_whenTargetRecipientIsNotAGroup_dropMessage() { + val insertResult = handlePollCreate( + pollCreate = DataMessage.PollCreate(question = "question?", options = listOf("a", "b", "c"), allowMultiple = false), + senderRecipient = alice, + threadRecipient = bob, + groupId = null + ) + + assert(insertResult == null) + } + + @Test + fun handlePollTerminate_whenIHaveValidProto_endPoll() { + val pollMessageId = insertPoll() + + val insertResult = DataMessageProcessor.handlePollTerminate( + context = ApplicationProvider.getApplicationContext(), + envelope = MessageContentFuzzer.envelope(200), + message = DataMessage(pollTerminate = DataMessage.PollTerminate(targetSentTimestamp = 100)), + senderRecipient = alice, + metadata = EnvelopeMetadata(alice.requireServiceId(), null, 1, false, null, harness.self.requireServiceId()), + threadRecipient = bob, + groupId = groupId, + receivedTime = 200 + ) + + assert(insertResult?.messageId != null) + val poll = SignalDatabase.polls.getPoll(pollMessageId) + assert(poll != null) + assert(poll!!.hasEnded) + } + + @Test + fun handlePollTerminate_whenIHaveDifferentTimestamp_dropMessage() { + insertPoll() + + val insertResult = DataMessageProcessor.handlePollTerminate( + context = ApplicationProvider.getApplicationContext(), + envelope = MessageContentFuzzer.envelope(200), + message = DataMessage(pollTerminate = DataMessage.PollTerminate(200)), + senderRecipient = alice, + metadata = EnvelopeMetadata(alice.requireServiceId(), null, 1, false, null, harness.self.requireServiceId()), + threadRecipient = bob, + groupId = groupId, + receivedTime = 200 + ) + + assert(insertResult == null) + } + + @Test + fun handlePollTerminate_whenMessageIsNotFromCreatorOfPoll_dropMessage() { + insertPoll() + + val insertResult = DataMessageProcessor.handlePollTerminate( + context = ApplicationProvider.getApplicationContext(), + envelope = MessageContentFuzzer.envelope(200), + message = DataMessage(pollTerminate = DataMessage.PollTerminate(100)), + senderRecipient = bob, + metadata = EnvelopeMetadata(alice.requireServiceId(), null, 1, false, null, harness.self.requireServiceId()), + threadRecipient = bob, + groupId = groupId, + receivedTime = 200 + ) + + assert(insertResult == null) + } + + @Test + fun handlePollTerminate_whenPollDoesNotExist_dropMessage() { + val insertResult = DataMessageProcessor.handlePollTerminate( + context = ApplicationProvider.getApplicationContext(), + envelope = MessageContentFuzzer.envelope(200), + message = DataMessage(pollTerminate = DataMessage.PollTerminate(100)), + senderRecipient = alice, + metadata = EnvelopeMetadata(alice.requireServiceId(), null, 1, false, null, harness.self.requireServiceId()), + threadRecipient = bob, + groupId = groupId, + receivedTime = 200 + ) + + assert(insertResult == null) + } + + @Test + fun handlePollVote_whenValidPollVote_processVote() { + insertPoll() + + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(0), + voteCount = 1 + ), + bob + ) + + assert(messageId != null) + assertThat(messageId!!.id).isEqualTo(1) + val poll = SignalDatabase.polls.getPoll(messageId.id) + assert(poll != null) + assertThat(poll!!.pollOptions[0].voterIds).isEqualTo(listOf(bob.id.toLong())) + } + + @Test + fun handlePollVote_whenMultipleVoteAllowed_processAllVote() { + insertPoll() + + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(0, 1, 2), + voteCount = 1 + ), + bob + ) + + assert(messageId != null) + val poll = SignalDatabase.polls.getPoll(messageId!!.id) + assert(poll != null) + assertThat(poll!!.pollOptions[0].voterIds).isEqualTo(listOf(bob.id.toLong())) + assertThat(poll.pollOptions[1].voterIds).isEqualTo(listOf(bob.id.toLong())) + assertThat(poll.pollOptions[2].voterIds).isEqualTo(listOf(bob.id.toLong())) + } + + @Test + fun handlePollVote_whenMultipleVoteSentToSingleVotePolls_dropMessage() { + insertPoll(false) + + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(0, 1, 2), + voteCount = 1 + ), + bob + ) + + assert(messageId == null) + } + + @Test + fun handlePollVote_whenVoteCountIsNotHigher_dropMessage() { + insertPoll() + + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(0, 1, 2), + voteCount = -1 + ), + bob + ) + + assert(messageId == null) + } + + @Test + fun handlePollVote_whenVoteOptionDoesNotExist_dropMessage() { + insertPoll() + + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(5), + voteCount = 1 + ), + bob + ) + + assert(messageId == null) + } + + @Test + fun handlePollVote_whenVoterNotInGroup_dropMessage() { + insertPoll() + + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(0, 1, 2), + voteCount = 1 + + ), + charlie + ) + + assert(messageId == null) + } + + @Test + fun handlePollVote_whenPollDoesNotExist_dropMessage() { + val messageId = handlePollVote( + DataMessage.PollVote( + targetAuthorAciBinary = alice.asMember().aciBytes, + targetSentTimestamp = 100, + optionIndexes = listOf(0, 1, 2), + voteCount = 1 + ), + bob + ) + + assert(messageId == null) + } + + private fun handlePollCreate(pollCreate: DataMessage.PollCreate, senderRecipient: Recipient, threadRecipient: Recipient, groupId: GroupId.V2?): MessageTable.InsertResult? { + return DataMessageProcessor.handlePollCreate( + envelope = MessageContentFuzzer.envelope(100), + message = DataMessage(pollCreate = pollCreate), + senderRecipient = senderRecipient, + threadRecipient = threadRecipient, + groupId = groupId, + receivedTime = 0, + context = ApplicationProvider.getApplicationContext(), + metadata = EnvelopeMetadata(alice.requireServiceId(), null, 1, false, null, harness.self.requireServiceId()) + ) + } + + private fun handlePollVote(pollVote: DataMessage.PollVote, senderRecipient: Recipient): MessageId? { + return DataMessageProcessor.handlePollVote( + context = ApplicationProvider.getApplicationContext(), + envelope = MessageContentFuzzer.envelope(100), + message = DataMessage(pollVote = pollVote), + senderRecipient = senderRecipient, + earlyMessageCacheEntry = null + ) + } + + private fun insertPoll(allowMultiple: Boolean = true): Long { + val envelope = MessageContentFuzzer.envelope(100) + val pollMessage = IncomingMessage(type = MessageType.NORMAL, from = alice.id, sentTimeMillis = envelope.timestamp!!, serverTimeMillis = envelope.serverTimestamp!!, receivedTimeMillis = 0, groupId = groupId) + val messageId = SignalDatabase.messages.insertMessageInbox(pollMessage).get() + SignalDatabase.polls.insertPoll("question?", allowMultiple, listOf("a", "b", "c"), alice.id.toLong(), messageId.messageId) + return messageId.messageId + } +} diff --git a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt index d350ba235b..2f7724dd71 100644 --- a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt +++ b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/ConversationElementGenerator.kt @@ -115,6 +115,7 @@ class ConversationElementGenerator { null, null, null, + null, -1, null, null, diff --git a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt index d6475e411f..2ca0d4e4d8 100644 --- a/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt +++ b/app/src/debug/java/org/thoughtcrime/securesms/components/settings/app/internal/conversation/test/InternalConversationTestFragment.kt @@ -44,6 +44,8 @@ import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory +import org.thoughtcrime.securesms.polls.PollOption +import org.thoughtcrime.securesms.polls.PollRecord import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stickers.StickerLocator @@ -336,5 +338,17 @@ class InternalConversationTestFragment : Fragment(R.layout.conversation_test_fra override fun onUpdateSignalClicked() { Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() } + + override fun onViewResultsClicked(pollId: Long) { + Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() + } + + override fun onViewPollClicked(messageId: Long) { + Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() + } + + override fun onToggleVote(poll: PollRecord, pollOption: PollOption, isChecked: Boolean) { + Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show() + } } } diff --git a/app/src/main/assets/fonts/SignalSymbols-Regular.otf b/app/src/main/assets/fonts/SignalSymbols-Regular.otf index c7e7d677f7af0a6c3dfabec08dd646917b546745..b647bf58974408ecfc43e3bdf48893aff93c9d53 100644 GIT binary patch literal 56628 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 delta 16562 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 N7e{!#<}S$){|5{Q!JhyC diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index f3d357a23e..0ebf960358 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -30,6 +30,8 @@ import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory; +import org.thoughtcrime.securesms.polls.PollRecord; +import org.thoughtcrime.securesms.polls.PollOption; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.stickers.StickerLocator; @@ -143,5 +145,8 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable, void onDisplayMediaNoLongerAvailableSheet(); void onShowUnverifiedProfileSheet(boolean forGroup); void onUpdateSignalClicked(); + void onViewResultsClicked(long pollId); + void onViewPollClicked(long messageId); + void onToggleVote(@NonNull PollRecord poll, @NonNull PollOption pollOption, Boolean isChecked); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/compose/RoundCheckbox.kt b/app/src/main/java/org/thoughtcrime/securesms/components/compose/RoundCheckbox.kt index 985a4c7902..8ab9d3fb88 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/compose/RoundCheckbox.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/compose/RoundCheckbox.kt @@ -18,7 +18,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Checkbox @@ -26,6 +25,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource @@ -34,6 +34,7 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.signal.core.ui.compose.DayNightPreviews import org.signal.core.ui.compose.theme.SignalTheme @@ -50,7 +51,11 @@ import org.thoughtcrime.securesms.R fun RoundCheckbox( checked: Boolean, onCheckedChange: (Boolean) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + size: Dp = 24.dp, + enabled: Boolean = true, + outlineColor: Color = MaterialTheme.colorScheme.outline, + checkedColor: Color = MaterialTheme.colorScheme.primary ) { val contentDescription = if (checked) { stringResource(R.string.SignalCheckbox_accessibility_checked_description) @@ -60,15 +65,14 @@ fun RoundCheckbox( Box( modifier = modifier - .padding(12.dp) - .size(24.dp) + .size(size) .aspectRatio(1f) .border( width = 1.5.dp, color = if (checked) { - MaterialTheme.colorScheme.primary + checkedColor } else { - MaterialTheme.colorScheme.outline + outlineColor }, shape = CircleShape ) @@ -76,7 +80,8 @@ fun RoundCheckbox( interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = { onCheckedChange(!checked) }, - onClickLabel = stringResource(R.string.SignalCheckbox_accessibility_on_click_label) + onClickLabel = stringResource(R.string.SignalCheckbox_accessibility_on_click_label), + enabled = enabled ) .semantics(mergeDescendants = true) { this.role = Role.Checkbox @@ -90,7 +95,7 @@ fun RoundCheckbox( ) { Image( imageVector = ImageVector.vectorResource(id = R.drawable.ic_check_circle_solid_24), - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary), + colorFilter = ColorFilter.tint(checkedColor), contentDescription = null, modifier = Modifier.fillMaxSize() ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java index 8e9678175a..3da38e6020 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiStrings.java @@ -10,5 +10,6 @@ public final class EmojiStrings { public static final String STICKER = "\u2B50"; public static final String GIFT = "\uD83C\uDF81"; public static final String CARD = "\uD83D\uDCB3"; + public static final String POLL = "\uD83D\uDCCA"; public static final String FAILED_STORY = "\u2757"; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java index c16ae1037d..68eeb4eb81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboard.java @@ -35,6 +35,7 @@ public class AttachmentKeyboard extends FrameLayout implements InputAwareLayout. private static final List DEFAULT_BUTTONS = Arrays.asList( AttachmentKeyboardButton.GALLERY, AttachmentKeyboardButton.FILE, + AttachmentKeyboardButton.POLL, AttachmentKeyboardButton.CONTACT, AttachmentKeyboardButton.LOCATION, AttachmentKeyboardButton.PAYMENT diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboardButton.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboardButton.java index 20812700f1..63bc50ff3c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboardButton.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/AttachmentKeyboardButton.java @@ -11,7 +11,8 @@ public enum AttachmentKeyboardButton { FILE(R.string.AttachmentKeyboard_file, R.drawable.symbol_file_24), PAYMENT(R.string.AttachmentKeyboard_payment, R.drawable.symbol_payment_24), CONTACT(R.string.AttachmentKeyboard_contact, R.drawable.symbol_person_circle_24), - LOCATION(R.string.AttachmentKeyboard_location, R.drawable.symbol_location_circle_24); + LOCATION(R.string.AttachmentKeyboard_location, R.drawable.symbol_location_circle_24), + POLL(R.string.AttachmentKeyboard_poll, R.drawable.symbol_poll_24); private final int titleRes; private final int iconRes; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index f9ba8715d9..e53a133599 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -56,6 +56,7 @@ import androidx.annotation.ColorInt; import androidx.annotation.DimenRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.compose.ui.platform.ComposeView; import androidx.core.content.ContextCompat; import androidx.lifecycle.LifecycleOwner; import androidx.media3.common.MediaItem; @@ -73,7 +74,6 @@ import org.signal.core.util.BidiUtil; import org.signal.core.util.DimensionUnit; import org.signal.core.util.StringUtil; import org.signal.core.util.logging.Log; -import org.signal.ringrtc.CallLinkRootKey; import org.thoughtcrime.securesms.BindableConversationItem; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.attachments.Attachment; @@ -130,6 +130,7 @@ import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.mms.SlidesClickedListener; import org.thoughtcrime.securesms.mms.TextSlide; import org.thoughtcrime.securesms.mms.VideoSlide; +import org.thoughtcrime.securesms.polls.PollRecord; import org.thoughtcrime.securesms.reactions.ReactionsConversationView; import org.thoughtcrime.securesms.recipients.LiveRecipient; import org.thoughtcrime.securesms.recipients.Recipient; @@ -236,6 +237,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo private Stub