Merge remote-tracking branch 'origin/dev' into closed_groups

# Conflicts:
#	app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
#	app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt
#	app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt
#	app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java
#	app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
#	app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java
#	app/src/main/java/org/thoughtcrime/securesms/database/model/ThreadRecord.java
#	app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsViewModel.kt
#	app/src/main/res/layout/activity_conversation_v2.xml
#	app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModelTest.kt
This commit is contained in:
SessionHero01 2024-10-22 15:57:22 +11:00
commit 0bc933dcec
No known key found for this signature in database
40 changed files with 309 additions and 189 deletions

View File

@ -273,9 +273,9 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
implementation 'androidx.activity:activity-ktx:1.5.1'
implementation 'androidx.activity:activity-compose:1.5.1'
implementation 'androidx.fragment:fragment-ktx:1.5.3'
implementation 'androidx.activity:activity-ktx:1.9.2'
implementation 'androidx.activity:activity-compose:1.9.2'
implementation 'androidx.fragment:fragment-ktx:1.8.4'
implementation "androidx.core:core-ktx:$coreVersion"
implementation "androidx.work:work-runtime-ktx:2.7.1"

View File

@ -127,11 +127,7 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
if (!isResume) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
if (TextSecurePreferences.isScreenSecurityEnabled(this)) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
}

View File

@ -145,6 +145,12 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
return smsDatabase.isOutgoingMessage(timestamp) || mmsDatabase.isOutgoingMessage(timestamp)
}
override fun isDeletedMessage(timestamp: Long): Boolean {
val smsDatabase = DatabaseComponent.get(context).smsDatabase()
val mmsDatabase = DatabaseComponent.get(context).mmsDatabase()
return smsDatabase.isDeletedMessage(timestamp) || mmsDatabase.isDeletedMessage(timestamp)
}
override fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) {
val database = DatabaseComponent.get(context).attachmentDatabase()
val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return

View File

@ -119,14 +119,14 @@ import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey
import org.thoughtcrime.securesms.conversation.ConversationActionBarDelegate
import org.thoughtcrime.securesms.conversation.disappearingmessages.DisappearingMessagesActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.*
import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnActionSelectedListener
import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnReactionSelectedListener
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.*
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.MESSAGE_TIMESTAMP
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.ON_COPY
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.ON_DELETE
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.ON_REPLY
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.ON_RESEND
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.ON_COPY
import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.ON_SAVE
import org.thoughtcrime.securesms.conversation.v2.dialogs.BlockedDialog
import org.thoughtcrime.securesms.conversation.v2.dialogs.LinkPreviewDialog
@ -168,6 +168,8 @@ import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.giph.ui.GiphyActivity
import org.thoughtcrime.securesms.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.search.getSearchName
import org.thoughtcrime.securesms.home.HomeActivity
import org.thoughtcrime.securesms.home.startHomeActivity
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
@ -913,6 +915,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// show or hide loading indicator
binding.loader.isVisible = state.showLoader
if (state.isMessageRequestAccepted == true) {
binding.messageRequestBar.visibility = View.GONE
}
}
}
}
@ -1169,8 +1175,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val openGroup = viewModel.openGroup
// Get the correct placeholder text for this type of empty conversation
val isNoteToSelf = recipient.isLocalNumber
val txtCS: CharSequence = when {
// note to self
recipient.isLocalNumber -> getString(R.string.noteToSelfEmpty)
// If this is a community which we cannot write to
@ -1187,8 +1193,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
.format()
}
recipient.isGroupRecipient -> {
// If this is a group or community that we CAN send messages to
// 10n1 and groups
recipient.is1on1 || recipient.isGroupRecipient -> {
Phrase.from(applicationContext, R.string.groupNoMessages)
.put(GROUP_NAME_KEY, recipient.toShortString())
.format()
@ -1591,14 +1597,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sendEmojiRemoval(emoji, message)
}
/**
* Called when the user is attempting to clear all instance of a specific emoji.
*/
override fun onClearAll(emoji: String, messageId: MessageId) {
reactionDb.deleteEmojiReactions(emoji, messageId)
viewModel.openGroup?.let { openGroup ->
lokiMessageDb.getServerID(messageId.id, !messageId.mms)?.let { serverId ->
OpenGroupApi.deleteAllReactions(openGroup.room, openGroup.server, serverId, emoji)
}
}
threadDb.notifyThreadUpdated(viewModel.threadId)
viewModel.onEmojiClear(emoji, messageId)
}
override fun onMicrophoneButtonMove(event: MotionEvent) {

View File

@ -41,7 +41,6 @@ import org.thoughtcrime.securesms.components.emoji.EmojiImageView
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel
import org.thoughtcrime.securesms.components.menu.ActionItem
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuItemHelper.userCanBanSelectedUsers
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuItemHelper.userCanDeleteSelectedItems
import org.thoughtcrime.securesms.database.MmsSmsDatabase
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
import org.thoughtcrime.securesms.database.model.MessageRecord
@ -559,10 +558,8 @@ class ConversationReactionOverlay : FrameLayout {
items += ActionItem(R.attr.menu_copy_icon, R.string.accountIDCopy, { handleActionItemClicked(Action.COPY_ACCOUNT_ID) })
}
// Delete message
if (userCanDeleteSelectedItems(context, message, openGroup, userPublicKey, blindedPublicKey)) {
items += ActionItem(R.attr.menu_trash_icon, R.string.delete, { handleActionItemClicked(Action.DELETE) },
R.string.AccessibilityId_deleteMessage, message.subtitle, ThemeUtil.getThemedColor(context, R.attr.danger))
}
items += ActionItem(R.attr.menu_trash_icon, R.string.delete, { handleActionItemClicked(Action.DELETE) },
R.string.AccessibilityId_deleteMessage, message.subtitle, ThemeUtil.getThemedColor(context, R.attr.danger))
// Ban user
if (userCanBanSelectedUsers(context, message, openGroup, userPublicKey, blindedPublicKey) && !isDeleteOnly) {
items += ActionItem(R.attr.menu_block_icon, R.string.banUser, { handleActionItemClicked(Action.BAN_USER) })

View File

@ -14,12 +14,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.squareup.phrase.Phrase
import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.HideDeleteAllDevicesDialog
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.HideDeleteEveryoneDialog
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.MarkAsDeletedForEveryone
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.MarkAsDeletedLocally
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.ShowOpenUrlDialog
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.EMOJI_KEY
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.Commands.*
import org.thoughtcrime.securesms.conversation.v2.ConversationViewModel.DeleteForEveryoneDialogData
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel
import org.thoughtcrime.securesms.ui.GetString
@ -49,9 +50,10 @@ fun ConversationV2Dialogs(
)
}
// delete message(s) for everyone
// delete message(s)
if(dialogsState.deleteEveryone != null){
var deleteForEveryone by remember { mutableStateOf(dialogsState.deleteEveryone.defaultToEveryone)}
val data = dialogsState.deleteEveryone
var deleteForEveryone by remember { mutableStateOf(data.defaultToEveryone)}
AlertDialog(
onDismissRequest = {
@ -60,17 +62,17 @@ fun ConversationV2Dialogs(
},
title = pluralStringResource(
R.plurals.deleteMessage,
dialogsState.deleteEveryone.messages.size,
dialogsState.deleteEveryone.messages.size
data.messages.size,
data.messages.size
),
text = pluralStringResource(
R.plurals.deleteMessageConfirm,
dialogsState.deleteEveryone.messages.size,
dialogsState.deleteEveryone.messages.size
data.messages.size,
data.messages.size
),
content = {
// add warning text, if any
dialogsState.deleteEveryone.warning?.let {
data.warning?.let {
Text(
text = it,
textAlign = TextAlign.Center,
@ -104,9 +106,9 @@ fun ConversationV2Dialogs(
),
option = RadioOption(
value = Unit,
title = GetString(stringResource(R.string.deleteMessageEveryone)),
title = GetString(data.deleteForEveryoneLabel),
selected = deleteForEveryone,
enabled = dialogsState.deleteEveryone.everyoneEnabled
enabled = data.everyoneEnabled
)
) {
deleteForEveryone = true
@ -120,9 +122,9 @@ fun ConversationV2Dialogs(
// delete messages based on chosen option
sendCommand(
if(deleteForEveryone) MarkAsDeletedForEveryone(
dialogsState.deleteEveryone.copy(defaultToEveryone = deleteForEveryone)
data.copy(defaultToEveryone = deleteForEveryone)
)
else MarkAsDeletedLocally(dialogsState.deleteEveryone.messages)
else MarkAsDeletedLocally(data.messages)
)
}
),
@ -133,65 +135,24 @@ fun ConversationV2Dialogs(
)
}
// delete message(s) for all my devices
if(dialogsState.deleteAllDevices != null){
var deleteAllDevices by remember { mutableStateOf(dialogsState.deleteAllDevices.defaultToEveryone) }
// Clear emoji
if(dialogsState.clearAllEmoji != null){
AlertDialog(
onDismissRequest = {
// hide dialog
sendCommand(HideDeleteAllDevicesDialog)
sendCommand(HideClearEmoji)
},
title = pluralStringResource(
R.plurals.deleteMessage,
dialogsState.deleteAllDevices.messages.size,
dialogsState.deleteAllDevices.messages.size
),
text = pluralStringResource(
R.plurals.deleteMessageConfirm,
dialogsState.deleteAllDevices.messages.size,
dialogsState.deleteAllDevices.messages.size
),
content = {
TitledRadioButton(
contentPadding = PaddingValues(
horizontal = LocalDimensions.current.xxsSpacing,
vertical = 0.dp
),
option = RadioOption(
value = Unit,
title = GetString(stringResource(R.string.deleteMessageDeviceOnly)),
selected = !deleteAllDevices
)
) {
deleteAllDevices = false
}
TitledRadioButton(
contentPadding = PaddingValues(
horizontal = LocalDimensions.current.xxsSpacing,
vertical = 0.dp
),
option = RadioOption(
value = Unit,
title = GetString(stringResource(R.string.deleteMessageDevicesAll)),
selected = deleteAllDevices
)
) {
deleteAllDevices = true
}
text = stringResource(R.string.emojiReactsClearAll).let { txt ->
Phrase.from(txt).put(EMOJI_KEY, dialogsState.clearAllEmoji.emoji).format().toString()
},
buttons = listOf(
DialogButtonModel(
text = GetString(stringResource(id = R.string.delete)),
text = GetString(stringResource(id = R.string.clear)),
color = LocalColors.current.danger,
onClick = {
// delete messages based on chosen option
// delete emoji
sendCommand(
if(deleteAllDevices) MarkAsDeletedForEveryone(
dialogsState.deleteAllDevices.copy(defaultToEveryone = deleteAllDevices)
)
else MarkAsDeletedLocally(dialogsState.deleteAllDevices.messages)
ClearEmoji(dialogsState.clearAllEmoji.emoji, dialogsState.clearAllEmoji.messageId)
)
}
),
@ -201,7 +162,6 @@ fun ConversationV2Dialogs(
)
)
}
}
}

View File

@ -43,7 +43,10 @@ import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.LokiMessageDatabase
import org.thoughtcrime.securesms.database.ReactionDatabase
import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.MessageId
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.dependencies.ConfigFactory
@ -61,6 +64,7 @@ class ConversationViewModel(
private val messageDataProvider: MessageDataProvider,
private val groupDb: GroupDatabase,
private val threadDb: ThreadDatabase,
private val reactionDb: ReactionDatabase,
private val lokiMessageDb: LokiMessageDatabase,
private val textSecurePreferences: TextSecurePreferences,
private val configFactory: ConfigFactory,
@ -339,12 +343,12 @@ class ConversationViewModel(
// hashes are required if wanting to delete messages from the 'storage server'
// They are not required for communities OR if all messages are outgoing
// also we can only delete deleted messages (marked as deleted) locally
// also we can only delete deleted messages and control messages (marked as deleted) locally
val canDeleteForEveryone = messages.all{ !it.isDeleted && !it.isControlMessage } && (
messages.all { it.isOutgoing } ||
conversationType == MessageType.COMMUNITY ||
messages.all { lokiMessageDb.getMessageServerHash(it.id, it.isMms) != null
})
messages.all { lokiMessageDb.getMessageServerHash(it.id, it.isMms) != null }
)
// There are three types of dialogs for deletion:
// 1- Delete on device only OR all devices - Used for Note to self
@ -354,11 +358,16 @@ class ConversationViewModel(
// the conversation is a note to self
conversationType == MessageType.NOTE_TO_SELF -> {
_dialogsState.update {
it.copy(deleteAllDevices = DeleteForEveryoneDialogData(
it.copy(deleteEveryone = DeleteForEveryoneDialogData(
messages = messages,
defaultToEveryone = false,
everyoneEnabled = true,
messageType = conversationType
everyoneEnabled = canDeleteForEveryone,
messageType = conversationType,
deleteForEveryoneLabel = application.getString(R.string.deleteMessageDevicesAll),
warning = if(canDeleteForEveryone) null else
application.resources.getQuantityString(
R.plurals.deleteMessageNoteToSelfWarning, messages.count(), messages.count()
)
)
)
}
@ -372,6 +381,7 @@ class ConversationViewModel(
messages = messages,
defaultToEveryone = isAdmin.value,
everyoneEnabled = true,
deleteForEveryoneLabel = application.getString(R.string.deleteMessageEveryone),
messageType = conversationType
)
)
@ -387,6 +397,7 @@ class ConversationViewModel(
defaultToEveryone = false,
everyoneEnabled = false, // disable 'delete for everyone' - can only delete locally in this case
messageType = conversationType,
deleteForEveryoneLabel = application.getString(R.string.deleteMessageEveryone),
warning = application.resources.getQuantityString(
R.plurals.deleteMessageWarning, messages.count(), messages.count()
)
@ -642,7 +653,7 @@ class ConversationViewModel(
).show()
}
_dialogsState.update { it.copy(deleteAllDevices = data) }
_dialogsState.update { it.copy(deleteEveryone = data) }
}
// hide loading indicator
@ -659,11 +670,8 @@ class ConversationViewModel(
try {
repository.deleteCommunityMessagesRemotely(threadId, data.messages)
// When this is done we simply need to remove the message locally
repository.markAsDeletedLocally(
messages = data.messages,
displayedMessage = application.getString(R.string.deleteMessageDeletedGlobally)
)
// When this is done we simply need to remove the message locally (leave nothing behind)
repository.deleteMessages(messages = data.messages, threadId = threadId)
// show confirmation toast
withContext(Dispatchers.Main) {
@ -855,9 +863,9 @@ class ConversationViewModel(
}
}
is Commands.HideDeleteAllDevicesDialog -> {
is Commands.HideClearEmoji -> {
_dialogsState.update {
it.copy(deleteAllDevices = null)
it.copy(clearAllEmoji = null)
}
}
@ -872,6 +880,35 @@ class ConversationViewModel(
is Commands.MarkAsDeletedForEveryone -> {
markAsDeletedForEveryone(command.data)
}
is Commands.ClearEmoji -> {
clearEmoji(command.emoji, command.messageId)
}
}
}
private fun clearEmoji(emoji: String, messageId: MessageId){
viewModelScope.launch(Dispatchers.Default) {
reactionDb.deleteEmojiReactions(emoji, messageId)
openGroup?.let { openGroup ->
lokiMessageDb.getServerID(messageId.id, !messageId.mms)?.let { serverId ->
OpenGroupApi.deleteAllReactions(
openGroup.room,
openGroup.server,
serverId,
emoji
)
}
}
threadDb.notifyThreadUpdated(threadId)
}
}
fun onEmojiClear(emoji: String, messageId: MessageId) {
// show a confirmation dialog
_dialogsState.update {
it.copy(clearAllEmoji = ClearAllEmoji(emoji, messageId))
}
}
@ -921,6 +958,7 @@ class ConversationViewModel(
private val messageDataProvider: MessageDataProvider,
private val groupDb: GroupDatabase,
private val threadDb: ThreadDatabase,
private val reactionDb: ReactionDatabase,
@ApplicationContext
private val context: Context,
private val lokiMessageDb: LokiMessageDatabase,
@ -939,6 +977,7 @@ class ConversationViewModel(
messageDataProvider = messageDataProvider,
groupDb = groupDb,
threadDb = threadDb,
reactionDb = reactionDb,
lokiMessageDb = lokiMessageDb,
textSecurePreferences = textSecurePreferences,
configFactory = configFactory,
@ -949,8 +988,8 @@ class ConversationViewModel(
data class DialogsState(
val openLinkDialogUrl: String? = null,
val deleteEveryone: DeleteForEveryoneDialogData? = null,
val deleteAllDevices: DeleteForEveryoneDialogData? = null,
val clearAllEmoji: ClearAllEmoji? = null,
val deleteEveryone: DeleteForEveryoneDialogData? = null
)
data class DeleteForEveryoneDialogData(
@ -958,13 +997,22 @@ class ConversationViewModel(
val messageType: MessageType,
val defaultToEveryone: Boolean,
val everyoneEnabled: Boolean,
val deleteForEveryoneLabel: String,
val warning: String? = null
)
data class ClearAllEmoji(
val emoji: String,
val messageId: MessageId
)
sealed class Commands {
data class ShowOpenUrlDialog(val url: String?) : Commands()
data class ClearEmoji(val emoji:String, val messageId: MessageId) : Commands()
data object HideDeleteEveryoneDialog : Commands()
data object HideDeleteAllDevicesDialog : Commands()
data object HideClearEmoji : Commands()
data class MarkAsDeletedLocally(val messages: Set<MessageRecord>): Commands()
data class MarkAsDeletedForEveryone(val data: DeleteForEveryoneDialogData): Commands()

View File

@ -42,18 +42,6 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
val blindedPublicKey = openGroup?.publicKey?.let { SodiumUtilities.blindedKeyPair(it, edKeyPair)?.publicKey?.asBytes }
?.let { AccountId(IdPrefix.BLINDED, it) }?.hexString
// Embedded function
fun userCanDeleteSelectedItems(): Boolean {
// admin can delete all combinations
if(adapter.isAdmin) return true
val allSentByCurrentUser = selectedItems.all { it.isOutgoing }
val allReceivedByCurrentUser = selectedItems.all { !it.isOutgoing }
if (openGroup == null) { return allSentByCurrentUser || allReceivedByCurrentUser }
if (allSentByCurrentUser) { return true }
return OpenGroupManager.isUserModerator(context, openGroup.groupId, userPublicKey, blindedPublicKey)
}
// Embedded function
fun userCanBanSelectedUsers(): Boolean {
if (openGroup == null) { return false }
@ -67,7 +55,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p
// Delete message
menu.findItem(R.id.menu_context_delete_message).isVisible = userCanDeleteSelectedItems()
menu.findItem(R.id.menu_context_delete_message).isVisible = true // can always delete since delete logic will be handled by the VM
// Ban user
menu.findItem(R.id.menu_context_ban_user).isVisible = userCanBanSelectedUsers()
// Ban and delete all

View File

@ -7,13 +7,6 @@ import org.thoughtcrime.securesms.groups.OpenGroupManager
object ConversationMenuItemHelper {
@JvmStatic
fun userCanDeleteSelectedItems(context: Context, message: MessageRecord, openGroup: OpenGroup?, userPublicKey: String, blindedPublicKey: String?): Boolean {
if (openGroup == null) return message.isOutgoing || !message.isOutgoing
if (message.isOutgoing) return true
return OpenGroupManager.isUserModerator(context, openGroup.groupId, userPublicKey, blindedPublicKey)
}
@JvmStatic
fun userCanBanSelectedUsers(context: Context, message: MessageRecord, openGroup: OpenGroup?, userPublicKey: String, blindedPublicKey: String?): Boolean {
if (openGroup == null) return false

View File

@ -89,7 +89,11 @@ class ControlMessageView : LinearLayout {
&& message.expiryMode != (MessagingModuleConfiguration.shared.storage.getExpirationConfiguration(message.threadId)?.expiryMode ?: ExpiryMode.NONE)
&& threadRecipient?.isGroupRecipient != true
binding.controlContentView.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) }
if (followSetting.isVisible) {
binding.controlContentView.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) }
} else {
binding.controlContentView.setOnClickListener(null)
}
}
}
message.isMediaSavedNotification -> {

View File

@ -234,7 +234,7 @@ class VisibleMessageView : FrameLayout {
showStatusMessage(message)
// Emoji Reactions
if (message.reactions.isNotEmpty()) {
if (!message.isDeleted && message.reactions.isNotEmpty()) {
val capabilities = lokiThreadDb.getOpenGroupChat(threadID)?.server?.let { lokiApiDb.getServerCapabilities(it) }
if (capabilities.isNullOrEmpty() || capabilities.contains(OpenGroupApi.Capability.REACTIONS.name.lowercase())) {
emojiReactionsBinding.value.root.let { root ->

View File

@ -106,6 +106,23 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
.any { MmsSmsColumns.Types.isOutgoingMessageType(it) }
}
fun isDeletedMessage(timestamp: Long): Boolean =
databaseHelper.writableDatabase.query(
TABLE_NAME,
arrayOf(ID, THREAD_ID, MESSAGE_BOX, ADDRESS),
DATE_SENT + " = ?",
arrayOf(timestamp.toString()),
null,
null,
null,
null
).use { cursor ->
cursor.asSequence()
.map { cursor.getColumnIndexOrThrow(MESSAGE_BOX) }
.map(cursor::getLong)
.any { MmsSmsColumns.Types.isDeletedMessage(it) }
}
fun incrementReceiptCount(
messageId: SyncMessageId,
timestamp: Long,
@ -913,7 +930,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
val groupReceiptDatabase = get(context).groupReceiptDatabase()
groupReceiptDatabase.deleteRowsForMessage(messageId)
val database = databaseHelper.writableDatabase
database.delete(TABLE_NAME, ID_WHERE, arrayOf(messageId.toString()))
database!!.delete(TABLE_NAME, ID_WHERE, arrayOf(messageId.toString()))
val threadDeleted = get(context).threadDatabase().update(threadId, false)
notifyConversationListeners(threadId)
notifyStickerListeners()

View File

@ -17,6 +17,9 @@
package org.thoughtcrime.securesms.database;
import static org.thoughtcrime.securesms.database.MmsDatabase.MESSAGE_BOX;
import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.BASE_DELETED_INCOMING_TYPE;
import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.BASE_DELETED_OUTGOING_TYPE;
import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.BASE_TYPE_MASK;
import android.content.Context;
import android.database.Cursor;
@ -98,6 +101,14 @@ public class MmsSmsDatabase extends Database {
}
}
public @Nullable MessageRecord getNonDeletedMessageForTimestamp(long timestamp) {
String selection = MmsSmsColumns.NORMALIZED_DATE_SENT + " = " + timestamp;
try (Cursor cursor = queryTables(PROJECTION, selection, null, null)) {
MmsSmsDatabase.Reader reader = readerFor(cursor);
return reader.getNext();
}
}
public @Nullable MessageRecord getMessageFor(long timestamp, String serializedAuthor) {
return getMessageFor(timestamp, serializedAuthor, true);
}
@ -342,7 +353,9 @@ public class MmsSmsDatabase extends Database {
public long getLastMessageTimestamp(long threadId) {
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
// make sure the last message isn't marked as deleted
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " +
"(ifnull("+SmsDatabase.TYPE+", "+MmsDatabase.MESSAGE_BOX+") & "+BASE_TYPE_MASK+") NOT IN ("+BASE_DELETED_OUTGOING_TYPE+", "+BASE_DELETED_INCOMING_TYPE+")"; // this ugly line checks whether the type is deleted (incoming or outgoing) for either the sms table or the mms table
try (Cursor cursor = queryTables(PROJECTION, selection, order, "1")) {
if (cursor.moveToFirst()) {

View File

@ -243,6 +243,7 @@ public class SmsDatabase extends MessagingDatabase {
contentValues.put(READ, 1);
contentValues.put(BODY, displayedMessage);
contentValues.put(HAS_MENTION, 0);
contentValues.put(STATUS, Status.STATUS_NONE);
database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)});
updateTypeBitmask(messageId, Types.BASE_TYPE_MASK,
@ -299,6 +300,28 @@ public class SmsDatabase extends MessagingDatabase {
return isOutgoing;
}
public boolean isDeletedMessage(long timestamp) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
Cursor cursor = null;
boolean isDeleted = false;
try {
cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE },
DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) },
null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isDeletedMessage(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
isDeleted = true;
}
}
} finally {
if (cursor != null) cursor.close();
}
return isDeleted;
}
@Override
public String getTypeColumn() {
return TYPE;

View File

@ -1288,6 +1288,16 @@ open class Storage @Inject constructor(
}
setRecipientHash(recipient, contact.hashCode().toString())
}
// if we have contacts locally but that are missing from the config, remove their corresponding thread
val removedContacts = getAllContacts().filter { localContact ->
moreContacts.firstOrNull {
it.id == localContact.accountID
} == null
}
removedContacts.forEach {
getThreadId(fromSerialized(it.accountID))?.let(::deleteConversation)
}
}
override fun addContacts(contacts: List<ConfigurationMessage.Contact>) {
@ -1766,10 +1776,17 @@ open class Storage @Inject constructor(
val timestamp = reaction.timestamp
val localId = reaction.localId
val isMms = reaction.isMms
val messageId = if (localId != null && localId > 0 && isMms != null) {
// bail early is the message is marked as deleted
val messagingDatabase: MessagingDatabase = if (isMms == true) DatabaseComponent.get(context).mmsDatabase()
else DatabaseComponent.get(context).smsDatabase()
if(messagingDatabase.getMessageRecord(localId)?.isDeleted == true) return
MessageId(localId, isMms)
} else if (timestamp != null && timestamp > 0) {
val messageRecord = mmsSmsDatabase.getMessageForTimestamp(timestamp) ?: return
if (messageRecord.isDeleted) return
MessageId(messageRecord.id, messageRecord.isMms)
} else return
reactionDatabase.addReaction(

View File

@ -193,6 +193,16 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners();
}
public void clearSnippet(long threadId){
ContentValues contentValues = new ContentValues(1);
contentValues.put(SNIPPET, "");
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
notifyConversationListListeners();
}
public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) {
ContentValues contentValues = new ContentValues(4);
@ -298,7 +308,7 @@ public class ThreadDatabase extends Database {
public void trimThreadBefore(long threadId, long timestamp) {
Log.i("ThreadDatabase", "Trimming thread: " + threadId + " before :"+timestamp);
DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp);
DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp, false);
DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp);
update(threadId, false);
notifyConversationListeners(threadId);
}
@ -707,10 +717,7 @@ public class ThreadDatabase extends Database {
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
long count = mmsSmsDatabase.getConversationCount(threadId);
MmsSmsDatabase.Reader reader = null;
try {
reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId));
try (MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId))) {
MessageRecord record = null;
if (reader != null) {
record = reader.getNext();
@ -724,7 +731,8 @@ public class ThreadDatabase extends Database {
record.getType(), unarchive, record.getExpiresIn(), record.getReadReceiptCount());
return false;
} else {
updateThread(threadId, 0, "", null, System.currentTimeMillis(), 0, 0, 0, false, 0, 0);
// for empty threads or if there is only deleted messages, show an empty snippet
clearSnippet(threadId);
return false;
}
} finally {
@ -772,10 +780,6 @@ public class ThreadDatabase extends Database {
return setLastSeen(threadId, lastSeenTime);
}
private boolean deleteThreadOnEmpty(long threadId) {
return false;
}
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
if (messageRecord.isMms()) {
MmsMessageRecord record = (MmsMessageRecord) messageRecord;

View File

@ -107,10 +107,12 @@ public class ThreadRecord extends DisplayRecord {
@Override
public CharSequence getDisplayBody(@NonNull Context context) {
if (isGroupUpdateMessage()) {
return lastMessage != null
? lastMessage.getDisplayBody(context).toString()
: context.getString(R.string.groupUpdated);
// no need to display anything if there are no messages
if(lastMessage == null){
return "";
}
else if (isGroupUpdateMessage()) {
return context.getString(R.string.groupUpdated);
} else if (isOpenGroupInvitation()) {
return context.getString(R.string.communityInvitation);
} else if (MmsSmsColumns.Types.isLegacyType(type)) {

View File

@ -365,6 +365,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
binding.seedReminderView.isVisible = false
}
// refresh search on resume, in case we a conversation was deleted
if (binding.globalSearchRecycler.isVisible){
globalSearchViewModel.refresh()
}
updateLegacyConfigView()
}

View File

@ -134,6 +134,16 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
cropImage(inputFile, outputFile)
}
private val hideRecoveryLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
if(result.data?.getBooleanExtra(RecoveryPasswordActivity.RESULT_RECOVERY_HIDDEN, false) == true){
viewModel.permanentlyHidePassword()
}
}
private val avatarSelection = AvatarSelection(this, onAvatarCropped, onPickImage)
private var showAvatarDialog: Boolean by mutableStateOf(false)
@ -183,7 +193,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
binding.composeView.setThemedContent {
Buttons()
val recoveryHidden by viewModel.recoveryHidden.collectAsState()
Buttons(recoveryHidden = recoveryHidden)
}
lifecycleScope.launch {
@ -383,7 +394,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
@Composable
fun Buttons() {
fun Buttons(
recoveryHidden: Boolean
) {
Column(
modifier = Modifier
.padding(horizontal = LocalDimensions.current.spacing)
@ -445,12 +458,15 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
Divider()
// Only show the recovery password option if the user has not chosen to permanently hide it
if (!prefs.getHidePassword()) {
if (!recoveryHidden) {
LargeItemButton(
R.string.sessionRecoveryPassword,
R.drawable.ic_shield_outline,
Modifier.contentDescription(R.string.AccessibilityId_sessionRecoveryPasswordMenuItem)
) { push<RecoveryPasswordActivity>() }
) {
hideRecoveryLauncher.launch(Intent(baseContext, RecoveryPasswordActivity::class.java))
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
Divider()
}

View File

@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import network.loki.messenger.R
@ -63,6 +64,10 @@ class SettingsViewModel @Inject constructor(
val showLoader: StateFlow<Boolean>
get() = _showLoader
private val _recoveryHidden: MutableStateFlow<Boolean> = MutableStateFlow(prefs.getHidePassword())
val recoveryHidden: StateFlow<Boolean>
get() = _recoveryHidden
/**
* Refreshes the avatar on the main settings page
*/
@ -228,6 +233,12 @@ class SettingsViewModel @Inject constructor(
}
}
fun permanentlyHidePassword() {
//todo we can simplify this once we expose all our sharedPrefs as flows
prefs.setHidePassword(true)
_recoveryHidden.update { true }
}
sealed class AvatarDialogState() {
object NoAvatar : AvatarDialogState()
data class UserAvatar(val address: Address) : AvatarDialogState()

View File

@ -1,16 +1,21 @@
package org.thoughtcrime.securesms.recoverypassword
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.setComposeContent
class RecoveryPasswordActivity : BaseActionBarActivity() {
companion object {
const val RESULT_RECOVERY_HIDDEN = "recovery_hidden"
}
private val viewModel: RecoveryPasswordViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
@ -25,7 +30,9 @@ class RecoveryPasswordActivity : BaseActionBarActivity() {
mnemonic = mnemonic,
seed = seed,
confirmHideRecovery = {
viewModel.permanentlyHidePassword()
val returnIntent = Intent()
returnIntent.putExtra(RESULT_RECOVERY_HIDDEN, true)
setResult(RESULT_OK, returnIntent)
finish()
},
copyMnemonic = viewModel::copyMnemonic

View File

@ -34,10 +34,6 @@ class RecoveryPasswordViewModel @Inject constructor(
.map { MnemonicCodec { MnemonicUtilities.loadFileContents(application, it) }.encode(it, MnemonicCodec.Language.Configuration.english) }
.stateIn(viewModelScope, SharingStarted.Eagerly, "")
fun permanentlyHidePassword() {
prefs.setHidePassword(true)
}
fun copyMnemonic() {
prefs.setHasViewedSeed(true)
ClipData.newPlainText("Seed", mnemonic.value)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -305,14 +305,14 @@
<TextView
android:padding="@dimen/medium_spacing"
android:textSize="@dimen/small_font_size"
android:textColor="?android:textColorSecondary"
style="@style/Signal.Text.Preview"
android:textColor="?android:textColorTertiary"
android:textAlignment="center"
android:id="@+id/placeholderText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/large_spacing"
app:layout_constraintTop_toBottomOf="@+id/outdatedDisappearingBanner"
android:elevation="8dp"
android:contentDescription="@string/AccessibilityId_control_message"
tools:text="Some Control Message Text"
/>
@ -339,7 +339,7 @@
android:paddingHorizontal="@dimen/massive_spacing"
android:paddingVertical="@dimen/small_spacing"
android:textSize="@dimen/text_size"
android:text="@string/block"/>
android:text="@string/deleteAfterGroupPR1BlockUser"/>
<TextView
android:id="@+id/sendAcceptsTextView"
@ -375,7 +375,7 @@
android:layout_height="@dimen/medium_button_height"
android:layout_marginStart="@dimen/medium_spacing"
android:layout_weight="1"
android:text="d" />
android:text="@string/delete" />
</LinearLayout>

View File

@ -31,7 +31,8 @@
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:src="@drawable/ic_trash_filled_32" />
app:tint="@color/white"
android:src="@drawable/ic_delete" />
<ImageView
android:id="@+id/scribble_undo_button"

View File

@ -6,14 +6,15 @@
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:padding="@dimen/small_spacing">
android:gravity="center_vertical"
android:paddingVertical="@dimen/small_spacing">
<ImageView
android:id="@+id/deletedMessageViewIconImageView"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/small_spacing"
android:src="?menu_trash_icon"
android:layout_width="19dp"
android:layout_height="19dp"
android:layout_marginStart="18dp"
android:src="@drawable/ic_delete"
app:tint="?android:textColorPrimary" />
<TextView
@ -22,7 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="@dimen/small_spacing"
android:layout_marginEnd="@dimen/large_spacing"
android:textSize="@dimen/very_small_font_size"
android:textColor="?android:textColorPrimary"
tools:text="This message has been deleted"

View File

@ -27,7 +27,7 @@
app:tint="?searchIconColor"
android:contentDescription="@string/search" />
<EditText
android:hint="@string/messages"
android:hint="@string/search"
android:imeOptions="actionSearch"
android:id="@+id/search_input"
android:paddingHorizontal="@dimen/small_spacing"

View File

@ -71,6 +71,12 @@
<item name="android:textSize">@dimen/very_large_font_size</item>
</style>
<style name="MenuTextAppearance" parent="TextAppearance.AppCompat.Widget.ActionBar.Menu">
<item name="android:textAllCaps">false</item>
<item name="android:textSize">@dimen/small2_font_size</item>
<item name="android:textStyle">bold</item>
</style>
<style name="TextAppearance.Session.Dialog.Title" parent="TextAppearance.AppCompat.Title">
<item name="android:textStyle">bold</item>
<item name="android:textSize">@dimen/medium2_font_size</item>

View File

@ -60,6 +60,8 @@
<item name="conversation_icon_attach_audio">@drawable/ic_audio_dark</item>
<item name="conversation_icon_attach_video">@drawable/ic_video_dark</item>
<item name="android:actionMenuTextAppearance">@style/MenuTextAppearance</item>
</style>
<!-- This should be the default theme for the application. -->

View File

@ -11,12 +11,6 @@
android:title="@string/lockApp"
android:summary="@string/lockAppDescription" />
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
android:defaultValue="@bool/screen_security_default"
android:key="pref_screen_security"
android:title="@string/screenshotNotifications"
android:summary="@string/screenshotNotificationsDescription" />
</PreferenceCategory>
<PreferenceCategory

View File

@ -46,6 +46,9 @@ class ConversationViewModelTest: BaseViewModelTest() {
textSecurePreferences = mock(),
lokiMessageDb = mock(),
application = mock(),
reactionDb = mock(),
configFactory = mock(),
groupManagerV2 = mock()
)
}

View File

@ -38,6 +38,7 @@ interface MessageDataProvider {
fun updateAudioAttachmentDuration(attachmentId: AttachmentId, durationMs: Long, threadId: Long)
fun isMmsOutgoing(mmsMessageId: Long): Boolean
fun isOutgoingMessage(timestamp: Long): Boolean
fun isDeletedMessage(timestamp: Long): Boolean
fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult)
fun handleFailedAttachmentUpload(attachmentId: Long)
fun getMessageForQuote(timestamp: Long, author: Address): Triple<Long, Boolean, String>?

View File

@ -39,6 +39,13 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
val message = message as? VisibleMessage
val storage = MessagingModuleConfiguration.shared.storage
// do not attempt to send if the message is marked as deleted
message?.sentTimestamp?.let{
if(messageDataProvider.isDeletedMessage(it)){
return@execute
}
}
val sentTimestamp = this.message.sentTimestamp
val sender = storage.getUserPublicKey()
if (sentTimestamp != null && sender != null) {
@ -97,7 +104,10 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
Log.w(TAG, "Failed to send $message::class.simpleName.", error)
val message = message as? VisibleMessage
if (message != null) {
if (!MessagingModuleConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) {
if (
MessagingModuleConfiguration.shared.messageDataProvider.isDeletedMessage(message.sentTimestamp!!) ||
!MessagingModuleConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)
) {
return // The message has been deleted
}
}

View File

@ -511,9 +511,15 @@ object MessageSender {
fun handleFailedMessageSend(message: Message, error: Exception, isSyncMessage: Boolean = false) {
val storage = MessagingModuleConfiguration.shared.storage
val timestamp = message.sentTimestamp!!
// no need to handle if message is marked as deleted
if(MessagingModuleConfiguration.shared.messageDataProvider.isDeletedMessage(message.sentTimestamp!!)){
return
}
val userPublicKey = storage.getUserPublicKey()!!
val timestamp = message.sentTimestamp!!
val author = message.sender ?: userPublicKey
if (isSyncMessage) storage.markAsSyncFailed(timestamp, author, error)

View File

@ -46,7 +46,8 @@ object LocalisedTimeUtil {
"${this.inWholeHours}h ${minutesRemaining}m"
} else if (this.inWholeMinutes > 0) {
val secondsRemaining = this.minus(1.minutes.times(this.inWholeMinutes.toInt())).inWholeSeconds
"${this.inWholeMinutes}m ${secondsRemaining}s"
if(secondsRemaining > 0) "${this.inWholeMinutes}m ${secondsRemaining}s"
else "${this.inWholeMinutes}m"
} else {
"0m ${this.inWholeSeconds}s"
}

View File

@ -114,7 +114,6 @@ interface TextSecurePreferences {
fun isEnterSendsEnabled(): Boolean
fun isPasswordDisabled(): Boolean
fun setPasswordDisabled(disabled: Boolean)
fun isScreenSecurityEnabled(): Boolean
fun getLastVersionCode(): Int
fun setLastVersionCode(versionCode: Int)
fun isPassphraseTimeoutEnabled(): Boolean
@ -219,7 +218,6 @@ interface TextSecurePreferences {
const val LED_BLINK_PREF_CUSTOM = "pref_led_blink_custom"
const val PASSPHRASE_TIMEOUT_INTERVAL_PREF = "pref_timeout_interval"
const val PASSPHRASE_TIMEOUT_PREF = "pref_timeout_passphrase"
const val SCREEN_SECURITY_PREF = "pref_screen_security"
const val ENTER_SENDS_PREF = "pref_enter_sends"
const val THREAD_TRIM_ENABLED = "pref_trim_threads"
internal const val LOCAL_NUMBER_PREF = "pref_local_number"
@ -666,11 +664,6 @@ interface TextSecurePreferences {
setBooleanPreference(context, DISABLE_PASSPHRASE_PREF, disabled)
}
@JvmStatic
fun isScreenSecurityEnabled(context: Context): Boolean {
return getBooleanPreference(context, SCREEN_SECURITY_PREF, context.resources.getBoolean(R.bool.screen_security_default))
}
fun getLastVersionCode(context: Context): Int {
return getIntegerPreference(context, LAST_VERSION_CODE_PREF, 0)
}
@ -1298,10 +1291,6 @@ class AppTextSecurePreferences @Inject constructor(
setBooleanPreference(TextSecurePreferences.DISABLE_PASSPHRASE_PREF, disabled)
}
override fun isScreenSecurityEnabled(): Boolean {
return getBooleanPreference(TextSecurePreferences.SCREEN_SECURITY_PREF, true)
}
override fun getLastVersionCode(): Int {
return getIntegerPreference(TextSecurePreferences.LAST_VERSION_CODE_PREF, 0)
}