Last active
September 2, 2020 14:22
-
-
Save PetkevichPavel/0c871f59fc825d689d4d4b9259b5988c to your computer and use it in GitHub Desktop.
onboarding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.berider.app.models.domain.onboarding | |
| import android.os.Parcelable | |
| import androidx.annotation.StringRes | |
| import com.berider.app.models.R | |
| import com.berider.app.models.domain.utils.ModelsConstants | |
| import kotlinx.android.parcel.Parcelize | |
| /** | |
| * Created by pavel.petkevich@skodaautodigilab.com on 09.April.2020 | |
| */ | |
| object Onboarding { | |
| val onboardings = listOf(Type.MAIN, Type.PRE_RIDE, Type.POST_RIDE) | |
| @Parcelize | |
| enum class Type(val defRcFile: String, val rcKeyName: String, @StringRes val stringRes: Int) : Parcelable { | |
| MAIN( | |
| "OnboardingMain.json", | |
| if (ModelsConstants.isStagingOrDev) "onb_main_staging" else "onb_main_prod", | |
| R.string.onboarding_main_item | |
| ), | |
| PRE_RIDE( | |
| "OnboardingPostRide.json", | |
| if (ModelsConstants.isStagingOrDev) "onb_before_ride_staging" else "onb_before_ride_prod", | |
| R.string.onboarding_pre_ride_item | |
| ), | |
| POST_RIDE( | |
| "OnboardingPreRide.json", | |
| if (ModelsConstants.isStagingOrDev) "onb_after_ride_staging" else "onb_after_ride_prod", | |
| R.string.onboarding_post_ride_item | |
| ), | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.berider.app.models.domain.onboarding | |
| import androidx.annotation.ColorRes | |
| import androidx.annotation.StringRes | |
| import androidx.annotation.StyleRes | |
| import com.berider.app.models.R | |
| import com.squareup.moshi.JsonClass | |
| /** | |
| * Created by pavel.petkevich@skodaautodigilab.com on 06.April.2020 | |
| */ | |
| @JsonClass(generateAdapter = true) | |
| data class OnboardingPage( | |
| val id: Int, | |
| val imageURL: String, | |
| val title_cs: String, | |
| val title_en: String, | |
| val content_cs: String, | |
| val content_en: String, | |
| val isSkippable: Boolean, | |
| val isIosOnly: Boolean, | |
| val type: String | |
| ) { | |
| companion object { | |
| const val TITLE_PARAM = "title" | |
| const val CONTENT_PARAM = "content" | |
| const val SMALL_GUIDELINE = 0.55f | |
| const val FULL_GUIDELINE = 0f | |
| fun getPageType(str: String) = Type.values().find { it.name == str } | |
| } | |
| enum class Type( | |
| @StringRes val stringResId: Int, | |
| @StyleRes val styleResId: Int, | |
| @ColorRes val colorResId: Int, | |
| val guidelinePosition: Float | |
| ) { | |
| BASE(R.string.general_next, R.style.BaseOutlineButton, R.color.transparent, SMALL_GUIDELINE), | |
| LOCATION(R.string.general_allow, R.style.MaterialPrimaryButtonBlack, R.color.base_black, FULL_GUIDELINE), | |
| START_RIDE(R.string.general_ride, R.style.MaterialPrimaryButtonBlack, R.color.base_black, SMALL_GUIDELINE), | |
| FINISH_RIDE(R.string.general_continue, R.style.MaterialPrimaryButtonBlack, R.color.base_black, SMALL_GUIDELINE), | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.berider.app.onboarding.ui | |
| import android.Manifest | |
| import android.content.pm.PackageManager | |
| import android.os.Bundle | |
| import androidx.core.os.bundleOf | |
| import androidx.lifecycle.Observer | |
| import androidx.lifecycle.lifecycleScope | |
| import androidx.recyclerview.widget.LinearLayoutManager | |
| import androidx.recyclerview.widget.PagerSnapHelper | |
| import androidx.recyclerview.widget.RecyclerView | |
| import com.airbnb.epoxy.EpoxyControllerAdapter | |
| import com.berider.app.analytics.base.Event | |
| import com.berider.app.common.base.BaseActivity | |
| import com.berider.app.common.utils.* | |
| import com.berider.app.models.domain.onboarding.Onboarding | |
| import com.berider.app.models.domain.onboarding.OnboardingPage | |
| import com.berider.app.models.domain.onboarding.OnboardingPage.Companion.getPageType | |
| import com.berider.app.onboarding.R | |
| import com.berider.app.onboarding.epoxy.PageController | |
| import kotlinx.android.synthetic.main.activity_onboarding.* | |
| import org.koin.androidx.viewmodel.ext.android.viewModel | |
| class OnboardingActivity : BaseActivity() { | |
| companion object { | |
| const val ONBOARDING_TYPE = "onboarding_type" | |
| fun prepareBundle(onboardingType: Onboarding.Type) = bundleOf(ONBOARDING_TYPE to onboardingType) | |
| } | |
| private val viewModel by viewModel<OnboardingViewModel>() | |
| private var data: List<OnboardingPage>? = null | |
| private var currentPagePos = 0 | |
| private var currentPage: OnboardingPage? = null | |
| private var layoutManager: LinearLayoutManager? = null | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| setContentView(R.layout.activity_onboarding) | |
| getBundleArg<Onboarding.Type>(getBundleName(), ONBOARDING_TYPE)?.let { | |
| analyticsService.logEvent(Event.Names.ONB_VIEW.name, Event.Parameters.TYPE.name to it.name) | |
| viewModel.fetchOnBoarding(it, isLocationPermissionGranted()) | |
| } ?: finish() | |
| onbMainBtn?.onClick(lifecycleScope) { | |
| currentPage?.chooseAction() | |
| } | |
| onbSkipBtn?.setOnClickListener { | |
| analyticsService.logEvent(Event.Names.ONB_PAGE_BTN_SKIP.name, *getData(currentPagePos)) | |
| finish() | |
| } | |
| } | |
| override fun setObservers() { | |
| super.setObservers() | |
| viewModel.data.observe(this, Observer { data -> | |
| this.data = data | |
| PageController.setData(data).adapter.setRecyclerView() | |
| onbMainConstraint.makeVisible() | |
| }) | |
| viewModel.uiState.observe(this, Observer { state -> | |
| processState(state) | |
| }) | |
| } | |
| private fun EpoxyControllerAdapter.setRecyclerView() { | |
| layoutManager = LinearLayoutManager(this@OnboardingActivity, LinearLayoutManager.HORIZONTAL, false) | |
| epoxyRecyclerView?.layoutManager = layoutManager | |
| epoxyRecyclerView?.adapter = this | |
| PagerSnapHelper().apply { | |
| epoxyRecyclerView.onFlingListener = null | |
| attachToRecyclerView(epoxyRecyclerView) | |
| onbIndicator.attachToRecyclerView(epoxyRecyclerView, this) | |
| } | |
| epoxyRecyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() { | |
| override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { | |
| super.onScrolled(recyclerView, dx, dy) | |
| layoutManager?.findLastCompletelyVisibleItemPosition().takeIf { it != -1 }?.apply { | |
| currentPagePos = this | |
| data?.get(this)?.run { | |
| setButton() | |
| analyticsService.logEvent(Event.Names.ONB_PAGE_VIEW.name, *getData(this@apply)) | |
| } | |
| } | |
| } | |
| }) | |
| } | |
| private fun OnboardingPage.setButton() { | |
| currentPage = this@setButton | |
| getPageType(type)?.apply { | |
| onbSkipBtn?.makeVisible(isSkippable) | |
| guidelineBtnWidth?.moveWithAnim(guidelineBtnWidth.currentPercent(), guidelinePosition) | |
| onbMainBtn?.setWith(stringResId, styleResId, colorResId)?.makeVisible() | |
| } | |
| } | |
| private fun OnboardingPage.chooseAction() { | |
| when (getPageType(type)) { | |
| OnboardingPage.Type.BASE -> nextPage() | |
| OnboardingPage.Type.LOCATION -> runWithPermission( | |
| Manifest.permission.ACCESS_FINE_LOCATION, | |
| analyticsService = analyticsService | |
| ) | |
| OnboardingPage.Type.START_RIDE -> finish() | |
| OnboardingPage.Type.FINISH_RIDE -> finish() | |
| } | |
| } | |
| override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { | |
| super.onRequestPermissionsResult(requestCode, permissions, grantResults) | |
| if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | |
| when (requestCode) { | |
| PermissionConstants.REQUEST_LOCATION -> finish() | |
| } | |
| } | |
| } | |
| private fun getData(position: Int) = data?.get(position)?.run { | |
| arrayOf(Event.Parameters.TYPE.name to type, Event.Parameters.PAGE_NUMBER.name to position) | |
| } ?: emptyArray() | |
| private fun nextPage() { | |
| layoutManager?.findLastCompletelyVisibleItemPosition()?.plus(1)?.let { pos -> | |
| if (currentPage == data?.last()) finish() | |
| else epoxyRecyclerView?.smoothScrollToPosition(pos) | |
| analyticsService.logEvent(Event.Names.ONB_PAGE_BTN_NEXT.name, *getData(data?.getOrLast(currentPagePos).orZero())) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.berider.app.onboarding.data | |
| import com.berider.app.common.base.BaseRepository | |
| import com.berider.app.common.core.ResponseState | |
| import com.berider.app.common.utils.SingleLiveEvent | |
| import com.berider.app.common.utils.emit | |
| import com.berider.app.models.domain.onboarding.Onboarding | |
| import com.berider.app.models.domain.onboarding.OnboardingPage | |
| import com.berider.app.models.domain.onboarding.OnboardingPage.Companion.getPageType | |
| import com.berider.app.onboarding.R | |
| import com.berider.app.remoteconfig.RemoteConfigFunctions | |
| /** | |
| * Created by pavel.petkevich@skodaautodigilab.com on 06.April.2020 | |
| */ | |
| interface IOnboardingRepository { | |
| val uiState: SingleLiveEvent<ResponseState> | |
| val data: SingleLiveEvent<List<OnboardingPage>?> | |
| fun fetchOnBoarding(onboardingType: Onboarding.Type, isLocationPermissionGranted: Boolean) | |
| } | |
| class OnboardingRepository(private val remoteConfigFunctions: RemoteConfigFunctions) : IOnboardingRepository, BaseRepository() { | |
| override val uiState: SingleLiveEvent<ResponseState> | |
| get() = responseState | |
| private val _data = SingleLiveEvent<List<OnboardingPage>?>() | |
| override val data: SingleLiveEvent<List<OnboardingPage>?> | |
| get() = _data | |
| override fun fetchOnBoarding(onboardingType: Onboarding.Type, isLocationPermissionGranted: Boolean) { | |
| responseState.emit(ResponseState.BlockingLoadingState.Start(bgColorRes = R.color.transparent)) | |
| remoteConfigFunctions.apply { | |
| Array<OnboardingPage>::class.fetchRemoteConfig( | |
| onboardingType.defRcFile, | |
| onboardingType.rcKeyName | |
| ) { list -> | |
| _data.emit(list?.removeLocationPermPage(onboardingType, isLocationPermissionGranted)?.filter { !it.isIosOnly }) | |
| responseState.emit(ResponseState.BlockingLoadingState.Stop()) | |
| } | |
| } | |
| } | |
| private fun Array<OnboardingPage>.removeLocationPermPage( | |
| onboardingType: Onboarding.Type, | |
| isLocationPermissionGranted: Boolean | |
| ) = toMutableList().apply { | |
| this.takeIf { onboardingType == Onboarding.Type.MAIN && isLocationPermissionGranted } | |
| ?.find { getPageType(it.type) == OnboardingPage.Type.LOCATION }?.let { remove(it) } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.berider.app.onboarding.ui | |
| import androidx.lifecycle.ViewModel | |
| import androidx.lifecycle.viewModelScope | |
| import com.berider.app.models.domain.onboarding.Onboarding | |
| import com.berider.app.onboarding.data.IOnboardingRepository | |
| import kotlinx.coroutines.launch | |
| /** | |
| * Created by pavel.petkevich@skodaautodigilab.com on 06.April.2020 | |
| */ | |
| class OnboardingViewModel(private val onboardingRepository: IOnboardingRepository) : ViewModel() { | |
| val uiState = onboardingRepository.uiState | |
| val data = onboardingRepository.data | |
| fun fetchOnBoarding(onboardingType: Onboarding.Type, isLocationPermissionGranted: Boolean) { | |
| viewModelScope.launch { | |
| onboardingRepository.fetchOnBoarding(onboardingType, isLocationPermissionGranted) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.berider.app.onboarding.epoxy | |
| import android.widget.ImageView | |
| import android.widget.ProgressBar | |
| import android.widget.TextView | |
| import com.airbnb.epoxy.EpoxyAttribute | |
| import com.airbnb.epoxy.EpoxyModelClass | |
| import com.airbnb.epoxy.EpoxyModelWithHolder | |
| import com.airbnb.epoxy.TypedEpoxyController | |
| import com.berider.app.common.epoxy.BaseEpoxyHolder | |
| import com.berider.app.common.utils.getStringViaLocale | |
| import com.berider.app.common.utils.makeVisible | |
| import com.berider.app.common.utils.setImageToView | |
| import com.berider.app.models.domain.onboarding.OnboardingPage | |
| import com.berider.app.onboarding.R | |
| /** | |
| * Created by pavel.petkevich@skodaautodigilab.com on 07.April.2020 | |
| */ | |
| @EpoxyModelClass | |
| abstract class PageModel : EpoxyModelWithHolder<PageModel.Holder?>() { | |
| @EpoxyAttribute | |
| lateinit var page: OnboardingPage | |
| override fun getDefaultLayout(): Int = R.layout.onboarding_page | |
| override fun bind(holder: Holder) { | |
| super.bind(holder) | |
| with(holder) { | |
| progressOnb.apply { | |
| makeVisible() | |
| imgOnbPage.setImageToView(page.imageURL) { | |
| makeVisible(false) | |
| } | |
| } | |
| txtOnbTitle.text = page.getStringViaLocale(OnboardingPage.TITLE_PARAM) | |
| txtOnbContent.text = page.getStringViaLocale(OnboardingPage.CONTENT_PARAM) | |
| } | |
| } | |
| class Holder : BaseEpoxyHolder() { | |
| val progressOnb: ProgressBar by bind(R.id.progressOnb) | |
| val imgOnbPage: ImageView by bind(R.id.imgOnbPage) | |
| val txtOnbTitle: TextView by bind(R.id.txtOnbTitle) | |
| val txtOnbContent: TextView by bind(R.id.txtOnbContent) | |
| } | |
| } | |
| class PageController : TypedEpoxyController<List<OnboardingPage>>() { | |
| companion object { | |
| fun setData(data: List<OnboardingPage>?) = PageController().apply { setData(data) } | |
| } | |
| override fun buildModels(pages: List<OnboardingPage>) { | |
| pages.forEach { | |
| page { | |
| id(it.id) | |
| page(it) | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment