App update v2 (#5424)

* add app update layout

* add app update layout

* add api call

* add meta data

* clean code

* change event function
This commit is contained in:
Abhinav Gupta
2023-02-21 14:53:48 +05:30
committed by GitHub Enterprise
parent b70b388539
commit 1749fbf390
17 changed files with 311 additions and 15 deletions

View File

@@ -19,6 +19,6 @@ class ConfigRepository : ResponseCallback() {
suspend fun fetchFirebaseRefreshAuthToken() =
apiResponseCallback(superAppRetrofitService().refreshFirebaseAuthToken())
suspend fun fetchHomeItems(): RepoResult<WidgetResponse> =
apiResponseCallback(superAppRetrofitService().fetchHomeItems())
suspend fun fetchHomeItems(availableAppVersionCode: Int?): RepoResult<WidgetResponse> =
apiResponseCallback(superAppRetrofitService().fetchHomeItems(availableAppVersionCode))
}

View File

@@ -453,10 +453,10 @@ class NewDashboardActivity :
versionCode: Int
) {
if (isAppUpdateAvailable) {
PreferenceManager.setIntPreferenceApp(CURRENT_VERSION_IN_STORE, versionCode)
inAppUpdateVM.inAppUpdate.observeNonNull(this) { inAppUpdateResponse ->
if (inAppUpdateResponse.softUpdateNudge == true) {
setBadge(getTabId(ProfileFragment.TAG))
PreferenceManager.setIntPreference(CURRENT_VERSION_IN_STORE, versionCode)
PreferenceManager.setBooleanPreference(
APP_UPDATE_ENABLE, inAppUpdateResponse.softUpdateNudge.orFalse()
)

View File

@@ -7,6 +7,7 @@
package com.naviapp.home.fragment
import android.animation.ObjectAnimator
import android.content.Context
import android.content.Intent
import android.graphics.Color
@@ -20,6 +21,7 @@ import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
@@ -40,6 +42,7 @@ import com.navi.base.utils.BaseUtils
import com.navi.common.constants.GI
import com.navi.common.constants.NPS_SUBMIT_DIALOG
import com.navi.common.extensions.isNotNull
import com.navi.common.extensions.isNull
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.listeners.DialogCancelListener
import com.navi.common.listeners.NewBottomSheetListener
@@ -49,14 +52,19 @@ import com.navi.common.model.PreviousScreenNameRequest
import com.navi.common.ui.activity.BaseActivity
import com.navi.common.ui.fragment.NewCommonBottomSheet
import com.navi.common.utils.*
import com.navi.design.utils.*
import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator
import com.navi.naviwidgets.actions.ShowInfoBottomSheetV3Action
import com.navi.naviwidgets.adapters.NaviAdapter
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.models.NaviWidget
import com.navi.naviwidgets.models.OptionSelectBottomSheetData
import com.navi.naviwidgets.models.WidgetChangedData
import com.navi.naviwidgets.models.response.*
import com.navi.naviwidgets.utils.CURRENT_VERSION_IN_STORE
import com.navi.naviwidgets.utils.CURRENT_VERSION_IN_STORE
import com.navi.naviwidgets.utils.REWARDS_TOOLTIP_ANIMATION_DELAY
import com.navi.naviwidgets.utils.getAnalyticsData
import com.navi.naviwidgets.utils.toCtaData
import com.navi.naviwidgets.viewholder.ViewHolderFactoryImpl
@@ -86,6 +94,7 @@ import com.naviapp.home.activity.StoryActivity
import com.naviapp.home.viewmodel.HomeVM
import com.naviapp.models.PageStatusType
import com.naviapp.models.request.RateDataRequest
import com.naviapp.models.response.AppUpdateData
import com.naviapp.models.response.NotificationSettings
import com.naviapp.nps.viewmodel.NpsVM
import com.naviapp.part_prepayment.PartPrePaymentActivity
@@ -128,6 +137,7 @@ class HomeFragment : PaymentBaseFragment(), WidgetCallback, PlayStoreActionListe
private var analyticsStartTs = System.currentTimeMillis()
private var totalRenderTs = System.currentTimeMillis()
private var isDataFromCache = false
private var appUpdateShimmer: ObjectAnimator? = null
override val screenName: String
get() = NaviAnalytics.NEW_HOME
@@ -245,7 +255,15 @@ class HomeFragment : PaymentBaseFragment(), WidgetCallback, PlayStoreActionListe
}
naviAnalyticsEventTracker.onHomePageApiCall()
analyticsStartTs = System.currentTimeMillis()
viewModel.fetchHomeItems(queryMap, density, connectivityType, callLoanDetailWVCacheApi)
viewModel.fetchHomeItems(
queryMap,
density,
connectivityType,
callLoanDetailWVCacheApi,
PreferenceManager.getIntPreferenceApp(
CURRENT_VERSION_IN_STORE
)
)
}
private fun initUI() {
@@ -349,6 +367,11 @@ class HomeFragment : PaymentBaseFragment(), WidgetCallback, PlayStoreActionListe
}
}
response?.extraData?.rewardsIntroPopup?.let { readRewardsAnnouncementData(it) }
response?.extraData?.appUpdateData?.let {
toShowAppUpdate(it)
} ?: kotlin.run {
binding.appUpdateLayout.root.isVisible = false
}
response?.extraData?.bottomSheetData?.let {
val commonBottomSheet = NewCommonBottomSheet.getInstance(it)
safelyShowBottomSheet(commonBottomSheet, NewCommonBottomSheet.TAG)
@@ -451,6 +474,59 @@ class HomeFragment : PaymentBaseFragment(), WidgetCallback, PlayStoreActionListe
}
}
private fun toShowAppUpdate(appUpdateData: AppUpdateData) {
binding.appUpdateLayout.root.isVisible = true
binding.appUpdateLayout.apply {
appUpdateData.bgColor?.let { bgColor ->
root.background = getNaviDrawable(
radii = CornerRadius(
rightTop = resources.getDimension(R.dimen.layout_dp_8),
leftTop = resources.getDimension(R.dimen.layout_dp_8)
),
backgroundColor = bgColor.parseColorSafe()
)
}
activity?.let {
tvTitle.text = appUpdateData.title?.text?.spannedText(
context = it,
span = appUpdateData.title.span
)
tvDescription.text = appUpdateData.subtitle?.text?.spannedText(
context = it,
span = appUpdateData.subtitle.span
)
}
tvIcon.showWhenDataIsAvailable(appUpdateData.leftIconCode)
appUpdateData.actionData?.let { btnData ->
actionBtn.background = getNaviDrawable(
cornerRadius = resources.getDimension(R.dimen.layout_dp_32).toInt(),
backgroundColor = btnData.bgColor.parseColorSafe()
)
btnTitle.text = btnData.title
root.setOnClickListener {
onClick(btnData)
}
}
if (appUpdateData.enableShimmer == true) {
pgvShimmer.isVisible = true
twlParent.post {
if(appUpdateShimmer.isNull()) {
appUpdateShimmer = moveViewWithDistanceAnimator(
pgvShimmer,
twlParent.width,
REWARDS_TOOLTIP_ANIMATION_DELAY
)
}
}
} else {
pgvShimmer.isVisible = false
}
appUpdateData.metaData?.viewedData?.eventName?.let {
NaviTrackEvent.trackEventOnClickStream(it)
}
}
}
private fun showSkipMandateBottomSheet(skipMandateBottomSheetData: BottomSheetData?) {
skipMandateBottomSheetData?.let {
if (AppLaunchUtils.isLandingFirstTimeAfterAppOpen(HOME_ITEMS_LAUNCH)) {
@@ -901,6 +977,8 @@ class HomeFragment : PaymentBaseFragment(), WidgetCallback, PlayStoreActionListe
override fun onDestroyView() {
super.onDestroyView()
appUpdateShimmer?.pause()
appUpdateShimmer = null
binding.unbind()
}

View File

@@ -12,6 +12,7 @@ import com.google.gson.annotations.SerializedName
import com.navi.base.model.ActionData
import com.navi.base.model.BottomSheetData
import com.navi.naviwidgets.models.NaviWidget
import com.naviapp.models.response.AppUpdateData
import com.naviapp.models.response.Header
import com.naviapp.rewards.models.RewardsAnnouncementData
import kotlinx.android.parcel.Parcelize
@@ -57,7 +58,9 @@ data class ExtraDataDetails(
@SerializedName("backgroundImage")
val backgroundImage: String? = null,
@SerializedName("naviApkData")
val naviApkData: NaviApkData?=null
val naviApkData: NaviApkData?=null,
@SerializedName("appUpdateData")
val appUpdateData: AppUpdateData? = null
)
data class NaviApkData(

View File

@@ -7,6 +7,7 @@
package com.naviapp.home.respository
import com.google.gson.reflect.TypeToken
import com.navi.common.network.models.RepoResult
import com.navi.common.utils.log
import com.naviapp.home.model.WidgetResponse
@@ -22,16 +23,18 @@ class HomeRepository @Inject constructor(@SuperAppRetroFit private val superAppR
suspend fun fetchHomeItems(
queryMap: HashMap<String, String>?,
density: String?,
connectivityType: String?
connectivityType: String?,
availableAppVersionCode: Int?
): RepoResult<WidgetResponse> {
return if (queryMap.isNullOrEmpty())
apiResponseCallback(superAppRetrofitService.fetchHomeItems())
apiResponseCallback(superAppRetrofitService.fetchHomeItems(availableAppVersionCode))
else
apiResponseCallback(
superAppRetrofitService.fetchHomeItems(
queryMap,
density.orEmpty(),
connectivityType.orEmpty()
connectivityType.orEmpty(),
availableAppVersionCode
)
)
}

View File

@@ -43,10 +43,11 @@ class HomeVM @Inject constructor(private val repository: HomeRepository) :
queryMap: HashMap<String, String>? = null,
density: String? = null,
connectivityType: String? = null,
callLoanDetailWVCacheApi: Boolean = false
callLoanDetailWVCacheApi: Boolean = false,
availableAppVersionCode: Int?
) {
coroutineScope.launch {
val response = repository.fetchHomeItems(queryMap, density, connectivityType)
val response = repository.fetchHomeItems(queryMap, density, connectivityType, availableAppVersionCode)
if (response.error == null && response.errors.isNullOrEmpty()) {
_homeItems.value = response.data
} else {

View File

@@ -0,0 +1,33 @@
/*
*
* * Copyright © 2022-23 by Navi Technologies Private Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.models.response
import com.google.gson.annotations.SerializedName
import com.navi.base.model.ActionData
import com.navi.base.model.GenericAnalytics
import com.navi.naviwidgets.models.NaviTextComponent
data class AppUpdateData(
@SerializedName("title")
val title: NaviTextComponent? = null,
@SerializedName("subtitle")
val subtitle: NaviTextComponent? = null,
@SerializedName("actionData")
val actionData: ActionData? = null,
@SerializedName("bgColor")
val bgColor: String? = null,
@SerializedName("leftIconCode")
val leftIconCode: String? = null,
@SerializedName("metaData")
val metaData: GenericAnalytics? = null,
@SerializedName("appVersionAvailable")
val appVersionAvailable: Int? = null,
@SerializedName("enableShimmer")
val enableShimmer: Boolean? = null
)

View File

@@ -13,6 +13,7 @@ import com.navi.base.model.DropOffInfo
import com.navi.common.model.*
import com.navi.common.network.models.GenericResponse
import com.navi.common.paymenthandler.model.PaymentStatusData
import com.navi.common.utils.Constants.AVAILABLE_APP_VERSION_CODE
import com.navi.common.utils.Constants.HEADER_CONNECTIVITY_TYPE
import com.navi.common.utils.Constants.HEADER_DEVICE_DENSITY
import com.navi.insurance.models.request.FlagUpdateRequest
@@ -587,10 +588,11 @@ interface RetrofitService {
suspend fun fetchHomeItems(
@QueryMap queryMap: HashMap<String, String>?,
@Header(HEADER_DEVICE_DENSITY) deviceDensity: String,
@Header(HEADER_CONNECTIVITY_TYPE) connectivityType: String
@Header(HEADER_CONNECTIVITY_TYPE) connectivityType: String,
@Header(AVAILABLE_APP_VERSION_CODE) availableAppVersionCode: Int?
): Response<GenericResponse<WidgetResponse>>
@GET("/v2/home") suspend fun fetchHomeItems(): Response<GenericResponse<WidgetResponse>>
@GET("/v2/home") suspend fun fetchHomeItems(@Header(AVAILABLE_APP_VERSION_CODE) availableAppVersionCode: Int?): Response<GenericResponse<WidgetResponse>>
@GET("/v1/cards?groupType=STORY")
suspend fun fetchStories(

View File

@@ -56,6 +56,7 @@ import com.navi.common.model.PushNotificationData
import com.navi.common.ui.activity.BaseActivity
import com.navi.common.utils.*
import com.navi.common.utils.Constants.LOGIN_SOURCE
import com.navi.naviwidgets.utils.CURRENT_VERSION_IN_STORE
import com.naviapp.BuildConfig
import com.naviapp.R
import com.naviapp.analytics.deeplink.DeeplinkManager
@@ -242,7 +243,9 @@ class SplashActivity : BaseActivity(), DeepLinkListener {
configVM.getGaId()
configVM.fetchFireBaseInstanceId()
if (FirebaseRemoteConfigHelper.getBoolean(HOME_PAGE_CALL_ON_SPLASH)) {
configVM.fetchHomePage()
configVM.fetchHomePage(PreferenceManager.getIntPreferenceApp(
CURRENT_VERSION_IN_STORE
))
}
}

View File

@@ -127,10 +127,10 @@ class ConfigVM : BaseVM(false) {
}
}
fun fetchHomePage() {
fun fetchHomePage(availableAppVersionCode: Int?) {
viewModelScope.launch(Dispatchers.IO) {
try {
TemporaryStorageHelper.homePageResponse = configRepository.fetchHomeItems().data
TemporaryStorageHelper.homePageResponse = configRepository.fetchHomeItems(availableAppVersionCode).data
} catch (ex: java.lang.Exception) {
ex.log()
}

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ /*
~ *
~ * * Copyright © 2022-23 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"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/twlParent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/tv_Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/layout_dp_16"
android:layout_marginStart="@dimen/layout_dp_16"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_navi_chat_logo" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tv_title"
android:layout_width="@dimen/layout_dp_0"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/layout_dp_10"
android:layout_marginTop="@dimen/layout_dp_16"
app:layout_constraintBottom_toTopOf="@id/tv_description"
app:layout_constraintEnd_toStartOf="@+id/action_btn"
app:layout_constraintStart_toEndOf="@id/tv_Icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Auto-debit scheduled for 1s" />
<com.navi.design.textview.NaviTextView
android:id="@+id/tv_description"
android:layout_width="@dimen/layout_dp_0"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/layout_dp_10"
android:layout_marginTop="@dimen/layout_dp_4"
android:layout_marginBottom="@dimen/layout_dp_16"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_btn"
app:layout_constraintStart_toEndOf="@id/tv_Icon"
app:layout_constraintTop_toBottomOf="@id/tv_title"
tools:text="Auto-debit scheduled for " />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/action_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/layout_dp_16"
android:paddingHorizontal="@dimen/dp_16"
android:paddingVertical="@dimen/dp_8"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/btn_title"
style="@style/TabFontStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/outrageous_orange"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Submit now" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.navi.design.customview.ParallelogramView
android:id="@+id/pgvShimmer"
android:layout_width="@dimen/layout_dp_40"
android:layout_height="@dimen/layout_dp_0"
android:layout_gravity="start"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tilt_direction="forward" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -72,6 +72,16 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_scroll_nudge" />
<include
android:id="@+id/app_update_layout"
layout="@layout/app_update_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/layout_dp_72"
android:visibility="gone"
android:elevation="@dimen/layout_dp_4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -86,7 +86,15 @@ object PreferenceManager {
editor.putInt(key, value)
editor.apply()
}
fun getIntPreferenceApp(key: String): Int {
return sharedPreferencesForApp.getInt(key, 0)
}
fun setIntPreferenceApp(key: String, value: Int) {
val editor = sharedPreferencesForApp.edit()
editor.putInt(key, value)
editor.apply()
}
fun getBooleanPreference(key: String, defValue: Boolean = false): Boolean {
return sharedPreferencesForSession.getBoolean(key, defValue)
}

View File

@@ -116,4 +116,5 @@ object Constants {
const val DIALOG_DISMISS = "DIALOG_DISMISS"
const val UPDATE_TYPE_PLAY_STORE = "navigateToPlayStore"
const val UPDATE_TYPE_WEB = "navigateToWeb"
const val AVAILABLE_APP_VERSION_CODE = "availableAppVersionCode"
}

View File

@@ -38,3 +38,28 @@ fun moveViewWithDistance(
}
.start()
}
fun moveViewWithDistanceAnimator(
view: View?,
distance: Int,
animDuration: Long = 1000,
isRepeatable: Boolean = true,
repeatDelay: Long? = null
): ObjectAnimator {
return ObjectAnimator.ofFloat(view, "translationX", distance.toFloat())
.apply {
duration = animDuration
if (isRepeatable) {
repeatCount = ObjectAnimator.INFINITE
repeatMode = ObjectAnimator.RESTART
} else {
repeatDelay?.let {
doOnEnd {
startDelay = repeatDelay
start()
}
}
}
doOnStart { view?.isVisible = true }
start()
}
}

View File

@@ -497,6 +497,7 @@ object NaviWidgetIconUtils {
private const val ICON_INFO_DARK_PURPLE = "ICON_INFO_DARK_PURPLE"
private const val ICON_UPI_SMALL = "ICON_UPI_SMALL"
private const val COPY_ICON = "COPY_ICON"
private const val RETRY_ICON = "RETRY_ICON"
fun updateIcon(
imageDetail: ImageDetail,
@@ -1018,6 +1019,7 @@ object NaviWidgetIconUtils {
THUMBS_DOWN -> R.drawable.ic_no_chat_resolution
ICON_UPI_SMALL -> R.drawable.upi_small
COPY_ICON -> R.drawable.copy_icon
RETRY_ICON -> R.drawable.retry_icon
else -> -1
}
}

View File

@@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M22.756,9.206L19.882,12.08H28.611V3.521L25.959,6.109C25.959,6.109 19.956,-0.689 10.347,3.203C10.347,3.203 2.371,5.642 2,16.28H6.592C6.592,16.28 6.073,10.097 12.033,7.371C12.033,7.371 17.729,4.561 22.756,9.206Z"
android:strokeWidth="0.25"
android:fillColor="#ffffff"
android:strokeColor="#332C2A"/>
<path
android:pathData="M22.756,9.206L19.882,12.08H28.611V3.521L25.959,6.109C25.959,6.109 19.956,-0.689 10.347,3.203C10.347,3.203 2.371,5.642 2,16.28H6.592C6.592,16.28 6.073,10.097 12.033,7.371C12.033,7.371 17.729,4.561 22.756,9.206Z"
android:fillColor="#FBFCFD"/>
<path
android:pathData="M9.244,22.538L12.118,19.663H3.389V28.222L6.041,25.635C6.041,25.635 12.044,32.433 21.653,28.541C21.653,28.541 29.618,26.101 30,15.463H25.408C25.408,15.463 25.927,21.647 19.967,24.372C19.967,24.372 14.271,27.183 9.244,22.538Z"
android:strokeWidth="0.25"
android:fillColor="#ffffff"
android:strokeColor="#332C2A"/>
<path
android:pathData="M4.344,12.865C4.344,14.042 4.482,15.188 4.758,16.28H2C2.18,11.147 4.142,7.912 6.115,5.95C4.98,8.007 4.344,10.362 4.344,12.865Z"
android:fillColor="#FF9E88"/>
<path
android:pathData="M22.756,9.206L19.882,12.08H28.611V3.521L25.959,6.109C25.959,6.109 19.956,-0.689 10.347,3.203C10.347,3.203 2.371,5.642 2,16.28H6.592C6.592,16.28 6.073,10.097 12.033,7.371C12.033,7.371 17.729,4.561 22.756,9.206Z"
android:strokeLineJoin="round"
android:strokeWidth="0.7"
android:fillColor="#00000000"
android:strokeColor="#21002A"
android:strokeLineCap="round"/>
<path
android:pathData="M26.924,24.638C24.549,27.671 21.653,28.552 21.653,28.552C12.044,32.433 6.052,25.646 6.052,25.646L3.389,28.233V19.674H6.052C8.48,24.171 13.232,27.226 18.705,27.226C21.759,27.226 24.591,26.271 26.924,24.638Z"
android:fillColor="#FF9E88"/>
<path
android:pathData="M9.244,22.538L12.118,19.663H3.389V28.222L6.041,25.635C6.041,25.635 12.044,32.433 21.653,28.541C21.653,28.541 29.618,26.101 30,15.463H25.408C25.408,15.463 25.927,21.647 19.967,24.372C19.967,24.372 14.271,27.183 9.244,22.538Z"
android:strokeLineJoin="round"
android:strokeWidth="0.7"
android:fillColor="#00000000"
android:strokeColor="#21002A"
android:strokeLineCap="round"/>
</vector>