mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-31 07:36:09 +00:00
feat: add view model storage and other activity logic for conv settings, including activity result for existing search convo logic
This commit is contained in:
@@ -1,37 +1,106 @@
|
||||
package org.thoughtcrime.securesms.conversation.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.databinding.ActivityConversationSettingsBinding
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.MediaOverviewActivity
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.database.LokiThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.mms.GlideApp
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), ActivityDispatcher {
|
||||
class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.OnClickListener {
|
||||
|
||||
companion object {
|
||||
// used to trigger displaying conversation search in calling parent activity
|
||||
const val RESULT_SEARCH = 22
|
||||
}
|
||||
|
||||
lateinit var binding: ActivityConversationSettingsBinding
|
||||
val viewModel: ConversationSettingsViewModel by viewModels()
|
||||
|
||||
override fun dispatchIntent(body: (Context) -> Intent?) {
|
||||
TODO()
|
||||
@Inject lateinit var threadDb: ThreadDatabase
|
||||
@Inject lateinit var lokiThreadDb: LokiThreadDatabase
|
||||
@Inject lateinit var viewModelFactory: ConversationSettingsViewModel.AssistedFactory
|
||||
val viewModel: ConversationSettingsViewModel by viewModels {
|
||||
val threadId = intent.getLongExtra(ConversationActivityV2.THREAD_ID, -1L)
|
||||
if (threadId == -1L) {
|
||||
finish()
|
||||
}
|
||||
viewModelFactory.create(threadId)
|
||||
}
|
||||
|
||||
override fun showDialog(baseDialog: BaseDialog, tag: String?) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
super.onCreate(savedInstanceState, ready)
|
||||
binding = ActivityConversationSettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
with (binding) {
|
||||
adminControlsGroup.isVisible = false
|
||||
binding.profilePictureView.root.glide = GlideApp.with(this)
|
||||
updateRecipientDisplay()
|
||||
binding.searchConversation.setOnClickListener(this)
|
||||
binding.allMedia.setOnClickListener(this)
|
||||
binding.pinConversation.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
}
|
||||
|
||||
private fun updateRecipientDisplay() {
|
||||
val recipient = viewModel.recipient ?: return
|
||||
// Setup profile image
|
||||
binding.profilePictureView.root.update(recipient)
|
||||
// Setup name
|
||||
binding.conversationName.text = when {
|
||||
recipient.isLocalNumber -> getString(R.string.note_to_self)
|
||||
else -> recipient.toShortString()
|
||||
}
|
||||
// Setup group description (if group)
|
||||
binding.conversationSubtitle.isVisible = recipient.isClosedGroupRecipient.apply {
|
||||
binding.conversationSubtitle.text = "TODO: This is a test for group descriptions"
|
||||
}
|
||||
// Toggle group-specific settings
|
||||
|
||||
// Set pinned state
|
||||
binding.pinConversation.setText(
|
||||
if (viewModel.isPinned()) R.string.conversation_settings_unpin_conversation
|
||||
else R.string.conversation_settings_pin_conversation
|
||||
)
|
||||
// Set auto-download state
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
if (v === binding.searchConversation) {
|
||||
setResult(RESULT_SEARCH)
|
||||
finish()
|
||||
} else if (v === binding.allMedia) {
|
||||
val threadRecipient = viewModel.recipient ?: return
|
||||
val intent = Intent(this, MediaOverviewActivity::class.java).apply {
|
||||
putExtra(MediaOverviewActivity.ADDRESS_EXTRA, threadRecipient.address)
|
||||
}
|
||||
startActivity(intent)
|
||||
} else if (v === binding.pinConversation) {
|
||||
viewModel.togglePin().invokeOnCompletion { e ->
|
||||
if (e != null) {
|
||||
// something happened
|
||||
Log.e("ConversationSettings", "Failed to toggle pin on thread", e)
|
||||
} else {
|
||||
updateRecipientDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.thoughtcrime.securesms.conversation.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
|
||||
sealed class ConversationSettingsActivityResult {
|
||||
object Finished: ConversationSettingsActivityResult()
|
||||
object SearchConversation: ConversationSettingsActivityResult()
|
||||
}
|
||||
|
||||
class ConversationSettingsActivityContract: ActivityResultContract<Long, ConversationSettingsActivityResult>() {
|
||||
|
||||
override fun createIntent(context: Context, input: Long?) = Intent(context, ConversationSettingsActivity::class.java).apply {
|
||||
putExtra(ConversationActivityV2.THREAD_ID, input ?: -1L)
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): ConversationSettingsActivityResult =
|
||||
when (resultCode) {
|
||||
ConversationSettingsActivity.RESULT_SEARCH -> ConversationSettingsActivityResult.SearchConversation
|
||||
else -> ConversationSettingsActivityResult.Finished
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,40 @@
|
||||
package org.thoughtcrime.securesms.conversation.settings
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.launch
|
||||
import org.thoughtcrime.securesms.database.Storage
|
||||
|
||||
class ConversationSettingsViewModel: ViewModel() {
|
||||
class ConversationSettingsViewModel(
|
||||
val threadId: Long,
|
||||
private val storage: Storage
|
||||
): ViewModel() {
|
||||
|
||||
val recipient get() = storage.getRecipientForThread(threadId)
|
||||
|
||||
fun isPinned() = storage.isThreadPinned(threadId)
|
||||
|
||||
fun togglePin() = viewModelScope.launch {
|
||||
val isPinned = storage.isThreadPinned(threadId)
|
||||
storage.setThreadPinned(threadId, !isPinned)
|
||||
}
|
||||
|
||||
// DI-related
|
||||
@dagger.assisted.AssistedFactory
|
||||
interface AssistedFactory {
|
||||
fun create(threadId: Long): Factory
|
||||
}
|
||||
class Factory @AssistedInject constructor(
|
||||
@Assisted private val threadId: Long,
|
||||
private val storage: Storage
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ConversationSettingsViewModel(threadId, storage) as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -90,7 +90,8 @@ import org.thoughtcrime.securesms.attachments.ScreenshotObserver
|
||||
import org.thoughtcrime.securesms.audio.AudioRecorder
|
||||
import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey
|
||||
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher
|
||||
import org.thoughtcrime.securesms.conversation.settings.ConversationSettingsActivity
|
||||
import org.thoughtcrime.securesms.conversation.settings.ConversationSettingsActivityContract
|
||||
import org.thoughtcrime.securesms.conversation.settings.ConversationSettingsActivityResult
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnActionSelectedListener
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnReactionSelectedListener
|
||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.BlockedDialog
|
||||
@@ -201,6 +202,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
}
|
||||
}
|
||||
|
||||
private val conversationSettingsCallback = registerForActivityResult(ConversationSettingsActivityContract()) { result ->
|
||||
if (result is ConversationSettingsActivityResult.SearchConversation) {
|
||||
// open search
|
||||
binding?.toolbar?.menu?.findItem(R.id.menu_search)?.expandActionView()
|
||||
}
|
||||
}
|
||||
|
||||
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
|
||||
private val linkPreviewViewModel: LinkPreviewViewModel by lazy {
|
||||
ViewModelProvider(this, LinkPreviewViewModel.Factory(LinkPreviewRepository()))
|
||||
@@ -329,7 +337,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
const val PICK_GIF = 10
|
||||
const val PICK_FROM_LIBRARY = 12
|
||||
const val INVITE_CONTACTS = 124
|
||||
|
||||
const val CONVERSATION_SETTINGS = 125 // used to open conversation search on result
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -963,8 +971,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
override fun onClick(v: View?) {
|
||||
if (v === binding?.toolbarContent?.profilePictureView?.root) {
|
||||
// open conversation settings
|
||||
val intent = Intent(this, ConversationSettingsActivity::class.java)
|
||||
startActivity(intent)
|
||||
conversationSettingsCallback.launch(viewModel.threadId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -705,7 +705,15 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
return mmsSmsDb.getConversationCount(threadID)
|
||||
}
|
||||
|
||||
override fun setThreadPinned(threadID: Long, isPinned: Boolean) {
|
||||
val threadDb = DatabaseComponent.get(context).threadDatabase()
|
||||
threadDb.setPinned(threadID, isPinned)
|
||||
}
|
||||
|
||||
override fun isThreadPinned(threadID: Long): Boolean {
|
||||
val threadDb = DatabaseComponent.get(context).threadDatabase()
|
||||
return threadDb.getPinned(threadID)
|
||||
}
|
||||
|
||||
override fun getAttachmentDataUri(attachmentId: AttachmentId): Uri {
|
||||
return PartAuthority.getAttachmentDataUri(attachmentId)
|
||||
|
||||
@@ -711,6 +711,13 @@ public class ThreadDatabase extends Database {
|
||||
notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
public boolean getPinned(long threadId) {
|
||||
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[]{IS_PINNED}, ID_WHERE, new String[] {""+threadId},null, null, null);
|
||||
boolean isPinned = cursor.moveToNext() && cursor.getInt(0) == 1;
|
||||
cursor.close();
|
||||
return isPinned;
|
||||
}
|
||||
|
||||
public void markAllAsRead(long threadId, boolean isGroupRecipient) {
|
||||
List<MarkedMessageInfo> messages = setRead(threadId, true);
|
||||
if (isGroupRecipient) {
|
||||
|
||||
@@ -9,16 +9,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/groupOptions"/>
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/adminControlsGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="adminContainer,adminSettingsTitle"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/profilePictureView"
|
||||
layout="@layout/view_large_profile_picture"
|
||||
@@ -205,6 +195,12 @@
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Admin settings -->
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/adminControlsGroup"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="adminContainer,adminSettingsTitle"/>
|
||||
<TextView
|
||||
style="@style/TextAppearance.Session.ConversationSettings.Subtitle"
|
||||
android:text="@string/conversation_settings_admin_settings_title"
|
||||
|
||||
@@ -867,6 +867,7 @@
|
||||
<string name="conversation_settings_search">Search Conversation</string>
|
||||
<string name="conversation_settings_all_media">All Media</string>
|
||||
<string name="conversation_settings_pin_conversation">Pin Conversation</string>
|
||||
<string name="conversation_settings_unpin_conversation">Unpin Conversation</string>
|
||||
<string name="conversation_settings_notifications">Notifications</string>
|
||||
<string name="conversation_settings_auto_download_title">Auto-download Media</string>
|
||||
<string name="conversation_settings_auto_download_summary">Automatically download media and files from this chat.</string>
|
||||
|
||||
@@ -153,6 +153,8 @@ interface StorageProtocol {
|
||||
fun trimThread(threadID: Long, threadLimit: Int)
|
||||
fun trimThreadBefore(threadID: Long, timestamp: Long)
|
||||
fun getMessageCount(threadID: Long): Long
|
||||
fun setThreadPinned(threadID: Long, isPinned: Boolean)
|
||||
fun isThreadPinned(threadID: Long): Boolean
|
||||
|
||||
// Contacts
|
||||
fun getContactWithSessionID(sessionID: String): Contact?
|
||||
|
||||
Reference in New Issue
Block a user