Most of the work for group/public calls

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-03-12 12:55:39 +01:00
parent b9091e5cc8
commit 624dea0ba1
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
7 changed files with 138 additions and 141 deletions

View File

@ -20,52 +20,15 @@
package com.nextcloud.talk.services.firebase
import android.annotation.SuppressLint
import android.app.Notification
import android.app.PendingIntent
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioManager
import android.net.Uri
import android.os.Bundle
import android.util.Base64
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.emoji.text.EmojiCompat
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.bluelinelabs.logansquare.LoganSquare
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.jobs.MessageNotificationWorker
import com.nextcloud.talk.models.SignatureVerification
import com.nextcloud.talk.models.json.push.DecryptedPushMessage
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.services.CallService
import com.nextcloud.talk.newarch.utils.MagicJson
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationWithId
import com.nextcloud.talk.utils.PushUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INCOMING_PUSH_MESSSAGE
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPEN_INCOMING_CALL
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
import com.nextcloud.talk.utils.preferences.AppPreferences
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import org.koin.core.KoinComponent
import org.koin.core.inject
import retrofit2.Retrofit
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.security.PrivateKey
import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException
class MagicFirebaseMessagingService : FirebaseMessagingService(), KoinComponent {
val tag: String = "MagicFirebaseMessagingService"

View File

@ -95,7 +95,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
var incomingTextRelativeLayout: RelativeLayout? = null
private val conversation: Conversation = Parcels.unwrap(originalBundle.getParcelable(BundleKeys.KEY_CONVERSATION))
private val userBeingCalled: UserNgEntity = originalBundle.getParcelable(BundleKeys.KEY_USER_ENTITY)!!
private val activeNotification: String = originalBundle.getString(BundleKeys.KEY_ACTIVE_NOTIFICATION)!!
private val activeNotification: String? = originalBundle.getString(BundleKeys.KEY_ACTIVE_NOTIFICATION)
override fun inflateView(
inflater: LayoutInflater,
@ -115,10 +115,12 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
}
private fun dismissIncomingCallNotification() {
val hideIncomingCallNotificationIntent = Intent(applicationContext, CallService::class.java)
hideIncomingCallNotificationIntent.action = BundleKeys.DISMISS_CALL_NOTIFICATION
hideIncomingCallNotificationIntent.putExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION, activeNotification)
applicationContext?.startService(hideIncomingCallNotificationIntent)
if (activeNotification != null) {
val hideIncomingCallNotificationIntent = Intent(applicationContext, CallService::class.java)
hideIncomingCallNotificationIntent.action = BundleKeys.DISMISS_CALL_NOTIFICATION
hideIncomingCallNotificationIntent.putExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION, activeNotification)
applicationContext?.startService(hideIncomingCallNotificationIntent)
}
}
@OnClick(R.id.callControlHangupView)

View File

@ -70,7 +70,7 @@ import org.koin.core.inject
import org.koin.core.parameter.parametersOf
import java.util.function.Consumer
class MessageNotificationWorker(
class NotificationWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams), KoinComponent {
@ -82,23 +82,22 @@ class MessageNotificationWorker(
val data = inputData
val decryptedPushMessageString: String = data.getString(BundleKeys.KEY_DECRYPTED_PUSH_MESSAGE)!!
val signatureVerificationString: String = data.getString(BundleKeys.KEY_SIGNATURE_VERIFICATION)!!
val conversationString: String = data.getString(BundleKeys.KEY_CONVERSATION)!!
val json = Json(MagicJson.customJsonConfiguration)
val decryptedPushMessage = LoganSquare.parse(decryptedPushMessageString, DecryptedPushMessage::class.java)
val signatureVerification = json.parse(SignatureVerification.serializer(), signatureVerificationString)
// we support Nextcloud Talk 4.0 and up so assuming "no-ping" capability here
val intent = Intent(applicationContext, MainActivity::class.java)
intent.action = BundleKeys.KEY_OPEN_CONVERSATION
intent.putExtra(BundleKeys.KEY_INTERNAL_USER_ID, signatureVerification.userEntity!!.id)
intent.putExtra(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id)
val conversation = json.parse(Conversation.serializer(), conversationString)
when (decryptedPushMessage.type) {
"room" -> {
showNotificationWithObjectData(this, decryptedPushMessage, signatureVerification, intent)
showNotificationWithObjectData(this, decryptedPushMessage, signatureVerification, conversation)
}
"chat" -> {
showNotificationWithObjectData(this, decryptedPushMessage, signatureVerification, intent)
showNotificationWithObjectData(this, decryptedPushMessage, signatureVerification, conversation)
}
"call" -> {
showNotification(decryptedPushMessage, signatureVerification, conversation)
}
else -> {
// do nothing
@ -108,12 +107,11 @@ class MessageNotificationWorker(
Result.success()
}
private fun showNotificationWithObjectData(coroutineScope: CoroutineScope, decryptedPushMessage: DecryptedPushMessage, signatureVerification: SignatureVerification, intent: Intent) {
private fun showNotificationWithObjectData(coroutineScope: CoroutineScope, decryptedPushMessage: DecryptedPushMessage, signatureVerification: SignatureVerification, conversation: Conversation) {
val nextcloudTalkRepository = networkComponents.getRepository(false, signatureVerification.userEntity!!.toUser())
val getNotificationUseCase = GetNotificationUseCase(nextcloudTalkRepository, apiErrorHandler)
getNotificationUseCase.invoke(coroutineScope, parametersOf(signatureVerification.userEntity, decryptedPushMessage.notificationId.toString()), object : UseCaseResponse<NotificationOverall> {
override suspend fun onSuccess(result: NotificationOverall) {
var conversationTypeString = "one2one"
val notification = result.ocs.notification
notification.messageRichParameters?.let { messageRichParameters ->
@ -138,9 +136,6 @@ class MessageNotificationWorker(
decryptedPushMessage.subject = notification.subject
}
if (callHashMap.containsKey("call-type")) {
conversationTypeString = callHashMap["call-type"]!!
}
}
val notificationUser = NotificationUser()
@ -156,22 +151,9 @@ class MessageNotificationWorker(
notificationUser.name = guestHashMap["name"]
}
var conversationType: Conversation.ConversationType = Conversation.ConversationType.ONE_TO_ONE_CONVERSATION
when (conversationTypeString) {
"public" -> {
conversationType = Conversation.ConversationType.PUBLIC_CONVERSATION
}
"group" -> {
conversationType = Conversation.ConversationType.GROUP_CONVERSATION
}
else -> {
// one2one
// do nothing
}
}
decryptedPushMessage.timestamp = notification.datetime.millis
decryptedPushMessage.notificationUser = notificationUser
showNotification(decryptedPushMessage, signatureVerification, conversationType, intent)
showNotification(decryptedPushMessage, signatureVerification, conversation)
}
override suspend fun onError(errorModel: ErrorModel?) {
@ -184,8 +166,8 @@ class MessageNotificationWorker(
private fun showNotification(
decryptedPushMessage: DecryptedPushMessage,
signatureVerification: SignatureVerification,
conversationType: Conversation.ConversationType = Conversation.ConversationType.ONE_TO_ONE_CONVERSATION, intent: Intent) {
val largeIcon = when (conversationType) {
conversation: Conversation) {
val largeIcon = when (conversation.type) {
Conversation.ConversationType.PUBLIC_CONVERSATION -> {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_link_black_24px)
}
@ -194,12 +176,21 @@ class MessageNotificationWorker(
}
else -> {
// one to one and unknown
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_chat_black_24dp)
if (decryptedPushMessage.type == "call") {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_baseline_person_black_24)
} else {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_chat_black_24dp)
}
}
}
val pendingIntent: PendingIntent? = PendingIntent.getActivity(applicationContext,
0, intent, 0)
var notificationId = decryptedPushMessage.timestamp.toInt()
val pendingIntent = if (decryptedPushMessage.type == "call") {
NotificationUtils.getIncomingCallIPendingIntent(applicationContext, applicationContext, conversation, signatureVerification.userEntity!!, null)
} else {
NotificationUtils.getNotificationPendingIntent(applicationContext, signatureVerification.userEntity!!.id, conversation.token!!)
}
val soundUri = NotificationUtils.getMessageSoundUri(applicationContext, appPreferences)
@ -247,7 +238,6 @@ class MessageNotificationWorker(
notificationBuilder.color = applicationContext.resources.getColor(R.color.colorPrimary)
}
var notificationId = decryptedPushMessage.timestamp.toInt()
val notificationInfoBundle = Bundle()
notificationInfoBundle.putLong(BundleKeys.KEY_INTERNAL_USER_ID, signatureVerification.userEntity!!.id)
@ -290,13 +280,13 @@ class MessageNotificationWorker(
override fun onSuccess(result: Drawable) {
super.onSuccess(result)
person.setIcon(IconCompat.createWithBitmap(result.toBitmap()))
notificationBuilder.setStyle(getStyle(decryptedPushMessage, conversationType, person.build(), style))
notificationBuilder.setStyle(getStyle(decryptedPushMessage, conversation.type!!, person.build(), style))
NotificationManagerCompat.from(applicationContext).notify(notificationId, notificationBuilder.build())
}
override fun onError(error: Drawable?) {
super.onError(error)
notificationBuilder.setStyle(getStyle(decryptedPushMessage, conversationType, person.build(), style))
notificationBuilder.setStyle(getStyle(decryptedPushMessage, conversation.type!!, person.build(), style))
NotificationManagerCompat.from(applicationContext).notify(notificationId, notificationBuilder.build())
}
}
@ -308,7 +298,7 @@ class MessageNotificationWorker(
networkComponents.getImageLoader(signatureVerification.userEntity!!.toUser()).load(request)
} else {
notificationBuilder.setStyle(getStyle(decryptedPushMessage, conversationType, person.build(), style))
notificationBuilder.setStyle(getStyle(decryptedPushMessage, conversation.type!!, person.build(), style))
NotificationManagerCompat.from(applicationContext).notify(notificationId, notificationBuilder.build())
}
}

View File

@ -6,10 +6,8 @@ import android.app.Service
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.drawable.Drawable
import android.media.AudioAttributes
import android.media.AudioManager
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
import android.util.Base64
import android.util.Log
@ -24,9 +22,8 @@ import coil.target.Target
import coil.transform.CircleCropTransformation
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.events.CallEvent
import com.nextcloud.talk.jobs.MessageNotificationWorker
import com.nextcloud.talk.jobs.NotificationWorker
import com.nextcloud.talk.models.SignatureVerification
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.ConversationOverall
@ -56,7 +53,6 @@ import org.greenrobot.eventbus.EventBus
import org.koin.core.KoinComponent
import org.koin.core.inject
import org.koin.core.parameter.parametersOf
import org.parceler.Parcels
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.security.PrivateKey
@ -124,22 +120,10 @@ class CallService : Service(), KoinComponent, CoroutineScope {
deleteAll -> {
NotificationUtils.cancelAllNotificationsForAccount(applicationContext, signatureVerification.userEntity!!)
}
type == "call" -> {
type == "call" && (conversation?.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION || conversation?.objectType.equals("share:password")) -> {
if (conversation != null) {
val generatedActiveNotificationId = signatureVerification.userEntity!!.id.toString() + "@" + decryptedPushMessage.notificationId!!.toString()
val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java)
fullScreenIntent.action = BundleKeys.KEY_OPEN_INCOMING_CALL
val bundle = Bundle()
bundle.putParcelable(BundleKeys.KEY_CONVERSATION, Parcels.wrap(conversation))
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, signatureVerification.userEntity)
bundle.putString(BundleKeys.KEY_ACTIVE_NOTIFICATION, generatedActiveNotificationId)
fullScreenIntent.putExtras(bundle)
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
val fullScreenPendingIntent = PendingIntent.getActivity(this@CallService, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST)
val fullScreenPendingIntent = NotificationUtils.getIncomingCallIPendingIntent(applicationContext, this@CallService, conversation, signatureVerification.userEntity!!, generatedActiveNotificationId)
val soundUri = NotificationUtils.getCallSoundUri(applicationContext, appPreferences)
val vibrationEffect = NotificationUtils.getVibrationEffect(appPreferences)
@ -148,23 +132,17 @@ class CallService : Service(), KoinComponent, CoroutineScope {
.getString(R.string.nc_notification_channel_calls), applicationContext.resources
.getString(R.string.nc_notification_channel_calls_description), true,
NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!,
audioAttributesBuilder.build(), vibrationEffect, false, null)
NotificationUtils.getCallAudioAttributes(true), vibrationEffect, false, null)
val userBaseUrl = Uri.parse(signatureVerification.userEntity!!.baseUrl).toString()
val uri: Uri = Uri.parse(signatureVerification.userEntity?.baseUrl)
var baseUrl = uri.host
var largeIcon = when (conversation.type) {
Conversation.ConversationType.PUBLIC_CONVERSATION -> {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_link_black_24px)
}
Conversation.ConversationType.GROUP_CONVERSATION -> {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_people_group_black_24px)
}
else -> {
// one to one and unknown
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_user)
}
if (baseUrl == null) {
baseUrl = signatureVerification.userEntity?.baseUrl
}
var largeIcon = BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_baseline_person_black_24)
val rejectCallIntent = Intent(this@CallService, CallService::class.java)
rejectCallIntent.action = BundleKeys.KEY_REJECT_INCOMING_CALL
rejectCallIntent.putExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION, generatedActiveNotificationId)
@ -174,7 +152,7 @@ class CallService : Service(), KoinComponent, CoroutineScope {
.setCategory(NotificationCompat.CATEGORY_CALL)
.setSmallIcon(R.drawable.ic_call_black_24dp)
.setLargeIcon(largeIcon)
.setSubText(userBaseUrl)
.setSubText(baseUrl)
.setShowWhen(true)
.setWhen(System.currentTimeMillis())
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage.subject.toString()))
@ -189,32 +167,28 @@ class CallService : Service(), KoinComponent, CoroutineScope {
notificationBuilder.setVibrate(vibrationEffect)
}
if (conversation.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION) {
val target = object : Target {
override fun onSuccess(result: Drawable) {
super.onSuccess(result)
largeIcon = result.toBitmap()
notificationBuilder.setLargeIcon(largeIcon)
showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
override fun onError(error: Drawable?) {
super.onError(error)
showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
val target = object : Target {
override fun onSuccess(result: Drawable) {
super.onSuccess(result)
largeIcon = result.toBitmap()
notificationBuilder.setLargeIcon(largeIcon)
showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
val avatarUrl = ApiUtils.getUrlForAvatarWithName(signatureVerification.userEntity!!.baseUrl, conversation.name, R.dimen.avatar_size)
val imageLoader = networkComponents.getImageLoader(signatureVerification.userEntity!!.toUser())
val request = Images().getRequestForUrl(
imageLoader, applicationContext, avatarUrl, signatureVerification.userEntity,
target, null, CircleCropTransformation())
imageLoader.load(request)
} else {
showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
override fun onError(error: Drawable?) {
super.onError(error)
showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
}
val avatarUrl = ApiUtils.getUrlForAvatarWithName(signatureVerification.userEntity!!.baseUrl, conversation.name, R.dimen.avatar_size)
val imageLoader = networkComponents.getImageLoader(signatureVerification.userEntity!!.toUser())
val request = Images().getRequestForUrl(
imageLoader, applicationContext, avatarUrl, signatureVerification.userEntity,
target, null, CircleCropTransformation())
imageLoader.load(request)
}
}
else -> {
@ -224,8 +198,9 @@ class CallService : Service(), KoinComponent, CoroutineScope {
val messageData = Data.Builder()
.putString(BundleKeys.KEY_DECRYPTED_PUSH_MESSAGE, LoganSquare.serialize(decryptedPushMessage))
.putString(BundleKeys.KEY_SIGNATURE_VERIFICATION, json.stringify(SignatureVerification.serializer(), signatureVerification))
.putString(BundleKeys.KEY_CONVERSATION, json.stringify(Conversation.serializer(), conversation))
.build()
val pushNotificationWork = OneTimeWorkRequest.Builder(MessageNotificationWorker::class.java).setInputData(messageData).build()
val pushNotificationWork = OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData).build()
WorkManager.getInstance().enqueue(pushNotificationWork)
}
}

View File

@ -21,23 +21,26 @@
package com.nextcloud.talk.utils
import android.annotation.TargetApi
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.app.*
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import androidx.core.app.NotificationManagerCompat
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.models.RingtoneSettings
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.preferences.AppPreferences
import org.parceler.Parcels
import java.io.IOException
import java.util.*
@ -49,6 +52,44 @@ object NotificationUtils {
val NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3"
val NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3"
fun getNotificationPendingIntent(applicationContext: Context, internalUserId: Long, conversationToken: String): PendingIntent {
val intent = Intent(applicationContext, MainActivity::class.java)
intent.action = BundleKeys.KEY_OPEN_CONVERSATION
intent.putExtra(BundleKeys.KEY_INTERNAL_USER_ID, internalUserId)
intent.putExtra(BundleKeys.KEY_CONVERSATION_TOKEN, conversationToken)
return PendingIntent.getActivity(applicationContext, 0, intent, 0)
}
fun getIncomingCallIPendingIntent(applicationContext: Context, serviceContext: Context, conversation: Conversation, user: UserNgEntity, notificationId: String?): PendingIntent {
val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java)
fullScreenIntent.action = BundleKeys.KEY_OPEN_INCOMING_CALL
val bundle = Bundle()
bundle.putParcelable(BundleKeys.KEY_CONVERSATION, Parcels.wrap(conversation))
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user)
if (notificationId != null) {
bundle.putString(BundleKeys.KEY_ACTIVE_NOTIFICATION, notificationId)
}
fullScreenIntent.putExtras(bundle)
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
return PendingIntent.getActivity(serviceContext, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
}
fun getCallAudioAttributes(isCall: Boolean): AudioAttributes {
return if (isCall) {
val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST)
audioAttributesBuilder.build()
} else {
val audioAttributesBuilder: AudioAttributes.Builder =
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)
audioAttributesBuilder.build()
}
}
fun getVibrationEffect(appPreferences: AppPreferences): LongArray? {
return if (appPreferences.shouldVibrateSetting) {
longArrayOf(0L, 400L, 800L, 600L, 800L, 800L, 800L, 1000L)

View File

@ -29,7 +29,6 @@ object BundleKeys {
val KEY_BASE_URL = "KEY_BASE_URL"
val KEY_IS_ACCOUNT_IMPORT = "KEY_IS_ACCOUNT_IMPORT"
val KEY_ORIGINAL_PROTOCOL = "KEY_ORIGINAL_PROTOCOL"
val KEY_ROOM = "KEY_CONVERSATION"
val KEY_OPERATION_CODE = "KEY_OPERATION_CODE"
val KEY_SHARE_INTENT = "KEY_SHARE_INTENT"
val KEY_APP_ITEM_PACKAGE_NAME = "KEY_APP_ITEM_PACKAGE_NAME"

View File

@ -0,0 +1,27 @@
<!--
~ /*
~ * Nextcloud Talk application
~ *
~ * @author Mario Danic
~ * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
~ *
~ * This program is free software: you can redistribute it and/or modify
~ * it under the terms of the GNU General Public License as published by
~ * the Free Software Foundation, either version 3 of the License, or
~ * at your option) any later version.
~ *
~ * This program is distributed in the hope that it will be useful,
~ * but WITHOUT ANY WARRANTY; without even the implied warranty of
~ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ * GNU General Public License for more details.
~ *
~ * You should have received a copy of the GNU General Public License
~ * along with this program. If not, see <http://www.gnu.org/licenses/>.
~ */
-->
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/black" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>