mirror of
https://github.com/oxen-io/session-android.git
synced 2025-03-29 07:12:15 +00:00
Merge branch 'ui' into ui
This commit is contained in:
commit
8f2f1a47fd
@ -47,14 +47,18 @@ import org.session.libsession.messaging.mentions.MentionsManager
|
|||||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification
|
import org.session.libsession.messaging.messages.control.DataExtractionNotification
|
||||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
||||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
||||||
|
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation
|
||||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
|
import org.session.libsession.messaging.sending_receiving.MessageSender.send
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
|
||||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
|
||||||
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
|
||||||
|
import org.session.libsession.utilities.Address.Companion.fromSerialized
|
||||||
import org.session.libsession.utilities.MediaTypes
|
import org.session.libsession.utilities.MediaTypes
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.ListenableFuture
|
import org.session.libsignal.utilities.ListenableFuture
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
@ -81,6 +85,8 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
|
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
|
||||||
import org.thoughtcrime.securesms.loki.utilities.push
|
import org.thoughtcrime.securesms.loki.utilities.push
|
||||||
|
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
|
||||||
|
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity.Companion.selectedContactsKey
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities
|
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.loki.utilities.toPx
|
import org.thoughtcrime.securesms.loki.utilities.toPx
|
||||||
import org.thoughtcrime.securesms.mediasend.Media
|
import org.thoughtcrime.securesms.mediasend.Media
|
||||||
@ -161,6 +167,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
const val TAKE_PHOTO = 7
|
const val TAKE_PHOTO = 7
|
||||||
const val PICK_GIF = 10
|
const val PICK_GIF = 10
|
||||||
const val PICK_FROM_LIBRARY = 12
|
const val PICK_FROM_LIBRARY = 12
|
||||||
|
const val INVITE_CONTACTS = 124
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
@ -579,8 +586,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
// region Interaction
|
// region Interaction
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
// TODO: Implement
|
return ConversationMenuHelper.onOptionItemSelected(this, item, thread)
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `position` is the adapter position; not the visual position
|
// `position` is the adapter position; not the visual position
|
||||||
@ -846,6 +852,25 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
sendAttachments(slideDeck.asAttachments(), body)
|
sendAttachments(slideDeck.asAttachments(), body)
|
||||||
}
|
}
|
||||||
|
INVITE_CONTACTS -> {
|
||||||
|
if (!thread.isOpenGroupRecipient) { return }
|
||||||
|
val extras = intent?.extras ?: return
|
||||||
|
if (!intent.hasExtra(SelectContactsActivity.selectedContactsKey)) { return }
|
||||||
|
val selectedContacts = extras.getStringArray(selectedContactsKey)!!
|
||||||
|
val openGroup = DatabaseFactory.getLokiThreadDatabase(this).getOpenGroupChat(threadID)
|
||||||
|
for (contact in selectedContacts) {
|
||||||
|
val recipient = Recipient.from(this, fromSerialized(contact), true)
|
||||||
|
val message = VisibleMessage()
|
||||||
|
message.sentTimestamp = System.currentTimeMillis()
|
||||||
|
val openGroupInvitation = OpenGroupInvitation()
|
||||||
|
openGroupInvitation.name = openGroup!!.name
|
||||||
|
openGroupInvitation.url = openGroup!!.joinURL
|
||||||
|
message.openGroupInvitation = openGroupInvitation
|
||||||
|
val outgoingTextMessage = OutgoingTextMessage.fromOpenGroupInvitation(openGroupInvitation, recipient, message.sentTimestamp)
|
||||||
|
DatabaseFactory.getSmsDatabase(this).insertMessageOutbox(-1, outgoingTextMessage, message.sentTimestamp!!)
|
||||||
|
MessageSender.send(message, recipient.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,49 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2.menus
|
package org.thoughtcrime.securesms.conversation.v2.menus
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffColorFilter
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.os.AsyncTask
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.pm.ShortcutInfoCompat
|
||||||
|
import androidx.core.content.pm.ShortcutManagerCompat
|
||||||
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.avatars.ContactPhoto
|
||||||
|
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
||||||
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
|
import org.session.libsession.messaging.sending_receiving.leave
|
||||||
import org.session.libsession.utilities.ExpirationUtil
|
import org.session.libsession.utilities.ExpirationUtil
|
||||||
|
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.session.libsignal.utilities.Log
|
||||||
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
|
import org.session.libsignal.utilities.toHexString
|
||||||
|
import org.thoughtcrime.securesms.*
|
||||||
|
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
|
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity
|
||||||
|
import org.thoughtcrime.securesms.loki.activities.EditClosedGroupActivity.Companion.groupIDKey
|
||||||
|
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
import org.thoughtcrime.securesms.loki.utilities.getColorWithID
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
|
import java.io.IOException
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
object ConversationMenuHelper {
|
object ConversationMenuHelper {
|
||||||
|
|
||||||
@ -61,6 +92,196 @@ object ConversationMenuHelper {
|
|||||||
} else {
|
} else {
|
||||||
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
|
inflater.inflate(R.menu.menu_conversation_unmuted, menu)
|
||||||
}
|
}
|
||||||
// TODO: Implement search
|
}
|
||||||
|
|
||||||
|
fun onOptionItemSelected(context: Context, item: MenuItem, thread: Recipient): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.menu_view_all_media -> { showAllMedia(context, thread) }
|
||||||
|
R.id.menu_search -> { search(context) }
|
||||||
|
R.id.menu_add_shortcut -> { addShortcut(context, thread) }
|
||||||
|
R.id.menu_expiring_messages -> { showExpiringMessagesDialog(context, thread) }
|
||||||
|
R.id.menu_expiring_messages_off -> { showExpiringMessagesDialog(context, thread) }
|
||||||
|
R.id.menu_unblock -> { unblock(context, thread) }
|
||||||
|
R.id.menu_block -> { block(context, thread) }
|
||||||
|
R.id.menu_copy_session_id -> { copySessionID(context, thread) }
|
||||||
|
R.id.menu_edit_group -> { editClosedGroup(context, thread) }
|
||||||
|
R.id.menu_leave_group -> { leaveClosedGroup(context, thread) }
|
||||||
|
R.id.menu_invite_to_open_group -> { inviteContacts(context, thread) }
|
||||||
|
R.id.menu_unmute_notifications -> { unmute(context, thread) }
|
||||||
|
R.id.menu_mute_notifications -> { mute(context, thread) }
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showAllMedia(context: Context, thread: Recipient) {
|
||||||
|
val intent = Intent(context, MediaOverviewActivity::class.java)
|
||||||
|
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, thread.address)
|
||||||
|
val activity = context as AppCompatActivity
|
||||||
|
activity.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun search(context: Context) {
|
||||||
|
Toast.makeText(context, "Not yet implemented", Toast.LENGTH_LONG).show() // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
private fun addShortcut(context: Context, thread: Recipient) {
|
||||||
|
object : AsyncTask<Void?, Void?, IconCompat?>() {
|
||||||
|
|
||||||
|
override fun doInBackground(vararg params: Void?): IconCompat? {
|
||||||
|
var icon: IconCompat? = null
|
||||||
|
val contactPhoto = thread.contactPhoto
|
||||||
|
if (contactPhoto != null) {
|
||||||
|
try {
|
||||||
|
var bitmap = BitmapFactory.decodeStream(contactPhoto.openInputStream(context))
|
||||||
|
bitmap = BitmapUtil.createScaledBitmap(bitmap, 300, 300)
|
||||||
|
icon = IconCompat.createWithAdaptiveBitmap(bitmap)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (icon == null) {
|
||||||
|
icon = IconCompat.createWithResource(context, if (thread.isGroupRecipient) R.mipmap.ic_group_shortcut else R.mipmap.ic_person_shortcut)
|
||||||
|
}
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPostExecute(icon: IconCompat?) {
|
||||||
|
val name = Optional.fromNullable<String>(thread.name)
|
||||||
|
.or(Optional.fromNullable<String>(thread.profileName))
|
||||||
|
.or(thread.toShortString())
|
||||||
|
val shortcutInfo = ShortcutInfoCompat.Builder(context, thread.address.serialize() + '-' + System.currentTimeMillis())
|
||||||
|
.setShortLabel(name)
|
||||||
|
.setIcon(icon)
|
||||||
|
.setIntent(ShortcutLauncherActivity.createIntent(context, thread.address))
|
||||||
|
.build()
|
||||||
|
if (ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)) {
|
||||||
|
Toast.makeText(context, context.resources.getString(R.string.ConversationActivity_added_to_home_screen), Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showExpiringMessagesDialog(context: Context, thread: Recipient) {
|
||||||
|
if (thread.isClosedGroupRecipient) {
|
||||||
|
val group = DatabaseFactory.getGroupDatabase(context).getGroup(thread.address.toGroupString()).orNull()
|
||||||
|
if (group?.isActive == false) { return }
|
||||||
|
}
|
||||||
|
ExpirationDialog.show(context, thread.expireMessages, ExpirationDialog.OnClickListener { expirationTime: Int ->
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(thread, expirationTime)
|
||||||
|
val message = ExpirationTimerUpdate(expirationTime)
|
||||||
|
message.recipient = thread.address.serialize()
|
||||||
|
message.sentTimestamp = System.currentTimeMillis()
|
||||||
|
val expiringMessageManager = ApplicationContext.getInstance(context).expiringMessageManager
|
||||||
|
expiringMessageManager.setExpirationTimer(message)
|
||||||
|
MessageSender.send(message, thread.address)
|
||||||
|
val activity = context as AppCompatActivity
|
||||||
|
activity.invalidateOptionsMenu()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unblock(context: Context, thread: Recipient) {
|
||||||
|
if (!thread.isContactRecipient) { return }
|
||||||
|
val title = R.string.ConversationActivity_unblock_this_contact_question
|
||||||
|
val message = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.ConversationActivity_unblock) { _, _ ->
|
||||||
|
DatabaseFactory.getRecipientDatabase(context)
|
||||||
|
.setBlocked(thread, false)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun block(context: Context, thread: Recipient) {
|
||||||
|
if (!thread.isContactRecipient) { return }
|
||||||
|
val title = R.string.RecipientPreferenceActivity_block_this_contact_question
|
||||||
|
val message = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { _, _ ->
|
||||||
|
DatabaseFactory.getRecipientDatabase(context)
|
||||||
|
.setBlocked(thread, true)
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copySessionID(context: Context, thread: Recipient) {
|
||||||
|
if (!thread.isContactRecipient) { return }
|
||||||
|
val sessionID = thread.address.toString()
|
||||||
|
val clip = ClipData.newPlainText("Session ID", sessionID)
|
||||||
|
val activity = context as AppCompatActivity
|
||||||
|
val manager = activity.getSystemService(PassphraseRequiredActionBarActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
manager.setPrimaryClip(clip)
|
||||||
|
Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun editClosedGroup(context: Context, thread: Recipient) {
|
||||||
|
if (!thread.isClosedGroupRecipient) { return }
|
||||||
|
val intent = Intent(context, EditClosedGroupActivity::class.java)
|
||||||
|
val groupID: String = thread.address.toGroupString()
|
||||||
|
intent.putExtra(groupIDKey, groupID)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun leaveClosedGroup(context: Context, thread: Recipient) {
|
||||||
|
if (!thread.isClosedGroupRecipient) { return }
|
||||||
|
val builder = AlertDialog.Builder(context)
|
||||||
|
builder.setTitle(context.resources.getString(R.string.ConversationActivity_leave_group))
|
||||||
|
builder.setCancelable(true)
|
||||||
|
val group = DatabaseFactory.getGroupDatabase(context).getGroup(thread.address.toGroupString()).orNull()
|
||||||
|
val admins = group.admins
|
||||||
|
val sessionID = TextSecurePreferences.getLocalNumber(context)
|
||||||
|
val isCurrentUserAdmin = admins.any { it.toString() == sessionID }
|
||||||
|
val message = if (isCurrentUserAdmin) {
|
||||||
|
"Because you are the creator of this group it will be deleted for everyone. This cannot be undone."
|
||||||
|
} else {
|
||||||
|
context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)
|
||||||
|
}
|
||||||
|
builder.setMessage(message)
|
||||||
|
builder.setPositiveButton(R.string.yes) { _, _ ->
|
||||||
|
var groupPublicKey: String?
|
||||||
|
var isClosedGroup: Boolean
|
||||||
|
try {
|
||||||
|
groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString()
|
||||||
|
isClosedGroup = DatabaseFactory.getLokiAPIDatabase(context).isClosedGroup(groupPublicKey)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
groupPublicKey = null
|
||||||
|
isClosedGroup = false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (isClosedGroup) {
|
||||||
|
MessageSender.leave(groupPublicKey!!, true)
|
||||||
|
// TODO: Disable input?
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.setNegativeButton(R.string.no, null)
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inviteContacts(context: Context, thread: Recipient) {
|
||||||
|
if (!thread.isOpenGroupRecipient) { return }
|
||||||
|
val intent = Intent(context, SelectContactsActivity::class.java)
|
||||||
|
val activity = context as AppCompatActivity
|
||||||
|
activity.startActivityForResult(intent, ConversationActivityV2.INVITE_CONTACTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unmute(context: Context, thread: Recipient) {
|
||||||
|
thread.setMuted(0)
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).setMuted(thread, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mute(context: Context, thread: Recipient) {
|
||||||
|
MuteDialog.show(context) { until: Long ->
|
||||||
|
thread.setMuted(until)
|
||||||
|
DatabaseFactory.getRecipientDatabase(context).setMuted(thread, until)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -33,7 +33,7 @@ class ExpirationTimerUpdate() : ControlMessage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal constructor(duration: Int) : this() {
|
constructor(duration: Int) : this() {
|
||||||
this.syncTarget = null
|
this.syncTarget = null
|
||||||
this.duration = duration
|
this.duration = duration
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user