Changed the missed call control message logic

The control message is now clickable when the phone toggle is disabled or when the microphone permission is not given
This commit is contained in:
ThomasSession 2024-09-02 14:07:19 +10:00
parent 4860adcd86
commit f44d066e67
19 changed files with 105 additions and 107 deletions

View File

@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.conversation.v2
import android.Manifest
import android.content.Context
import android.content.Intent
import android.database.Cursor
@ -33,7 +34,9 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import com.bumptech.glide.RequestManager
import com.squareup.phrase.Phrase
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.MissingMicrophonePermissionDialog
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.getSubbedCharSequence
@ -172,43 +175,6 @@ class ConversationAdapter(
is ControlMessageViewHolder -> {
viewHolder.view.bind(message, messageBefore)
when {
// Click behaviour for first missed call control message
//todo this behaviour is different than iOS where the control message is always clickable when the call toggle is disabled in the privacy page
message.isCallLog && message.isFirstMissedCall -> {
viewHolder.view.setOnClickListener {
context.showSessionDialog {
val titleTxt = context.getSubbedString(
R.string.callsMissedCallFrom,
NAME_KEY to message.individualRecipient.name!!
)
title(titleTxt)
val bodyTxt = context.getSubbedCharSequence(
R.string.callsYouMissedCallPermissions,
NAME_KEY to message.individualRecipient.name!!
)
text(bodyTxt)
button(R.string.sessionSettings) {
Intent(context, PrivacySettingsActivity::class.java)
.let(context::startActivity)
}
cancelButton()
}
}
}
// Click behaviour for missed calls due to missing permission
message.isCallLog && message.isMissedPermissionCall -> {
viewHolder.view.setOnClickListener {
MissingMicrophonePermissionDialog.show(context)
}
}
// non clickable in other cases
else -> viewHolder.view.setOnClickListener(null)
}
}
}
}

View File

@ -1,6 +1,8 @@
package org.thoughtcrime.securesms.conversation.v2.messages
import android.Manifest
import android.content.Context
import android.content.Intent
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
@ -11,7 +13,6 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.squareup.phrase.Phrase
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import network.loki.messenger.R
import network.loki.messenger.databinding.ViewControlMessageBinding
import network.loki.messenger.libsession_util.util.ExpiryMode
@ -19,11 +20,19 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.getColorFromAttr
import org.thoughtcrime.securesms.MissingMicrophonePermissionDialog
import org.thoughtcrime.securesms.conversation.disappearingmessages.DisappearingMessages
import org.thoughtcrime.securesms.conversation.disappearingmessages.expiryMode
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.getSubbedCharSequence
import org.thoughtcrime.securesms.ui.getSubbedString
import javax.inject.Inject
@AndroidEntryPoint
class ControlMessageView : LinearLayout {
@ -32,6 +41,12 @@ class ControlMessageView : LinearLayout {
private val binding = ViewControlMessageBinding.inflate(LayoutInflater.from(context), this, true)
private val infoDrawable by lazy {
val d = ResourcesCompat.getDrawable(resources, R.drawable.ic_info_outline_white_24dp, context.theme)
d?.setTint(context.getColorFromAttr(R.attr.message_received_text_color))
d
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
@ -98,17 +113,63 @@ class ControlMessageView : LinearLayout {
val drawable = when {
message.isIncomingCall -> R.drawable.ic_incoming_call
message.isOutgoingCall -> R.drawable.ic_outgoing_call
message.isFirstMissedCall -> R.drawable.ic_info_outline_light
else -> R.drawable.ic_missed_call
}
binding.textView.isVisible = false
binding.callTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(ResourcesCompat.getDrawable(resources, drawable, context.theme), null, null, null)
binding.callTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(
ResourcesCompat.getDrawable(resources, drawable, context.theme),
null, null, null)
binding.callTextView.text = messageBody
if (message.expireStarted > 0 && message.expiresIn > 0) {
binding.expirationTimerView.isVisible = true
binding.expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
}
// remove clicks by default
setOnClickListener(null)
hideInfo()
// handle click behaviour depending on criteria
if (message.isMissedCall || message.isFirstMissedCall) {
when {
// if we're currently missing the audio/microphone permission,
// show a dedicated permission dialog
!Permissions.hasAll(context, Manifest.permission.RECORD_AUDIO) -> {
showInfo()
setOnClickListener {
MissingMicrophonePermissionDialog.show(context)
}
}
// when the call toggle is disabled in the privacy screen,
// show a dedicated privacy dialog
!TextSecurePreferences.isCallNotificationsEnabled(context) -> {
showInfo()
setOnClickListener {
context.showSessionDialog {
val titleTxt = context.getSubbedString(
R.string.callsMissedCallFrom,
NAME_KEY to message.individualRecipient.name!!
)
title(titleTxt)
val bodyTxt = context.getSubbedCharSequence(
R.string.callsYouMissedCallPermissions,
NAME_KEY to message.individualRecipient.name!!
)
text(bodyTxt)
button(R.string.sessionSettings) {
Intent(context, PrivacySettingsActivity::class.java)
.let(context::startActivity)
}
cancelButton()
}
}
}
}
}
}
}
@ -116,6 +177,24 @@ class ControlMessageView : LinearLayout {
binding.callView.isVisible = message.isCallLog
}
fun showInfo(){
binding.callTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(
binding.callTextView.compoundDrawablesRelative.first(),
null,
infoDrawable,
null
)
}
fun hideInfo(){
binding.callTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(
binding.callTextView.compoundDrawablesRelative.first(),
null,
null,
null
)
}
fun recycle() {
}

View File

@ -41,7 +41,6 @@ public interface MmsSmsColumns {
protected static final long MISSED_CALL_TYPE = 3;
protected static final long JOINED_TYPE = 4;
protected static final long FIRST_MISSED_CALL_TYPE = 5;
protected static final long MISSED_PERMISSION_CALL_TYPE = 6;
protected static final long BASE_INBOX_TYPE = 20;
protected static final long BASE_OUTBOX_TYPE = 21;
@ -236,7 +235,7 @@ public interface MmsSmsColumns {
public static boolean isCallLog(long type) {
long baseType = type & BASE_TYPE_MASK;
return baseType == INCOMING_CALL_TYPE || baseType == OUTGOING_CALL_TYPE ||
baseType == MISSED_CALL_TYPE || baseType == FIRST_MISSED_CALL_TYPE || baseType == MISSED_PERMISSION_CALL_TYPE;
baseType == MISSED_CALL_TYPE || baseType == FIRST_MISSED_CALL_TYPE;
}
public static boolean isExpirationTimerUpdate(long type) {
@ -267,10 +266,6 @@ public interface MmsSmsColumns {
return (type & BASE_TYPE_MASK) == MISSED_CALL_TYPE;
}
public static boolean isMissedPermissionCall(long type) {
return (type & BASE_TYPE_MASK) == MISSED_PERMISSION_CALL_TYPE;
}
public static boolean isFirstMissedCall(long type) {
return (type & BASE_TYPE_MASK) == FIRST_MISSED_CALL_TYPE;
}

View File

@ -501,8 +501,6 @@ public class SmsDatabase extends MessagingDatabase {
return Types.MISSED_CALL_TYPE;
case CALL_FIRST_MISSED:
return Types.FIRST_MISSED_CALL_TYPE;
case CALL_MISSED_PERMISSION:
return Types.MISSED_PERMISSION_CALL_TYPE;
default:
return 0;
}

View File

@ -142,9 +142,6 @@ public abstract class DisplayRecord {
public boolean isFirstMissedCall() {
return SmsDatabase.Types.isFirstMissedCall(type);
}
public boolean isMissedPermissionCall() {
return SmsDatabase.Types.isMissedPermissionCall(type);
}
public boolean isDeleted() { return MmsSmsColumns.Types.isDeletedMessage(type); }
public boolean isMessageRequestResponse() { return MmsSmsColumns.Types.isMessageRequestResponse(type); }

View File

@ -134,8 +134,6 @@ public abstract class MessageRecord extends DisplayRecord {
callType = CallMessageType.CALL_OUTGOING;
} else if (isMissedCall()) {
callType = CallMessageType.CALL_MISSED;
} else if (isMissedPermissionCall()) {
callType = CallMessageType.CALL_MISSED_PERMISSION;
} else {
callType = CallMessageType.CALL_FIRST_MISSED;
}

View File

@ -62,26 +62,15 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
if (!approvedContact && storage.getUserPublicKey() != sender) continue
// if the user has not enabled voice/video calls
if (!textSecurePreferences.isCallNotificationsEnabled()) {
// or if the user has not granted audio/microphone permissions
if (
!textSecurePreferences.isCallNotificationsEnabled() ||
!Permissions.hasAll(context, Manifest.permission.RECORD_AUDIO)
) {
Log.d("Loki","Dropping call message if call notifications disabled")
if (nextMessage.type != PRE_OFFER) continue
val sentTimestamp = nextMessage.sentTimestamp ?: continue
if (textSecurePreferences.setShownCallNotification()) {
// first time call notification encountered
val notification = CallNotificationBuilder.getFirstCallNotification(context, sender)
context.getSystemService(NotificationManager::class.java).notify(CallNotificationBuilder.WEBRTC_NOTIFICATION, notification)
insertMissedCall(sender, sentTimestamp, isFirstCall = true)
} else {
insertMissedCall(sender, sentTimestamp)
}
continue
}
// or if the user has not granted audio/microphone permissions
else if (!Permissions.hasAll(context, Manifest.permission.RECORD_AUDIO)) {
if (nextMessage.type != PRE_OFFER) continue
val sentTimestamp = nextMessage.sentTimestamp ?: continue
Log.d("Loki", "Attempted to receive a call without audio permissions")
insertMissedPermissionCall(sender, sentTimestamp)
insertMissedCall(sender, sentTimestamp)
continue
}
@ -103,20 +92,10 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
}
}
private fun insertMissedCall(sender: String, sentTimestamp: Long, isFirstCall: Boolean = false) {
private fun insertMissedCall(sender: String, sentTimestamp: Long) {
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 (isFirstCall) {
storage.insertCallMessage(sender, CallMessageType.CALL_FIRST_MISSED, sentTimestamp)
} else {
storage.insertCallMessage(sender, CallMessageType.CALL_MISSED, sentTimestamp)
}
}
private fun insertMissedPermissionCall(sender: String, sentTimestamp: Long) {
val currentUserPublicKey = storage.getUserPublicKey()
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_PERMISSION, sentTimestamp)
storage.insertCallMessage(sender, CallMessageType.CALL_MISSED, sentTimestamp)
}
private fun incomingHangup(callMessage: CallMessage) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

View File

@ -5,8 +5,8 @@
android:viewportHeight="20">
<path
android:pathData="M14.414,7l3.293,-3.293a1,1 0,0 0,-1.414 -1.414L13,5.586V4a1,1 0,1 0,-2 0v4.003a0.996,0.996 0,0 0,0.617 0.921A0.997,0.997 0,0 0,12 9h4a1,1 0,1 0,0 -2h-1.586z"
android:fillColor="#000000"/>
android:fillColor="?message_received_text_color"/>
<path
android:pathData="M2,3a1,1 0,0 1,1 -1h2.153a1,1 0,0 1,0.986 0.836l0.74,4.435a1,1 0,0 1,-0.54 1.06l-1.548,0.773a11.037,11.037 0,0 0,6.105 6.105l0.774,-1.548a1,1 0,0 1,1.059 -0.54l4.435,0.74a1,1 0,0 1,0.836 0.986V17a1,1 0,0 1,-1 1h-2C7.82,18 2,12.18 2,5V3z"
android:fillColor="#000000"/>
android:fillColor="?message_received_text_color"/>
</vector>

View File

@ -5,8 +5,8 @@
android:viewportHeight="20">
<path
android:pathData="M2,3a1,1 0,0 1,1 -1h2.153a1,1 0,0 1,0.986 0.836l0.74,4.435a1,1 0,0 1,-0.54 1.06l-1.548,0.773a11.037,11.037 0,0 0,6.105 6.105l0.774,-1.548a1,1 0,0 1,1.059 -0.54l4.435,0.74a1,1 0,0 1,0.836 0.986V17a1,1 0,0 1,-1 1h-2C7.82,18 2,12.18 2,5V3z"
android:fillColor="#000000"/>
android:fillColor="?danger"/>
<path
android:pathData="M16.707,3.293a1,1 0,0 1,0 1.414L15.414,6l1.293,1.293a1,1 0,0 1,-1.414 1.414L14,7.414l-1.293,1.293a1,1 0,1 1,-1.414 -1.414L12.586,6l-1.293,-1.293a1,1 0,0 1,1.414 -1.414L14,4.586l1.293,-1.293a1,1 0,0 1,1.414 0z"
android:fillColor="#000000"/>
android:fillColor="?danger"/>
</vector>

View File

@ -5,8 +5,8 @@
android:viewportHeight="20">
<path
android:pathData="M17.924,2.617a0.997,0.997 0,0 0,-0.215 -0.322l-0.004,-0.004A0.997,0.997 0,0 0,17 2h-4a1,1 0,1 0,0 2h1.586l-3.293,3.293a1,1 0,0 0,1.414 1.414L16,5.414V7a1,1 0,1 0,2 0V3a0.997,0.997 0,0 0,-0.076 -0.383z"
android:fillColor="#000000"/>
android:fillColor="?message_received_text_color"/>
<path
android:pathData="M2,3a1,1 0,0 1,1 -1h2.153a1,1 0,0 1,0.986 0.836l0.74,4.435a1,1 0,0 1,-0.54 1.06l-1.548,0.773a11.037,11.037 0,0 0,6.105 6.105l0.774,-1.548a1,1 0,0 1,1.059 -0.54l4.435,0.74a1,1 0,0 1,0.836 0.986V17a1,1 0,0 1,-1 1h-2C7.82,18 2,12.18 2,5V3z"
android:fillColor="#000000"/>
android:fillColor="?message_received_text_color"/>
</vector>

View File

@ -65,7 +65,6 @@
tools:text="You missed a call"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:drawableTint="?message_received_text_color"
app:drawableStartCompat="@drawable/ic_missed_call" />
</FrameLayout>

View File

@ -48,7 +48,7 @@
<item name="menu_selectall_icon">@drawable/ic_baseline_select_all_24</item>
<item name="menu_split_icon">@drawable/ic_baseline_call_split_24</item>
<item name="menu_popup_expand">@drawable/ic_baseline_launch_24</item>
<item name="menu_info_icon">@drawable/ic_baseline_info_24</item>
<item name="menu_info_icon">@drawable/ic_info_outline_white_24dp</item>
<item name="menu_pin_icon">@drawable/ic_outline_pin_24</item>
<item name="menu_unpin_icon">@drawable/ic_outline_pin_off_24</item>
<item name="menu_mark_all_as_read">@drawable/ic_outline_mark_chat_read_24</item>

View File

@ -4,6 +4,5 @@ enum class CallMessageType {
CALL_MISSED,
CALL_INCOMING,
CALL_OUTGOING,
CALL_FIRST_MISSED,
CALL_MISSED_PERMISSION,
CALL_FIRST_MISSED
}

View File

@ -8,22 +8,21 @@ import org.session.libsession.messaging.calls.CallMessageType
import org.session.libsession.messaging.calls.CallMessageType.CALL_FIRST_MISSED
import org.session.libsession.messaging.calls.CallMessageType.CALL_INCOMING
import org.session.libsession.messaging.calls.CallMessageType.CALL_MISSED
import org.session.libsession.messaging.calls.CallMessageType.CALL_MISSED_PERMISSION
import org.session.libsession.messaging.calls.CallMessageType.CALL_OUTGOING
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage.Kind.SCREENSHOT
import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.getExpirationTypeDisplayValue
import org.session.libsession.utilities.truncateIdForDisplay
import org.session.libsignal.utilities.Log
import org.session.libsession.utilities.StringSubstitutionConstants.COUNT_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.DISAPPEARING_MESSAGES_TYPE_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.OTHER_NAME_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY
import org.session.libsession.utilities.getExpirationTypeDisplayValue
import org.session.libsession.utilities.truncateIdForDisplay
import org.session.libsignal.utilities.Log
object UpdateMessageBuilder {
const val TAG = "libsession"
@ -266,7 +265,6 @@ object UpdateMessageBuilder {
CALL_INCOMING -> Phrase.from(context, R.string.callsCalledYou).put(NAME_KEY, senderName).format().toString()
CALL_OUTGOING -> Phrase.from(context, R.string.callsYouCalled).put(NAME_KEY, senderName).format().toString()
CALL_MISSED, CALL_FIRST_MISSED -> Phrase.from(context, R.string.callsMissedCallFrom).put(NAME_KEY, senderName).format().toString()
CALL_MISSED_PERMISSION -> Phrase.from(context, R.string.callsMissedCallFrom).put(NAME_KEY, senderName).format().toString() + "\n" + context.getString(R.string.permissionsMicrophoneDescription)
}
}
}