Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ object RemoteConfig {
const val PURCHASE_FLOW_DISCLAIMER_EN = "purchase_flow_android_disclaimer_en"
const val PURCHASE_FLOW_DISCLAIMER_RU = "purchase_flow_android_disclaimer_ru"
const val PURCHASE_FLOW_DISCLAIMER_BE = "purchase_flow_android_disclaimer_be"

const val BANNERS_ANDROID = "banners_android"
}
58 changes: 58 additions & 0 deletions app/src/main/java/org/stepic/droid/ui/fragments/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.findFragment
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.home_streak_view.*
Expand All @@ -16,8 +19,14 @@ import org.stepic.droid.base.FragmentBase
import org.stepic.droid.configuration.RemoteConfig
import org.stepic.droid.core.presenters.HomeStreakPresenter
import org.stepic.droid.core.presenters.contracts.HomeStreakView
import org.stepic.droid.databinding.ItemBannerBinding
import org.stepic.droid.util.commitNow
import org.stepik.android.domain.banner.interactor.BannerInteractor
import org.stepik.android.domain.banner.model.Banner
import org.stepik.android.domain.home.interactor.HomeInteractor
import org.stepik.android.view.banner.mapper.BannerResourcesMapper
import org.stepik.android.view.banner.extension.bind
import org.stepik.android.view.banner.extension.handleItemClick
import org.stepik.android.view.course_list.ui.fragment.CourseListPopularFragment
import org.stepik.android.view.course_list.ui.fragment.CourseListUserHorizontalFragment
import org.stepik.android.view.course_list.ui.fragment.CourseListVisitedHorizontalFragment
Expand All @@ -28,6 +37,7 @@ import org.stepik.android.view.stories.ui.fragment.StoriesFragment
import ru.nobird.android.stories.transition.SharedTransitionsManager
import ru.nobird.android.stories.ui.delegate.SharedTransitionContainerDelegate
import javax.inject.Inject
import kotlin.math.min

class HomeFragment : FragmentBase(), HomeStreakView, FastContinueNewHomeFragment.Callback {
companion object {
Expand All @@ -47,6 +57,12 @@ class HomeFragment : FragmentBase(), HomeStreakView, FastContinueNewHomeFragment
@Inject
lateinit var remoteConfig: FirebaseRemoteConfig

@Inject
lateinit var bannerResourcesMapper: BannerResourcesMapper

@Inject
lateinit var bannerInteractor: BannerInteractor

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
analytic.reportAmplitudeEvent(AmplitudeAnalytic.Home.HOME_SCREEN_OPENED)
Expand Down Expand Up @@ -75,6 +91,8 @@ class HomeFragment : FragmentBase(), HomeStreakView, FastContinueNewHomeFragment

homeStreakPresenter.attachView(this)
homeStreakPresenter.onNeedShowStreak()

homeMainContainer.post { setupBanners() }
}

override fun onStart() {
Expand Down Expand Up @@ -138,6 +156,46 @@ class HomeFragment : FragmentBase(), HomeStreakView, FastContinueNewHomeFragment
}
}

private fun setupBanners() {
val banners = bannerInteractor
.getBanners(Banner.Screen.HOME)
.blockingGet()

/**
* Account for streak view and stories
*/
val offset =
if (remoteConfig.getBoolean(RemoteConfig.IS_NEW_HOME_SCREEN_ENABLED)) {
2
} else {
1
}

banners.forEach { banner ->
val binding = ItemBannerBinding.inflate(layoutInflater, homeMainContainer, false)

binding.root.setOnClickListener {
binding.handleItemClick(banner, childFragmentManager)
}

binding.bind(banner, bannerResourcesMapper)

val insertionIndex = min(banner.position + offset, homeMainContainer.childCount - 1)
val previousFragment = homeMainContainer.getChildAt(insertionIndex - 1).findFragment<Fragment>()

homeMainContainer.addView(binding.root, insertionIndex)
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
val margin =
if (previousFragment is LearningActionsFragment) {
resources.getDimensionPixelOffset(R.dimen.course_list_side_padding)
} else {
0
}
topMargin = margin
}
}
}

override fun onFastContinueLoaded(isVisible: Boolean) {
val padding = if (isVisible) {
resources.getDimensionPixelOffset(R.dimen.fast_continue_widget_height)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.stepik.android.domain.banner.interactor

import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import io.reactivex.Single
import org.stepic.droid.configuration.RemoteConfig
import org.stepic.droid.preferences.SharedPreferenceHelper
import org.stepik.android.domain.banner.model.Banner
import java.lang.Exception
import javax.inject.Inject

class BannerInteractor
@Inject
constructor(
private val firebaseRemoteConfig: FirebaseRemoteConfig,
private val sharedPreferenceHelper: SharedPreferenceHelper,
private val gson: Gson
) {
fun getBanners(screen: Banner.Screen): Single<List<Banner>> =
Single.fromCallable {
val bannersJson = firebaseRemoteConfig.getString(RemoteConfig.BANNERS_ANDROID)
if (bannersJson.isEmpty()) {
emptyList()
} else {
try {
val banners =
gson.fromJson<List<Banner>>(
bannersJson,
TypeToken.getParameterized(
ArrayList::class.java,
Banner::class.java
).type
)

val language = sharedPreferenceHelper.languageForFeatured

banners.filter {
it.type != null &&
it.screen != null &&
it.language == language &&
it.screen == screen
}
} catch (e: Exception) {
emptyList()
}
}
}
}
39 changes: 39 additions & 0 deletions app/src/main/java/org/stepik/android/domain/banner/model/Banner.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.stepik.android.domain.banner.model

import com.google.gson.annotations.SerializedName

data class Banner(
@SerializedName("title")
val title: String,
@SerializedName("type")
val type: ColorType?,
@SerializedName("lang")
val language: String,
@SerializedName("description")
val description: String,
@SerializedName("url")
val url: String,
@SerializedName("screen")
val screen: Screen?,
@SerializedName("position")
val position: Int
) {
enum class ColorType(val type: String) {
@SerializedName("blue")
BLUE("blue"),

@SerializedName("violet")
VIOLET("violet"),

@SerializedName("green")
GREEN("green")
}

enum class Screen(val screen: String) {
Comment thread
rostikjoystick marked this conversation as resolved.
@SerializedName("catalog")
CATALOG("catalog"),

@SerializedName("home")
HOME("home")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.stepik.android.presentation.banner

import org.stepik.android.domain.banner.model.Banner

interface BannerFeature {
sealed class State {
object Idle : State()
object Loading : State()
object Empty : State()
data class Content(val banners: List<Banner>) : State()
}

sealed class Message {
data class InitMessage(
val screen: Banner.Screen,
val forceUpdate: Boolean = false
) : Message()
object BannersError : Message()
data class BannersResult(val banners: List<Banner>) : Message()
}

sealed class Action {
data class LoadBanners(val screen: Banner.Screen) : Action()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.stepik.android.presentation.banner.dispatcher

import io.reactivex.Scheduler
import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import org.stepic.droid.di.qualifiers.BackgroundScheduler
import org.stepic.droid.di.qualifiers.MainScheduler
import org.stepik.android.domain.banner.interactor.BannerInteractor
import org.stepik.android.presentation.banner.BannerFeature
import ru.nobird.android.presentation.redux.dispatcher.RxActionDispatcher
import javax.inject.Inject

class BannerActionDispatcher
@Inject
constructor(
private val bannerInteractor: BannerInteractor,
@BackgroundScheduler
private val backgroundScheduler: Scheduler,
@MainScheduler
private val mainScheduler: Scheduler
) : RxActionDispatcher<BannerFeature.Action, BannerFeature.Message>() {
override fun handleAction(action: BannerFeature.Action) {
when (action) {
is BannerFeature.Action.LoadBanners -> {
compositeDisposable += bannerInteractor
.getBanners(action.screen)
.observeOn(mainScheduler)
.subscribeOn(backgroundScheduler)
.subscribeBy(
onSuccess = { onNewMessage(BannerFeature.Message.BannersResult(it)) },
onError = { onNewMessage(BannerFeature.Message.BannersError) }
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.stepik.android.presentation.banner.reducer

import org.stepik.android.presentation.banner.BannerFeature.State
import org.stepik.android.presentation.banner.BannerFeature.Message
import org.stepik.android.presentation.banner.BannerFeature.Action
import ru.nobird.app.presentation.redux.reducer.StateReducer
import javax.inject.Inject

class BannerReducer
@Inject
constructor() : StateReducer<State, Message, Action> {
override fun reduce(state: State, message: Message): Pair<State, Set<Action>> =
when (message) {
is Message.InitMessage -> {
if (state is State.Idle || message.forceUpdate) {
State.Loading to setOf(Action.LoadBanners(message.screen))
} else {
null
}
}
is Message.BannersError -> {
if (state is State.Loading) {
State.Empty to emptySet()
} else {
null
}
}
is Message.BannersResult -> {
if (state is State.Loading) {
val newState =
if (message.banners.isEmpty()) {
State.Empty
} else {
State.Content(message.banners)
}
newState to emptySet()
} else {
null
}
}
} ?: state to emptySet()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.stepik.android.presentation.catalog

import org.stepik.android.domain.catalog.model.CatalogBlock
import org.stepik.android.presentation.banner.BannerFeature
import org.stepik.android.presentation.course_continue_redux.CourseContinueFeature
import org.stepik.android.presentation.course_list_redux.CourseListFeature
import org.stepik.android.presentation.course_list_redux.model.CatalogBlockStateWrapper
Expand All @@ -16,7 +17,8 @@ interface CatalogFeature {
val storiesState: StoriesFeature.State,
val filtersState: FiltersFeature.State,
val blocksState: BlocksState,
val courseContinueState: CourseContinueFeature.State
val courseContinueState: CourseContinueFeature.State,
val bannerState: BannerFeature.State
)

sealed class BlocksState {
Expand All @@ -42,6 +44,7 @@ interface CatalogFeature {
data class ProgressMessage(val message: ProgressFeature.Message) : Message()
data class EnrollmentMessage(val message: EnrollmentFeature.Message) : Message()
data class WishlistMessage(val message: WishlistFeature.Message) : Message()
data class BannerMessage(val message: BannerFeature.Message) : Message()
}

sealed class Action {
Expand All @@ -54,6 +57,7 @@ interface CatalogFeature {
data class FiltersAction(val action: FiltersFeature.Action) : Action()
data class CourseListAction(val action: CourseListFeature.Action) : Action()
data class CourseContinueAction(val action: CourseContinueFeature.Action) : Action()
data class BannerAction(val action: BannerFeature.Action) : Action()

sealed class ViewAction : Action() {
data class CourseContinueViewAction(val viewAction: CourseContinueFeature.Action.ViewAction) : ViewAction()
Expand Down
Loading