TP-57152 | crm faq combined (#9950)

Co-authored-by: Varun Jain <varun.jain@navi.com>
This commit is contained in:
Sayed Owais Ali
2024-02-28 15:09:55 +05:30
committed by GitHub
parent 41bc3e6d6a
commit 244d728545
40 changed files with 2285 additions and 76 deletions

View File

@@ -33,4 +33,5 @@ object CommonPrefConstants {
const val DISABLE = "DISABLE"
const val LATEST_NETWORK_SPEED_IN_MBps = "LATEST_NETWORK_SPEED_IN_MBps"
const val PREFERENCE_SSL_KEY = "sslKey"
const val FAQ_RECENT_SEARCH = "FAQ_RECENT_SEARCH"
}

View File

@@ -46,6 +46,7 @@ android {
dependencies {
implementation project(':navi-common')
implementation libs.android.flexbox
implementation libs.android.material
implementation libs.androidx.appcompat
implementation libs.androidx.constraintlayout
@@ -57,6 +58,7 @@ dependencies {
implementation libs.androidx.paging.compose
implementation libs.coil.video
implementation libs.dagger.hiltAndroid
implementation libs.pierfrancescosoffritti.androidyoutubeplayer
androidTestImplementation libs.androidx.test.espresso.core
androidTestImplementation libs.androidx.test.junit

View File

@@ -17,6 +17,7 @@ import com.navi.chat.ui.activities.NaviHelpCenterActivity
import com.navi.chat.ui.activities.NaviTicketViewActivity
import com.navi.chat.ui.activities.SupportScreenActivity
import com.navi.chat.ui.fragments.ChatAttachmentBottomSheet
import com.navi.chat.ui.fragments.FaqSearchFragment
import com.navi.chat.ui.fragments.NaviChatFragment
import dagger.BindsInstance
import dagger.Component
@@ -49,4 +50,6 @@ interface NaviChatComponent {
fun inject(naviTicketViewActivity: NaviTicketViewActivity)
fun inject(supportScreenActivity: SupportScreenActivity)
fun inject(faqSearchFragment: FaqSearchFragment)
}

View File

@@ -0,0 +1,14 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.interfaces
import com.navi.base.model.ActionData
interface ChatCtaClickListener {
fun onChatCtaClick(actionData: ActionData)
}

View File

@@ -8,5 +8,5 @@
package com.navi.chat.interfaces
interface FaqClickListener {
fun onFaqClick(title: String)
fun onFaqClick(title: String, recentSearch: String?)
}

View File

@@ -0,0 +1,12 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.interfaces
interface RecentSearchClickInterface {
fun onRecentSearchClick(recentSearch: String)
}

View File

@@ -0,0 +1,12 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.models
data class FaqRecentSearchesList(val recentSearches: List<FaqRecentSearchData>)
data class FaqRecentSearchData(val title: String)

View File

@@ -22,7 +22,10 @@ data class SupportScreenResponse(
@SerializedName("showFaqTitle") val showFaqTitle: Boolean? = null,
@SerializedName("tabsData") var tabsData: List<SupportScreenTabsData>? = null,
@SerializedName("actionData") val actionData: ActionData? = null,
@SerializedName("ctaBottomSheetData") val ctaBottomSheetData: CtaBottomSheetData? = null
@SerializedName("ctaBottomSheetData") val ctaBottomSheetData: CtaBottomSheetData? = null,
@SerializedName("searchBarPlaceholder") val searchBarData: SearchBarData? = null,
@SerializedName("showSearchBar") val showSearchBar: Boolean? = null,
@SerializedName("searchThresholdLimit") val searchThresholdLimit: Double? = null
)
data class SupportScreenTabsData(
@@ -36,13 +39,16 @@ data class TabContent(
)
data class QuestionAnswerList(
var distance: List<Int>? = null,
var score: Int = 0,
var isFirstItemInList: Boolean = false,
var isLastItemInList: Boolean = false,
var isExpanded: Boolean = false,
var tabName: String? = null,
@SerializedName("question") val question: TextWithStyle? = null,
@SerializedName("answer") val answer: TextWithStyle? = null,
@SerializedName("cta") val ctaInfo: CtaInfo? = null
@SerializedName("cta") val ctaInfo: CtaInfo? = null,
@SerializedName("videodata") val videoData: VideoData? = null
)
data class HelpOptions(
@@ -59,6 +65,10 @@ data class CtaInfo(
@SerializedName(IS_GROOT_DEEPLINK) val isGrootDeeplink: Boolean? = false
)
data class VideoData(@SerializedName("action_data") val actionData: VideoActionData? = null)
data class VideoActionData(@SerializedName("video_id") val videoId: String? = null)
@Parcelize
data class CtaBottomSheetData(
@SerializedName("title") val title: TextWithStyle? = null,
@@ -66,3 +76,5 @@ data class CtaBottomSheetData(
@SerializedName("primaryCTA") val primaryCta: TextWithStyle? = null,
@SerializedName("secondaryCTA") val secondaryCta: TextWithStyle? = null
) : Parcelable
data class SearchBarData(@SerializedName("text") val searchBarPlaceholder: String? = null)

View File

@@ -13,6 +13,7 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
@@ -29,16 +30,23 @@ import com.navi.chat.databinding.ActivitySupportScreenBinding
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.ChatCtaClickListener
import com.navi.chat.interfaces.FaqClickListener
import com.navi.chat.interfaces.FaqCtaClickListener
import com.navi.chat.interfaces.HelpOptionsClickListener
import com.navi.chat.models.FaqRecentSearchData
import com.navi.chat.models.FaqRecentSearchesList
import com.navi.chat.models.NaviChatSystemLocalData
import com.navi.chat.models.response.CtaBottomSheetData
import com.navi.chat.models.response.CtaInfo
import com.navi.chat.models.response.SupportScreenResponse
import com.navi.chat.models.response.SupportScreenTabsData
import com.navi.chat.ui.adapters.SupportScreenViewPagerAdapter
import com.navi.chat.ui.fragments.CtaUnderFaqBottomSheet
import com.navi.chat.ui.fragments.FaqSearchFragment
import com.navi.chat.ui.fragments.SupportScreenFaqFragment
import com.navi.chat.utils.CHAT_INITIATED
import com.navi.chat.utils.NAVI_CHAT_SYSTEM_LOCAL_DATA
import com.navi.chat.utils.NaviChatAnalytics
import com.navi.chat.utils.NaviChatAnalytics.Companion.CALL_US_CLICK
import com.navi.chat.utils.NaviChatAnalytics.Companion.CONFIG_ID
@@ -46,11 +54,13 @@ import com.navi.chat.utils.NaviChatAnalytics.Companion.CTA_NAME
import com.navi.chat.utils.NaviChatAnalytics.Companion.CTA_UNDER_FAQ_CLICK
import com.navi.chat.utils.NaviChatAnalytics.Companion.EMAIL_US_CLICK
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_CLICK
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_CLICK_FROM_SEARCH_SCREEN
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_QUESTION_TEXT
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_TAB_CLICK
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_TAB_NAME
import com.navi.chat.utils.NaviChatAnalytics.Companion.SEND_E_MAIL
import com.navi.chat.utils.NaviChatAnalytics.Companion.TIMESTAMP
import com.navi.chat.utils.RECENT_SEARCHES_LIST_SIZE
import com.navi.chat.viewmodels.SupportScreenSharedViewModel
import com.navi.chat.viewmodels.SupportScreenViewModel
import com.navi.common.listeners.HeaderInteractionListener
@@ -66,7 +76,6 @@ import com.navi.design.utils.CornerRadius
import com.navi.design.utils.dpToPx
import com.navi.design.utils.getNaviDrawable
import com.navi.design.utils.setFontStyle
import com.navi.design.utils.setSpannableString
import com.navi.naviwidgets.actions.ChatDeepLinkClickAction
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import dagger.hilt.android.EntryPointAccessors
@@ -78,7 +87,8 @@ class SupportScreenActivity :
NewBottomSheetListener,
HelpOptionsClickListener,
FaqClickListener,
FaqCtaClickListener {
FaqCtaClickListener,
ChatCtaClickListener {
override val screenName: String
get() = SupportScreenActivity::class.java.simpleName
@@ -98,6 +108,9 @@ class SupportScreenActivity :
PreferenceManager.getStringPreference(CommonPrefConstants.USER_EXTERNAL_ID)
private val crmEventTracker = NaviChatAnalytics.naviChatAnalytics.CRM()
private val faqSearchScreenTag: String = FaqSearchFragment.TAG
private var faqSearchFragment: FaqSearchFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_support_screen)
@@ -157,13 +170,11 @@ class SupportScreenActivity :
hideShowViews(true)
setProperties(response.header)
binding.titleTv.showWhenDataIsAvailable(response.title)
response?.faqTitle?.let { title ->
binding.faqTv.isVisible = true
binding.faqTv.setSpannableString(title)
}
setCtaBottomSheetData(response.ctaBottomSheetData)
setSupportScreenData(response)
response?.tabsData?.let { data ->
supportScreenSharedViewModel.setTabData(data)
supportScreenSharedViewModel.setCombinedFaqList()
if (data.size == 1) {
if (response.showFaqTitle == true) {
binding.supportScreenTl.visibility = View.GONE
@@ -175,9 +186,16 @@ class SupportScreenActivity :
}
setUpViewPagerAdapter(data)
}
if (response.showSearchBar == true) {
binding.searchField.visibility = View.VISIBLE
binding.searchField.hint = response.searchBarData?.searchBarPlaceholder.orEmpty()
binding.searchField.setOnClickListener { addFaqSearchFragment() }
} else {
binding.searchField.visibility = View.GONE
}
response?.actionData?.primaryAction?.let { actionData ->
binding.footerCta.setText(actionData.title)
binding.footerLayout.isVisible = true
binding.footerCta.isVisible = true
binding.footerCta.setOnClickListener {
sourceScreenName = intent?.extras?.getString(KEY_SCREEN_NAME)
@@ -191,10 +209,58 @@ class SupportScreenActivity :
)
moveToNextScreen(actionData)
}
} ?: run { binding.footerCta.isVisible = false }
}
?: run {
binding.footerLayout.isVisible = false
binding.footerCta.isVisible = false
}
}
}
private fun addFaqSearchFragment() {
val fragment =
supportFragmentManager.findFragmentByTag(faqSearchScreenTag) ?: getFaqSearchFragment()
val fragmentTransaction = supportFragmentManager.beginTransaction()
faqSearchFragment = fragment as? FaqSearchFragment
if (!supportFragmentManager.isStateSaved && !supportFragmentManager.isDestroyed) {
hideFragments(fragmentTransaction)
fragmentTransaction.addToBackStack(FaqSearchFragment.TAG)
if (fragment.isAdded) {
fragmentTransaction.show(fragment)
} else {
fragmentTransaction.add(R.id.search_layout, fragment, faqSearchScreenTag)
}
fragmentTransaction.commit()
}
}
private fun hideFragments(fragmentTransaction: FragmentTransaction) {
val fragments = supportFragmentManager.fragments
fragments.forEach { currentFragment ->
currentFragment?.let { fragmentTransaction.hide(it) }
}
}
private fun getFaqSearchFragment() =
FaqSearchFragment().apply {
val bundle = Bundle()
bundle.putParcelable(
NAVI_CHAT_SYSTEM_LOCAL_DATA,
intent.extras?.getParcelable<NaviChatSystemLocalData>(NAVI_CHAT_SYSTEM_LOCAL_DATA)
)
bundle.putParcelable(HEADER_DATA, supportScreenSharedViewModel.getHeaderData())
bundle.putParcelable(
CHAT_WITH_US_ACTION_DATA,
supportScreenSharedViewModel.getFooterActionData()
)
bundle.putString(SEARCH_BAR_HINT, supportScreenSharedViewModel.getSearchBarHint())
bundle.putDouble(
SEARCH_THRESHOLD_LIMIT,
supportScreenSharedViewModel.getSearchThresholdLimit()
)
arguments = bundle
}
private fun callSupportScreenApi() {
sourceScreenName?.let {
hideShowViews(false)
@@ -267,7 +333,7 @@ class SupportScreenActivity :
}
}
override fun onFaqClick(question: String) {
override fun onFaqClick(question: String, recentSearch: String?) {
crmEventTracker.sendEvent(
FAQ_CLICK,
hashMapOf(
@@ -275,6 +341,31 @@ class SupportScreenActivity :
CONFIG_ID to sourceScreenName.orEmpty(),
)
)
val recentSearches =
PreferenceManager.getObjectPrefrences(
CommonPrefConstants.FAQ_RECENT_SEARCH,
FaqRecentSearchesList::class.java
)
if (!recentSearch.isNullOrEmpty()) {
var updatedRecentSearches =
recentSearches?.recentSearches?.toMutableList() ?: mutableListOf()
updatedRecentSearches.add(0, FaqRecentSearchData(recentSearch))
updatedRecentSearches = updatedRecentSearches.distinct().toMutableList()
if (updatedRecentSearches.size > RECENT_SEARCHES_LIST_SIZE) {
updatedRecentSearches = updatedRecentSearches.subList(0, RECENT_SEARCHES_LIST_SIZE)
}
PreferenceManager.setObjectPreference(
CommonPrefConstants.FAQ_RECENT_SEARCH,
FaqRecentSearchesList(updatedRecentSearches)
)
crmEventTracker.sendEvent(
FAQ_CLICK_FROM_SEARCH_SCREEN,
hashMapOf(
FAQ_QUESTION_TEXT to question,
CONFIG_ID to sourceScreenName.orEmpty(),
)
)
}
}
override fun onCtaClick(ctaInfo: CtaInfo) {
@@ -305,6 +396,10 @@ class SupportScreenActivity :
)
}
override fun onChatCtaClick(actionData: ActionData) {
moveToNextScreen(actionData)
}
private fun onCtaBottomSheetClick(chatDeepLinkClickAction: ChatDeepLinkClickAction) {
DeepLinkManager.getDeepLinkListener()
?.fetchBranchSDKData(
@@ -436,10 +531,25 @@ class SupportScreenActivity :
ctaBottomSheetData?.let { supportScreenSharedViewModel.setCtaUnderFaqBottomSheetData(it) }
}
private fun setSupportScreenData(response: SupportScreenResponse?) {
response?.header?.let { supportScreenSharedViewModel.setHeaderData(it) }
response?.actionData?.let { supportScreenSharedViewModel.setFooterActionData(it) }
response?.searchBarData?.searchBarPlaceholder?.let {
supportScreenSharedViewModel.setSearchBarHint(it)
}
response?.searchThresholdLimit?.let {
supportScreenSharedViewModel.setSearchThresholdLimit(it)
}
}
companion object {
const val KEY_SCREEN_NAME = "SCREEN_NAME"
const val CTA_UNDER_FAQ_BOTTOM_SHEET_DATA = "CTA_UNDER_FAQ_BOTTOM_SHEET_DATA"
const val CTA_UNDER_FAQ_BOTTOM_SHEET_ACTION_DATA = "CTA_UNDER_FAQ_BOTTOM_SHEET_ACTION_DATA"
const val CTA_UNDER_FAQ_IS_GROOT_DEEPLINK = "CTA_UNDER_FAQ_IS_GROOT_DEEPLINK"
const val HEADER_DATA = "HEADER_DATA"
const val CHAT_WITH_US_ACTION_DATA = "CHAT_WITH_US_ACTION_DATA"
const val SEARCH_BAR_HINT = "SEARCH_BAR_HINT"
const val SEARCH_THRESHOLD_LIMIT = "SEARCH_THRESHOLD_LIMIT"
}
}

View File

@@ -0,0 +1,46 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.navi.chat.databinding.RecentSearchOptionLayoutBinding
import com.navi.chat.interfaces.RecentSearchClickInterface
import com.navi.chat.models.FaqRecentSearchData
import com.navi.chat.viewholder.RecentSearchOptionVH
class RecentSearchOptionAdapter(
private var optionList: List<FaqRecentSearchData>,
private val recentSearchClickInterface: RecentSearchClickInterface
) : RecyclerView.Adapter<RecentSearchOptionVH>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecentSearchOptionVH {
val itemHolder =
RecentSearchOptionLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return RecentSearchOptionVH(itemHolder)
}
override fun onBindViewHolder(holder: RecentSearchOptionVH, position: Int) {
holder.bind(optionList[position]) { recentSearch ->
recentSearchClickInterface.onRecentSearchClick(recentSearch)
}
}
override fun getItemCount(): Int {
return optionList.size
}
fun updateData(list: List<FaqRecentSearchData>) {
this.optionList = list
notifyDataSetChanged()
}
}

View File

@@ -12,35 +12,50 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.RecyclerView
import com.navi.base.model.ActionData
import com.navi.base.utils.isValidIndex
import com.navi.base.utils.BaseUtils
import com.navi.chat.databinding.FaqListSingleItemWithHeadingBinding
import com.navi.chat.databinding.HelpOptionsItemBinding
import com.navi.chat.models.response.CtaInfo
import com.navi.chat.models.response.HelpOptions
import com.navi.chat.models.response.QuestionAnswerList
import com.navi.chat.utils.NaviChatAnalytics
import com.navi.common.utils.isNetworkAvailable
import com.navi.design.font.FontWeightEnum
import com.navi.design.utils.setFontStyle
import com.navi.design.utils.setSpannableString
import com.navi.design.utils.underlinedText
import com.navi.naviwidgets.R
import com.navi.naviwidgets.extensions.addOnMultipleClicksHandler
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.utils.NaviWidgetIconUtils
import com.navi.naviwidgets.utils.setButtonLoaderState
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
class SupportScreenFaqAdapter(
private val faqsList: List<QuestionAnswerList>?,
private var faqsList: List<QuestionAnswerList>?,
private val helpOptionsList: List<HelpOptions>?,
val onOptionClicked: (actionData: ActionData, title: String) -> Unit,
val onFaqClicked: (question: String) -> Unit,
val onCtaUnderFaqClicked: (ctaInfo: CtaInfo) -> Unit
val onFaqPosition: (position: Int) -> Unit,
val onCtaUnderFaqClicked: (ctaInfo: CtaInfo) -> Unit,
private val lifecycle: Lifecycle,
val onFaqSearchScreenOpened: Boolean? = false,
val onFaqSearchFooterCtaClicked: (() -> Unit)? = null
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var expandedItemPosition: Int = RecyclerView.NO_POSITION
private val crmEventTracker = NaviChatAnalytics.naviChatAnalytics.CRM()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_FAQ -> {
FaqListVH(
lifecycle,
FaqListSingleItemWithHeadingBinding.inflate(
LayoutInflater.from(parent.context),
parent,
@@ -85,8 +100,55 @@ class SupportScreenFaqAdapter(
override fun getItemCount(): Int = (faqsList?.size ?: 0) + (helpOptionsList?.size ?: 0)
inner class FaqListVH(val binding: FaqListSingleItemWithHeadingBinding) :
RecyclerView.ViewHolder(binding.root) {
inner class FaqListVH(
val lifecycle: Lifecycle,
val binding: FaqListSingleItemWithHeadingBinding
) : RecyclerView.ViewHolder(binding.root) {
private var youTubePlayer: YouTubePlayer? = null
private var currentVideoId: String? = null
init {
lifecycle.addObserver(binding.youtubePlayerView)
binding.overlayView.setOnClickListener {
if (!binding.networkErrorLayout.isVisible) {
crmEventTracker.sendEvent(
NaviChatAnalytics.VIDEO_FAQ_CLICK,
hashMapOf(
NaviChatAnalytics.PHONE_NUMBER to BaseUtils.getPhoneNumber().toString()
)
)
}
youTubePlayer?.play()
}
binding.youtubePlayerView.addYouTubePlayerListener(
object : AbstractYouTubePlayerListener() {
override fun onReady(youTubePlayer: YouTubePlayer) {
this@FaqListVH.youTubePlayer = youTubePlayer
binding.networkErrorLayout.visibility = View.GONE
binding.shimmerLayout.visibility = View.GONE
currentVideoId?.let { youTubePlayer.cueVideo(it, 0f) }
}
override fun onStateChange(
youTubePlayer: YouTubePlayer,
state: PlayerConstants.PlayerState
) {
binding.networkErrorLayout.visibility = View.GONE
binding.shimmerLayout.visibility = View.GONE
when (state) {
PlayerConstants.PlayerState.VIDEO_CUED -> {
binding.overlayView.visibility = View.VISIBLE
}
else -> {
binding.overlayView.visibility = View.GONE
}
}
}
}
)
}
fun bind(
position: Int,
faqData: QuestionAnswerList?,
@@ -97,12 +159,37 @@ class SupportScreenFaqAdapter(
binding.heading.visibility = View.VISIBLE
binding.sectionDivider.visibility = View.GONE
binding.divider.visibility = View.VISIBLE
binding.faqSearchFooterSection.root.visibility = View.GONE
binding.heading.showWhenDataIsAvailable(it.tabName)
if (it.isLastItemInList) {
if (onFaqSearchScreenOpened == true) {
binding.divider.visibility = View.GONE
binding.faqSearchFooterSection.root.visibility = View.VISIBLE
binding.faqSearchFooterSection.cta.underlinedText()
binding.faqSearchFooterSection.cta.setOnClickListener {
onFaqSearchFooterCtaClicked?.invoke()
}
} else {
binding.divider.visibility = View.GONE
binding.sectionDivider.visibility = View.VISIBLE
}
}
} else if (it.isLastItemInList) {
binding.heading.visibility = View.GONE
binding.sectionDivider.visibility = View.VISIBLE
binding.divider.visibility = View.GONE
if (onFaqSearchScreenOpened == true) {
binding.sectionDivider.visibility = View.GONE
binding.faqSearchFooterSection.root.visibility = View.VISIBLE
binding.faqSearchFooterSection.cta.underlinedText()
binding.faqSearchFooterSection.cta.setOnClickListener {
onFaqSearchFooterCtaClicked?.invoke()
}
} else {
binding.faqSearchFooterSection.root.visibility = View.GONE
binding.sectionDivider.visibility = View.VISIBLE
}
} else {
binding.faqSearchFooterSection.root.visibility = View.GONE
binding.heading.visibility = View.GONE
binding.sectionDivider.visibility = View.GONE
binding.divider.visibility = View.VISIBLE
@@ -111,25 +198,28 @@ class SupportScreenFaqAdapter(
binding.apply {
question.setFontStyle(FontWeightEnum.TT_MEDIUM)
faqData?.isExpanded?.let { setFaqArrowIcon(it, arrowDown) }
setFaqArrowIcon(position, arrowDown)
question.setSpannableString(faqData?.question)
question.isVisible = true
setButtonLoaderState(binding.cta, binding.buttonLoader, false, "")
setYoutubePlayer(position, faqData?.videoData?.actionData?.videoId)
}
if (faqData?.isExpanded == true) {
if (isExpanded(position)) {
binding.description.setFontStyle(FontWeightEnum.TT_REGULAR)
binding.description.setSpannableString(faqData.answer)
binding.cta.showWhenDataIsAvailable(faqData.ctaInfo?.title)
binding.description.setSpannableString(faqData?.answer)
binding.cta.showWhenDataIsAvailable(faqData?.ctaInfo?.title)
binding.cta.setOnClickListener {
if (faqData.ctaInfo?.isGrootDeeplink == true) {
if (faqData?.ctaInfo?.isGrootDeeplink == true) {
setButtonLoaderState(binding.cta, binding.buttonLoader, true, "")
}
faqData.ctaInfo?.let { ctaInfo -> onCtaUnderFaqClicked(ctaInfo) }
faqData?.ctaInfo?.let { ctaInfo -> onCtaUnderFaqClicked(ctaInfo) }
}
} else {
binding.description.isVisible = false
binding.cta.isVisible = false
}
binding.faqList.addOnMultipleClicksHandler {
if (faqData != null) {
onItemClick.invoke(position, faqData)
@@ -137,10 +227,38 @@ class SupportScreenFaqAdapter(
}
}
private fun setFaqArrowIcon(expanded: Boolean, arrowDown: AppCompatImageView) {
if (expanded) arrowDown.setBackgroundResource(R.drawable.ic_arrow_up_green)
private fun isExpanded(position: Int): Boolean {
return expandedItemPosition == position
}
private fun setFaqArrowIcon(position: Int, arrowDown: AppCompatImageView) {
if (isExpanded(position)) arrowDown.setBackgroundResource(R.drawable.ic_arrow_up_green)
else arrowDown.setBackgroundResource(R.drawable.ic_arrow_purple_down)
}
private fun setYoutubePlayer(position: Int, videoId: String?) {
if (isExpanded(position) && videoId != null) {
binding.playerLayout.visibility = View.VISIBLE
if (isNetworkAvailable()) {
binding.shimmerLayout.visibility = View.VISIBLE
binding.networkErrorLayout.visibility = View.GONE
currentVideoId = videoId
youTubePlayer?.cueVideo(videoId, 0f)
} else {
binding.networkErrorLayout.visibility = View.VISIBLE
crmEventTracker.sendEvent(
NaviChatAnalytics.NO_CONNECTIVITY_VIDEO_FAQ_PLACEHOLDER,
hashMapOf(
NaviChatAnalytics.PHONE_NUMBER to BaseUtils.getPhoneNumber().toString()
)
)
}
} else {
binding.playerLayout.visibility = View.GONE
binding.shimmerLayout.visibility = View.GONE
binding.networkErrorLayout.visibility = View.GONE
}
}
}
inner class HelpOptionsVH(val binding: HelpOptionsItemBinding) :
@@ -169,19 +287,25 @@ class SupportScreenFaqAdapter(
}
private fun setExpandFaqClickListener(position: Int, faqData: QuestionAnswerList) {
faqsList?.let { list ->
if (isValidIndex(position, list.size)) {
if (faqData.isExpanded) {
list[position].isExpanded = false
notifyItemChanged(position)
} else {
onFaqClicked(faqData.question?.text.toString())
list.forEach { it.isExpanded = false }
list[position].isExpanded = true
notifyDataSetChanged()
}
}
if (expandedItemPosition == position) {
expandedItemPosition = RecyclerView.NO_POSITION
notifyItemChanged(position)
} else {
val previousExpandedPosition = expandedItemPosition
expandedItemPosition = position
notifyItemChanged(previousExpandedPosition)
notifyItemChanged(position)
onFaqClicked(faqData.question?.text.toString())
}
if (!faqData.isLastItemInList) {
onFaqPosition(position)
}
}
fun updateFaqList(faqList: List<QuestionAnswerList>?) {
this.faqsList = faqList
expandedItemPosition = RecyclerView.NO_POSITION
notifyDataSetChanged()
}
companion object {

View File

@@ -0,0 +1,350 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.ui.fragments
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import com.navi.base.model.ActionData
import com.navi.base.sharedpref.CommonPrefConstants
import com.navi.base.sharedpref.PreferenceManager
import com.navi.chat.R
import com.navi.chat.adapters.RecentSearchOptionAdapter
import com.navi.chat.databinding.FragmentFaqSearchBinding
import com.navi.chat.db.utils.crmEventTracker
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.ChatCtaClickListener
import com.navi.chat.interfaces.FaqClickListener
import com.navi.chat.interfaces.FaqCtaClickListener
import com.navi.chat.interfaces.HelpOptionsClickListener
import com.navi.chat.interfaces.RecentSearchClickInterface
import com.navi.chat.models.FaqRecentSearchData
import com.navi.chat.models.FaqRecentSearchesList
import com.navi.chat.models.response.QuestionAnswerList
import com.navi.chat.ui.adapters.SupportScreenFaqAdapter
import com.navi.chat.utils.ANIMATION_DURATION
import com.navi.chat.utils.FAQ_SEARCH_THRESHOLD_LIMIT
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_SEARCHED_WITH_NO_RESULTS
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_SEARCHED_WITH_RESULTS
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_SEARCH_QUERY_TEXT
import com.navi.chat.utils.NaviChatAnalytics.Companion.FAQ_SEARCH_SCREEN_VIEWED
import com.navi.chat.viewmodels.FaqSearchResultState
import com.navi.chat.viewmodels.FaqSearchScreenViewModel
import com.navi.chat.viewmodels.SupportScreenSharedViewModel
import com.navi.common.listeners.HeaderInteractionListener
import com.navi.common.model.Header
import com.navi.common.network.models.GenericErrorResponse
import com.navi.common.ui.fragment.BaseFragment
import com.navi.common.utils.hideKeyboard
import com.navi.common.utils.observeNonNull
import com.navi.design.utils.underlinedText
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import dagger.hilt.android.EntryPointAccessors
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class FaqSearchFragment : BaseFragment(), RecentSearchClickInterface, HeaderInteractionListener {
private lateinit var binding: FragmentFaqSearchBinding
private lateinit var supportScreenFaqListAdapter: SupportScreenFaqAdapter
private var helpOptionsClickListener: HelpOptionsClickListener? = null
private var faqClickListener: FaqClickListener? = null
private var ctaUnderFaqClickListener: FaqCtaClickListener? = null
private var chatCtaClickListener: ChatCtaClickListener? = null
private var header: Header? = null
private var chatWithUsActionData: ActionData? = null
private var searchBarHint: String? = null
private var searchThresholdLimit: Double = FAQ_SEARCH_THRESHOLD_LIMIT
private var searchText: String? = ""
private var faqSearchFragmentOpenedFirstTime = false
private val recentSearchesAdapter = RecentSearchOptionAdapter(emptyList(), this)
private val supportScreenSharedViewModel by lazy {
ViewModelProvider(requireActivity())[SupportScreenSharedViewModel::class.java]
}
@Inject lateinit var viewModel: FaqSearchScreenViewModel
override fun onAttach(context: Context) {
super.onAttach(context)
helpOptionsClickListener = context as? HelpOptionsClickListener
faqClickListener = context as? FaqClickListener
ctaUnderFaqClickListener = context as? FaqCtaClickListener
chatCtaClickListener = context as? ChatCtaClickListener
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_faq_search, container, false)
return binding.root
}
override fun onResume() {
super.onResume()
if (faqSearchFragmentOpenedFirstTime) {
viewModel.updateSearchText("$searchText")
}
faqSearchFragmentOpenedFirstTime = true
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
DaggerNaviChatComponent.builder()
.context(requireContext())
.naviChatModule(NaviChatModule)
.appDependencies(
EntryPointAccessors.fromApplication(
requireContext().applicationContext,
NaviChatModuleDependencies::class.java
)
)
.build()
.inject(this)
readArguments()
binding.searchEt.requestFocus()
openKeyboard(binding.searchEt) {}
crmEventTracker.sendEvent(FAQ_SEARCH_SCREEN_VIEWED)
initUI()
}
private fun initUI() {
val recyclerView = binding.recentSearchesView.rvRecentSearches
val layoutManager = FlexboxLayoutManager(requireContext())
layoutManager.flexDirection = FlexDirection.ROW
layoutManager.justifyContent = JustifyContent.FLEX_START
recyclerView.layoutManager = layoutManager
setProperties(header)
setEmptyScreenData(chatWithUsActionData)
binding.resultsRv.adapter =
SupportScreenFaqAdapter(
emptyList(),
null,
onOptionClicked = { actionData, title ->
helpOptionsClickListener?.onHelpOptionClick(actionData, title)
},
onFaqClicked = { question -> faqClickListener?.onFaqClick(question, searchText) },
onFaqPosition = { position -> binding.resultsRv.smoothScrollToPosition(position) },
onCtaUnderFaqClicked = { ctaInfo -> ctaUnderFaqClickListener?.onCtaClick(ctaInfo) },
lifecycle,
true,
onFaqSearchFooterCtaClicked = {
chatWithUsActionData?.primaryAction?.let { actionData ->
chatCtaClickListener?.onChatCtaClick(actionData)
}
}
)
binding.searchEt.hint = searchBarHint
viewModel.setSearchThresholdLimit(searchThresholdLimit)
supportScreenSharedViewModel.combinedFaqList.observeNonNull(viewLifecycleOwner) {
viewModel.setFaqList(it)
}
binding.searchEt.addTextChangedListener(
object : TextWatcher {
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
if (s.isNullOrEmpty()) {
binding.closeIv.visibility = View.GONE
} else {
binding.closeIv.visibility = View.VISIBLE
}
searchText = s.toString()
viewModel.updateSearchText(s.toString())
}
}
)
binding.searchEt.setOnKeyListener { _, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_ENTER) {
context?.let { hideKeyboard(it, binding.searchEt) }
}
false
}
binding.closeIv.setOnClickListener {
binding.searchEt.text.clear()
viewModel.updateSearchText("")
}
viewModel.fetchSearchResults()
lifecycleScope.launchWhenStarted {
launch { viewModel.searchResults.collectLatest { setViewState(it) } }
}
}
private fun showFaqs(faqList: List<QuestionAnswerList>?) {
faqList?.forEach { faqs ->
faqs.isFirstItemInList = false
faqs.isLastItemInList = false
}
if (faqList?.isNotEmpty() == true) {
faqList.first().isFirstItemInList = true
faqList.last().isLastItemInList = true
}
(binding.resultsRv.adapter as? SupportScreenFaqAdapter)?.updateFaqList(faqList)
}
private fun readArguments() {
arguments?.getParcelable<Header>(HEADER_DATA)?.let { header = it }
arguments?.getParcelable<ActionData>(CHAT_WITH_US_ACTION_DATA)?.let {
chatWithUsActionData = it
}
arguments?.getString(SEARCH_BAR_HINT)?.let { searchBarHint = it }
arguments?.getDouble(SEARCH_THRESHOLD_LIMIT)?.let { searchThresholdLimit = it }
}
private fun showRecentSearches() {
val recentSearchesList: List<FaqRecentSearchData> =
PreferenceManager.getObjectPrefrences(
CommonPrefConstants.FAQ_RECENT_SEARCH,
FaqRecentSearchesList::class.java
)
?.recentSearches
.orEmpty()
if (recentSearchesList.isNotEmpty()) {
binding.recentSearchesView.root.visibility = View.VISIBLE
binding.recentSearchesView.heading.showWhenDataIsAvailable(
getString(R.string.recent_searches)
)
recentSearchesAdapter.updateData(recentSearchesList)
binding.recentSearchesView.rvRecentSearches.adapter = recentSearchesAdapter
} else {
binding.recentSearchesView.root.visibility = View.GONE
}
}
override fun setProperties(header: Header?) {
binding.headerView.setProperties(header, this)
}
private fun setViewState(responseState: FaqSearchResultState) {
binding.apply {
loaderView.stopShimmer()
loaderView.isVisible = false
recentSearchesView.root.isVisible = false
resultsRv.isVisible = false
emptyView.root.isVisible = false
errorView.root.isVisible = false
when (responseState) {
is FaqSearchResultState.RecentSearches -> {
showRecentSearches()
}
is FaqSearchResultState.Loading -> {
loaderView.isVisible = true
loaderView.startShimmer()
}
is FaqSearchResultState.Success -> {
resultsRv.isVisible = true
showFaqs(responseState.searchResultList)
crmEventTracker.sendEvent(
FAQ_SEARCHED_WITH_RESULTS,
hashMapOf(FAQ_SEARCH_QUERY_TEXT to searchText.toString())
)
}
is FaqSearchResultState.Empty -> {
emptyView.root.isVisible = true
crmEventTracker.sendEvent(
FAQ_SEARCHED_WITH_NO_RESULTS,
hashMapOf(FAQ_SEARCH_QUERY_TEXT to searchText.toString())
)
}
is FaqSearchResultState.Error -> {
errorView.root.isVisible = true
}
}
}
}
private fun setEmptyScreenData(actionData: ActionData?) {
binding.apply {
emptyView.cta.underlinedText()
emptyView.cta.setOnClickListener {
actionData?.primaryAction?.let { actionData ->
chatCtaClickListener?.onChatCtaClick(actionData)
}
}
}
}
override fun onBackPressed() {
val fragmentManager = requireActivity().supportFragmentManager
fragmentManager.popBackStackImmediate()
}
override fun onTopPressed(actionData: ActionData?, exitBottomSheetData: GenericErrorResponse?) {
onBackPressed()
}
fun post(delay: Long = 0, task: () -> Unit) {
Handler(Looper.getMainLooper())
.postDelayed(
{
if (view == null || requireView().visibility != View.VISIBLE) return@postDelayed
task()
},
delay
)
}
private fun openKeyboard(view: View, onFinish: () -> Unit) {
post(ANIMATION_DURATION) {
val imm = context?.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
post(ANIMATION_DURATION, onFinish)
}
}
override fun onRecentSearchClick(recentSearch: String) {
binding.searchEt.setText(recentSearch)
viewModel.updateSearchText(recentSearch)
}
override fun onStop() {
super.onStop()
viewModel.updateSearchText("")
}
override val screenName: String
get() = TAG
companion object {
const val TAG = "FaqSearchFragment"
const val HEADER_DATA = "HEADER_DATA"
const val CHAT_WITH_US_ACTION_DATA = "CHAT_WITH_US_ACTION_DATA"
const val SEARCH_BAR_HINT = "SEARCH_BAR_HINT"
const val SEARCH_THRESHOLD_LIMIT = "SEARCH_THRESHOLD_LIMIT"
}
}

View File

@@ -13,7 +13,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.navi.chat.R
import com.navi.chat.databinding.FragmentSupportScreenFaqBinding
@@ -34,8 +34,14 @@ class SupportScreenFaqFragment : BaseFragment() {
private var helpOptionsClickListener: HelpOptionsClickListener? = null
private var faqClickListener: FaqClickListener? = null
private var ctaUnderFaqClickListener: FaqCtaClickListener? = null
private val supportScreenSharedViewModel by lazy {
ViewModelProvider(requireActivity())[SupportScreenSharedViewModel::class.java]
private val supportScreenSharedViewModel: SupportScreenSharedViewModel by activityViewModels()
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
supportScreenTab?.let { name ->
val content = supportScreenSharedViewModel.getTabContent(name)
content?.let { showFaqs(content.faqList, content.helpOptionsList) }
}
}
override fun onAttach(context: Context) {
@@ -85,6 +91,10 @@ class SupportScreenFaqFragment : BaseFragment() {
}
private fun showFaqs(faqList: List<QuestionAnswerList>?, helpOptionsList: List<HelpOptions>?) {
faqList?.forEach {
it.isFirstItemInList = false
it.isLastItemInList = false
}
if (faqList?.isNotEmpty() == true) {
faqList.first().isFirstItemInList = true
faqList.last().isLastItemInList = true
@@ -102,8 +112,10 @@ class SupportScreenFaqFragment : BaseFragment() {
onOptionClicked = { actionData, title ->
helpOptionsClickListener?.onHelpOptionClick(actionData, title)
},
onFaqClicked = { question -> faqClickListener?.onFaqClick(question) },
onCtaUnderFaqClicked = { ctaInfo -> ctaUnderFaqClickListener?.onCtaClick(ctaInfo) }
onFaqClicked = { question -> faqClickListener?.onFaqClick(question, null) },
onFaqPosition = { position -> binding.faqRv.smoothScrollToPosition(position) },
onCtaUnderFaqClicked = { ctaInfo -> ctaUnderFaqClickListener?.onCtaClick(ctaInfo) },
lifecycle
)
binding.faqRv.adapter = supportScreenFaqListAdapter
}

View File

@@ -24,6 +24,7 @@ const val CSAT_ANIMATION_DURATION = 400
const val CSAT_TOP_MARGIN = 60
const val SPINNER_DISABLED_ALPHA_VALUE = 0.5f
const val PROGRESS_LOADER_PURPLE_LOTTIE = "progress_loader_purple.lottie"
const val ANIMATION_DURATION: Long = 350
/* Event names */
const val CSAT_SURVEY_LAUNCHED = "chat_CSAT_survey_launched"
@@ -38,6 +39,10 @@ const val HOME_SCREEN = "home_screen"
const val CHAT_INITIATED = "chat_initiated"
const val TICKET_TAB_CLICKED = "ticket_tab_clicked"
const val TICKET_CLICKED = "ticket_clicked"
const val FAQ_SEARCH_DEBOUNCE_DURATION = 500L
const val FAQ_SEARCH_QUERY_MIN_LENGTH = 3
const val RECENT_SEARCHES_LIST_SIZE = 5
const val FAQ_SEARCH_THRESHOLD_LIMIT = 0.75
/* Event param constants */
const val TRACKING_UUID = "uuid"

View File

@@ -32,6 +32,7 @@ class NaviChatAnalytics private constructor() {
companion object {
const val CUSTOMER_ID = "customer_id"
const val PHONE_NUMBER = "phone_number"
const val HELP_CENTER_TOUCHPOINT_CLICK = "help_center_touchpoint_click"
const val HELP_TOUCHPOINT_CLICK = "help_touchpoint_click"
const val ACTIVE_CHAT_CLICK = "active_chat_click"
@@ -62,6 +63,8 @@ class NaviChatAnalytics private constructor() {
const val CHAT_ATTACHMENT_PREVIEW_AFTER_SEND = "chat_attachment_preview_after_send"
const val CHAT_SCREEN_CLOSES = "chat_screen_closes"
const val CHAT_DEEPLINK_CLICK = "chat_deeplink_click"
const val VIDEO_FAQ_CLICK = "video_faq_click"
const val NO_CONNECTIVITY_VIDEO_FAQ_PLACEHOLDER = "no_connectivity_video_faq_placeholder"
const val TICKET_ID = "ticket_id"
const val NEW_CHAT_TRIGGERED = "new_chat_triggered"
const val EXISTING_CHAT_TRIGGERED = "existing_chat_triggered"
@@ -103,6 +106,11 @@ class NaviChatAnalytics private constructor() {
const val FAQ_CTA_BOTTOM_SHEET_CONTINUE_CLICK = "faq_cta_bottom_sheet_continue_click"
const val FAQ_CTA_BOTTOM_SHEET_GO_BACK_CLICK = "faq_cta_bottom_sheet_go_back_click"
const val FAQ_TAB_SWITCH = "faq_tab_switch"
const val FAQ_CLICK_FROM_SEARCH_SCREEN = "faq_click_from_search_screen"
const val FAQ_SEARCH_SCREEN_VIEWED = "faq_search_screen_viewed"
const val FAQ_SEARCHED_WITH_RESULTS = "faq_searched_with_results"
const val FAQ_SEARCHED_WITH_NO_RESULTS = "faq_searched_no_results"
const val FAQ_SEARCH_QUERY_TEXT = "faq_search_query_text"
val naviChatAnalytics: NaviChatAnalytics by lazy { Holder.INSTANCE }
}

View File

@@ -0,0 +1,23 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.viewholder
import androidx.recyclerview.widget.RecyclerView
import com.navi.chat.databinding.RecentSearchOptionLayoutBinding
import com.navi.chat.models.FaqRecentSearchData
class RecentSearchOptionVH(private var binding: RecentSearchOptionLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(recentSearchItemDetail: FaqRecentSearchData, onClickRecentSearch: (String) -> Unit) {
binding.apply {
title.text = recentSearchItemDetail.title
title.setOnClickListener() { onClickRecentSearch.invoke(recentSearchItemDetail.title) }
}
}
}

View File

@@ -0,0 +1,208 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.chat.viewmodels
import androidx.lifecycle.viewModelScope
import com.navi.base.utils.isNotNull
import com.navi.chat.models.response.QuestionAnswerList
import com.navi.chat.repositories.NaviChatRepository
import com.navi.chat.utils.FAQ_SEARCH_DEBOUNCE_DURATION
import com.navi.chat.utils.FAQ_SEARCH_QUERY_MIN_LENGTH
import com.navi.chat.utils.FAQ_SEARCH_THRESHOLD_LIMIT
import com.navi.common.utils.log
import com.navi.common.viewmodel.BaseVM
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class FaqSearchScreenViewModel
@Inject
constructor(private val naviChatRepository: NaviChatRepository) : BaseVM() {
private val _searchResults = MutableSharedFlow<FaqSearchResultState>()
val searchResults =
_searchResults.asSharedFlow().onStart { emit(FaqSearchResultState.RecentSearches) }
private val _faqTextSearch = MutableStateFlow("")
private val faqTextSearch: StateFlow<String> = _faqTextSearch.asStateFlow()
private var allFaqsList: List<QuestionAnswerList>? = null
private var searchThresholdLimit = FAQ_SEARCH_THRESHOLD_LIMIT
@OptIn(FlowPreview::class)
fun fetchSearchResults() {
viewModelScope.launch {
_faqTextSearch.debounce(FAQ_SEARCH_DEBOUNCE_DURATION).collectLatest {
if (faqTextSearch.value.length < FAQ_SEARCH_QUERY_MIN_LENGTH) {
_searchResults.emit(FaqSearchResultState.RecentSearches)
return@collectLatest
} else {
_searchResults.emit(FaqSearchResultState.Loading)
if (allFaqsList != null && faqTextSearch.value.isNotNull()) {
val response =
withContext(Dispatchers.IO) {
processFaqListWithQuery(allFaqsList, faqTextSearch.value)
}
if (response == null) _searchResults.emit(FaqSearchResultState.Error)
else {
if (response.isEmpty()) {
_searchResults.emit(FaqSearchResultState.Empty)
} else {
_searchResults.emit(FaqSearchResultState.Success(response))
}
}
}
}
}
}
}
fun updateSearchText(searchText: String?) {
val trimmedSearchText = searchText?.trim()
trimmedSearchText?.let { _faqTextSearch.value = it }
}
fun setFaqList(faqList: List<QuestionAnswerList>?) {
allFaqsList = faqList
viewModelScope.launch { _searchResults.emit(FaqSearchResultState.RecentSearches) }
}
fun setSearchThresholdLimit(thresholdLimit: Double) {
searchThresholdLimit = thresholdLimit
}
private fun calculateMinDistance(queryWord: String, question: String?, answer: String?): Int {
try {
if (question == null && answer == null) {
return Int.MAX_VALUE
}
var wordsInQuestion = question?.split("\\s+".toRegex()) ?: emptyList()
wordsInQuestion =
wordsInQuestion.map { word -> word.replace("[^a-zA-Z0-9]".toRegex(), "") }
var wordsInAnswer = answer?.split("\\s+".toRegex()) ?: emptyList()
wordsInAnswer = wordsInAnswer.map { word -> word.replace("[^a-zA-Z0-9]".toRegex(), "") }
val distances = mutableListOf<Int>()
for (wordInQuestion in wordsInQuestion) {
if (wordInQuestion.contains(queryWord, ignoreCase = true)) {
distances.add(0)
} else {
val distanceToQuery = calculateLevenshteinDistance(queryWord, wordInQuestion)
distances.add(distanceToQuery)
}
}
for (wordInAnswer in wordsInAnswer) {
if (wordInAnswer.contains(queryWord, ignoreCase = true)) {
distances.add(0)
} else {
val distanceToQuery = calculateLevenshteinDistance(queryWord, wordInAnswer)
distances.add(distanceToQuery)
}
}
return distances.minOrNull() ?: 0
} catch (e: Exception) {
e.log()
return 0
}
}
private fun processFaqListWithQuery(
faqList: List<QuestionAnswerList>?,
query: String
): List<QuestionAnswerList>? {
try {
faqList?.forEach { faq ->
val distances = mutableListOf<Int>()
query.split("\\s+".toRegex()).forEach { queryWord ->
if (queryWord.length >= FAQ_SEARCH_QUERY_MIN_LENGTH) {
val minDistance =
calculateMinDistance(queryWord, faq.question?.text, faq.answer?.text)
distances.add(minDistance)
}
}
faq.distance = distances
faq.score = distances.sum()
}
val sortedFaqs = faqList?.sortedBy { it.score }
val threshold = getThresholdForQueryText(query)
return sortedFaqs?.filter { it.score <= threshold }
} catch (e: Exception) {
e.log()
return null
}
}
private fun getThresholdForQueryText(query: String): Double {
val words = query.split("\\s+".toRegex())
var threshold = 0.0
for (word in words) {
threshold += word.length * searchThresholdLimit
}
return threshold
}
private fun calculateLevenshteinDistance(source: String, target: String): Int {
try {
val sourceString = source.lowercase()
val targetString = target.lowercase()
val sourceLength = sourceString.length + 1
val targetLength = targetString.length + 1
var currentCosts = IntArray(sourceLength)
var newCosts = IntArray(sourceLength)
for (i in 0 until sourceLength) {
currentCosts[i] = i
}
for (j in 1 until targetLength) {
newCosts[0] = j
for (i in 1 until sourceLength) {
if (sourceString[i - 1] == targetString[j - 1]) {
newCosts[i] = currentCosts[i - 1]
} else {
newCosts[i] =
minOf(currentCosts[i - 1] + 1, newCosts[i - 1] + 1, currentCosts[i] + 1)
}
}
val swap = currentCosts
currentCosts = newCosts
newCosts = swap
}
return currentCosts[sourceLength - 1]
} catch (e: Exception) {
e.log()
return 0
}
}
}
sealed class FaqSearchResultState {
data object Loading : FaqSearchResultState()
data object Empty : FaqSearchResultState()
data class Success(val searchResultList: List<QuestionAnswerList>?) : FaqSearchResultState()
data object Error : FaqSearchResultState()
data object RecentSearches : FaqSearchResultState()
}

View File

@@ -8,20 +8,39 @@
package com.navi.chat.viewmodels
import androidx.lifecycle.MutableLiveData
import com.navi.base.model.ActionData
import com.navi.chat.base.ChatBaseVM
import com.navi.chat.models.response.CtaBottomSheetData
import com.navi.chat.models.response.QuestionAnswerList
import com.navi.chat.models.response.SupportScreenTabsData
import com.navi.chat.models.response.TabContent
import com.navi.chat.utils.FAQ_SEARCH_THRESHOLD_LIMIT
import com.navi.common.model.Header
class SupportScreenSharedViewModel : ChatBaseVM() {
private val _faqTabData = MutableLiveData<List<SupportScreenTabsData>>()
private val _ctaUnderFaqBottomSheetData = MutableLiveData<CtaBottomSheetData>()
private val _headerData = MutableLiveData<Header>()
private val _footerActionData = MutableLiveData<ActionData>()
private val _searchBarHint = MutableLiveData<String>()
private val _combinedFaqList = MutableLiveData<List<QuestionAnswerList>?>()
private val _searchThresholdLimit = MutableLiveData<Double>()
val combinedFaqList: MutableLiveData<List<QuestionAnswerList>?>
get() = _combinedFaqList
fun setTabData(list: List<SupportScreenTabsData>) {
_faqTabData.value = list
}
fun getTabData(): List<SupportScreenTabsData>? = _faqTabData.value
fun setCombinedFaqList() {
val contentList = getTabData()?.map { it.content }
val combinedFaqs = contentList?.flatMap { it?.faqList.orEmpty() }
_combinedFaqList.value = combinedFaqs
}
fun getTabName(position: Int): String? = _faqTabData.value?.getOrNull(position)?.tabName
fun getTabContent(tabName: String): TabContent? {
@@ -35,4 +54,36 @@ class SupportScreenSharedViewModel : ChatBaseVM() {
fun getCtaUnderFaqBottomSheetData(): CtaBottomSheetData? {
return _ctaUnderFaqBottomSheetData.value
}
fun setHeaderData(headers: Header) {
_headerData.value = headers
}
fun getHeaderData(): Header? {
return _headerData.value
}
fun setFooterActionData(actionData: ActionData) {
_footerActionData.value = actionData
}
fun getFooterActionData(): ActionData? {
return _footerActionData.value
}
fun setSearchBarHint(hint: String) {
_searchBarHint.value = hint
}
fun getSearchBarHint(): String? {
return _searchBarHint.value
}
fun setSearchThresholdLimit(limit: Double) {
_searchThresholdLimit.value = limit
}
fun getSearchThresholdLimit(): Double {
return _searchThresholdLimit.value ?: FAQ_SEARCH_THRESHOLD_LIMIT
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="4dp" />
<stroke
android:width="1dp"
android:color="@color/ctaPurplePrimaryColor" />
</shape>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="4dp" />
<stroke
android:width="1dp"
android:color="@color/border_dark_grey_color" />
</shape>

View File

@@ -0,0 +1,14 @@
<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="M15.589,15.591C15.264,15.916 14.736,15.916 14.41,15.591L4.41,5.591C4.085,5.265 4.085,4.737 4.41,4.412C4.736,4.087 5.263,4.087 5.589,4.412L15.589,14.412C15.914,14.738 15.914,15.265 15.589,15.591Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M15.589,4.412C15.914,4.737 15.914,5.265 15.589,5.591L5.589,15.591C5.263,15.916 4.736,15.916 4.41,15.591C4.085,15.265 4.085,14.738 4.41,14.412L14.41,4.412C14.736,4.087 15.264,4.087 15.589,4.412Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,414 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="170dp"
android:height="97dp"
android:viewportWidth="170"
android:viewportHeight="97">
<path
android:pathData="M156.35,83.32C155.81,84.38 154.7,85.02 153.51,85.02L144.87,85.22C144.87,85.22 139.99,85.42 140.2,74.44C140.2,74.44 140.4,62.24 147.72,62.04C147.72,62.04 155.85,61.43 157.07,74.03C157.07,74.03 157.95,80.18 156.35,83.31V83.32Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="149.35"
android:startY="88.94"
android:endX="147.7"
android:endY="54.91"
android:type="linear">
<item android:offset="0" android:color="#FFE1EBF4"/>
<item android:offset="1" android:color="#00F0F6FE"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M147.52,90.73L147.91,72.42C147.91,72.38 147.95,72.38 147.96,72.42L148.36,79.5C148.36,79.51 148.38,79.52 148.39,79.52C148.64,79.43 150.29,78.8 151.08,76.92C151.1,76.89 151.14,76.9 151.13,76.93C150.99,77.55 150.44,79.16 148.34,80.13C148.34,80.14 148.33,80.15 148.33,80.16L148.53,90.74C148.53,90.74 148.52,90.77 148.51,90.77H147.54C147.54,90.77 147.51,90.75 147.52,90.74V90.73Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="149.32"
android:startY="92.48"
android:endX="149.32"
android:endY="70.93"
android:type="linear">
<item android:offset="0.03" android:color="#FFCEDCEA"/>
<item android:offset="1" android:color="#00E4ECF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M30.13,86.14C29.8,86.8 29.1,87.2 28.36,87.2L23.01,87.32C23.01,87.32 19.98,87.45 20.1,80.63C20.1,80.63 20.23,73.07 24.77,72.94C24.77,72.94 29.82,72.56 30.57,80.38C30.57,80.38 31.12,84.2 30.13,86.14V86.14Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="25.79"
android:startY="89.63"
android:endX="24.76"
android:endY="68.52"
android:type="linear">
<item android:offset="0" android:color="#FFE1EBF4"/>
<item android:offset="1" android:color="#00F0F6FE"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M24.65,90.74L24.89,79.38C24.89,79.36 24.92,79.36 24.92,79.38L25.17,83.77C25.17,83.77 25.18,83.79 25.19,83.79C25.34,83.73 26.36,83.34 26.86,82.17C26.87,82.15 26.89,82.16 26.89,82.18C26.81,82.56 26.46,83.56 25.16,84.17C25.16,84.17 25.15,84.17 25.15,84.18L25.28,90.74C25.28,90.74 25.27,90.76 25.26,90.76H24.66C24.66,90.76 24.65,90.75 24.65,90.74H24.65Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="25.77"
android:startY="91.83"
android:endX="25.77"
android:endY="78.46"
android:type="linear">
<item android:offset="0.03" android:color="#FFCEDCEA"/>
<item android:offset="1" android:color="#00E4ECF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M58.57,8.26H57.84V7.92C57.84,7.1 57.62,6.31 57.18,5.62C56.75,4.93 56.13,4.39 55.39,4.04C54.66,3.69 53.84,3.55 53.03,3.65C52.23,3.75 51.47,4.07 50.84,4.59C50.29,3.14 49.25,1.92 47.9,1.15C46.55,0.39 44.97,0.12 43.44,0.39C41.91,0.67 40.53,1.47 39.53,2.67C38.53,3.86 37.99,5.36 37.98,6.91C37.98,7.42 38.04,7.94 38.16,8.43C37.35,8.69 36.66,9.23 36.2,9.95C35.76,10.68 35.58,11.54 35.71,12.38C35.85,13.22 36.28,13.99 36.93,14.54C37.58,15.09 38.41,15.4 39.26,15.39H58.57C59.04,15.41 59.52,15.33 59.97,15.16C60.42,14.98 60.83,14.73 61.17,14.39C61.51,14.06 61.79,13.66 61.98,13.22C62.17,12.78 62.26,12.31 62.26,11.82C62.26,11.34 62.16,10.87 61.98,10.43C61.79,9.99 61.51,9.59 61.17,9.26C60.83,8.93 60.42,8.67 59.97,8.49C59.52,8.32 59.04,8.24 58.57,8.26L58.57,8.26Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="47.45"
android:startY="20.65"
android:endX="51.11"
android:endY="-0.56"
android:type="linear">
<item android:offset="0" android:color="#FFE2EDF7"/>
<item android:offset="1" android:color="#00F0F6FE"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M137.74,31.51H138.22V31.29C138.22,30.74 138.38,30.21 138.67,29.75C138.96,29.29 139.38,28.92 139.87,28.69C140.36,28.45 140.91,28.36 141.45,28.42C141.99,28.49 142.5,28.7 142.93,29.04C143.3,28.06 144,27.24 144.91,26.72C145.82,26.2 146.89,26.02 147.92,26.2C148.95,26.39 149.88,26.93 150.56,27.74C151.23,28.54 151.6,29.56 151.6,30.61C151.6,30.95 151.56,31.3 151.48,31.64C152.03,31.81 152.5,32.17 152.8,32.66C153.11,33.15 153.22,33.73 153.13,34.29C153.04,34.86 152.76,35.38 152.32,35.75C151.88,36.12 151.33,36.33 150.75,36.33H137.74C137.42,36.34 137.09,36.29 136.79,36.17C136.49,36.05 136.21,35.88 135.98,35.65C135.75,35.43 135.57,35.16 135.44,34.86C135.31,34.56 135.24,34.25 135.24,33.92C135.24,33.6 135.31,33.28 135.44,32.98C135.56,32.68 135.75,32.42 135.98,32.19C136.21,31.96 136.49,31.79 136.79,31.68C137.09,31.56 137.42,31.51 137.74,31.52H137.74V31.51Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="144.2"
android:startY="39.85"
android:endX="144.2"
android:endY="20.93"
android:type="linear">
<item android:offset="0" android:color="#FFE2EDF7"/>
<item android:offset="1" android:color="#00F0F6FE"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M30.52,95.78C30.52,95.78 101.48,74.58 168.45,96.18L30.52,95.78Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="163.07"
android:startY="86.94"
android:endX="45.67"
android:endY="101.18"
android:type="linear">
<item android:offset="0" android:color="#FFDAE6EF"/>
<item android:offset="1" android:color="#00F3F8FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M1.33,95.4C1.33,95.4 73.72,74.2 140.69,95.79L1.33,95.4Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="6.16"
android:startY="89.8"
android:endX="105.81"
android:endY="92.62"
android:type="linear">
<item android:offset="0" android:color="#FFDAE6EF"/>
<item android:offset="1" android:color="#00F3F8FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M82.95,91.8L37.75,88.9L53.79,34.84L94.56,44.31L82.95,91.8Z"
android:fillColor="#A5B9D3"/>
<path
android:pathData="M91.2,44.31L56.08,36.83L49.36,32.86L91.96,42.33L91.2,44.31Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="83.61"
android:startY="41.49"
android:endX="54.34"
android:endY="35.19"
android:type="linear">
<item android:offset="0" android:color="#FFCEDCEA"/>
<item android:offset="0.79" android:color="#FFF3F8FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M115.61,50.84C115.61,65.47 106.17,77.34 94.53,77.34C82.89,77.34 73.45,65.47 73.45,50.84C73.45,47.23 74.02,43.79 75.06,40.66C75.53,39.24 76.09,37.89 76.74,36.61C76.76,36.56 76.79,36.5 76.82,36.45C77.37,35.39 77.97,34.38 78.63,33.44C79.43,32.28 80.3,31.22 81.25,30.26C84.87,26.55 89.49,24.34 94.53,24.34C99.18,24.34 103.47,26.22 106.95,29.42C108.07,30.46 109.11,31.62 110.05,32.9C110.57,33.62 111.06,34.37 111.52,35.15C111.55,35.2 111.58,35.24 111.6,35.29C114.12,39.66 115.61,45.03 115.61,50.84Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="94.53"
android:startY="73.15"
android:endX="94.53"
android:endY="28.22"
android:type="linear">
<item android:offset="0" android:color="#FFE8EEF9"/>
<item android:offset="1" android:color="#FFC9D8F1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M94.53,77.34C103.91,77.34 111.52,67.49 111.52,55.34C111.52,43.19 103.91,33.34 94.53,33.34C85.15,33.34 77.54,43.19 77.54,55.34C77.54,67.49 85.15,77.34 94.53,77.34Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="94.66"
android:startY="62.46"
android:endX="94.33"
android:endY="43.84"
android:type="linear">
<item android:offset="0" android:color="#FFFFFFFF"/>
<item android:offset="1" android:color="#FFE4E9F2"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M104.12,7.69C104.59,6.94 106.38,5.45 106.98,5.16L115.9,12.05C114.49,14.81 115.21,14.06 113.26,15.76C112.85,16.11 112.27,16.22 111.75,16.02C107.73,14.53 105.05,12.16 103.89,8.81C103.75,8.42 103.91,8.04 104.12,7.69Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="112.94"
android:startY="12.07"
android:endX="106.21"
android:endY="9.21"
android:type="linear">
<item android:offset="0" android:color="#FFE8EEF9"/>
<item android:offset="1" android:color="#FFC9D8F1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M107.87,6.35L114.71,11.15C113.98,14.48 110.97,15.71 110.97,15.71C105.76,13.52 104.47,10.09 104.47,10.09C105.18,7.23 107.87,6.35 107.87,6.35Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="105.61"
android:startY="10.84"
android:endX="111.52"
android:endY="11.05"
android:type="linear">
<item android:offset="0" android:color="#FFE8EEF9"/>
<item android:offset="1" android:color="#FFC9D8F1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M85.24,7.52C84.9,11.05 82.86,13.99 79.3,16.39C78.85,16.69 78.25,16.73 77.77,16.48C75.48,15.29 76.35,15.85 74.33,13.5L81.38,4.7C82.03,4.85 84.12,5.87 84.75,6.49C85.04,6.78 85.28,7.11 85.24,7.52L85.24,7.52Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="77.2"
android:startY="12.91"
android:endX="83.06"
android:endY="8.54"
android:type="linear">
<item android:offset="0" android:color="#FFE8EEF9"/>
<item android:offset="1" android:color="#FFC9D8F1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M84.97,8.9C84.97,8.9 84.53,12.54 79.98,15.9C79.98,15.9 76.77,15.41 75.27,12.35L80.79,6.07C80.79,6.07 83.61,6.3 84.97,8.9Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="84.03"
android:startY="9.98"
android:endX="78.34"
android:endY="11.58"
android:type="linear">
<item android:offset="0" android:color="#FFE8EEF9"/>
<item android:offset="1" android:color="#FFC9D8F1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M114.83,28.25C114.51,49.24 73.4,48.62 73.72,27.63C73.89,21.95 80.31,9.02 83.81,7.47C91.44,4.86 98.62,4.97 105.36,7.79C109.73,9.69 114.97,20.74 114.83,28.25Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="104.79"
android:startY="32.85"
android:endX="79.86"
android:endY="13.69"
android:type="linear">
<item android:offset="0" android:color="#FFE8EEF9"/>
<item android:offset="1" android:color="#FFC9D8F1"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M114.46,31.34C110.59,48.16 77.35,47.65 73.99,30.73C75.29,31.7 76.94,32.29 78.73,32.31C81.51,32.36 83.97,31.04 85.38,29.02C85.67,28.62 85.91,28.19 86.1,27.74C86.48,26.84 87.03,26.02 87.77,25.38C89.45,23.94 91.77,23.07 94.34,23.11C96.9,23.15 99.37,24.16 101.01,25.75C101.66,26.37 102.12,27.16 102.45,27.99C102.62,28.44 102.84,28.87 103.11,29.27C104.46,31.35 106.88,32.75 109.67,32.79C111.46,32.82 113.13,32.28 114.46,31.35V31.34Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M88.65,20.87C88.72,22 87.99,22.99 87.01,23.05C86.03,23.12 85.23,22.22 85.22,21.09C85.21,20 85.94,19.09 86.86,19.03C87.78,18.98 88.58,19.78 88.65,20.87H88.65Z"
android:fillColor="#5B6A89"/>
<path
android:pathData="M85.79,20.17C85.66,20.95 86.09,21.72 86.77,21.87C87.45,22.01 88.1,21.43 88.17,20.62C88.24,19.84 87.77,19.14 87.13,19.04C86.5,18.94 85.91,19.43 85.79,20.18V20.17Z"
android:fillColor="#1F365F"/>
<path
android:pathData="M87.83,19.9C87.84,19.51 87.53,19.19 87.14,19.18C86.75,19.18 86.44,19.49 86.43,19.87C86.42,20.26 86.73,20.58 87.12,20.58C87.51,20.59 87.82,20.28 87.83,19.9Z"
android:fillColor="#FEFFFF"/>
<path
android:pathData="M103.81,21.22C103.88,22.35 103.14,23.36 102.16,23.42C101.17,23.48 100.36,22.58 100.35,21.44C100.34,20.34 101.08,19.43 102.01,19.37C102.93,19.31 103.74,20.12 103.81,21.22H103.81Z"
android:fillColor="#5B6A89"/>
<path
android:pathData="M100.92,20.51C100.79,21.3 101.23,22.08 101.91,22.22C102.6,22.36 103.25,21.78 103.32,20.95C103.39,20.17 102.92,19.47 102.28,19.37C101.64,19.26 101.04,19.76 100.92,20.51Z"
android:fillColor="#1F365F"/>
<path
android:pathData="M102.98,20.24C102.99,19.85 102.68,19.53 102.28,19.52C101.89,19.51 101.57,19.83 101.57,20.22C101.56,20.61 101.87,20.93 102.26,20.93C102.65,20.94 102.97,20.63 102.98,20.24Z"
android:fillColor="#FEFFFF"/>
<path
android:pathData="M87.86,14.43C87.86,14.43 87.77,16.87 84.26,17.26"
android:strokeWidth="1.008"
android:fillColor="#00000000"
android:strokeColor="#8EA2CF"
android:strokeLineCap="round"/>
<path
android:pathData="M101.9,14.62C101.9,14.62 101.93,17.07 105.42,17.54"
android:strokeWidth="1.008"
android:fillColor="#00000000"
android:strokeColor="#8EA2CF"
android:strokeLineCap="round"/>
<path
android:pathData="M95.52,30.59C94.94,31.02 93.82,31.1 93.18,30.57C92.48,30.16 90.98,28.35 90.63,27.18C90.53,26.84 90.74,26.48 91.08,26.39C93.07,25.85 95.59,25.88 97.72,26.5C98.07,26.6 98.24,26.96 98.12,27.3C97.71,28.4 96.08,30.34 95.52,30.59H95.52Z"
android:fillColor="#6B7EA0"/>
<path
android:strokeWidth="1"
android:pathData="M94.29,35.12L94.35,29.39"
android:fillColor="#00000000"
android:strokeColor="#6B7EA0"
android:strokeLineCap="round"/>
<path
android:pathData="M73.91,13.81C76.07,9.42 78.52,6.45 81.34,4.67C81.5,4.57 81.72,4.68 81.73,4.87C82,9.99 80.88,15.77 78.6,19.25C78.32,19.66 77.7,19.88 77.23,19.7C75.45,19.03 74.54,16.83 73.85,14.91C73.74,14.6 73.77,14.11 73.92,13.82L73.91,13.81Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="80.58"
android:startY="9.57"
android:endX="75.35"
android:endY="17.73"
android:type="linear">
<item android:offset="0" android:color="#FFB7C5E6"/>
<item android:offset="1" android:color="#FF8EA2CF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M116.69,13.3C116.49,15.33 116.11,17.68 114.54,18.76C114.13,19.04 113.47,18.98 113.11,18.63C110.07,15.79 107.61,10.45 106.68,5.4C106.64,5.21 106.82,5.05 107,5.11C110.17,6.18 113.25,8.49 116.38,12.25C116.59,12.5 116.73,12.97 116.69,13.3V13.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="108.86"
android:startY="9.63"
android:endX="115.86"
android:endY="16.32"
android:type="linear">
<item android:offset="0" android:color="#FFB7C5E6"/>
<item android:offset="1" android:color="#FF8EA2CF"/>
</gradient>
</aapt:attr>
</path>
<path
android:strokeWidth="1"
android:pathData="M98.16,36.93C98.16,36.93 94.36,32.97 90.6,37.08"
android:fillColor="#00000000"
android:strokeColor="#6B7EA0"
android:strokeLineCap="round"/>
<path
android:pathData="M88.15,95.79L40.65,91.84C40.44,91.82 40.23,91.77 40.04,91.68C38.31,90.89 39.36,86.66 41.27,86.72L88.27,90.62C89.19,90.65 89.92,91.36 89.96,92.26L90.03,94.02C90.07,95.04 89.19,95.87 88.15,95.79H88.15Z"
android:fillColor="#B3C9E2"/>
<path
android:pathData="M129.71,40.65H54.21C52.63,40.65 51.35,41.93 51.35,43.52V92.75C51.35,94.34 52.63,95.62 54.21,95.62H129.71C131.3,95.62 132.58,94.34 132.58,92.75V43.52C132.58,41.93 131.3,40.65 129.71,40.65Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="92.09"
android:startY="101.08"
android:endX="91.89"
android:endY="50.25"
android:type="linear">
<item android:offset="0" android:color="#FFCEDCEA"/>
<item android:offset="1" android:color="#FFF3F8FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M57.33,40.65L51.35,62.46V43.52C51.35,41.93 52.63,40.65 54.21,40.65H57.33Z"
android:strokeAlpha="0.51"
android:fillColor="#A5B9D3"
android:fillAlpha="0.51"/>
<path
android:pathData="M56.11,37.15L41.93,90.28C41.71,91.1 40.8,91.52 40.03,91.16L34.73,88.65C34.14,88.37 33.83,87.7 34,87.07L48.18,33.94C48.4,33.11 49.31,32.69 50.08,33.06L55.37,35.57C55.97,35.85 56.28,36.52 56.11,37.15Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="37.15"
android:startY="91.89"
android:endX="51.63"
android:endY="37.36"
android:type="linear">
<item android:offset="0" android:color="#FFCEDCEA"/>
<item android:offset="0.79" android:color="#FFF3F8FF"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M100.13,47.06H83.8C82.78,47.06 81.96,47.88 81.96,48.89C81.96,49.9 82.78,50.72 83.8,50.72H100.13C101.14,50.72 101.96,49.9 101.96,48.89C101.96,47.88 101.14,47.06 100.13,47.06Z"
android:strokeAlpha="0.3"
android:fillColor="#A5B9D3"
android:fillAlpha="0.3"/>
<path
android:pathData="M80.38,40.65L73.9,60.88H65.55L68.57,40.65H80.38Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="77"
android:startY="35.58"
android:endX="66.08"
android:endY="64.33"
android:type="linear">
<item android:offset="0" android:color="#FFCEDCEA"/>
<item android:offset="1" android:color="#00E4ECF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M118.25,40.65L111.77,60.88H103.42L106.43,40.65H118.25Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="114.87"
android:startY="35.58"
android:endX="103.95"
android:endY="64.33"
android:type="linear">
<item android:offset="0" android:color="#FFCEDCEA"/>
<item android:offset="1" android:color="#00E4ECF9"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M81.44,39.06V39.57C81.44,40.28 80.87,40.85 80.16,40.85H69.17C68.47,40.85 67.9,40.28 67.9,39.57V39.06C67.9,35.72 70.6,33.01 73.94,33.01H75.39C78.74,33.01 81.44,35.72 81.44,39.06Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="71.38"
android:startY="42.82"
android:endX="80.15"
android:endY="31.31"
android:type="linear">
<item android:offset="0.36" android:color="#FFFFFFFF"/>
<item android:offset="1" android:color="#FFE6EBF4"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M119.3,39.31L119.29,39.82C119.28,40.53 118.7,41.09 117.99,41.08L107,40.92C106.3,40.91 105.73,40.33 105.74,39.62L105.75,39.11C105.8,35.77 108.55,33.1 111.89,33.15L113.34,33.17C116.68,33.23 119.35,35.97 119.3,39.31H119.3Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="114.08"
android:startY="42.5"
android:endX="109.16"
android:endY="31.04"
android:type="linear">
<item android:offset="0.2" android:color="#FFFFFFFF"/>
<item android:offset="1" android:color="#FFE6EBF4"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M1,95.64H169"
android:strokeLineJoin="round"
android:strokeWidth="1.19616"
android:fillColor="#00000000"
android:strokeColor="#A5B9D3"
android:strokeLineCap="round"/>
</vector>

View File

@@ -0,0 +1,14 @@
<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="M9.167,3.335C5.945,3.335 3.333,5.946 3.333,9.168C3.333,12.39 5.945,15.001 9.167,15.001C12.388,15.001 15,12.39 15,9.168C15,5.946 12.388,3.335 9.167,3.335ZM1.666,9.168C1.666,5.026 5.024,1.668 9.167,1.668C13.309,1.668 16.667,5.026 16.667,9.168C16.667,13.31 13.309,16.668 9.167,16.668C5.024,16.668 1.666,13.31 1.666,9.168Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M13.286,13.287C13.611,12.962 14.139,12.962 14.464,13.287L18.089,16.912C18.414,17.237 18.414,17.765 18.089,18.091C17.764,18.416 17.236,18.416 16.911,18.091L13.286,14.466C12.96,14.14 12.96,13.613 13.286,13.287Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,14 @@
<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="M9.167,3.335C5.945,3.335 3.333,5.946 3.333,9.168C3.333,12.39 5.945,15.001 9.167,15.001C12.388,15.001 15,12.39 15,9.168C15,5.946 12.388,3.335 9.167,3.335ZM1.666,9.168C1.666,5.026 5.024,1.668 9.167,1.668C13.309,1.668 16.667,5.026 16.667,9.168C16.667,13.31 13.309,16.668 9.167,16.668C5.024,16.668 1.666,13.31 1.666,9.168Z"
android:fillColor="#B5ACB9"
android:fillType="evenOdd"/>
<path
android:pathData="M13.286,13.287C13.611,12.962 14.139,12.962 14.464,13.287L18.089,16.912C18.414,17.237 18.414,17.765 18.089,18.091C17.764,18.416 17.236,18.416 16.911,18.091L13.286,14.466C12.96,14.14 12.96,13.613 13.286,13.287Z"
android:fillColor="#B5ACB9"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,77 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="54dp"
android:viewportWidth="64"
android:viewportHeight="54">
<path
android:pathData="M46.33,51.779L2.4,22.449L1.27,23.509C0.69,24.009 0.32,24.749 0.32,25.569V50.939C0.32,52.459 1.55,53.689 3.07,53.689H43.2C43.89,53.689 44.52,53.419 45,52.999L46.33,51.779Z"
android:strokeLineJoin="round"
android:strokeWidth="0.25"
android:fillColor="#CACDCC"
android:strokeColor="#A1A7A5"
android:strokeLineCap="round"/>
<path
android:pathData="M44.5,21.609H4.37C2.85,21.609 1.62,22.839 1.62,24.359V49.729C1.62,51.249 2.85,52.479 4.37,52.479H44.5C46.02,52.479 47.25,51.249 47.25,49.729V24.359C47.25,22.839 46.02,21.609 44.5,21.609Z"
android:fillColor="#F7F7F7"/>
<path
android:pathData="M47.16,23.679C42.97,31.969 34.36,37.649 24.43,37.649C14.5,37.649 5.91,31.969 1.71,23.669C2.01,22.489 3.09,21.609 4.37,21.609H44.5C45.78,21.609 46.85,22.489 47.16,23.679Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M33.15,37.041L18.4,28.531V45.561L33.15,37.051V37.041Z"
android:strokeWidth="0.25"
android:fillColor="#CACDCC"
android:strokeColor="#A1A7A5"/>
<path
android:pathData="M44.5,21.609H4.37C2.85,21.609 1.62,22.839 1.62,24.359V49.729C1.62,51.249 2.85,52.479 4.37,52.479H44.5C46.02,52.479 47.25,51.249 47.25,49.729V24.359C47.25,22.839 46.02,21.609 44.5,21.609Z"
android:strokeLineJoin="round"
android:strokeWidth="0.25"
android:fillColor="#00000000"
android:strokeColor="#A1A7A5"
android:strokeLineCap="round"/>
<path
android:pathData="M47.24,24.361V34.791H47.16C39.28,34.791 32.72,29.111 31.36,21.621H44.5C46.02,21.621 47.24,22.851 47.24,24.361Z"
android:strokeAlpha="0.2"
android:fillColor="#7A7F88"
android:fillAlpha="0.2"/>
<path
android:pathData="M47.24,32.349C55.86,32.349 62.86,25.349 62.86,16.729C62.86,8.109 55.87,1.109 47.24,1.109C38.61,1.109 31.62,8.109 31.62,16.729C31.62,25.349 38.62,32.349 47.24,32.349Z"
android:strokeLineJoin="round"
android:strokeWidth="0.25"
android:fillColor="#CACDCC"
android:strokeColor="#A1A7A5"
android:strokeLineCap="round"/>
<path
android:pathData="M48.06,31.549C56.68,31.549 63.68,24.549 63.68,15.929C63.68,7.309 56.69,0.309 48.06,0.309C39.43,0.309 32.44,7.309 32.44,15.929C32.44,24.549 39.44,31.549 48.06,31.549Z"
android:strokeLineJoin="round"
android:strokeWidth="0.25"
android:fillColor="#ffffff"
android:strokeColor="#A1A7A5"
android:strokeLineCap="round"/>
<path
android:pathData="M48.06,29.27C55.43,29.27 61.4,23.3 61.4,15.93C61.4,8.56 55.43,2.59 48.06,2.59C40.69,2.59 34.72,8.56 34.72,15.93C34.72,23.3 40.69,29.27 48.06,29.27Z"
android:strokeWidth="0.25"
android:fillColor="#FA5A5A"
android:strokeColor="#A1A7A5"/>
<path
android:pathData="M58.07,24.742C59.89,22.462 60.99,19.562 60.99,16.422C60.99,9.052 55.02,3.082 47.65,3.082C43.74,3.082 40.22,4.762 37.77,7.452C40.21,4.492 43.92,2.602 48.06,2.602C55.42,2.602 61.4,8.572 61.4,15.942C61.4,19.322 60.15,22.402 58.07,24.752V24.742Z"
android:strokeLineJoin="round"
android:strokeWidth="0.25"
android:fillColor="#A1A7A5"
android:strokeColor="#A1A7A5"
android:strokeLineCap="round"/>
<path
android:pathData="M48.06,24.082C48.982,24.082 49.73,23.334 49.73,22.412C49.73,21.49 48.982,20.742 48.06,20.742C47.138,20.742 46.39,21.49 46.39,22.412C46.39,23.334 47.138,24.082 48.06,24.082Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M48.82,17.448L47.8,19.218C47.88,19.208 47.98,19.208 48.06,19.208C49.18,19.208 50.16,19.778 50.73,20.648L52.04,19.338C51.27,18.338 50.12,17.638 48.82,17.458V17.448ZM44.08,19.338L45.39,20.648C45.71,20.178 46.14,19.788 46.65,19.538L47.89,17.398C46.35,17.448 44.97,18.198 44.09,19.338H44.08Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M50.69,14.209L49.75,15.849C51.16,16.189 52.39,16.989 53.3,18.089L54.6,16.789C53.58,15.599 52.23,14.689 50.7,14.209H50.69ZM48.06,13.789C45.46,13.789 43.11,14.959 41.53,16.789L42.83,18.089C44.08,16.579 45.96,15.629 48.06,15.629C48.33,15.629 48.61,15.639 48.87,15.689L49.86,13.979C49.28,13.859 48.68,13.789 48.06,13.789Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M52.49,11.078L51.56,12.688C53.21,13.278 54.66,14.288 55.8,15.578L57.1,14.278C55.84,12.878 54.26,11.778 52.49,11.078ZM48.06,10.258C44.48,10.258 41.25,11.808 39.03,14.288L40.33,15.588C42.23,13.448 44.99,12.088 48.07,12.088C49,12.088 49.9,12.218 50.76,12.438L51.7,10.808C50.56,10.438 49.33,10.258 48.07,10.258H48.06Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M53.135,8.542L51.75,7.742L42.755,23.322L44.14,24.122L53.135,8.542Z"
android:fillColor="#ffffff"/>
</vector>

View File

@@ -33,8 +33,7 @@
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="header_view,title_tv,content_layout,footer_cta"
app:constraint_referenced_ids="header_view,title_tv,content_layout,footer_layout"
tools:visibility="visible" />
<com.navi.common.customview.HeaderView
@@ -62,7 +61,7 @@
android:layout_height="@dimen/dp_0"
android:background="@color/white"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@id/footer_cta"
app:layout_constraintBottom_toTopOf="@id/footer_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title_tv">
@@ -79,14 +78,38 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_4">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/search_field"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_48"
android:layout_marginHorizontal="@dimen/dp_12"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_22"
android:background="@drawable/bg_rounded_rectangle_grey_border_4dp_radius"
android:drawableStart="@drawable/ic_search_grey"
android:drawablePadding="@dimen/dp_8"
android:focusableInTouchMode="false"
android:fontFamily="@font/tt_regular"
android:gravity="center_vertical"
android:hint="@string/search"
android:paddingHorizontal="@dimen/layout_dp_16"
android:paddingVertical="@dimen/dp_12"
android:textColor="@color/naviBlackText"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/support_screen_tl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/support_screen_tl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_8"
app:layout_goneMarginTop="@dimen/dp_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_field"
app:tabBackground="@null"
app:tabGravity="start"
app:tabIndicatorColor="@null"
@@ -97,19 +120,6 @@
app:tabPaddingTop="@dimen/dp_0"
app:tabRippleColor="@null" />
<com.navi.design.textview.NaviTextView
android:id="@+id/faq_tv"
style="@style/Title5BoldTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_12"
android:layout_marginTop="@dimen/dp_16"
android:paddingBottom="@dimen/dp_8"
android:textColor="@color/textPrimaryColor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/support_screen_tl"
tools:text="Frequently asked questions" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
@@ -123,24 +133,46 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.navi.design.textview.NaviTextView
android:id="@+id/footer_cta"
style="@style/Small_Bold_White"
android:layout_width="@dimen/dp_0"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/footer_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginVertical="@dimen/dp_24"
android:background="@drawable/rounded_new_positive_button"
android:gravity="center"
android:letterSpacing="0.02"
android:paddingVertical="@dimen/dp_14"
android:textColor="@color/white"
tools:visibility="visible"
android:background="@drawable/top_shadow_rectangle_white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/content_layout"
tools:text="Chat with us"
tools:visibility="visible" />
app:layout_constraintTop_toBottomOf="@id/content_layout">
<com.navi.design.textview.NaviTextView
android:id="@+id/footer_cta"
style="@style/Small_Bold_White"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_48"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginVertical="@dimen/dp_24"
android:background="@drawable/rounded_new_positive_button"
android:gravity="center"
android:letterSpacing="0.02"
android:paddingVertical="@dimen/dp_14"
android:textColor="@color/white"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Chat with us"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/search_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -71,11 +71,69 @@
app:layout_constraintTop_toBottomOf="@id/header_cl"
tools:text="You can use this insurance as a trial insurance till validity. This months premium has been paid by us and to continue using the benefits you have to pay the next months premium before policy expires." />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/player_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_12"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/description">
<com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
android:id="@+id/youtube_player_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/dp_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<include
android:id="@+id/network_error_layout"
android:visibility="gone"
tools:visibility="visible"
layout="@layout/network_error_video_faq_placeholder" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible"
app:shimmer_base_color="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_grey_rectangle" />
</com.facebook.shimmer.ShimmerFrameLayout>
</com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView>
<View
android:id="@+id/overlay_view"
android:layout_width="@dimen/dp_0"
android:layout_height="@dimen/dp_0"
android:clickable="true"
android:focusable="true"
android:background="@drawable/bg_rounded_corners_white"
app:layout_constraintBottom_toBottomOf="@id/youtube_player_view"
app:layout_constraintEnd_toEndOf="@id/youtube_player_view"
app:layout_constraintStart_toStartOf="@id/youtube_player_view"
app:layout_constraintTop_toTopOf="@id/youtube_player_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.navi.design.textview.NaviTextView
android:id="@+id/cta"
android:layout_width="@dimen/layout_dp_150"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_24"
android:background="@drawable/bg_rounded_white_blue_border_4"
android:fontFamily="@font/tt_medium"
android:gravity="center"
@@ -87,7 +145,8 @@
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/description"
app:layout_constraintTop_toBottomOf="@id/player_layout"
app:layout_goneMarginTop="@dimen/dp_24"
tools:text="Cams website" />
<com.airbnb.lottie.LottieAnimationView
@@ -129,5 +188,17 @@
app:layout_constraintTop_toBottomOf="@id/items"
app:layout_constraintWidth_percent="1" />
<include
android:id="@+id/faq_search_footer_section"
layout="@layout/faq_search_footer_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/section_divider" />
</LinearLayout>
</layout>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:id="@+id/recentSearchesLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<com.navi.design.textview.NaviTextView
android:id="@+id/heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_12"
android:fontFamily="@font/tt_medium"
android:textColor="@color/grey_charcoal"
android:textSize="@dimen/sp_16"
app:layout_constraintBottom_toBottomOf="@id/rvCsatOptions"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnPrimary"
tools:text="Recent Searches" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvRecentSearches"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:layout_marginStart="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@id/feedback"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/ratingLayout"
app:layout_goneMarginBottom="@dimen/layout_dp_32"
android:layout_marginEnd="@dimen/dp_16"
tools:itemCount="@integer/integer_5"
tools:listitem="@layout/recent_search_option_layout" />
</LinearLayout>
</layout>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/emptyCl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_fearful_dog" />
<com.navi.design.textview.NaviTextView
android:id="@+id/heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_24"
android:fontFamily="@font/tt_semi_bold"
android:textColor="@color/dark_gray"
android:text="@string/oops"
android:textSize="@dimen/sp_16" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subHeading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_4"
android:textColor="@color/titleTertiaryColor"
android:fontFamily="@font/tt_medium"
android:text="@string/we_haven_t_covered_this_in_our_faqs"
android:textSize="@dimen/sp_14" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_24"
android:fontFamily="@font/tt_regular"
android:text="@string/reach_out_to_our_support_team"
android:textSize="@dimen/sp_12"
app:fontFamily="@font/tt_regular" />
<com.navi.design.textview.NaviTextView
android:id="@+id/cta"
android:textColor="@color/ctaTitleDark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_6"
android:fontFamily="@font/tt_medium"
android:text="@string/chat_with_us"
android:textSize="@dimen/sp_14" />
</LinearLayout>
</layout>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/emptyCl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_68"
android:background="@color/white"
android:gravity="center"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ivIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_something_went_wrong" />
<com.navi.design.textview.NaviTextView
android:id="@+id/heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/dark_gray"
android:layout_marginTop="@dimen/layout_dp_16"
android:fontFamily="@font/tt_regular"
android:text="@string/error_screen_title"
android:textSize="@dimen/sp_16"
app:fontFamily="@font/tt_semi_bold" />
<com.navi.design.textview.NaviTextView
android:id="@+id/subHeading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_12"
android:fontFamily="@font/tt_medium"
android:textColor="@color/titleTertiaryColor"
android:text="@string/please_refresh_the_page"
android:textSize="@dimen/sp_14" />
</LinearLayout>
</layout>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="@dimen/layout_dp_2"
android:background="@drawable/ic_dotted_divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_16"
android:fontFamily="@font/tt_regular"
android:gravity="start"
android:text="@string/unable_to_find_what_you_are_looking_for"
android:textColor="@color/charcol_gray"
android:textSize="@dimen/sp_14"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@id/cta"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/divider" />
<com.navi.design.textview.NaviTextView
android:id="@+id/cta"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_5"
android:layout_marginBottom="@dimen/dp_16"
android:fontFamily="@font/tt_medium"
android:gravity="start"
android:text="@string/chat_with_us"
android:textColor="@color/ctaTitleDark"
android:textSize="@dimen/sp_14"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ * Copyright © 2024 by Navi Technologies Private Limited
~ * All rights reserved. Strictly confidential
~ */
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:clickable="true"
android:focusable="true"
tools:context=".ui.fragments.FaqSearchFragment">
<com.navi.common.customview.HeaderView
android:id="@+id/header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/loader_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_24"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_field">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/shimmer_layout_faq_search" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
<RelativeLayout
android:id="@+id/search_field"
android:layout_width="match_parent"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/dp_24"
android:background="@drawable/bg_rounded_rectangle_black_border_4dp_radius"
android:paddingHorizontal="@dimen/layout_dp_16"
app:layout_constraintTop_toBottomOf="@id/header_view">
<ImageView
android:id="@+id/search_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="@dimen/dp_12"
android:layout_centerVertical="true"
android:contentDescription="@string/search_icon"
android:src="@drawable/ic_search_faq" />
<EditText
android:id="@+id/search_et"
style="@style/TitleFontStyle"
android:textSize="@dimen/sp_14"
android:paddingVertical="@dimen/dp_12"
android:maxLength="@integer/integer_100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_dp_8"
android:layout_marginEnd="@dimen/layout_dp_10"
android:layout_toStartOf="@id/close_iv"
android:layout_toEndOf="@id/search_iv"
android:background="@null"
android:fontFamily="@font/tt_regular"
android:inputType="text" />
<ImageView
android:id="@+id/close_iv"
android:paddingVertical="@dimen/dp_12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:contentDescription="@string/clear_text"
android:src="@drawable/ic_clear_text"
android:visibility="gone" />
</RelativeLayout>
<include
android:id="@+id/recent_searches_view"
layout="@layout/faq_recent_searches_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_24"
app:layout_constraintTop_toBottomOf="@id/search_field" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/results_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_below="@id/search_field"
android:layout_marginTop="@dimen/layout_dp_16"
android:background="@color/white"
android:clipToPadding="false"
android:paddingBottom="@dimen/layout_dp_20"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_field" />
<include
android:id="@+id/error_view"
layout="@layout/faq_search_error_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_72"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_field" />
<include
android:id="@+id/empty_view"
layout="@layout/faq_search_empty_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_80"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_field" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_rounded_white_grey_border_4"
xmlns:app="http://schemas.android.com/apk/res-auto">
<View
android:id="@+id/no_internet_error_view"
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_70"
android:background="@drawable/no_connectivity_icon_faq"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/dp_24"/>
<com.navi.design.textview.NaviTextView
android:id="@+id/no_internet_error_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/no_internet_error_view"
app:layout_constraintBottom_toTopOf="@id/no_internet_error_sub_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_2"
app:layout_constrainedWidth="true"
android:gravity="center"
android:text="@string/unable_to_load_video"
android:textColor="@color/black"
android:fontFamily="@font/tt_medium" />
<com.navi.design.textview.NaviTextView
android:id="@+id/no_internet_error_sub_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="@dimen/dp_21"
android:layout_marginStart="@dimen/dp_48"
android:layout_marginEnd="@dimen/dp_48"
app:layout_constrainedWidth="true"
android:gravity="center"
android:text="@string/try_switching_internet_connection_or_try_again_later"
android:fontFamily="@font/tt_regular" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/recentSearchOption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="@dimen/dp_16"
tools:ignore="RtlSymmetry">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
style="@style/CsatOptionStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_16"
android:background="@drawable/bg_rounded_rectangle_grey_border_4dp_radius"
android:fontFamily="@font/tt_regular"
android:paddingHorizontal="@dimen/dp_12"
android:paddingVertical="@dimen/dp_8"
android:textSize="@dimen/sp_14"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Mutual Funds " />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ *
~ * * Copyright © 2022 by Navi Technologies Private Limited
~ * * All rights reserved. Strictly confidential
~ *
~ */
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content">
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_grey_rectangle" />
</LinearLayout>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ *
~ * * Copyright © 2024 by Navi Technologies Private Limited
~ * * All rights reserved. Strictly confidential
~ *
~ */
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_44"
android:layout_marginHorizontal="@dimen/dp_16"
android:background="@drawable/bg_grey_8dp_radius" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_44"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
android:background="@drawable/bg_grey_8dp_radius" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_44"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
android:background="@drawable/bg_grey_8dp_radius" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dp_88"
android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_32"
android:background="@drawable/bg_grey_8dp_radius" />
</LinearLayout>

View File

@@ -35,4 +35,16 @@
<string name="default_value">00:00</string>
<string name="audio_player">Audio Player</string>
<string name="audio_player_thumbnail">Audio Player Thumbnail</string>
<string name="unable_to_load_video">Unable to load video</string>
<string name="try_switching_internet_connection_or_try_again_later">Try switching internet connection or try again later</string>
<string name="recent_searches">Recent Searches</string>
<string name="please_refresh_the_page">Please refresh the page</string>
<string name="chat_with_us"><![CDATA[Chat with us >]]></string>
<string name="reach_out_to_our_support_team">In the meantime reach out to our support team</string>
<string name="oops">Oops!</string>
<string name="we_haven_t_covered_this_in_our_faqs">It looks like we haven\'t covered this in our FAQs.</string>
<string name="clear_text">clear_text</string>
<string name="search_icon">search_icon</string>
<string name="unable_to_find_what_you_are_looking_for">Unable to find what you are looking for ?</string>
<string name="search">Search…</string>
</resources>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ /*
~ * Copyright © 2019 by Navi Technologies Private Limited
~ * All rights reserved. Strictly confidential
~ */
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/strokeTertiaryColor" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="2dp" android:color="#FFFFFF"/>
<corners android:radius="6dp"/>
<padding android:left="8dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>