feat: add a first call missed control message and info popup with link to privacy settings
This commit is contained in:
parent
e7b0707377
commit
34863c5cab
|
@ -1,10 +1,13 @@
|
||||||
package org.thoughtcrime.securesms.conversation.v2
|
package org.thoughtcrime.securesms.conversation.v2
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||||
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
|
||||||
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
|
||||||
|
@ -12,6 +15,7 @@ import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
|
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
||||||
|
|
||||||
class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int, VisibleMessageView, MotionEvent) -> Unit,
|
class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPress: (MessageRecord, Int, VisibleMessageView, MotionEvent) -> Unit,
|
||||||
private val onItemSwipeToReply: (MessageRecord, Int) -> Unit, private val onItemLongPress: (MessageRecord, Int) -> Unit,
|
private val onItemSwipeToReply: (MessageRecord, Int) -> Unit, private val onItemLongPress: (MessageRecord, Int) -> Unit,
|
||||||
|
@ -76,7 +80,26 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
|
||||||
}
|
}
|
||||||
view.contentViewDelegate = visibleMessageContentViewDelegate
|
view.contentViewDelegate = visibleMessageContentViewDelegate
|
||||||
}
|
}
|
||||||
is ControlMessageViewHolder -> viewHolder.view.bind(message, messageBefore)
|
is ControlMessageViewHolder -> {
|
||||||
|
viewHolder.view.bind(message, messageBefore)
|
||||||
|
if (message.isCallLog && message.isFirstMissedCall) {
|
||||||
|
viewHolder.view.setOnClickListener {
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle(R.string.CallNotificationBuilder_first_call_title)
|
||||||
|
.setMessage(R.string.CallNotificationBuilder_first_call_message)
|
||||||
|
.setPositiveButton(R.string.activity_settings_title) { _, _ ->
|
||||||
|
val intent = Intent(context, PrivacySettingsActivity::class.java)
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
.setNeutralButton(R.string.cancel) { d, _ ->
|
||||||
|
d.dismiss()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewHolder.view.setOnClickListener(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package org.thoughtcrime.securesms.conversation.v2.messages
|
package org.thoughtcrime.securesms.conversation.v2.messages
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
@ -53,6 +51,7 @@ class ControlMessageView : LinearLayout {
|
||||||
val drawable = when {
|
val drawable = when {
|
||||||
message.isIncomingCall -> R.drawable.ic_incoming_call
|
message.isIncomingCall -> R.drawable.ic_incoming_call
|
||||||
message.isOutgoingCall -> R.drawable.ic_outgoing_call
|
message.isOutgoingCall -> R.drawable.ic_outgoing_call
|
||||||
|
message.isFirstMissedCall -> R.drawable.ic_info_outline_light
|
||||||
else -> R.drawable.ic_missed_call
|
else -> R.drawable.ic_missed_call
|
||||||
}
|
}
|
||||||
binding.iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, drawable, context.theme))
|
binding.iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, drawable, context.theme))
|
||||||
|
|
|
@ -32,6 +32,7 @@ public interface MmsSmsColumns {
|
||||||
protected static final long OUTGOING_CALL_TYPE = 2;
|
protected static final long OUTGOING_CALL_TYPE = 2;
|
||||||
protected static final long MISSED_CALL_TYPE = 3;
|
protected static final long MISSED_CALL_TYPE = 3;
|
||||||
protected static final long JOINED_TYPE = 4;
|
protected static final long JOINED_TYPE = 4;
|
||||||
|
protected static final long FIRST_MISSED_CALL_TYPE = 5;
|
||||||
|
|
||||||
protected static final long BASE_INBOX_TYPE = 20;
|
protected static final long BASE_INBOX_TYPE = 20;
|
||||||
protected static final long BASE_OUTBOX_TYPE = 21;
|
protected static final long BASE_OUTBOX_TYPE = 21;
|
||||||
|
@ -208,7 +209,7 @@ public interface MmsSmsColumns {
|
||||||
|
|
||||||
public static boolean isCallLog(long type) {
|
public static boolean isCallLog(long type) {
|
||||||
long baseType = type & BASE_TYPE_MASK;
|
long baseType = type & BASE_TYPE_MASK;
|
||||||
return baseType == INCOMING_CALL_TYPE || baseType == OUTGOING_CALL_TYPE || baseType == MISSED_CALL_TYPE;
|
return baseType == INCOMING_CALL_TYPE || baseType == OUTGOING_CALL_TYPE || baseType == MISSED_CALL_TYPE || baseType == FIRST_MISSED_CALL_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExpirationTimerUpdate(long type) {
|
public static boolean isExpirationTimerUpdate(long type) {
|
||||||
|
@ -239,6 +240,11 @@ public interface MmsSmsColumns {
|
||||||
return (type & BASE_TYPE_MASK) == MISSED_CALL_TYPE;
|
return (type & BASE_TYPE_MASK) == MISSED_CALL_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFirstMissedCall(long type) {
|
||||||
|
return (type & BASE_TYPE_MASK) == FIRST_MISSED_CALL_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean isGroupUpdate(long type) {
|
public static boolean isGroupUpdate(long type) {
|
||||||
return (type & GROUP_UPDATE_BIT) != 0;
|
return (type & GROUP_UPDATE_BIT) != 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,9 @@ public class SmsDatabase extends MessagingDatabase {
|
||||||
case CALL_MISSED:
|
case CALL_MISSED:
|
||||||
type |= Types.MISSED_CALL_TYPE;
|
type |= Types.MISSED_CALL_TYPE;
|
||||||
break;
|
break;
|
||||||
|
case CALL_FIRST_MISSED:
|
||||||
|
type |= Types.FIRST_MISSED_CALL_TYPE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,9 @@ public abstract class DisplayRecord {
|
||||||
public boolean isMissedCall() {
|
public boolean isMissedCall() {
|
||||||
return SmsDatabase.Types.isMissedCall(type);
|
return SmsDatabase.Types.isMissedCall(type);
|
||||||
}
|
}
|
||||||
|
public boolean isFirstMissedCall() {
|
||||||
|
return SmsDatabase.Types.isFirstMissedCall(type);
|
||||||
|
}
|
||||||
public boolean isDeleted() { return MmsSmsColumns.Types.isDeletedMessage(type); }
|
public boolean isDeleted() { return MmsSmsColumns.Types.isDeletedMessage(type); }
|
||||||
public boolean isMessageRequestResponse() { return MmsSmsColumns.Types.isMessageRequestResponse(type); }
|
public boolean isMessageRequestResponse() { return MmsSmsColumns.Types.isMessageRequestResponse(type); }
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,16 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
if (isScreenshotNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
|
if (isScreenshotNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize())));
|
||||||
else if (isMediaSavedNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
|
else if (isMediaSavedNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize())));
|
||||||
} else if (isCallLog()) {
|
} else if (isCallLog()) {
|
||||||
CallMessageType callType = isIncomingCall() ? CallMessageType.CALL_INCOMING : isOutgoingCall() ? CallMessageType.CALL_OUTGOING : CallMessageType.CALL_MISSED;
|
CallMessageType callType;
|
||||||
|
if (isIncomingCall()) {
|
||||||
|
callType = CallMessageType.CALL_INCOMING;
|
||||||
|
} else if (isOutgoingCall()) {
|
||||||
|
callType = CallMessageType.CALL_OUTGOING;
|
||||||
|
} else if (isMissedCall()) {
|
||||||
|
callType = CallMessageType.CALL_MISSED;
|
||||||
|
} else {
|
||||||
|
callType = CallMessageType.CALL_FIRST_MISSED;
|
||||||
|
}
|
||||||
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildCallMessage(context, callType, getIndividualRecipient().getAddress().serialize()));
|
return new SpannableString(UpdateMessageBuilder.INSTANCE.buildCallMessage(context, callType, getIndividualRecipient().getAddress().serialize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,10 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
||||||
// first time call notification encountered
|
// first time call notification encountered
|
||||||
val notification = CallNotificationBuilder.getFirstCallNotification(context)
|
val notification = CallNotificationBuilder.getFirstCallNotification(context)
|
||||||
context.getSystemService(NotificationManager::class.java).notify(CallNotificationBuilder.WEBRTC_NOTIFICATION, notification)
|
context.getSystemService(NotificationManager::class.java).notify(CallNotificationBuilder.WEBRTC_NOTIFICATION, notification)
|
||||||
|
insertMissedCall(sender, sentTimestamp, isFirstCall = true)
|
||||||
|
} else {
|
||||||
|
insertMissedCall(sender, sentTimestamp)
|
||||||
}
|
}
|
||||||
insertMissedCall(sender, sentTimestamp)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
when (nextMessage.type) {
|
when (nextMessage.type) {
|
||||||
|
@ -63,10 +65,14 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertMissedCall(sender: String, sentTimestamp: Long) {
|
private fun insertMissedCall(sender: String, sentTimestamp: Long, isFirstCall: Boolean = false) {
|
||||||
val currentUserPublicKey = storage.getUserPublicKey()
|
val currentUserPublicKey = storage.getUserPublicKey()
|
||||||
if (sender == currentUserPublicKey) return // don't insert a "missed" due to call notifications disabled if it's our own sender
|
if (sender == currentUserPublicKey) return // don't insert a "missed" due to call notifications disabled if it's our own sender
|
||||||
storage.insertCallMessage(sender, CallMessageType.CALL_MISSED, sentTimestamp)
|
if (isFirstCall) {
|
||||||
|
storage.insertCallMessage(sender, CallMessageType.CALL_FIRST_MISSED, sentTimestamp)
|
||||||
|
} else {
|
||||||
|
storage.insertCallMessage(sender, CallMessageType.CALL_MISSED, sentTimestamp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun incomingHangup(callMessage: CallMessage) {
|
private fun incomingHangup(callMessage: CallMessage) {
|
||||||
|
|
|
@ -3,5 +3,6 @@ package org.session.libsession.messaging.calls
|
||||||
enum class CallMessageType {
|
enum class CallMessageType {
|
||||||
CALL_MISSED,
|
CALL_MISSED,
|
||||||
CALL_INCOMING,
|
CALL_INCOMING,
|
||||||
CALL_OUTGOING
|
CALL_OUTGOING,
|
||||||
|
CALL_FIRST_MISSED,
|
||||||
}
|
}
|
|
@ -114,6 +114,8 @@ object UpdateMessageBuilder {
|
||||||
context.getString(R.string.MessageRecord_s_called_you, senderName)
|
context.getString(R.string.MessageRecord_s_called_you, senderName)
|
||||||
CallMessageType.CALL_OUTGOING ->
|
CallMessageType.CALL_OUTGOING ->
|
||||||
context.getString(R.string.MessageRecord_called_s, senderName)
|
context.getString(R.string.MessageRecord_called_s, senderName)
|
||||||
|
CallMessageType.CALL_FIRST_MISSED ->
|
||||||
|
context.getString(R.string.MessageRecord_missed_call_from, senderName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue