TP-41986 | User confirmation before sending attachment (#8245)

Co-authored-by: Shivam Goyal <shivam.goyal@navi.com>
This commit is contained in:
Varun Jain
2023-10-23 18:54:01 +05:30
committed by GitHub
parent d8a3bb6dc1
commit e716bd030d
48 changed files with 1264 additions and 134 deletions

View File

@@ -48,7 +48,7 @@ awaitility = "4.1.0"
branch = "5.1.1"
cashfree = "2.0.6"
chucker = "4.0.0"
coilCompose = "2.4.0"
coil = "2.4.0"
compose-bom = "2023.09.01"
compose-lib = '1.5.3'
delight-advancedWebView = "v3.0.0"
@@ -226,8 +226,8 @@ cashfree = { module = "com.cashfree.pg:api", version.ref = "cashfree" }
chucker-library = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" }
chucker-libraryNoOp = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
coil-video = { module = "io.coil-kt:coil-video", version.ref = "coil" }
dagger-hiltAndroid = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
dagger-hiltAndroidCompiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
dagger-hiltAndroidTesting = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" }

View File

@@ -55,6 +55,8 @@ dependencies {
implementation libs.android.material
implementation libs.coil.video
implementation libs.dagger.hiltAndroid
kapt libs.dagger.hiltCompiler

View File

@@ -48,6 +48,11 @@
android:exported="false"
android:screenOrientation="portrait"
android:theme="@style/BaseThemeStyle" />
<activity
android:name=".ui.activities.AttachmentConfirmationActivity"
android:exported="false"
android:screenOrientation="portrait"
android:theme="@style/BaseThemeStyle" />
</application>
</manifest>

View File

@@ -7,10 +7,21 @@
package com.navi.chat.db.utils
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.OpenableColumns
import android.webkit.MimeTypeMap
import com.google.gson.Gson
import com.navi.chat.utils.TIME_FORMAT_TO_DISPLAY
import com.navi.chat.utils.USER_SENDER_TYPE
import com.navi.chat.utils.maxFileSizeInKb
import com.navi.common.utils.log
import com.navi.naviwidgets.models.NaviChatWidget
import com.navi.naviwidgets.models.response.*
import java.io.File
fun transformFirestoreToWidgetModel(data: Map<String, Any>): NaviChatWidget =
when (data["widget_name"]) {
@@ -105,4 +116,92 @@ fun getTimerExpiredResponse(timerExpired: Boolean, senderName: String) =
senderName = senderName
),
shouldDisplayInChatHistory = false
)
)
fun getMimeType(context: Context, uri: Uri): String? {
return context.contentResolver.getType(uri)
}
fun timeInString(seconds: Int): String {
return String.format(
TIME_FORMAT_TO_DISPLAY, (seconds / 3600 * 60 + ((seconds % 3600) / 60)), (seconds % 60)
)
}
@SuppressLint("Range")
fun getFileName(contentResolver: ContentResolver, uri: Uri): String {
val uriString = uri.toString()
var displayName = ""
if (uriString.startsWith("content://")) {
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
} finally {
cursor?.close()
}
} else if (uriString.startsWith("file://")) {
displayName = File(uriString).name
}
return displayName
}
fun isFileTooLarge(
contentResolver: ContentResolver,
uri: Uri,
fileExtension: String?,
maxFileSizeMap: Map<String, Long>?
): Boolean {
val fileSizeInKb = getFileSizeInBytes(contentResolver, uri)
val maxSize = fileExtension?.let { maxFileSizeMap?.get(it.uppercase()) } ?: maxFileSizeInKb.toLong()
return fileSizeInKb > maxSize * 1024
}
private fun getFileSizeInBytes(contentResolver: ContentResolver, uri: Uri): Long {
var fileSize: Long = 0
if (uri.scheme == "content") {
val cursor = contentResolver.query(uri, null, null, null, null)
cursor?.use {
if (it.moveToFirst()) {
val sizeIndex = it.getColumnIndex(OpenableColumns.SIZE)
if (sizeIndex != -1) {
fileSize = it.getLong(sizeIndex)
}
}
}
} else if (uri.scheme == "file") {
try {
val file = uri.path?.let { File(it) }
if (file != null) {
if (file.exists()) {
fileSize = file.length()
}
}
} catch (e: Exception) {
e.log()
}
}
return fileSize
}
fun stringToMap(data: String): Map<String, Long> {
val keyValuePairs = data.removeSurrounding("{", "}").split(", ").map { it.split("=") }
return keyValuePairs.associate { (key, value) -> key to value.toLong() }
}
fun fileSizeWithinLimit(
context: Context,
selectedUri: Uri,
contentResolver: ContentResolver,
fileSizesMap: Map<String, Long>
): Boolean {
val mimeType = getMimeType(context, Uri.parse(selectedUri.toString()))
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)?.lowercase()
if (!isFileTooLarge(contentResolver, selectedUri, extension, fileSizesMap)) return true
return false
}

View File

@@ -0,0 +1,13 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.interfaces
interface AttachmentOptionClickListener {
fun onGalleryAttachmentOptionClicked()
fun onFileAttachmentOptionClicked()
}

View File

@@ -0,0 +1,69 @@
package com.navi.chat.ui.activities
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.compose.setContent
import com.navi.chat.db.utils.getFileName
import com.navi.chat.ui.compose.AttachmentPickerScreen
import com.navi.chat.utils.FILENAME
import com.navi.chat.utils.FILE_SIZES
import com.navi.chat.utils.FILE_TYPE_PARAM
import com.navi.chat.utils.FILE_URI
import com.navi.chat.utils.NAVI_CHAT_ATTACHMENT_CONFIRMATION_ACTIVITY
import com.navi.chat.utils.URI_LIST
import com.navi.common.model.ModuleNameV2
import com.navi.common.ui.activity.BaseActivity
class AttachmentConfirmationActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val data = intent.getStringExtra(FILE_TYPE_PARAM)
val fileSizes = intent.getStringExtra(FILE_SIZES)
setContent {
AttachmentPickerScreen(
onBackPress = { onBackPressed() },
onSendClick = { selectedImages ->
val intent = createMyIntent(selectedImages)
startActivity(intent)
},
onPlayClick = { fileUri ->
val intent = Intent(this, NaviChatViewVideoActivity::class.java)
val bundle = Bundle()
bundle.putString(
FILENAME, getFileName(this.contentResolver, fileUri)
)
intent.putExtra(FILE_URI, fileUri.toString())
intent.putExtras(bundle)
this.startActivity(intent)
},
onImageClick = { fileUri ->
val intent = Intent(this, NaviChatViewImageActivity::class.java)
val bundle = Bundle()
bundle.putString(
FILENAME, getFileName(this.contentResolver, fileUri)
)
intent.putExtra(FILE_URI, fileUri.toString())
intent.putExtras(bundle)
this.startActivity(intent)
},
data,
fileSizes.toString()
)
}
}
private fun createMyIntent(selectedImages: List<Uri>): Intent {
val intent = Intent(this, NaviChatActivity::class.java)
intent.putParcelableArrayListExtra(URI_LIST, ArrayList(selectedImages))
setResult(RESULT_OK, intent)
finish()
return intent
}
override val screenName: String
get() = NAVI_CHAT_ATTACHMENT_CONFIRMATION_ACTIVITY
override val moduleName: ModuleNameV2
get() = ModuleNameV2.CHAT
}

View File

@@ -7,22 +7,49 @@
package com.navi.chat.ui.activities
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.navi.chat.R
import com.navi.chat.base.ChatBaseActivity
import com.navi.chat.databinding.ActivityNaviChatBinding
import com.navi.chat.db.utils.getFileName
import com.navi.chat.interfaces.AttachmentOptionClickListener
import com.navi.chat.models.NaviChatSystemLocalData
import com.navi.chat.ui.fragments.NaviChatFragment
import com.navi.chat.utils.CONVERSATION_ID_PARAM
import com.navi.chat.utils.DOCUMENT_ITEM
import com.navi.chat.utils.FILE_SIZES
import com.navi.chat.utils.FILE_TYPE_PARAM
import com.navi.chat.utils.GALLERY_ITEM
import com.navi.chat.utils.NAVI_CHAT_SYSTEM_LOCAL_DATA
import com.navi.chat.utils.URI_LIST
import com.navi.chat.viewmodels.AttachmentPickerViewModel
class NaviChatActivity : ChatBaseActivity() {
class NaviChatActivity : ChatBaseActivity() , AttachmentOptionClickListener{
private lateinit var binding: ActivityNaviChatBinding
private val currentScreenTag: String = NaviChatFragment.TAG
private var chatFragment: NaviChatFragment? = null
private val attachmentPickerViewModel by lazy {
ViewModelProvider(this)[AttachmentPickerViewModel::class.java]
}
private var resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
val receivedDataList: List<Uri>? = data?.getParcelableArrayListExtra(URI_LIST)
receivedDataList?.forEach { receivedData ->
attachmentPickerViewModel.setImagePickerData(
receivedData.toString(), getFileName(contentResolver, receivedData)
)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -88,4 +115,17 @@ class NaviChatActivity : ChatBaseActivity() {
var isChatActivityVisible: Boolean = false
private set
}
override fun onGalleryAttachmentOptionClicked() {
val intent = Intent(this, AttachmentConfirmationActivity::class.java)
intent.putExtra(FILE_TYPE_PARAM, GALLERY_ITEM)
intent.putExtra(FILE_SIZES, attachmentPickerViewModel.getFileSizes().toString())
resultLauncher.launch(intent)
}
override fun onFileAttachmentOptionClicked() {
val intent = Intent(this, AttachmentConfirmationActivity::class.java)
intent.putExtra(FILE_TYPE_PARAM, DOCUMENT_ITEM)
intent.putExtra(FILE_SIZES, attachmentPickerViewModel.getFileSizes().toString())
resultLauncher.launch(intent)
}
}

View File

@@ -10,6 +10,7 @@ package com.navi.chat.ui.activities
import android.os.Bundle
import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.databinding.DataBindingUtil
import com.bumptech.glide.Glide
import com.navi.chat.R
@@ -20,6 +21,7 @@ import com.navi.chat.di.dependencies.NaviChatModuleDependencies
import com.navi.chat.di.modules.NaviChatModule
import com.navi.chat.utils.ChatFileHelper
import com.navi.chat.utils.FILENAME
import com.navi.chat.utils.FILE_URI
import com.navi.chat.utils.ToolbarInteraction
import dagger.hilt.android.EntryPointAccessors
import java.io.File
@@ -71,8 +73,11 @@ class NaviChatViewImageActivity : ChatBaseActivity(), ToolbarInteraction {
val sd: File = this.cacheDir
val folder = File(sd, "/chat/")
val outputFile = File(folder, fileName)
val uri =
val uri = if (outputFile.exists()) {
FileProvider.getUriForFile(this, "${this.packageName}.fileprovider", outputFile)
} else {
intent.getStringExtra(FILE_URI)?.toUri()
}
Glide.with(this).load(uri).into(binding.ivImageAttachment)
} else {
Toast.makeText(this, getString(R.string.file_cannot_be_opened), Toast.LENGTH_LONG)

View File

@@ -19,6 +19,7 @@ import android.view.*
import android.widget.SeekBar
import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import com.navi.chat.R
@@ -228,10 +229,15 @@ class NaviChatViewVideoActivity :
val sd: File = this.cacheDir
val folder = File(sd, "/chat/")
val outputFile = File(folder, it)
val uri =
val uri = if (outputFile.exists()) {
FileProvider.getUriForFile(this, "${this.packageName}.fileprovider", outputFile)
} else {
intent.getStringExtra(FILE_URI)?.toUri()
}
mediaPlayer?.apply {
setDataSource(applicationContext, uri)
if (uri != null) {
setDataSource(applicationContext, uri)
}
}
mediaPlayer?.setOnPreparedListener(this)
mediaPlayer?.setOnCompletionListener(this)
@@ -304,12 +310,12 @@ class NaviChatViewVideoActivity :
private fun pauseMediaPlayer() {
mediaPlayer?.pause()
binding.ivPlayPauseVideo.setImageResource(R.drawable.ic_play_video)
binding.ivPlayPauseVideo.setImageResource(R.drawable.ic_play_video_purple)
}
private fun startMediaPlayer() {
mediaPlayer?.start()
binding.ivPlayPauseVideo.setImageResource(R.drawable.ic_pause_icon)
binding.ivPlayPauseVideo.setImageResource(R.drawable.ic_pause_icon_purple)
}
@SuppressLint("ClickableViewAccessibility")
@@ -352,7 +358,7 @@ class NaviChatViewVideoActivity :
// Go to start when video completes
override fun onCompletion(mp: MediaPlayer?) {
setVideoPlayerControlsVisibility(true)
binding.ivPlayPauseVideo.setImageResource(R.drawable.ic_play_video)
binding.ivPlayPauseVideo.setImageResource(R.drawable.ic_play_video_purple)
mediaPlayerSeekTo(0)
}

View File

@@ -0,0 +1,34 @@
package com.navi.chat.ui.components
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.CallSuper
import com.navi.chat.utils.MIME_TYPE_DOCX
import com.navi.chat.utils.MIME_TYPE_DOC
import com.navi.chat.utils.MIME_TYPE_PDF
class FileDocument : ActivityResultContract<Array<String>, Uri?>() {
@CallSuper
override fun createIntent(context: Context, input: Array<String>): Intent {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = MIME_TYPE_PDF
intent.putExtra(
Intent.EXTRA_MIME_TYPES, arrayOf(
MIME_TYPE_PDF, MIME_TYPE_DOC, MIME_TYPE_DOCX
)
)
return intent
}
override fun getSynchronousResult(
context: Context, input: Array<String>
): SynchronousResult<Uri?>? = null
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
return intent.takeIf { resultCode == Activity.RESULT_OK }?.data
}
}

View File

@@ -0,0 +1,29 @@
package com.navi.chat.ui.components
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.CallSuper
import com.navi.chat.utils.IMAGE_TYPE
import com.navi.chat.utils.VIDEO_TYPE
class GalleryDocument : ActivityResultContract<Array<String>, Uri?>() {
@CallSuper
override fun createIntent(context: Context, input: Array<String>): Intent {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = IMAGE_TYPE
intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf(IMAGE_TYPE, VIDEO_TYPE))
return intent
}
override fun getSynchronousResult(
context: Context, input: Array<String>
): SynchronousResult<Uri?>? = null
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
return intent.takeIf { resultCode == Activity.RESULT_OK }?.data
}
}

View File

@@ -0,0 +1,417 @@
package com.navi.chat.ui.compose
import android.annotation.SuppressLint
import android.content.Intent
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.decode.VideoFrameDecoder
import coil.request.ImageRequest
import com.navi.chat.R
import com.navi.chat.db.utils.fileSizeWithinLimit
import com.navi.chat.db.utils.getFileName
import com.navi.chat.db.utils.getMimeType
import com.navi.chat.db.utils.stringToMap
import com.navi.chat.db.utils.timeInString
import com.navi.chat.ui.components.FileDocument
import com.navi.chat.ui.components.GalleryDocument
import com.navi.chat.ui.theme.NaviChatColor
import com.navi.chat.utils.MIME_TYPE_DOCX
import com.navi.chat.utils.MIME_TYPE_DOC
import com.navi.chat.utils.GALLERY_ITEM
import com.navi.chat.utils.IMAGE_TYPE
import com.navi.chat.utils.MIME_TYPE_IMAGE
import com.navi.chat.utils.MIME_TYPE_PDF
import com.navi.chat.utils.MIME_TYPE_VIDEO
import com.navi.chat.utils.VIDEO_TYPE
import com.navi.chat.utils.maxFileLimit
import com.navi.design.theme.composeFontFamily
import com.navi.design.theme.ttComposeFontFamily
import kotlinx.coroutines.launch
@Composable
fun AttachmentPickerScreen(
onBackPress: () -> Unit = {},
onSendClick: (selectedImages: MutableList<Uri>) -> Unit,
onPlayClick: (fileUri: Uri) -> Unit,
onImageClick: (fileUri: Uri) -> Unit,
data: String?,
fileSizes: String,
) {
val selectedImages = remember { mutableStateListOf<Uri>() }
val primaryButtonEnabled = remember { mutableStateOf(false) }
Scaffold(modifier = Modifier.fillMaxSize(), topBar = {
NaviChatHeader(
title = stringResource(id = R.string.navi_chat_toolbar_title),
onNavigationIconClick = { onBackPress() },
modifier = Modifier
)
}, bottomBar = {
NaviChatFooter(
secondaryButtonText = stringResource(id = R.string.cancel),
primaryButtonText = stringResource(id = R.string.send),
onSecondaryButtonClicked = { onBackPress() },
onPrimaryButtonClicked = { onSendClick(selectedImages) },
primaryButtonEnabled = primaryButtonEnabled.value
)
}, content = { paddingValues ->
NaviChatAttachmentScreenBody(
selectedImages,
data,
paddingValues,
fileSizes,
onPlayClick = { uri -> onPlayClick(uri) },
onImageClick = { uri -> onImageClick(uri) },
primaryButtonEnabled = primaryButtonEnabled
)
})
}
@Composable
fun NaviChatAttachmentScreenBody(
selectedImages: MutableList<Uri>,
data: String?,
padding: PaddingValues,
fileSizes: String,
onPlayClick: (fileUri: Uri) -> Unit,
onImageClick: (fileUri: Uri) -> Unit,
primaryButtonEnabled: MutableState<Boolean>
) {
val context = LocalContext.current
val contentResolver = context.contentResolver
val fileSizesMap = stringToMap(fileSizes)
primaryButtonEnabled.value = selectedImages.size != 0
val photoPickerLauncher = rememberLauncherForActivityResult(
contract = GalleryDocument()
) { selectedUri: Uri? ->
if (selectedUri != null) {
if (fileSizeWithinLimit(context, selectedUri, contentResolver, fileSizesMap)) {
selectedImages.add(selectedUri)
} else Toast.makeText(
context, R.string.file_size_exceeded, Toast.LENGTH_SHORT
).show()
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
contentResolver?.takePersistableUriPermission(selectedUri, takeFlags)
}
}
val documentPickerLauncher = rememberLauncherForActivityResult(
contract = FileDocument()
) { selectedUri: Uri? ->
if (selectedUri != null) {
if (fileSizeWithinLimit(context, selectedUri, contentResolver, fileSizesMap)) {
selectedImages.add(selectedUri)
} else Toast.makeText(
context, R.string.file_size_exceeded, Toast.LENGTH_SHORT
).show()
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
contentResolver?.takePersistableUriPermission(selectedUri, takeFlags)
}
}
val lazyListState = rememberLazyGridState()
val scope = rememberCoroutineScope()
fun scrollToLastItem() {
val lastIndex = selectedImages.size - 1
if (lastIndex >= 0) {
scope.launch {
lazyListState.animateScrollToItem(lastIndex)
}
}
}
LaunchedEffect(selectedImages.size) {
scrollToLastItem()
}
LazyVerticalGrid(
columns = GridCells.Fixed(2),
state = lazyListState,
modifier = Modifier
.fillMaxSize()
.padding(
bottom = padding.calculateBottomPadding(), top = 16.dp, start = 8.dp, end = 8.dp
)
) {
items(selectedImages.size + 1) { index ->
if (index < selectedImages.size) {
GalleryItem(selectedImages[index],
onRemoveClick = { selectedImages.removeAt(index) },
onPlayClick = { onPlayClick(selectedImages[index]) },
onImageClick = { onImageClick(selectedImages[index]) })
} else {
if (data == GALLERY_ITEM && selectedImages.size < maxFileLimit) {
AddMedia(photoPickerLauncher)
} else if (selectedImages.size < maxFileLimit) {
AddDocument(documentPickerLauncher)
}
}
}
}
}
@SuppressLint("SuspiciousIndentation")
@Composable
fun GalleryItem(
fileUri: Uri,
onRemoveClick: () -> Unit,
onPlayClick: (fileUri:Uri) -> Unit = {},
onImageClick: (fileUri:Uri) -> Unit = {}
) {
val context = LocalContext.current
val mimeType = getMimeType(context, Uri.parse(fileUri.toString()))
val allowedMimeTypes = listOf(
MIME_TYPE_PDF,
MIME_TYPE_DOC,
MIME_TYPE_DOCX,
)
Box(
modifier = Modifier
.padding(14.dp)
.background(Color.White)
.height(138.dp)
.border(1.dp, NaviChatColor.ctaPrimary, RoundedCornerShape(4.dp)),
) {
if (mimeType != null) {
if (mimeType.startsWith(MIME_TYPE_IMAGE)) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current).data(fileUri).allowHardware(false).build(),
contentDescription = "Image preview",
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clickable(interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { onImageClick(fileUri) }),
)
}
if (allowedMimeTypes.any { mimeType.startsWith(it) }) {
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
content = {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(id = R.drawable.ic_doc_preview),
contentDescription = "Document preview",
)
Spacer(modifier = Modifier.size(10.dp))
val fileName = getFileName(context.contentResolver, fileUri)
Text(
text = fileName,
fontSize = 12.sp,
lineHeight = 18.sp,
fontFamily = composeFontFamily,
color = Color.Black,
modifier = Modifier.padding(horizontal = 10.dp),
textAlign = TextAlign.Center,
maxLines = 2,
)
}
})
}
if (mimeType.startsWith(MIME_TYPE_VIDEO)) {
val imageLoader = ImageLoader.Builder(context).components {
add(VideoFrameDecoder.Factory())
}.crossfade(true).build()
val painter = rememberAsyncImagePainter(
model = fileUri,
imageLoader = imageLoader
)
Box(
modifier= Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { onPlayClick(fileUri) })
) {
Image(
painter = painter,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Image(
painter = painterResource(id = R.drawable.ic_play_video_button),
contentDescription = "Play video",
modifier = Modifier.align(Alignment.Center)
)
val retriever = MediaMetadataRetriever()
retriever.setDataSource(context, fileUri)
val videoDurationString =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
val videoDuration = videoDurationString?.toLong()
retriever.release()
Box(
modifier = Modifier
.fillMaxSize()
.padding(top = 115.dp, end = 2.dp),
contentAlignment = Alignment.BottomEnd
) {
val videoDurationInSeconds =
timeInString((videoDuration?.div(1000))?.toInt() ?: 0)
Text(
text = videoDurationInSeconds,
color = Color.White,
fontSize = 12.sp,
modifier = Modifier.padding(4.dp)
)
}
}
}
}
}
Box(
modifier = Modifier
.offset(8.dp, (-12).dp)
.fillMaxSize(),
contentAlignment = Alignment.TopEnd
) {
IconButton(
onClick = onRemoveClick
) {
Image(
painter = painterResource(id = R.drawable.ic_remove_image),
contentDescription = "Remove File",
)
}
}
}
@Composable
fun AddMedia(photoPickerLauncher: ManagedActivityResultLauncher<Array<String>, Uri?>)
{
Box(modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() }, indication = null
) {
photoPickerLauncher.launch(
arrayOf(
IMAGE_TYPE,
VIDEO_TYPE,
)
)
}
.padding(14.dp)
.border(
width = 1.dp, color = NaviChatColor.ctaPrimary, shape = RoundedCornerShape(size = 4.dp)
)
.height(138.dp), contentAlignment = Alignment.Center, content = {
Column(
modifier = Modifier.padding(start = 15.dp, end = 15.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(id = R.drawable.ic_add_image),
contentDescription = stringResource(id = R.string.add_media)
)
Spacer(modifier = Modifier.size(8.dp))
Text(
text = stringResource(id = R.string.add_media),
fontSize = 16.sp,
lineHeight = 24.sp,
fontFamily = ttComposeFontFamily,
fontWeight = FontWeight(600),
color = NaviChatColor.textSecondary,
)
}
})
}
@Composable
fun AddDocument(documentPickerLauncher: ManagedActivityResultLauncher<Array<String>, Uri?>)
{
Box(modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() }, indication = null
) {
documentPickerLauncher.launch(
arrayOf(
MIME_TYPE_PDF,
MIME_TYPE_DOC,
MIME_TYPE_DOCX,
)
)
}
.padding(14.dp)
.border(
width = 1.dp, color = NaviChatColor.ctaPrimary, shape = RoundedCornerShape(size = 4.dp)
)
.height(138.dp), contentAlignment = Alignment.Center, content = {
Column(
modifier = Modifier.padding(start = 15.dp, end = 15.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(id = R.drawable.ic_add_image),
contentDescription = stringResource(id = R.string.add_attachment),
)
Spacer(modifier = Modifier.size(8.dp))
Text(
text = stringResource(id = R.string.add_attachment),
fontSize = 16.sp,
lineHeight = 24.sp,
fontFamily = ttComposeFontFamily,
fontWeight = FontWeight(600),
color = NaviChatColor.textSecondary,
textAlign = TextAlign.Center
)
}
})
}

View File

@@ -0,0 +1,171 @@
package com.navi.chat.ui.compose
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.navi.chat.R
import com.navi.chat.ui.theme.NaviChatColor
import com.navi.common.utils.ClickDebounce
import com.navi.common.utils.get
import com.navi.design.font.FontWeightEnum
import com.navi.design.theme.getFontWeight
import com.navi.design.theme.ttComposeFontFamily
@Composable
fun NaviChatHeader(
title: String,
navigationIcon: Int = R.drawable.ic_chat_back_arrow,
onNavigationIconClick: (() -> Unit),
modifier: Modifier,
backgroundColor: Color = Color.White
) {
TopAppBar(title = {
Text(
text = title,
fontSize = 14.sp,
fontFamily = ttComposeFontFamily,
fontWeight = getFontWeight(FontWeightEnum.TT_REGULAR),
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(end = 60.dp)
)
}, navigationIcon = {
IconButton(onClick = { onNavigationIconClick.invoke() }) {
Image(painter = painterResource(navigationIcon), contentDescription = null)
}
}, modifier = modifier.height(48.dp),
elevation = 1.dp,
backgroundColor = backgroundColor
)
}
@Composable
fun NaviChatFooter(
secondaryButtonText: String,
primaryButtonText: String,
onSecondaryButtonClicked: () -> Unit,
onPrimaryButtonClicked: () -> Unit,
primaryButtonEnabled: Boolean = true
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 32.dp)
.background(Color.White),
) {
Spacer(modifier = Modifier.height(24.dp))
Row {
SecondaryRoundedButton(
modifier = Modifier.weight(1f),
text = secondaryButtonText
) {
onSecondaryButtonClicked.invoke()
}
Spacer(modifier = Modifier.width(16.dp))
ThemeRoundedButton(
modifier = Modifier.weight(1f),
text = primaryButtonText,
enabled = primaryButtonEnabled
) {
onPrimaryButtonClicked.invoke()
}
}
}
}
@Composable
fun ThemeRoundedButton(
modifier: Modifier = Modifier,
text: String,
cornerRadius: Dp = 4.dp,
paddingValues: PaddingValues = PaddingValues(vertical = 14.dp, horizontal = 10.dp),
fontSize: TextUnit = 14.sp,
enabled: Boolean = true,
onClick: () -> Unit,
) {
val clickDebounce = remember { ClickDebounce.get() }
Button(
elevation = ButtonDefaults.elevation(defaultElevation = 0.dp),
onClick = { clickDebounce.processClick { onClick() } },
modifier = modifier,
shape = RoundedCornerShape(cornerRadius),
contentPadding = paddingValues,
enabled = enabled,
colors = ButtonDefaults.buttonColors(
backgroundColor = NaviChatColor.ctaPrimary,
disabledBackgroundColor = NaviChatColor.ctaDisabled
)
) {
Text(
text = text,
fontSize = fontSize,
fontFamily = ttComposeFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
color = NaviChatColor.textWhite,
letterSpacing = 0.sp
)
}
}
@Composable
fun SecondaryRoundedButton(
modifier: Modifier = Modifier,
text: String,
cornerRadius: Dp = 4.dp,
paddingValues: PaddingValues = PaddingValues(vertical = 14.dp, horizontal = 10.dp),
onClick: () -> Unit
) {
val clickDebounce = remember { ClickDebounce.get() }
Button(
elevation = ButtonDefaults.elevation(defaultElevation = 0.dp),
onClick = { clickDebounce.processClick { onClick() } },
modifier = modifier,
shape = RoundedCornerShape(cornerRadius),
colors = ButtonDefaults.buttonColors(
backgroundColor = NaviChatColor.ctaSecondary
),
contentPadding = paddingValues
) {
Text(
text = text,
fontSize = 14.sp,
fontFamily = ttComposeFontFamily,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
color = NaviChatColor.ctaPrimary,
letterSpacing = 0.sp
)
}
}

View File

@@ -9,19 +9,17 @@ package com.navi.chat.ui.fragments
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.OpenableColumns
import android.view.View
import android.view.ViewStub
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.CallSuper
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
@@ -35,6 +33,7 @@ import com.navi.chat.databinding.ChatAttachmentBottomSheetBinding
import com.navi.chat.di.components.DaggerNaviChatComponent
import com.navi.chat.di.dependencies.NaviChatModuleDependencies
import com.navi.chat.di.modules.NaviChatModule
import com.navi.chat.interfaces.AttachmentOptionClickListener
import com.navi.chat.interfaces.ChatAttachmentOptionsInterface
import com.navi.chat.models.ChatAttachmentData
import com.navi.chat.utils.FILE_MANAGER
@@ -43,7 +42,6 @@ import com.navi.chat.viewmodels.AttachmentPickerViewModel
import dagger.hilt.android.EntryPointAccessors
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterface {
@@ -53,11 +51,15 @@ class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterf
private val attachmentPickerViewModel by lazy {
ViewModelProvider(requireActivity())[AttachmentPickerViewModel::class.java]
}
private var attachmentOptionClickListener: AttachmentOptionClickListener? = null
override val screenName: String
get() = TAG
override fun onAttach(context: Context) {
super.onAttach(context)
attachmentOptionClickListener = context as? AttachmentOptionClickListener
}
override fun setContainerView(viewStub: ViewStub) {
viewStub.layoutResource = R.layout.chat_attachment_bottom_sheet
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
@@ -97,7 +99,7 @@ class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterf
private fun createAttachmentOptionList(): ArrayList<ChatAttachmentData> {
val attachmentDataArrayList = ArrayList<ChatAttachmentData>()
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_gallery_icon)?.let {
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_gallery_icon_new)?.let {
attachmentDataArrayList.add(
ChatAttachmentData(
it,
@@ -106,7 +108,7 @@ class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterf
)
)
}
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_file_manager_icon)?.let {
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_file_manager_icon_new)?.let {
attachmentDataArrayList.add(
ChatAttachmentData(
it,
@@ -122,44 +124,15 @@ class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterf
if (isLegacyExternalStoragePermissionRequired()) {
requestLegacyWriteExternalStoragePermission()
} else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
resultLauncher.launch(Intent.createChooser(intent, "Select image or video"))
attachmentOptionClickListener?.onGalleryAttachmentOptionClicked()
}
}
private var resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
attachmentPickerViewModel.setImagePickerData(
result.data, result.data?.data?.let { getFileName(it) }
)
}
}
private fun openFile() {
if (isLegacyExternalStoragePermissionRequired()) {
requestLegacyWriteExternalStoragePermission()
} else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/*"
putExtra(
Intent.EXTRA_MIME_TYPES,
arrayOf(
DOC,
DOCX,
PDF
)
)
putExtra(
Intent.ACTION_OPEN_DOCUMENT,
requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
)
}
resultLauncher1.launch(intent)
attachmentOptionClickListener?.onFileAttachmentOptionClicked()
}
}
@@ -183,36 +156,6 @@ class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterf
}
}
private var resultLauncher1 =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
attachmentPickerViewModel.setPdfPickerData(
result.data, result.data?.data?.let { getFileName(it) }
)
}
}
@SuppressLint("Range")
private fun getFileName(uri: Uri): String {
val uriString = uri.toString()
var displayName = ""
if (uriString.startsWith("content://")) {
var cursor: Cursor? = null
try {
cursor = activity?.contentResolver?.query(uri, null, null, null, null)
if (cursor != null && cursor.moveToFirst()) {
displayName =
cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
} finally {
cursor?.close()
}
} else if (uriString.startsWith("file://")) {
displayName = File(uriString).name
}
return displayName
}
override fun onClickChatAttachmentOption(attachmentsType: String) {
when (attachmentsType) {
GALLERY -> {
@@ -233,4 +176,4 @@ class ChatAttachmentBottomSheet : BaseBottomSheet(), ChatAttachmentOptionsInterf
const val DOC = "application/msword"
const val DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
}
}
}

View File

@@ -380,6 +380,7 @@ class NaviChatFragment : ChatBaseFragment(), WidgetCallback, MessageOperation, T
fileSizes.clear()
it?.let {
fileSizes.putAll(it.fileSizeInKB.orEmpty())
it.fileSizeInKB?.let { it1 -> attachmentPickerViewModel.setFileSizes(it1) }
it.botTypingCuesDelayInMs?.let { botDelayInMs ->
delayForTypingCuesForBot = botDelayInMs
Timber.d("Updated delayForTypingCuesForBot to $botDelayInMs")

View File

@@ -0,0 +1,11 @@
package com.navi.chat.ui.theme
import androidx.compose.ui.graphics.Color
object NaviChatColor {
val textSecondary = Color(0xFF444444)
val textWhite = Color(0xFFFFFFFF)
val ctaPrimary = Color(0xFF1F002A)
val ctaSecondary = Color(0xFFF5F5F5)
val ctaDisabled = Color(0xFFB5ACB9)
}

View File

@@ -50,6 +50,20 @@ const val BOT = "BOT"
const val DIGITAL_GOLD = "DIGITAL_GOLD"
const val FILENAME = "filename"
const val NODE_UUID_PARAM = "nodeUuid"
const val GALLERY_ITEM = "image"
const val DOCUMENT_ITEM = "document"
const val URI_LIST = "uriList"
const val FILE_SIZES = "fileSizes"
const val FILE_URI = "fileUri"
const val IMAGE_TYPE = "image/*"
const val VIDEO_TYPE = "video/*"
const val MIME_TYPE_IMAGE = "image/"
const val MIME_TYPE_VIDEO = "video/"
const val MIME_TYPE_PDF = "application/pdf"
const val MIME_TYPE_DOC = "application/msword"
const val MIME_TYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
const val maxFileLimit = 6
const val maxFileSizeInKb = 15000
/* Video Player Instance Save Constant */
const val CURRENT_VIDEO_POSITION_IN_MS = "currentVideoPositionInMs"
@@ -59,3 +73,4 @@ const val TIME_FORMAT_TO_DISPLAY = "%02d:%02d"
/* Screen names */
const val NAVI_CHAT_VIEW_VIDEO_ACTIVITY = "naviChatViewVideoActivity"
const val NAVI_CHAT_ATTACHMENT_CONFIRMATION_ACTIVITY = "naviChatAttachmentConfirmationActivity"

View File

@@ -12,6 +12,7 @@ import android.app.Application
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.core.net.toUri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.navi.chat.R
@@ -39,6 +40,7 @@ class AttachmentPickerViewModel(application: Application) : AndroidViewModel(app
private val defaultMaxFileSizeInMb = 15
var maxFileSizeInMb = defaultMaxFileSizeInMb
private var fileSizes = HashMap<String, Int>()
val dispatcher = CoroutineDispatcherProvider()
val fileHelper = ChatFileHelper(dispatcher)
private var isToastVisible = false
@@ -116,14 +118,21 @@ class AttachmentPickerViewModel(application: Application) : AndroidViewModel(app
}
}
fun setImagePickerData(data: Intent?, fileName: String?) {
fun setImagePickerData(data: String?, fileName: String?) {
viewModelScope.launch(dispatcher.io) {
val imageData = data?.data
imageData?.let { emitFileData(imageData, fileName, FileHelper.JPG_EXTENSION) }
data?.let { emitFileData(data.toUri(), fileName, FileHelper.JPG_EXTENSION) }
dismissDialog()
}
}
fun setFileSizes(sizes: HashMap<String, Int>) {
fileSizes = sizes
}
fun getFileSizes(): HashMap<String, Int> {
return fileSizes
}
private fun dismissDialog() {
viewModelScope.launch(dispatcher.main) { _dismissBottomSheetDialog.emit(true) }
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25dp"
android:height="24dp"
android:viewportWidth="25"
android:viewportHeight="24">
<path
android:pathData="M12.5,2C6.977,2 2.5,6.477 2.5,12C2.5,17.523 6.977,22 12.5,22C18.023,22 22.5,17.523 22.5,12C22.5,6.477 18.023,2 12.5,2ZM16.5,13H13.5V16H11.5V13H8.5V11H11.5V8H13.5V11H16.5V13Z"
android:fillColor="#1F002A"/>
</vector>

View File

@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="54dp"
android:height="54dp"
android:viewportWidth="54"
android:viewportHeight="54">
<path
android:pathData="M9.065,4.648C10.308,3.406 11.993,2.708 13.75,2.708H32.521C33.106,2.708 33.668,2.941 34.082,3.355L46.228,15.501C46.642,15.915 46.875,16.476 46.875,17.062V44.666C46.875,46.423 46.177,48.109 44.935,49.351C43.692,50.593 42.007,51.291 40.25,51.291H9.333C8.114,51.291 7.125,50.303 7.125,49.083C7.125,47.863 8.114,46.875 9.333,46.875H40.25C40.836,46.875 41.397,46.642 41.812,46.228C42.226,45.814 42.458,45.252 42.458,44.666V17.977L31.606,7.125H13.75C13.164,7.125 12.603,7.357 12.189,7.771C11.774,8.186 11.542,8.747 11.542,9.333V18.166C11.542,19.386 10.553,20.375 9.333,20.375C8.114,20.375 7.125,19.386 7.125,18.166V9.333C7.125,7.576 7.823,5.891 9.065,4.648Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M31.417,2.708C32.637,2.708 33.625,3.697 33.625,4.916V15.958H44.667C45.887,15.958 46.875,16.947 46.875,18.166C46.875,19.386 45.887,20.375 44.667,20.375H31.417C30.197,20.375 29.208,19.386 29.208,18.166V4.916C29.208,3.697 30.197,2.708 31.417,2.708Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M2.708,33.625C2.708,32.406 3.697,31.417 4.917,31.417H27C28.22,31.417 29.208,32.406 29.208,33.625C29.208,34.845 28.22,35.834 27,35.834H4.917C3.697,35.834 2.708,34.845 2.708,33.625Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M18.813,25.439C19.676,24.576 21.074,24.576 21.936,25.439L28.561,32.064C29.424,32.926 29.424,34.324 28.561,35.187L21.936,41.812C21.074,42.674 19.676,42.674 18.813,41.812C17.951,40.949 17.951,39.551 18.813,38.689L23.877,33.625L18.813,28.562C17.951,27.699 17.951,26.301 18.813,25.439Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M21.999,9.315V7.046C21.997,5.854 21.522,4.909 20.586,4.237C20.028,3.837 19.386,3.643 18.622,3.643H18.61C17.436,3.646 16.261,3.647 15.086,3.647C13.465,3.647 11.844,3.645 10.223,3.642C10.129,3.642 10.062,3.616 10.048,3.602C9.639,3.197 9.119,3 8.452,3C8.222,3 7.993,3 7.762,3H6.619C6.153,3 5.687,3 5.221,3.002C4.989,3.003 4.803,3.02 4.636,3.056C3.641,3.27 2.999,4.103 3,5.18C3.002,7.286 3.002,9.393 3.002,11.5C3.002,13.535 3.002,15.571 3.004,17.606C3.004,18.674 3.41,19.56 4.208,20.237C4.809,20.748 5.544,20.997 6.455,20.997H12.497C13.019,20.997 13.54,20.997 14.062,20.999C14.583,20.999 15.105,21 15.627,21C16.787,21 17.678,20.996 18.519,20.99C19.018,20.986 19.426,20.925 19.769,20.805C21.159,20.316 21.992,19.114 21.995,17.59C22.001,14.832 22,12.027 21.999,9.316V9.315ZM8.035,19.377C7.796,19.377 7.557,19.376 7.318,19.376C7.02,19.376 6.723,19.377 6.425,19.38H6.401C5.883,19.38 5.417,19.195 5.091,18.86C4.76,18.522 4.588,18.06 4.592,17.525C4.604,16.11 4.601,14.671 4.599,13.28C4.598,12.683 4.597,12.086 4.597,11.489C4.597,11.489 4.597,7.028 4.597,5.275C4.597,4.753 4.731,4.618 5.246,4.618H5.282C5.58,4.618 5.88,4.619 6.178,4.62C6.477,4.621 6.777,4.622 7.076,4.622C7.598,4.622 8.023,4.618 8.415,4.611H8.442C8.807,4.611 9.01,4.744 9.164,5.086C9.384,5.572 9.627,6.055 9.863,6.523C9.963,6.722 10.063,6.919 10.161,7.118C10.573,7.951 11.238,8.373 12.139,8.373H12.155C12.952,8.369 13.816,8.367 14.875,8.367C15.49,8.367 16.107,8.367 16.722,8.368C17.332,8.368 17.941,8.369 18.551,8.369C19.033,8.369 19.403,8.478 19.715,8.712C20.177,9.059 20.401,9.539 20.402,10.18C20.402,11.087 20.402,11.994 20.402,12.901C20.401,14.428 20.4,16.008 20.406,17.561C20.407,18.075 20.237,18.521 19.915,18.85C19.579,19.193 19.092,19.382 18.547,19.382H18.537C17.306,19.377 15.966,19.375 14.32,19.375C13.392,19.375 12.464,19.375 11.536,19.377C10.608,19.377 9.68,19.379 8.753,19.379C8.514,19.379 8.276,19.379 8.037,19.378L8.035,19.377ZM18.554,6.785C17.953,6.786 17.352,6.786 16.752,6.786H15.015C14.17,6.786 13.174,6.786 12.177,6.792C11.83,6.792 11.644,6.676 11.516,6.376C11.38,6.061 11.233,5.758 11.078,5.437C11.051,5.379 11.023,5.322 10.994,5.263C10.994,5.263 14.463,5.262 15.425,5.262C16.492,5.262 17.56,5.262 18.627,5.264C19.52,5.265 20.113,5.716 20.34,6.569C20.394,6.771 20.401,6.998 20.408,7.239V7.28C20.321,7.225 20.238,7.177 20.151,7.133C19.681,6.899 19.162,6.785 18.565,6.785H18.553L18.554,6.785Z"
android:fillColor="#1F002A"/>
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11.746,19.998C9.058,19.998 6.369,19.994 3.682,20.001C2.814,20.003 2.092,19.712 1.542,19.047C1.156,18.579 1.007,18.023 1.006,17.435C1,13.479 0.996,9.522 1.006,5.566C1.011,4.055 2.087,3.006 3.624,3.004C9.059,2.998 14.493,2.999 19.929,3.004C21.188,3.005 22.107,3.693 22.438,4.868C22.508,5.115 22.531,5.38 22.531,5.637C22.537,9.545 22.537,13.453 22.535,17.361C22.535,18.957 21.471,19.995 19.841,19.996C17.143,19.998 14.446,19.996 11.748,19.996L11.746,19.998ZM13.621,15.005C15.995,13.522 18.351,12.051 20.7,10.571C20.78,10.52 20.833,10.369 20.834,10.264C20.848,8.743 20.853,7.224 20.859,5.703C20.863,4.94 20.552,4.635 19.773,4.635C14.438,4.635 9.103,4.635 3.768,4.635C2.992,4.635 2.677,4.942 2.676,5.699C2.676,8.144 2.676,10.588 2.676,13.032C2.676,13.132 2.676,13.233 2.676,13.386C2.821,13.296 2.91,13.244 2.996,13.186C3.998,12.51 5.014,11.854 5.995,11.152C7.152,10.324 8.498,10.429 9.523,11.373C10.859,12.604 12.241,13.787 13.621,15.005ZM14.87,18.365C14.815,18.296 14.8,18.27 14.78,18.252C12.612,16.33 10.446,14.407 8.275,12.49C7.895,12.154 7.504,12.14 7.07,12.432C5.663,13.377 4.258,14.324 2.861,15.282C2.764,15.348 2.689,15.509 2.685,15.629C2.666,16.187 2.676,16.745 2.677,17.303C2.679,18.069 2.983,18.365 3.767,18.365C7.355,18.365 10.944,18.365 14.531,18.365H14.871H14.87ZM20.858,12.44C18.832,13.701 16.874,14.92 14.903,16.148C15.712,16.863 16.487,17.554 17.274,18.233C17.365,18.312 17.516,18.357 17.64,18.358C18.374,18.37 19.109,18.365 19.844,18.364C20.53,18.363 20.837,18.067 20.839,17.402C20.846,15.864 20.851,14.326 20.858,12.788C20.858,12.694 20.858,12.601 20.858,12.44Z"
android:fillColor="#1F002A"/>
<path
android:pathData="M16.25,8.81C16.272,10.232 15.083,11.424 13.626,11.441C12.201,11.456 11.002,10.3 10.958,8.872C10.917,7.479 12.141,6.258 13.593,6.245C15.035,6.232 16.226,7.384 16.248,8.81H16.25ZM13.614,7.892C13.049,7.894 12.64,8.306 12.646,8.867C12.65,9.365 13.082,9.794 13.578,9.791C14.147,9.789 14.577,9.384 14.585,8.843C14.594,8.326 14.147,7.889 13.614,7.892Z"
android:fillColor="#1F002A"/>
</vector>

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="53dp"
android:height="53dp"
android:viewportWidth="53"
android:viewportHeight="53">
<path
android:strokeWidth="1"
android:pathData="M26.5,26.5m-26,0a26,26 0,1 1,52 0a26,26 0,1 1,-52 0"
android:fillColor="#F9F9F9"
android:strokeColor="#EBEBEB"/>
<path
android:pathData="M22,34.375C23.237,34.375 24.25,33.362 24.25,32.125V20.875C24.25,19.638 23.237,18.625 22,18.625C20.763,18.625 19.75,19.638 19.75,20.875V32.125C19.75,33.362 20.763,34.375 22,34.375ZM28.75,20.875V32.125C28.75,33.362 29.763,34.375 31,34.375C32.237,34.375 33.25,33.362 33.25,32.125V20.875C33.25,19.638 32.237,18.625 31,18.625C29.763,18.625 28.75,19.638 28.75,20.875Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="54dp"
android:height="54dp"
android:viewportWidth="54"
android:viewportHeight="54">
<path
android:pathData="M15.75,13.802V40.199C15.75,42.211 18.065,43.434 19.849,42.339L41.512,29.14C43.162,28.147 43.162,25.853 41.512,24.834L19.849,11.661C18.065,10.566 15.75,11.789 15.75,13.802Z"
android:fillColor="#F9F9F9"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="98dp"
android:height="98dp"
android:viewportWidth="98"
android:viewportHeight="98">
<path
android:pathData="M46,46m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
android:fillColor="#1F002A"/>
<path
android:pathData="M41,40.134V51.866C41,52.761 42.029,53.304 42.822,52.817L52.45,46.951C53.183,46.51 53.183,45.49 52.45,45.037L42.822,39.183C42.029,38.696 41,39.239 41,40.134Z"
android:fillColor="#F5F5F5"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="53dp"
android:height="53dp"
android:viewportWidth="53"
android:viewportHeight="53">
<path
android:strokeWidth="1"
android:pathData="M26.5,26.5m-26,0a26,26 0,1 1,52 0a26,26 0,1 1,-52 0"
android:fillColor="#F9F9F9"
android:strokeColor="#EBEBEB"/>
<path
android:pathData="M20.059,18.944V34.057C20.059,35.21 21.385,35.91 22.406,35.283L34.809,27.726C35.754,27.157 35.754,25.844 34.809,25.261L22.406,17.719C21.385,17.092 20.059,17.792 20.059,18.944Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,0L12,0A12,12 0,0 1,24 12L24,12A12,12 0,0 1,12 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
android:fillColor="#ffffff"/>
<path
android:pathData="M12,3C7.029,3 3,7.029 3,12C3,16.971 7.029,21 12,21C16.971,21 21,16.971 21,12C21,7.029 16.971,3 12,3ZM1,12C1,5.925 5.925,1 12,1C18.075,1 23,5.925 23,12C23,18.075 18.075,23 12,23C5.925,23 1,18.075 1,12Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M15.707,8.293C16.098,8.683 16.098,9.317 15.707,9.707L9.707,15.707C9.317,16.098 8.683,16.098 8.293,15.707C7.902,15.317 7.902,14.683 8.293,14.293L14.293,8.293C14.683,7.902 15.317,7.902 15.707,8.293Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M8.293,8.293C8.683,7.902 9.317,7.902 9.707,8.293L15.707,14.293C16.098,14.683 16.098,15.317 15.707,15.707C15.317,16.098 14.683,16.098 14.293,15.707L8.293,9.707C7.902,9.317 7.902,8.683 8.293,8.293Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,12m0,12a12,12 0,1 1,0 -24a12,12 0,1 1,0 24"
android:fillColor="#F5F5F5"/>
<path
android:pathData="M16.465,12.019C16.465,12.368 16.316,12.667 16.066,12.867L10.529,17.806C10.13,18.105 9.531,18.055 9.232,17.656C8.932,17.257 8.932,16.708 9.331,16.409L14.22,12.118C14.27,12.068 14.27,12.019 14.22,11.919L9.331,7.628C8.932,7.279 8.882,6.73 9.232,6.331C9.581,5.932 10.13,5.882 10.529,6.232C10.529,6.232 10.529,6.232 10.579,6.281L16.116,11.17C16.316,11.37 16.465,11.719 16.465,12.019Z"
android:fillColor="#1F002A"/>
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M10,10m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0"
android:strokeWidth="2"
android:fillColor="#1F002A"
android:strokeColor="#ffffff"/>
</vector>

View File

@@ -57,7 +57,7 @@
android:id="@+id/ivPlayPauseVideo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_pause_icon"
android:src="@drawable/ic_pause_icon_purple"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintStart_toStartOf="parent"
@@ -84,8 +84,8 @@
app:layout_constraintEnd_toStartOf="@id/totalDuration"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/dp_34"
android:progressTint="@color/outrageous_orange"
android:thumb="@drawable/ic_tooltip_slider_thumb"
android:progressTint="@color/dark_purple"
android:thumb="@drawable/ic_tooltip_slider_purple"
app:layout_constraintHorizontal_bias="0.0"
tools:progress="50"
android:progressBackgroundTint="@color/white"

View File

@@ -23,7 +23,7 @@
android:layout_marginStart="@dimen/layout_dp_16"
android:layout_marginTop="@dimen/layout_dp_16"
android:textColor="@color/dark_gray"
android:fontFamily="@font/tt_regular"
android:fontFamily="@font/tt_medium"
android:textSize="@dimen/sp_18"
android:text="@string/add_files"
app:layout_constraintEnd_toEndOf="parent"

View File

@@ -26,6 +26,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvChat"
android:background="@color/white"
android:layout_width="match_parent"
android:layout_height="@dimen/layout_dp_0"
android:visibility="visible"
@@ -99,7 +100,8 @@
android:id="@+id/ivSendAttachment"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_9"
android:padding="@dimen/dp_7"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"

View File

@@ -25,7 +25,7 @@
app:layout_constraintEnd_toStartOf="@id/guideline1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_description"
tools:src="@drawable/ic_gallery_icon" />
tools:src="@drawable/ic_gallery_icon_new" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline1"
@@ -62,7 +62,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/layout_dp_32"
android:contentDescription="@string/right_arrow"
android:src="@drawable/ic_right_arrow"
android:src="@drawable/ic_right_arrow_purple_white_bg"
app:layout_constraintBottom_toBottomOf="@id/tv_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline2"

View File

@@ -13,6 +13,8 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:background="@color/white"
android:elevation="@dimen/dp_1"
android:layout_height="@dimen/layout_dp_48">
<androidx.constraintlayout.widget.ConstraintLayout

View File

@@ -17,6 +17,11 @@
<string name="error_playing_video">Error occurred while playing video</string>
<string name="error_while_calling_api">The link you are trying to access has expired.</string>
<string name="characters_left">%d/%d characters left</string>
<string name="cancel">Cancel</string>
<string name="send">Send</string>
<string name="add_attachment">Add Attachment</string>
<string name="add_media">Add Media</string>
<string name="file_size_exceeded">File size is too large</string>
<!--Default value-->
<string name="default_value">00:00</string>
</resources>

View File

@@ -84,7 +84,7 @@ class NaviChatReceivedMessageWithAttachmentLayout @JvmOverloads constructor(
when (state) {
VIEW_STATE -> {
tvReceivedMessage.progressImage.setBackgroundResource(R.drawable.ic_preview)
tvReceivedMessage.progressImage.setBackgroundResource(R.drawable.ic_preview_purple)
tvReceivedMessage.progressState.progress = ProgressBar.GONE
tvReceivedMessage.progressImage.setOnClickListener {
naviChatFileAttachmentWidget.widgetData?.fileName?.let { uri ->
@@ -95,7 +95,7 @@ class NaviChatReceivedMessageWithAttachmentLayout @JvmOverloads constructor(
}
}
DOWNLOAD_STATE, FAILED_STATE -> {
tvReceivedMessage.progressImage.setBackgroundResource(R.drawable.ic_download)
tvReceivedMessage.progressImage.setBackgroundResource(R.drawable.ic_download_purple)
tvReceivedMessage.progressState.progress = ProgressBar.GONE
tvReceivedMessage.progressImage.setOnClickListener {
naviChatFileAttachmentWidget.widgetData.let { widgetData ->
@@ -134,9 +134,9 @@ class NaviChatReceivedMessageWithAttachmentLayout @JvmOverloads constructor(
private fun getBackgroundDrawable(fileType: String?): Int {
return when (fileType) {
AWSFileType.MP4.value -> R.drawable.ic_video_preview
AWSFileType.MP4.value -> R.drawable.ic_video_preview_purple
AWSFileType.JPG.value, AWSFileType.JPEG.value,
AWSFileType.PNG.value -> R.drawable.ic_image_preview
AWSFileType.PNG.value -> R.drawable.ic_image_preview_purple
else -> R.drawable.ic_document_preview
}
}

View File

@@ -86,7 +86,11 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
when (state) {
VIEW_STATE -> {
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_preview)
if (naviChatFileAttachmentWidget.widgetData?.fileType == AWSFileType.MP4.value) {
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_play_preview_purple)
} else {
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_preview_purple)
}
tvSentMessage.progressState.progress = ProgressBar.GONE
tvSentMessage.progressImage.setOnClickListener {
naviChatFileAttachmentWidget.id?.let {
@@ -97,7 +101,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
}
FAILURE_AND_RETRY_STATE -> {
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_retry)
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_retry_new)
tvSentMessage.progressState.progress = ProgressBar.GONE
tvSentMessage.progressImage.setOnClickListener {
naviChatFileAttachmentWidget.id?.let {
@@ -106,7 +110,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
}
UPLOADING_STATE -> {
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_cancel)
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_cancel_purple)
tvSentMessage.progressState.progress = ProgressBar.VISIBLE
tvSentMessage.progressImage.setOnClickListener {
naviChatFileAttachmentWidget.id?.let {
@@ -116,7 +120,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
DOWNLOAD_STATE,
FAILED_STATE -> {
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_download)
tvSentMessage.progressImage.setBackgroundResource(R.drawable.ic_download_purple)
tvSentMessage.progressState.progress = ProgressBar.GONE
tvSentMessage.progressImage.setOnClickListener {
naviChatFileAttachmentWidget.widgetData?.fileUuid?.let {
@@ -163,10 +167,10 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
private fun getBackgroundDrawable(fileType: String?): Int {
return when (fileType) {
AWSFileType.MP4.value -> R.drawable.ic_video_preview
AWSFileType.MP4.value -> R.drawable.ic_video_preview_purple
AWSFileType.JPG.value,
AWSFileType.JPEG.value,
AWSFileType.PNG.value -> R.drawable.ic_image_preview
AWSFileType.PNG.value -> R.drawable.ic_image_preview_purple
else -> R.drawable.ic_document_preview
}
}

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:pathData="M6,6m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
android:fillColor="#1F002A"/>
<path
android:pathData="M9.121,3.023L9.121,3.023A0.563,0.563 0,0 1,9.121 3.818L3.818,9.122A0.563,0.563 58.086,0 1,3.023 9.122L3.023,9.122A0.563,0.563 58.086,0 1,3.023 8.326L8.326,3.023A0.563,0.563 0,0 1,9.121 3.023z"
android:fillColor="#ffffff"/>
<path
android:pathData="M3.023,3.023L3.023,3.023A0.563,0.563 75.486,0 1,3.818 3.023L9.122,8.326A0.563,0.563 51.903,0 1,9.122 9.122L9.122,9.122A0.563,0.563 51.903,0 1,8.326 9.122L3.023,3.819A0.563,0.563 75.486,0 1,3.023 3.023z"
android:fillColor="#ffffff"/>
</vector>

View File

@@ -1,27 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="21dp"
android:viewportWidth="16"
android:viewportHeight="21">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M0.004,10.379C0.004,7.386 0.004,4.393 0.004,1.4C0.005,0.752 0.322,0.277 0.908,0.072C1.065,0.018 1.242,0.007 1.41,0.007C4.644,0.003 7.877,0.008 11.111,-0C11.548,-0.001 11.899,0.149 12.2,0.443C13.308,1.527 14.417,2.609 15.525,3.693C15.846,4.005 16,4.377 16,4.825C15.996,9.675 15.998,14.526 15.997,19.376C15.997,20.037 15.677,20.523 15.087,20.725C14.931,20.779 14.753,20.788 14.584,20.788C10.197,20.791 5.81,20.782 1.423,20.799C0.592,20.802 -0.003,20.166 0,19.381C0.011,16.381 0.004,13.38 0.004,10.379ZM11.114,0.868H10.816C7.719,0.868 4.623,0.868 1.527,0.868C1.033,0.868 0.889,1.008 0.888,1.488C0.888,7.428 0.888,13.368 0.888,19.308C0.888,19.791 1.03,19.927 1.527,19.928C5.842,19.928 10.157,19.928 14.472,19.928C14.969,19.928 15.112,19.791 15.112,19.309C15.112,14.552 15.112,9.796 15.112,5.039V4.77H14.81C14.039,4.77 13.266,4.775 12.495,4.768C11.938,4.764 11.495,4.539 11.256,4.038C11.16,3.84 11.124,3.598 11.12,3.376C11.105,2.551 11.114,1.727 11.114,0.868ZM14.367,3.911C13.576,3.138 12.777,2.357 11.999,1.599C11.999,2.181 11.994,2.815 12.001,3.447C12.005,3.74 12.191,3.899 12.529,3.909C12.844,3.919 13.161,3.911 13.477,3.911C13.79,3.911 14.101,3.911 14.367,3.911Z"
android:fillColor="#6B6B6B"/>
<path
android:strokeWidth="1"
android:pathData="M3.6,8.799H12.4"
android:fillColor="#00000000"
android:strokeColor="#6B6B6B"
android:strokeLineCap="round"/>
<path
android:strokeWidth="1"
android:pathData="M3.6,12H12.4"
android:fillColor="#00000000"
android:strokeColor="#6B6B6B"
android:strokeLineCap="round"/>
<path
android:strokeWidth="1"
android:pathData="M3.6,15.199H12.4"
android:fillColor="#00000000"
android:strokeColor="#6B6B6B"
android:strokeLineCap="round"/>
android:pathData="M6,2C4.906,2 4,2.906 4,4V20C4,21.094 4.906,22 6,22H18C19.094,22 20,21.094 20,20V6.586L15.414,2H6ZM6,4H14V8H18V20H6V4ZM8,10V12H16V10H8ZM8,13V15H16V13H8ZM8,16V18H13V16H8Z"
android:fillColor="#1F002A"/>
</vector>

View File

@@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1"
android:pathData="M12,12m-11.5,0a11.5,11.5 0,1 1,23 0a11.5,11.5 0,1 1,-23 0"
android:fillColor="#1F002A"
android:strokeColor="#1F002A"/>
<path
android:pathData="M6.75,13.167C7.072,13.167 7.334,13.428 7.334,13.75V16.084C7.334,16.238 7.395,16.387 7.505,16.496C7.614,16.605 7.762,16.667 7.917,16.667H16.084C16.238,16.667 16.387,16.605 16.496,16.496C16.605,16.387 16.667,16.238 16.667,16.084V13.75C16.667,13.428 16.928,13.167 17.25,13.167C17.573,13.167 17.834,13.428 17.834,13.75V16.084C17.834,16.548 17.649,16.993 17.321,17.321C16.993,17.649 16.548,17.834 16.084,17.834H7.917C7.453,17.834 7.008,17.649 6.68,17.321C6.351,16.993 6.167,16.548 6.167,16.084V13.75C6.167,13.428 6.428,13.167 6.75,13.167Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M8.671,10.421C8.899,10.193 9.268,10.193 9.496,10.421L12,12.925L14.504,10.421C14.732,10.193 15.101,10.193 15.329,10.421C15.557,10.649 15.557,11.018 15.329,11.246L12.413,14.163C12.185,14.39 11.815,14.39 11.587,14.163L8.671,11.246C8.443,11.018 8.443,10.649 8.671,10.421Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M12,6.167C12.323,6.167 12.584,6.428 12.584,6.75V13.75C12.584,14.073 12.323,14.334 12,14.334C11.678,14.334 11.417,14.073 11.417,13.75V6.75C11.417,6.428 11.678,6.167 12,6.167Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="25dp"
android:viewportWidth="24"
android:viewportHeight="25">
<path
android:pathData="M5,4.5C4.735,4.5 4.48,4.605 4.293,4.793C4.105,4.98 4,5.235 4,5.5V19.5C4,19.765 4.105,20.02 4.293,20.207C4.48,20.395 4.735,20.5 5,20.5H19C19.265,20.5 19.52,20.395 19.707,20.207C19.895,20.02 20,19.765 20,19.5V11.5C20,10.948 20.448,10.5 21,10.5C21.552,10.5 22,10.948 22,11.5V19.5C22,20.296 21.684,21.059 21.121,21.621C20.559,22.184 19.796,22.5 19,22.5H5C4.204,22.5 3.441,22.184 2.879,21.621C2.316,21.059 2,20.296 2,19.5V5.5C2,4.704 2.316,3.941 2.879,3.379C3.441,2.816 4.204,2.5 5,2.5H13C13.552,2.5 14,2.948 14,3.5C14,4.052 13.552,4.5 13,4.5H5Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M6.732,7.232C7.201,6.763 7.837,6.5 8.5,6.5C9.163,6.5 9.799,6.763 10.268,7.232C10.737,7.701 11,8.337 11,9C11,9.663 10.737,10.299 10.268,10.768C9.799,11.237 9.163,11.5 8.5,11.5C7.837,11.5 7.201,11.237 6.732,10.768C6.263,10.299 6,9.663 6,9C6,8.337 6.263,7.701 6.732,7.232ZM8.5,8.5C8.367,8.5 8.24,8.553 8.146,8.646C8.053,8.74 8,8.867 8,9C8,9.133 8.053,9.26 8.146,9.354C8.24,9.447 8.367,9.5 8.5,9.5C8.633,9.5 8.76,9.447 8.854,9.354C8.947,9.26 9,9.133 9,9C9,8.867 8.947,8.74 8.854,8.646C8.76,8.553 8.633,8.5 8.5,8.5Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M15.293,9.793C15.683,9.402 16.317,9.402 16.707,9.793L21.707,14.793C22.098,15.183 22.098,15.817 21.707,16.207C21.317,16.598 20.683,16.598 20.293,16.207L16,11.914L5.707,22.207C5.317,22.598 4.683,22.598 4.293,22.207C3.902,21.817 3.902,21.183 4.293,20.793L15.293,9.793Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M15,5.5C15,4.948 15.448,4.5 16,4.5H22C22.552,4.5 23,4.948 23,5.5C23,6.052 22.552,6.5 22,6.5H16C15.448,6.5 15,6.052 15,5.5Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -1,9 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="1dp"
android:color="@color/border_grey_color" />
<corners
android:bottomLeftRadius="@dimen/dp_16"
android:bottomRightRadius="@dimen/dp_20"
android:topLeftRadius="@dimen/dp_16" />
<solid android:color="@color/navi_chat_received_message_bg_color" />
android:bottomLeftRadius="@dimen/dp_20"
android:bottomRightRadius="@dimen/dp_4"
android:topLeftRadius="@dimen/dp_20"
android:topRightRadius="@dimen/dp_20" />
<solid android:color="@color/white" />
</shape>

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1"
android:pathData="M12,12m-11.5,0a11.5,11.5 0,1 1,23 0a11.5,11.5 0,1 1,-23 0"
android:fillColor="#F9F9F9"
android:strokeColor="#EBEBEB"/>
<path
android:pathData="M9.083,8.578V15.422C9.083,15.944 9.684,16.261 10.146,15.977L15.763,12.555C16.19,12.298 16.19,11.703 15.763,11.439L10.146,8.024C9.684,7.74 9.083,8.057 9.083,8.578Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,8 @@
<vector android:height="24dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#F9F9F9"
android:pathData="M12,12m-11.5,0a11.5,11.5 0,1 1,23 0a11.5,11.5 0,1 1,-23 0"
android:strokeColor="#EBEBEB" android:strokeWidth="1"/>
<path android:fillColor="#1F002A" android:fillType="evenOdd" android:pathData="M6.249,12C6.313,12.11 6.397,12.251 6.502,12.413C6.771,12.829 7.169,13.383 7.686,13.934C8.731,15.049 10.191,16.083 12,16.083C13.809,16.083 15.269,15.049 16.314,13.934C16.831,13.383 17.229,12.829 17.498,12.413C17.603,12.251 17.688,12.11 17.751,12C17.688,11.89 17.603,11.749 17.498,11.587C17.229,11.171 16.831,10.617 16.314,10.066C15.269,8.951 13.809,7.917 12,7.917C10.191,7.917 8.731,8.951 7.686,10.066C7.169,10.617 6.771,11.171 6.502,11.587C6.397,11.749 6.313,11.89 6.249,12ZM18.417,12C18.938,11.739 18.938,11.739 18.938,11.739L18.937,11.737L18.935,11.733L18.929,11.72C18.923,11.709 18.915,11.694 18.905,11.675C18.885,11.636 18.855,11.582 18.817,11.514C18.74,11.377 18.627,11.184 18.478,10.954C18.182,10.496 17.742,9.883 17.165,9.268C16.022,8.049 14.275,6.75 12,6.75C9.725,6.75 7.978,8.049 6.835,9.268C6.258,9.883 5.818,10.496 5.522,10.954C5.373,11.184 5.26,11.377 5.183,11.514C5.145,11.582 5.115,11.636 5.095,11.675C5.085,11.694 5.077,11.709 5.071,11.72L5.065,11.733L5.063,11.737L5.062,11.738C5.062,11.738 5.062,11.739 5.583,12L5.062,11.739C4.979,11.903 4.979,12.097 5.062,12.261L5.583,12C5.062,12.261 5.061,12.261 5.062,12.261L5.063,12.263L5.065,12.267L5.071,12.28C5.077,12.291 5.085,12.306 5.095,12.325C5.115,12.364 5.145,12.418 5.183,12.486C5.26,12.623 5.373,12.816 5.522,13.046C5.818,13.504 6.258,14.117 6.835,14.732C7.978,15.951 9.725,17.25 12,17.25C14.275,17.25 16.022,15.951 17.165,14.732C17.742,14.117 18.182,13.504 18.478,13.046C18.627,12.816 18.74,12.623 18.817,12.486C18.855,12.418 18.885,12.364 18.905,12.325C18.915,12.306 18.923,12.291 18.929,12.28L18.935,12.267L18.937,12.263L18.938,12.262C18.938,12.262 18.938,12.261 18.417,12ZM18.417,12L18.938,12.261C19.021,12.097 19.02,11.903 18.938,11.739L18.417,12Z"/>
<path android:fillColor="#1F002A" android:fillType="evenOdd" android:pathData="M12,10.834C11.356,10.834 10.834,11.356 10.834,12C10.834,12.645 11.356,13.167 12,13.167C12.645,13.167 13.167,12.645 13.167,12C13.167,11.356 12.645,10.834 12,10.834ZM9.667,12C9.667,10.712 10.712,9.667 12,9.667C13.289,9.667 14.334,10.712 14.334,12C14.334,13.289 13.289,14.334 12,14.334C10.712,14.334 9.667,13.289 9.667,12Z"/>
</vector>

View File

@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,12m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"
android:fillColor="#ffffff"/>
<group>
<clip-path
android:pathData="M5,5h14v14h-14z"/>
<path
android:pathData="M11.858,16.65C10.555,16.65 9.452,16.198 8.548,15.293C7.643,14.389 7.191,13.286 7.191,11.983C7.191,10.68 7.643,9.577 8.548,8.673C9.452,7.768 10.555,7.316 11.858,7.316C12.529,7.316 13.171,7.455 13.783,7.732C14.396,8.009 14.921,8.405 15.358,8.921V7.9C15.358,7.578 15.619,7.316 15.941,7.316V7.316C16.264,7.316 16.525,7.578 16.525,7.9V10.4C16.525,10.952 16.077,11.4 15.525,11.4H13.025C12.703,11.4 12.441,11.139 12.441,10.816V10.816C12.441,10.494 12.703,10.233 13.025,10.233H14.891C14.58,9.689 14.155,9.261 13.616,8.95C13.076,8.639 12.49,8.483 11.858,8.483C10.886,8.483 10.059,8.823 9.379,9.504C8.698,10.184 8.358,11.011 8.358,11.983C8.358,12.955 8.698,13.782 9.379,14.462C10.059,15.143 10.886,15.483 11.858,15.483C12.607,15.483 13.282,15.269 13.885,14.841C14.376,14.493 14.747,14.055 15,13.526C15.105,13.306 15.317,13.15 15.561,13.15V13.15C15.974,13.15 16.277,13.543 16.107,13.92C15.797,14.608 15.334,15.192 14.716,15.673C13.88,16.324 12.927,16.65 11.858,16.65Z"
android:fillColor="#1F002A"/>
</group>
</vector>

View File

@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<clip-path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M15,8V16H5V8H15ZM16,6H4C3.45,6 3,6.45 3,7V17C3,17.55 3.45,18 4,18H16C16.55,18 17,17.55 17,17V13.5L21,17.5V6.5L17,10.5V7C17,6.45 16.55,6 16,6Z"
android:fillColor="#000000"/>
</group>
</vector>

View File

@@ -18,8 +18,8 @@
app:layout_constraintEnd_toStartOf="@id/tvMessage"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/ic_image_preview"
tools:src="@drawable/ic_image_preview" />
android:src="@drawable/ic_image_preview_purple"
tools:src="@drawable/ic_image_preview_purple" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tvMessage"
@@ -59,6 +59,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tvMessage"
app:layout_constraintTop_toTopOf="@id/tvMessage"
tools:src="@drawable/ic_cancel" />
tools:src="@drawable/ic_cancel_purple" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -54,7 +54,7 @@
android:layout_gravity="end"
android:paddingLeft="@dimen/dp_4"
app:layout_constraintBottom_toBottomOf="@id/tvTimestamp"
app:layout_constraintTop_toTopOf="@id/tvTimestamp"
app:layout_constraintTop_toBottomOf="@id/tvSentMessage"
app:layout_constraintEnd_toEndOf="@id/tvSentMessage"
android:visibility="gone"
tools:visibility="visible"