mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-26 01:37:43 +00:00
Added a new control message type to handle missed calls due to permissions
This commit is contained in:
parent
5795998b81
commit
4860adcd86
@ -0,0 +1,29 @@
|
||||
package org.thoughtcrime.securesms
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.squareup.phrase.Phrase
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
|
||||
|
||||
class MissingMicrophonePermissionDialog {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun show(context: Context) = context.showSessionDialog {
|
||||
title(R.string.permissionsMicrophone)
|
||||
text(
|
||||
Phrase.from(context, R.string.permissionsMicrophoneAccessRequired)
|
||||
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||
.format().toString())
|
||||
button(R.string.sessionSettings, R.string.AccessibilityId_sessionSettings) {
|
||||
val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val uri = Uri.fromParts("package", context.packageName, null)
|
||||
intent.setData(uri)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.conversation.v2
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.util.SparseArray
|
||||
import android.util.SparseBooleanArray
|
||||
import android.view.MotionEvent
|
||||
@ -30,6 +31,9 @@ import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
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.thoughtcrime.securesms.MissingMicrophonePermissionDialog
|
||||
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
|
||||
import org.thoughtcrime.securesms.showSessionDialog
|
||||
import org.thoughtcrime.securesms.ui.getSubbedCharSequence
|
||||
@ -121,7 +125,11 @@ class ConversationAdapter(
|
||||
val senderId = message.individualRecipient.address.serialize()
|
||||
val senderIdHash = senderId.hashCode()
|
||||
updateQueue.trySend(senderId)
|
||||
if (contactCache[senderIdHash] == null && !contactLoadedCache.getOrDefault(senderIdHash, false)) {
|
||||
if (contactCache[senderIdHash] == null && !contactLoadedCache.getOrDefault(
|
||||
senderIdHash,
|
||||
false
|
||||
)
|
||||
) {
|
||||
getSenderInfo(senderId)?.let { contact ->
|
||||
contactCache[senderIdHash] = contact
|
||||
}
|
||||
@ -129,49 +137,77 @@ class ConversationAdapter(
|
||||
val contact = contactCache[senderIdHash]
|
||||
|
||||
visibleMessageView.bind(
|
||||
message,
|
||||
messageBefore,
|
||||
getMessageAfter(position, cursor),
|
||||
glide,
|
||||
searchQuery,
|
||||
contact,
|
||||
senderId,
|
||||
lastSeen.get(),
|
||||
visibleMessageViewDelegate,
|
||||
onAttachmentNeedsDownload,
|
||||
lastSentMessageId
|
||||
message,
|
||||
messageBefore,
|
||||
getMessageAfter(position, cursor),
|
||||
glide,
|
||||
searchQuery,
|
||||
contact,
|
||||
senderId,
|
||||
lastSeen.get(),
|
||||
visibleMessageViewDelegate,
|
||||
onAttachmentNeedsDownload,
|
||||
lastSentMessageId
|
||||
)
|
||||
|
||||
if (!message.isDeleted) {
|
||||
visibleMessageView.onPress = { event -> onItemPress(message, viewHolder.adapterPosition, visibleMessageView, event) }
|
||||
visibleMessageView.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
|
||||
visibleMessageView.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition, visibleMessageView) }
|
||||
visibleMessageView.onPress = { event ->
|
||||
onItemPress(
|
||||
message,
|
||||
viewHolder.adapterPosition,
|
||||
visibleMessageView,
|
||||
event
|
||||
)
|
||||
}
|
||||
visibleMessageView.onSwipeToReply =
|
||||
{ onItemSwipeToReply(message, viewHolder.adapterPosition) }
|
||||
visibleMessageView.onLongPress =
|
||||
{ onItemLongPress(message, viewHolder.adapterPosition, visibleMessageView) }
|
||||
} else {
|
||||
visibleMessageView.onPress = null
|
||||
visibleMessageView.onSwipeToReply = null
|
||||
visibleMessageView.onLongPress = null
|
||||
}
|
||||
}
|
||||
|
||||
is ControlMessageViewHolder -> {
|
||||
viewHolder.view.bind(message, messageBefore)
|
||||
if (message.isCallLog && message.isFirstMissedCall) {
|
||||
viewHolder.view.setOnClickListener {
|
||||
context.showSessionDialog {
|
||||
val titleTxt = context.getSubbedString(R.string.callsMissedCallFrom, NAME_KEY to message.individualRecipient.name!!)
|
||||
title(titleTxt)
|
||||
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)
|
||||
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)
|
||||
button(R.string.sessionSettings) {
|
||||
Intent(context, PrivacySettingsActivity::class.java)
|
||||
.let(context::startActivity)
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewHolder.view.setOnClickListener(null)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ 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.MissingMicrophonePermissionDialog
|
||||
import org.thoughtcrime.securesms.media.MediaOverviewActivity
|
||||
import org.thoughtcrime.securesms.ShortcutLauncherActivity
|
||||
import org.thoughtcrime.securesms.calls.WebRtcCallActivity
|
||||
@ -182,20 +183,7 @@ object ConversationMenuHelper {
|
||||
// or if the user has not granted audio/microphone permissions
|
||||
else if (!Permissions.hasAll(context, Manifest.permission.RECORD_AUDIO)) {
|
||||
Log.d("Loki", "Attempted to make a call without audio permissions")
|
||||
context.showSessionDialog {
|
||||
title(R.string.permissionsMicrophone)
|
||||
text(Phrase.from(context, R.string.permissionsMicrophoneAccessRequired)
|
||||
.put(APP_NAME_KEY, context.getString(R.string.app_name))
|
||||
.format().toString())
|
||||
button(R.string.sessionSettings, R.string.AccessibilityId_sessionSettings) {
|
||||
val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val uri = Uri.fromParts("package", context.packageName, null)
|
||||
intent.setData(uri)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
MissingMicrophonePermissionDialog.show(context)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ 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;
|
||||
@ -234,7 +235,8 @@ 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;
|
||||
return baseType == INCOMING_CALL_TYPE || baseType == OUTGOING_CALL_TYPE ||
|
||||
baseType == MISSED_CALL_TYPE || baseType == FIRST_MISSED_CALL_TYPE || baseType == MISSED_PERMISSION_CALL_TYPE;
|
||||
}
|
||||
|
||||
public static boolean isExpirationTimerUpdate(long type) {
|
||||
@ -265,6 +267,10 @@ 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;
|
||||
}
|
||||
|
@ -501,6 +501,8 @@ 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;
|
||||
}
|
||||
|
@ -142,6 +142,9 @@ 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); }
|
||||
|
||||
|
@ -134,6 +134,8 @@ 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;
|
||||
}
|
||||
|
@ -78,8 +78,10 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
||||
}
|
||||
// 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")
|
||||
//TODO display something to let the user know they missed a call due to missing permission
|
||||
insertMissedPermissionCall(sender, sentTimestamp)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -111,6 +113,12 @@ class CallMessageProcessor(private val context: Context, private val textSecureP
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
private fun incomingHangup(callMessage: CallMessage) {
|
||||
val callId = callMessage.callId ?: return
|
||||
val hangupIntent = WebRtcCallService.remoteHangupIntent(context, callId)
|
||||
|
@ -5,4 +5,5 @@ enum class CallMessageType {
|
||||
CALL_INCOMING,
|
||||
CALL_OUTGOING,
|
||||
CALL_FIRST_MISSED,
|
||||
CALL_MISSED_PERMISSION,
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package org.session.libsession.messaging.utilities
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableString
|
||||
import com.squareup.phrase.Phrase
|
||||
import org.session.libsession.R
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
@ -9,6 +8,7 @@ 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
|
||||
@ -24,7 +24,6 @@ import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_K
|
||||
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.Util
|
||||
|
||||
object UpdateMessageBuilder {
|
||||
const val TAG = "libsession"
|
||||
@ -267,6 +266,7 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user