mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Reworked notifications channel, using incoming call one for playing ringtone instead of SDK
This commit is contained in:
parent
44457665ef
commit
0707e60c26
7 changed files with 194 additions and 145 deletions
|
|
@ -27,6 +27,7 @@ rls_uri=sips:rls@sip.linphone.org
|
|||
[sound]
|
||||
#remove this property for any application that is not Linphone public version itself
|
||||
ec_calibrator_cool_tones=1
|
||||
disable_ringing=1
|
||||
|
||||
[audio]
|
||||
android_disable_audio_focus_requests=1
|
||||
|
|
|
|||
|
|
@ -262,6 +262,8 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
|
|||
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
audioManager.registerAudioDeviceCallback(audioDeviceCallback, coreThread)
|
||||
|
||||
corePreferences.linphoneConfigurationVersion = "6.0"
|
||||
|
||||
Log.i("$TAG Report Core created and started")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,13 @@ class CorePreferences @UiThread constructor(private val context: Context) {
|
|||
config.setInt("app", "dark_mode", value)
|
||||
}
|
||||
|
||||
@get:WorkerThread @set:WorkerThread
|
||||
var linphoneConfigurationVersion: String
|
||||
get() = config.getString("app", "linphonerc_version", "5.2")!!
|
||||
set(value) {
|
||||
config.setString("app", "linphonerc_version", value)
|
||||
}
|
||||
|
||||
@get:WorkerThread
|
||||
val darkModeAllowed: Boolean
|
||||
get() = config.getBool("ui", "dark_mode_allowed", true)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
import android.media.RingtoneManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.AnyThread
|
||||
|
|
@ -88,6 +91,7 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
private const val MISSED_CALL_TAG = "Missed call"
|
||||
const val CHAT_NOTIFICATIONS_GROUP = "CHAT_NOTIF_GROUP"
|
||||
|
||||
private const val INCOMING_CALL_ID = 1
|
||||
private const val MISSED_CALL_ID = 10
|
||||
}
|
||||
|
||||
|
|
@ -321,12 +325,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
private var currentlyDisplayedChatRoomId: String = ""
|
||||
|
||||
init {
|
||||
createServiceChannel()
|
||||
createIncomingCallNotificationChannel()
|
||||
createMissedCallNotificationChannel()
|
||||
createActiveCallNotificationChannel()
|
||||
createMessageChannel()
|
||||
|
||||
for (notification in notificationManager.activeNotifications) {
|
||||
if (notification.tag.isNullOrEmpty()) {
|
||||
Log.w(
|
||||
|
|
@ -365,8 +363,10 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
if (core.callsNb == 0) {
|
||||
Log.w("$TAG No call anymore, stopping service")
|
||||
stopCallForeground()
|
||||
} else {
|
||||
Log.i("$TAG At least a call is still running")
|
||||
} else if (currentForegroundServiceNotificationId == -1) {
|
||||
Log.i(
|
||||
"$TAG At least a call is still running and no foreground service notification was found"
|
||||
)
|
||||
val call = core.currentCall ?: core.calls.first()
|
||||
startCallForeground(call)
|
||||
}
|
||||
|
|
@ -379,9 +379,33 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
coreService = null
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun createChannels(clearPreviousChannels: Boolean) {
|
||||
if (clearPreviousChannels) {
|
||||
Log.w("$TAG We were asked to remove all existing notification channels")
|
||||
for (channel in notificationManager.notificationChannels) {
|
||||
Log.i("$TAG Deleting notification channel ID [${channel.id}]")
|
||||
notificationManager.deleteNotificationChannel(channel.id)
|
||||
}
|
||||
}
|
||||
|
||||
createServiceChannel()
|
||||
createIncomingCallNotificationChannel()
|
||||
createMissedCallNotificationChannel()
|
||||
createActiveCallNotificationChannel()
|
||||
createMessageChannel()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun onCoreStarted(core: Core) {
|
||||
Log.i("$TAG Core has been started")
|
||||
|
||||
val rcVersion = corePreferences.linphoneConfigurationVersion
|
||||
val clearPreviousChannels = rcVersion == "5.2"
|
||||
coreContext.postOnMainThread {
|
||||
createChannels(clearPreviousChannels)
|
||||
}
|
||||
|
||||
core.addListener(coreListener)
|
||||
}
|
||||
|
||||
|
|
@ -394,6 +418,22 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
@WorkerThread
|
||||
private fun showCallNotification(call: Call, isIncoming: Boolean) {
|
||||
val notifiable = getNotifiableForCall(call)
|
||||
if (!isIncoming && call.dir == Call.Dir.Incoming) {
|
||||
if (currentForegroundServiceNotificationId == INCOMING_CALL_ID) {
|
||||
// This is an accepted incoming call, remove notification before adding it again to change channel
|
||||
Log.i(
|
||||
"$TAG Incoming call with notification ID [${notifiable.notificationId}] was accepted, cancelling notification before adding it again to the right channel"
|
||||
)
|
||||
if (coreService != null) {
|
||||
Log.i(
|
||||
"$TAG Service found, stopping it as foreground before cancelling notification"
|
||||
)
|
||||
coreService?.stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
}
|
||||
cancelNotification(INCOMING_CALL_ID)
|
||||
currentForegroundServiceNotificationId = -1
|
||||
}
|
||||
}
|
||||
|
||||
val callNotificationIntent = Intent(context, CallActivity::class.java)
|
||||
callNotificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
|
@ -417,10 +457,16 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
pendingIntent,
|
||||
isIncoming
|
||||
)
|
||||
notify(notifiable.notificationId, notification)
|
||||
|
||||
if (notifiable.notificationId == currentForegroundServiceNotificationId) {
|
||||
startCallForeground(call)
|
||||
if (isIncoming) {
|
||||
notify(INCOMING_CALL_ID, notification)
|
||||
if (currentForegroundServiceNotificationId == -1) {
|
||||
startIncomingCallForeground(notification)
|
||||
}
|
||||
} else {
|
||||
notify(notifiable.notificationId, notification)
|
||||
if (currentForegroundServiceNotificationId == -1) {
|
||||
startCallForeground(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -465,6 +511,26 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
notify(MISSED_CALL_ID, notification, MISSED_CALL_TAG)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun startIncomingCallForeground(notification: Notification) {
|
||||
Log.i("$TAG Trying to start foreground Service using incoming call notification")
|
||||
val service = coreService
|
||||
if (service != null) {
|
||||
Log.i(
|
||||
"$TAG Service found, starting it as foreground using notification ID [$INCOMING_CALL_ID] with type PHONE_CALL"
|
||||
)
|
||||
Compatibility.startServiceForeground(
|
||||
service,
|
||||
INCOMING_CALL_ID,
|
||||
notification,
|
||||
Compatibility.FOREGROUND_SERVICE_TYPE_PHONE_CALL
|
||||
)
|
||||
currentForegroundServiceNotificationId = INCOMING_CALL_ID
|
||||
} else {
|
||||
Log.w("$TAG Core Foreground Service hasn't started yet...")
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun startCallForeground(call: Call) {
|
||||
Log.i("$TAG Trying to start/update foreground Service using call notification")
|
||||
|
|
@ -863,19 +929,15 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
channel
|
||||
).apply {
|
||||
try {
|
||||
style.setIsVideo(isVideo)
|
||||
style.setAnswerButtonColorHint(
|
||||
context.getColor(R.color.success_500)
|
||||
)
|
||||
style.setDeclineButtonColorHint(
|
||||
context.getColor(R.color.danger_500)
|
||||
)
|
||||
style.setIsVideo(false)
|
||||
setStyle(style)
|
||||
} catch (iae: IllegalArgumentException) {
|
||||
Log.e(
|
||||
"$TAG Can't use notification call style: $iae"
|
||||
)
|
||||
}
|
||||
setColorized(true)
|
||||
setOnlyAlertOnce(true)
|
||||
setSmallIcon(smallIcon)
|
||||
setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
|
|
@ -1017,7 +1079,7 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
|
||||
return PendingIntent.getBroadcast(
|
||||
context,
|
||||
notifiable.notificationId,
|
||||
3,
|
||||
hangupIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
|
@ -1032,7 +1094,7 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
|
||||
return PendingIntent.getBroadcast(
|
||||
context,
|
||||
notifiable.notificationId,
|
||||
2,
|
||||
answerIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
|
@ -1146,13 +1208,16 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
val id = context.getString(R.string.notification_channel_incoming_call_id)
|
||||
val name = context.getString(R.string.notification_channel_incoming_call_name)
|
||||
|
||||
val ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
|
||||
val audioAttributes = AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setLegacyStreamType(AudioManager.STREAM_RING)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build()
|
||||
|
||||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = name
|
||||
lightColor = context.getColor(R.color.main1_500)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
enableVibration(true)
|
||||
enableLights(true)
|
||||
setShowBadge(false)
|
||||
setSound(ringtone, audioAttributes)
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
|
@ -1164,11 +1229,8 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
|
||||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = name
|
||||
lightColor = context.getColor(R.color.main1_500)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
enableVibration(true)
|
||||
enableLights(true)
|
||||
setShowBadge(false)
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
|
@ -1181,9 +1243,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_DEFAULT).apply {
|
||||
description = name
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
enableVibration(false)
|
||||
enableLights(false)
|
||||
setShowBadge(false)
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
|
@ -1195,11 +1254,8 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
|
||||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = name
|
||||
lightColor = context.getColor(R.color.main1_500)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
setShowBadge(true)
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
|
@ -1211,9 +1267,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
|
||||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW).apply {
|
||||
description = name
|
||||
enableVibration(false)
|
||||
enableLights(false)
|
||||
setShowBadge(false)
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import org.linphone.LinphoneApplication.Companion.coreContext
|
|||
import org.linphone.core.AudioDevice
|
||||
import org.linphone.core.Call
|
||||
import org.linphone.core.CallListenerStub
|
||||
import org.linphone.core.MediaDirection
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.AudioUtils
|
||||
|
||||
|
|
@ -55,15 +54,9 @@ class TelecomCallControlCallback(
|
|||
Log.i("$TAG Call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]")
|
||||
if (state == Call.State.Connected) {
|
||||
if (call.dir == Call.Dir.Incoming) {
|
||||
val videoEnabled = call.currentParams.isVideoEnabled && call.currentParams.videoDirection != MediaDirection.Inactive
|
||||
scope.launch {
|
||||
val type = if (videoEnabled) {
|
||||
CallAttributesCompat.CALL_TYPE_VIDEO_CALL
|
||||
} else {
|
||||
CallAttributesCompat.CALL_TYPE_AUDIO_CALL
|
||||
}
|
||||
Log.i("$TAG Answering call with type [$type]")
|
||||
callControl.answer(type)
|
||||
Log.i("$TAG Answering call")
|
||||
callControl.answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)
|
||||
}
|
||||
} else {
|
||||
scope.launch {
|
||||
|
|
@ -74,7 +67,7 @@ class TelecomCallControlCallback(
|
|||
} else if (state == Call.State.End) {
|
||||
scope.launch {
|
||||
Log.i("$TAG Disconnecting call")
|
||||
callControl.disconnect(DisconnectCause(DisconnectCause.REMOTE))
|
||||
callControl.disconnect(DisconnectCause(DisconnectCause.LOCAL))
|
||||
}
|
||||
} else if (state == Call.State.Pausing) {
|
||||
scope.launch {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import org.linphone.core.AudioDevice
|
|||
import org.linphone.core.Call
|
||||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreListenerStub
|
||||
import org.linphone.core.MediaDirection
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
|
|
@ -51,98 +50,7 @@ class TelecomManager @WorkerThread constructor(context: Context) {
|
|||
private val coreListener = object : CoreListenerStub() {
|
||||
@WorkerThread
|
||||
override fun onCallCreated(core: Core, call: Call) {
|
||||
Log.i("$TAG Call created: $call")
|
||||
|
||||
val address = call.remoteAddress
|
||||
val friend = coreContext.contactsManager.findContactByAddress(address)
|
||||
val displayName = friend?.name ?: LinphoneUtils.getDisplayName(address)
|
||||
|
||||
val uri = Uri.parse(address.asStringUriOnly())
|
||||
|
||||
val direction = if (call.dir == Call.Dir.Outgoing) {
|
||||
CallAttributesCompat.DIRECTION_OUTGOING
|
||||
} else {
|
||||
CallAttributesCompat.DIRECTION_INCOMING
|
||||
}
|
||||
|
||||
val params = if (call.dir == Call.Dir.Outgoing) {
|
||||
call.params
|
||||
} else {
|
||||
call.remoteParams
|
||||
}
|
||||
val type = if (params?.isVideoEnabled == true && params.videoDirection != MediaDirection.Inactive) {
|
||||
CallAttributesCompat.CALL_TYPE_VIDEO_CALL
|
||||
} else {
|
||||
CallAttributesCompat.CALL_TYPE_AUDIO_CALL
|
||||
}
|
||||
|
||||
val capabilities = CallAttributesCompat.SUPPORTS_SET_INACTIVE or CallAttributesCompat.SUPPORTS_TRANSFER
|
||||
|
||||
val callAttributes = CallAttributesCompat(
|
||||
displayName,
|
||||
uri,
|
||||
direction,
|
||||
type,
|
||||
capabilities
|
||||
)
|
||||
Log.i("$TAG Adding call to Telecom's CallsManager with attributes [$callAttributes]")
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
callsManager.addCall(
|
||||
callAttributes,
|
||||
{ callType -> // onAnswer
|
||||
Log.i("$TAG We're asked to answer the call with type [$callType]")
|
||||
coreContext.postOnCoreThread {
|
||||
if (LinphoneUtils.isCallIncoming(call.state)) {
|
||||
Log.i("$TAG Answering call")
|
||||
coreContext.answerCall(call) // TODO FIXME: use call type
|
||||
}
|
||||
}
|
||||
},
|
||||
{ disconnectCause -> // onDisconnect
|
||||
Log.i(
|
||||
"$TAG We're asked to terminate the call with reason [$disconnectCause]"
|
||||
)
|
||||
coreContext.postOnCoreThread {
|
||||
Log.i(
|
||||
"$TAG Terminating call [${call.remoteAddress.asStringUriOnly()}]"
|
||||
)
|
||||
call.terminate() // TODO FIXME: use cause
|
||||
}
|
||||
},
|
||||
{ // onSetActive
|
||||
Log.i("$TAG We're asked to resume the call")
|
||||
coreContext.postOnCoreThread {
|
||||
Log.i("$TAG Resuming call")
|
||||
call.resume()
|
||||
}
|
||||
},
|
||||
{ // onSetInactive
|
||||
Log.i("$TAG We're asked to pause the call")
|
||||
coreContext.postOnCoreThread {
|
||||
Log.i("$TAG Pausing call")
|
||||
call.pause()
|
||||
}
|
||||
}
|
||||
) {
|
||||
val callbacks = TelecomCallControlCallback(call, this, scope)
|
||||
|
||||
coreContext.postOnCoreThread {
|
||||
val callId = call.callLog.callId.orEmpty()
|
||||
if (callId.isNotEmpty()) {
|
||||
Log.i("$TAG Storing our callbacks for call ID [$callId]")
|
||||
map[callId] = callbacks
|
||||
}
|
||||
}
|
||||
|
||||
// We must first call setCallback on callControlScope before using it
|
||||
callbacks.onCallControlCallbackSet()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Failed to add call to Telecom's CallsManager!")
|
||||
}
|
||||
}
|
||||
onCallCreated(call)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +65,91 @@ class TelecomManager @WorkerThread constructor(context: Context) {
|
|||
)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun onCallCreated(call: Call) {
|
||||
Log.i("$TAG Call created: $call")
|
||||
|
||||
val address = call.remoteAddress
|
||||
val friend = coreContext.contactsManager.findContactByAddress(address)
|
||||
val displayName = friend?.name ?: LinphoneUtils.getDisplayName(address)
|
||||
|
||||
val uri = Uri.parse(address.asStringUriOnly())
|
||||
|
||||
val direction = if (call.dir == Call.Dir.Outgoing) {
|
||||
CallAttributesCompat.DIRECTION_OUTGOING
|
||||
} else {
|
||||
CallAttributesCompat.DIRECTION_INCOMING
|
||||
}
|
||||
|
||||
val capabilities = CallAttributesCompat.SUPPORTS_SET_INACTIVE or CallAttributesCompat.SUPPORTS_TRANSFER
|
||||
|
||||
val callAttributes = CallAttributesCompat(
|
||||
displayName,
|
||||
uri,
|
||||
direction,
|
||||
CallAttributesCompat.CALL_TYPE_AUDIO_CALL,
|
||||
capabilities
|
||||
)
|
||||
Log.i("$TAG Adding call to Telecom's CallsManager with attributes [$callAttributes]")
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
callsManager.addCall(
|
||||
callAttributes,
|
||||
{ callType -> // onAnswer
|
||||
Log.i("$TAG We're asked to answer the call with type [$callType]")
|
||||
coreContext.postOnCoreThread {
|
||||
if (LinphoneUtils.isCallIncoming(call.state)) {
|
||||
Log.i("$TAG Answering call")
|
||||
coreContext.answerCall(call)
|
||||
}
|
||||
}
|
||||
},
|
||||
{ disconnectCause -> // onDisconnect
|
||||
Log.i(
|
||||
"$TAG We're asked to terminate the call with reason [$disconnectCause]"
|
||||
)
|
||||
coreContext.postOnCoreThread {
|
||||
Log.i(
|
||||
"$TAG Terminating call [${call.remoteAddress.asStringUriOnly()}]"
|
||||
)
|
||||
call.terminate() // TODO FIXME: use cause
|
||||
}
|
||||
},
|
||||
{ // onSetActive
|
||||
Log.i("$TAG We're asked to resume the call")
|
||||
coreContext.postOnCoreThread {
|
||||
Log.i("$TAG Resuming call")
|
||||
call.resume()
|
||||
}
|
||||
},
|
||||
{ // onSetInactive
|
||||
Log.i("$TAG We're asked to pause the call")
|
||||
coreContext.postOnCoreThread {
|
||||
Log.i("$TAG Pausing call")
|
||||
call.pause()
|
||||
}
|
||||
}
|
||||
) {
|
||||
val callbacks = TelecomCallControlCallback(call, this, scope)
|
||||
|
||||
coreContext.postOnCoreThread {
|
||||
val callId = call.callLog.callId.orEmpty()
|
||||
if (callId.isNotEmpty()) {
|
||||
Log.i("$TAG Storing our callbacks for call ID [$callId]")
|
||||
map[callId] = callbacks
|
||||
}
|
||||
}
|
||||
|
||||
// We must first call setCallback on callControlScope before using it
|
||||
callbacks.onCallControlCallbackSet()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Failed to add call to Telecom's CallsManager!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun onCoreStarted(core: Core) {
|
||||
Log.i("$TAG Core has been started")
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
<string name="assistant_web_platform_link" translatable="false">subscribe.linphone.org</string>
|
||||
<string name="assistant_linphone_contact_us_link" translatable="false">linphone.org/contact</string>
|
||||
|
||||
<string name="notification_channel_call_id" translatable="false">linphone_notification_call_id</string>
|
||||
<string name="notification_channel_incoming_call_id" translatable="false">linphone_notification_incoming_call_id</string>
|
||||
<string name="notification_channel_missed_call_id" translatable="false">linphone_notification_missed_call_id</string>
|
||||
<string name="notification_channel_service_id" translatable="false">linphone_notification_service_id</string>
|
||||
<string name="notification_channel_chat_id" translatable="false">linphone_notification_chat_id</string>
|
||||
<string name="notification_channel_call_id" translatable="false">linphone_6.0_notification_call_id</string>
|
||||
<string name="notification_channel_incoming_call_id" translatable="false">linphone_6.0_notification_incoming_call_id</string>
|
||||
<string name="notification_channel_missed_call_id" translatable="false">linphone_6.0_notification_missed_call_id</string>
|
||||
<string name="notification_channel_service_id" translatable="false">linphone_6.0_notification_service_id</string>
|
||||
<string name="notification_channel_chat_id" translatable="false">linphone_6.0_notification_chat_id</string>
|
||||
|
||||
<string name="emoji_love" translatable="false">❤️</string>
|
||||
<string name="emoji_thumbs_up" translatable="false">👍</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue