Commit fa9765ed authored by Vladislav Bogdashkin's avatar Vladislav Bogdashkin 🎣

Merge branch 'feature/plan_types_download' into develop

parents 5ee2709b b338b557
package com.biganto.visual.roompark.conductor.dialogs
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.annotation.LayoutRes
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.BaseRoomParkActivity
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.di.dagger.ActivityModule
import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.PerScreen
import com.biganto.visual.roompark.domain.interactor.SettingsInteractor
import com.biganto.visual.roompark.domain.use_case.DownloadUseCase
import com.biganto.visual.roompark.util.view_utils.snackbar.ISnackBarProvider
import com.bluelinelabs.conductor.Controller
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.FitCenter
import com.google.android.material.textview.MaterialTextView
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import jp.wasabeef.glide.transformations.BlurTransformation
import jp.wasabeef.glide.transformations.ColorFilterTransformation
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
class DownloadPlansDialogController : Controller {
constructor():super()
constructor(args: Bundle) : super(args)
lateinit var progressBarDownload: ProgressBar
lateinit var downloaderBg: ImageView
lateinit var downloadTourTitleText: MaterialTextView
lateinit var cancelDownloadText: MaterialTextView
lateinit var downloadingContentText: MaterialTextView
override fun onContextAvailable(context: Context) {
super.onContextAvailable(context)
DaggerPlansDownloaderScreenComponent.factory()
.create(RoomParkApplication.component,activity as RoomParkMainActivity)
.inject(this)
}
private var downloadToken = DownloadUseCase.CancellationToken(false)
@Inject
lateinit var downloader: SettingsInteractor
@Inject
lateinit var rpActivity: RoomParkMainActivity
lateinit var snackbar: ISnackBarProvider
private val disposables = CompositeDisposable()
override fun onDetach(view: View) {
disposables.clear()
super.onDetach(view)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
snackbar = ActivityModule.provideSnackBar(rpActivity)
progressBarDownload = view.findViewById(R.id.downloadProgress)
downloaderBg = view.findViewById(R.id.backgroundDownloader)
cancelDownloadText = view.findViewById(R.id.cancelDownloadButton)
downloadTourTitleText = view.findViewById(R.id.tourToDownloadTitle)
downloadingContentText = view.findViewById(R.id.downloadingTitle)
downloadingContentText.text = resources?.getString(R.string.download_plan_types_screen_title)
// progress.visibility = View.VISIBLE
downloadToken = DownloadUseCase.CancellationToken(false)
downloadTourTitleText.text = ""
Glide.with(view)
.load(R.drawable.flat_placeholder)
.transform(BlurTransformation(13, 4)
,ColorFilterTransformation(0x99000000.toInt())
,FitCenter())
.into(downloaderBg)
disposables.add(
downloader.downloadTourPlans(downloadToken)
// .onErrorReturn { parseError(it);TourPreviewEntity() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe (
{ model ->
downloadTourTitleText.text = "${model.currentProgress}/${model.totalProgress}"
updateProgressBar(model.currentProgress, model.totalProgress)
if (model.currentProgress == model.totalProgress){
snackbar.showSnackBar(R.string.plan_types_download_completed)
handleBack()
}
}
,{error ->
Timber.e(error)
snackbar.showSnackBar(error.localizedMessage)
handleBack()
}
))
cancelDownloadText.setOnClickListener {handleBack() }
// downloadTour(it.tour.tour_id, downloadToken)
// view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
return view
}
private fun updateProgressBar(curr:Int, max:Int){
activity?.runOnUiThread {
progressBarDownload.max = max
progressBarDownload.progress = curr
}
}
@LayoutRes
fun getLayoutId() = R.layout.download_tour_layout
override fun handleBack(): Boolean {
downloadToken.isCancelled = true
return router.popController(this)
}
}
@PerScreen
@Component(
modules = [PlansDownloaderScreenModule::class],
dependencies = [AppComponent::class])
interface PlansDownloaderScreenComponent {
@Component.Factory
interface Factory{
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
): PlansDownloaderScreenComponent
}
//
// val presenter: ArticlesScreenPresenter
fun inject(controller: DownloadPlansDialogController)
}
@Module
abstract class PlansDownloaderScreenModule{
@PerScreen
@Binds
abstract fun provideActivity(activity: RoomParkMainActivity): BaseRoomParkActivity
}
......@@ -6,15 +6,12 @@ import com.biganto.visual.roompark.data.repository.api.room_park.IRoomParkApi
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.db.requrey.model.DealEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.PlanPresetEntity
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
import com.biganto.visual.roompark.data.repository.mapper.fromRawList
import com.biganto.visual.roompark.domain.contract.DealContract
import com.biganto.visual.roompark.domain.custom_exception.CustomApiException
import com.biganto.visual.roompark.domain.model.DealModel
import com.biganto.visual.roompark.domain.model.EstateModel
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import com.biganto.visual.roompark.domain.model.fromEntity
import io.reactivex.Completable
import io.reactivex.Observable
......@@ -31,10 +28,9 @@ const val TEST_DEAL_TOKEN =
"183|PQZi0LmaYswPmFHcLb0pHITkg_7aJNC4x2IIC90kbRKE1GBt2m48tdi-1jb9jo9MYoxCGyvJtDy3ret7_nAoAg=="
class EstateRepository @Inject constructor(
private val local: ILocalStore,
local: ILocalStore,
private val api: IRoomParkApi,
private val db: IDb,
private val file: FileModule
private val db: IDb
): DealContract {
......@@ -96,106 +92,6 @@ class EstateRepository @Inject constructor(
}
private fun getPlanTypesApi(estateId: Int): Observable<List<PlanPresetEntity>> =
api.getEstatePlanTypes(estateId)
.doOnNext { Timber.d("raw0 $it") }
.map { fromRawList(it, ::fromRaw) }
.map {
it.onEach { plan ->
val e = EstateEntity()
e.setId(estateId)
plan.estateId = e
}.toList()
}
.doOnNext(db::blockingUpsert)
.subscribeOn(Schedulers.io())
override fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>> =
Observable.mergeDelayError(
arrayListOf(getPlanTypesApi(estateId))
).map { l -> List(l.size) { fromEntity(l[it]) } }
private fun getPlanApi(estateId: Int
, planId:Int
, furniture:Boolean? = null
, sizes:Boolean? = null
, walls:Boolean? = null
, electric:Boolean? = null) =
api.getDirectPlan(estateId, planId,
furniture?:false
,sizes?:false
,walls?:false
,electric?:false)
.map{
val sFile = getPlanFile(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric)
file.saveFileToDisk(sFile,it)
sFile.path
}
.subscribeOn(Schedulers.io())
private fun getPlanFile(estateId: Int
, planId:Int
, furniture:Boolean? = null
, sizes:Boolean? = null
, walls:Boolean? = null
, electric:Boolean? = null)
= FileModule.getDirectory(file.context
,FileModule.FileDirectory.PlanTypeDir(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric
))
override fun getEmptyPlan(estateId: Int
,planId:Int): Observable<String> =
Observable.mergeDelayError(
arrayListOf(getPlanApi(estateId,planId))
)
override fun getPlan(estateId: Int
,planId:Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String> =
Observable.fromCallable { getPlanFile(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric) }.switchMap {
if (it.exists()) Observable.just(it.path)
else getPlanApi(estateId
,planId
, furniture
, sizes
, walls
, electric
)
}
// fun getPlanRequestString(estateId: Int
// , planId:Int
// , furniture:Boolean
// , sizes:Boolean
// , walls:Boolean
// , electric:Boolean
// ) = api.getDirectPlan(estateId,planId,furniture,sizes,electric).
private val getDealsApi: Observable<List<DealEntity>> =
local.recentUser()
......
package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.androidplayer.data.repository.local.ILocalStore
import com.biganto.visual.roompark.data.repository.api.room_park.IRoomParkApi
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.PlanPresetEntity
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
import com.biganto.visual.roompark.data.repository.mapper.fromRawList
import com.biganto.visual.roompark.domain.contract.FlatPlanContract
import com.biganto.visual.roompark.domain.model.FeatureModel
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import com.biganto.visual.roompark.domain.model.fromEntity
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import java.util.*
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 20.04.2020.
*/
class PlanRepository @Inject constructor(
private val local: ILocalStore,
private val api: IRoomParkApi,
private val db: IDb,
private val file: FileModule
)
: FlatPlanContract {
private fun getPlanTypesApi(estateId: Int): Observable<List<PlanPresetEntity>> =
api.getEstatePlanTypes(estateId)
.doOnNext { Timber.d("raw0 $it") }
.map { fromRawList(it, ::fromRaw) }
.map {
it.onEach { plan ->
val e = EstateEntity()
e.setId(estateId)
plan.estateId = e
}.toList()
}
.doOnNext(db::blockingUpsert)
.subscribeOn(Schedulers.io())
override fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>> =
Observable.mergeDelayError(
arrayListOf(getPlanTypesApi(estateId))
).map { l -> List(l.size) { fromEntity(l[it]) } }
private fun getPlanApi(estateId: Int
, planId:Int
, furniture:Boolean? = null
, sizes:Boolean? = null
, walls:Boolean? = null
, electric:Boolean? = null) =
api.getDirectPlan(estateId, planId,
furniture?:false
,sizes?:false
,walls?:false
,electric?:false)
.map{
val sFile = getPlanFile(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric)
file.saveFileToDisk(sFile,it)
sFile.path
}
.subscribeOn(Schedulers.io())
override fun getPlanFile(featuresVariant: PlanFeaturesVariant)
= getPlanFile(
estateId = featuresVariant.estateId,
planId = featuresVariant.planId,
furniture = featuresVariant.furniture,
sizes = featuresVariant.sizes,
walls = featuresVariant.walls,
electric = featuresVariant.electric
)
fun getPlanFile(estateId: Int
, planId:Int
, furniture:Boolean? = null
, sizes:Boolean? = null
, walls:Boolean? = null
, electric:Boolean? = null)
= FileModule.getDirectory(file.context
, FileModule.FileDirectory.PlanTypeDir(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric
))
// override fun getEmptyPlan(estateId: Int
// ,planId:Int): Observable<String> =
// Observable.mergeDelayError(
// arrayListOf(getPlanApi(estateId,planId))
// )
override fun getPlan(featuresVariant: PlanFeaturesVariant)
= getPlan(
estateId = featuresVariant.estateId,
planId = featuresVariant.planId,
furniture = featuresVariant.furniture,
sizes = featuresVariant.sizes,
walls = featuresVariant.walls,
electric = featuresVariant.electric
)
override fun getPlan(estateId: Int
,planId:Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String> =
Observable.fromCallable { getPlanFile(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric) }.switchMap {
if (it.exists()) Observable.just(it.path)
else getPlanApi(estateId
,planId
, furniture
, sizes
, walls
, electric
)
}
// fun getPlanRequestString(estateId: Int
// , planId:Int
// , furniture:Boolean
// , sizes:Boolean
// , walls:Boolean
// , electric:Boolean
// ) = api.getDirectPlan(estateId,planId,furniture,sizes,electric).
}
private fun BitSet.getOrNull(index:Int) =
if (index<0 || index>=this.size()) null
else get(index)
private fun Int.getBit(index: Int): Boolean? {
if (index<0) return null
return Integer.toBinaryString(shr(index)).last()=='1'
}
val PlanPresetModel.featuresVariants : List<PlanFeaturesVariant>
get() {
if (this.features.isNullOrEmpty()) return arrayListOf()
val maxInd = this.features.size
val f = features.indexOfFirst { it is FeatureModel.Furniture }
val s = features.indexOfFirst { it is FeatureModel.Sizes }
val e = features.indexOfFirst { it is FeatureModel.Electric }
val w = features.indexOfFirst { it is FeatureModel.Walls }
val resList = mutableListOf<PlanFeaturesVariant>()
var increment = 0
var allTrue: Boolean
do{
val v = PlanFeaturesVariant(
estateId,planId,
furniture = increment.getBit(f),
sizes = increment.getBit(s),
electric = increment.getBit(e),
walls = increment.getBit(w)
)
allTrue = v.furniture?:true && v.electric?:true && v.sizes?:true && v.walls?:true
resList.add(v)
increment++
}while (!allTrue)
return resList
}
data class PlanFeaturesVariant(
val estateId: Int,
val planId:Int,
val furniture:Boolean?,
val sizes:Boolean?,
val walls:Boolean?,
val electric:Boolean?
)
\ No newline at end of file
......@@ -36,7 +36,7 @@ private const val TIMEOUT_SECONDS=120L
private const val WRITE_SECONDS=120L
private const val READ_SECONDS=120L
val INTERCEPT_LOG_LEVEL = HttpLoggingInterceptor.Level.HEADERS
val INTERCEPT_LOG_LEVEL = HttpLoggingInterceptor.Level.NONE
@Module
class RetrofitModule{
......
......@@ -60,7 +60,6 @@ class FileModule @Inject constructor(val context: Application) {
fun saveFileToDisk(file:File,content: String){
Timber.d("write to : $file")
Timber.d("write to : ${file.name}")
// file.createNewFile()
file.parentFile.mkdirs()
file.writeText(content) //to json array because core unity method parse data like TourData[] Estate[] etc..
......
......@@ -175,7 +175,7 @@ class DownloadManagerService @Inject constructor(
val sink = Okio.buffer(Okio.appendingSink(fileStorage))
val buffer = sink.buffer()
var read = 0L
val step = 4096
val step = 8192
val source = response.source()
var timer = System.currentTimeMillis()
var stop: Boolean = false
......@@ -194,7 +194,6 @@ class DownloadManagerService @Inject constructor(
}
}
model.isDownloaded = (source.exhausted()
&& (model.fileDownloadedSize == model.tempOverallFileSize
|| model.tempOverallFileSize == 0L))
......@@ -268,9 +267,10 @@ class DownloadManagerService @Inject constructor(
mergeFiles(fileEntities)
val jlist = db.getTourFilesJunction(tour.id).toList()
Timber.d("jlist:: ${jlist?.size} start ${jlist?.firstOrNull()?.id}")
val junctionList = fileEntities
.map {file ->
val entity = jlist.firstOrNull{it.tour == tour.id && it.file == file.uri}
val entity = jlist.firstOrNull{it.tour == tour.id && it.file.uri() == file.uri.uri()}
?: TourFileJunctionEntity().apply {
setTour(tour.id)
setFile(file.uri)
......@@ -279,8 +279,8 @@ class DownloadManagerService @Inject constructor(
totalSize += file.totalSize
entity
}
Timber.w("fffff $downloadedSize $totalSize")
Timber.d("junctionList:: ${junctionList.size} start ${junctionList.first().id}")
setDownloadInfo(
raw.id.toString()
, tempLoadedFiles = 0
......@@ -337,7 +337,6 @@ class DownloadManagerService @Inject constructor(
.observeOn(Schedulers.computation())
// .doOnNext{Timber.d("7 ${it}")}
.flatMap { model ->
Timber.d(" model __ ${model}")
setDownloadInfo(
model.tourId
, totalSizedDiffSize = model.tempTourTotalDiff
......@@ -377,10 +376,13 @@ class DownloadManagerService @Inject constructor(
private fun downloadTourFromQueue(): Observable<String> =
Observable.fromCallable { activeDownloading.set(true);downloadQueue.poll() }
.doOnNext { Timber.d("to load tour: ${it}") }
.flatMap { db.getTourPreview(it).observable() }
.filter {
Timber.d("to load tour: ${it.isDownloaded} ${it.id}")
val forward = it.isDownloaded != DownloadState.Downloaded
activeDownloading.set(forward)
Timber.d("to load tour: $forward")
if (!forward)
notifyDownloadProgress()
forward
......@@ -519,6 +521,7 @@ class DownloadManagerService @Inject constructor(
disposable.add(
toursToDownloadObserver
.flatMap { db.getTourPreview(it).observable() }
.filter { it.isDownloaded != DownloadState.Downloaded }
.doOnNext {
it.isDownloaded = DownloadState.Downloading
it.downloadedFiles = 0
......@@ -531,18 +534,20 @@ class DownloadManagerService @Inject constructor(
.subscribeOn(Schedulers.io())
.doOnError(Timber::e)
.subscribe { downloadQueue.add(it) }
)
}
private fun getMeta(tour: TourPreviewEntity): Observable<String> =
api.getTourMetaAsString(tour.id)
.doOnNext { meta ->
tour.let {
.map { meta ->
tour.apply {
val metaUri = RevisionString("$META_PREDICTION${tour.id}$META_FILE_TYPE")
it.setMetaFileEntityId(metaUri)
setMetaFileEntityId(metaUri)
fileModule.saveFileToDisk(fileModule.getFile(metaUri.uri()), meta)
}
}
.flatMap { db.upsertTourPreview(it) }
.map { tour.id }
.onErrorReturn {
setTourStatus(tour.id,DownloadState.Crushed)
......
......@@ -66,6 +66,8 @@ interface AppComponent : AndroidInjector<RoomParkApplication>{
fun provideTour():TourContract
fun providePlan():FlatPlanContract
fun provideLifeCycle(): ForegroundLifecycleObserver
fun provideNotifivations(): INotificationCenter
......
......@@ -59,6 +59,10 @@ abstract class ContractRepositoryModule {
@Binds
@Singleton
abstract fun provideTourContract(impl: ToursRepository): TourContract
@Binds
@Singleton
abstract fun providePlanContract(impl: PlanRepository): FlatPlanContract
}
......
......@@ -2,7 +2,6 @@ package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.domain.model.DealModel
import com.biganto.visual.roompark.domain.model.EstateModel
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import io.reactivex.Completable
import io.reactivex.Observable
......@@ -15,17 +14,6 @@ interface DealContract{
fun getFavorites() : Observable<List<EstateModel>>
fun getEstate(estateId: Int): Observable<EstateModel>
fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>>
fun getEmptyPlan(estateId: Int, planId: Int): Observable<String>
fun getPlan(
estateId: Int
, planId: Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String>
fun getDeals(): Observable<List<DealModel>>
fun fetchEstate(building: Int, number: Int): Observable<EstateModel>
fun setDealRead(dealId: String): Completable
......
package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.data.data_provider.PlanFeaturesVariant
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import io.reactivex.Observable
import java.io.File
/**
* Created by Vladislav Bogdashkin on 20.04.2020.
*/
interface FlatPlanContract{
fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>>
fun getPlan(
estateId: Int
, planId: Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String>
fun getPlanFile(featuresVariant: PlanFeaturesVariant): File
fun getPlan(featuresVariant: PlanFeaturesVariant): Observable<String>
}
\ No newline at end of file
......@@ -17,7 +17,6 @@ class EstateInteractor @Inject constructor(
fun getPlanTypes(estateId: Int) =
useCase.getEstatePlanPresets(estateId)
fun getPlan(estateId: Int,planId:Int) = useCase.getPlan(estateId,planId)
fun getPlan(plan:PlanPresetModel) =
useCase.getPlan(
......
......@@ -28,11 +28,17 @@ class SettingsInteractor @Inject constructor(
private val settingsUseCase: SettingsUseCase,
private val activity: BaseRoomParkActivity,
private val subUc: SubscriptionUseCase,
private val toursUc: TourPreviewsUseCase
private val toursUc: TourPreviewsUseCase,
private val planTypes: PlanTypesUseCase
){
fun downloadTourPlans(cancellationToken:DownloadUseCase.CancellationToken)
= planTypes.downloadAllPlanTypes(cancellationToken)
fun fetchToursSizes() = toursUc.fetchToursSizes()
fun fetchPlanTypesSizes() = planTypes.fetchNotDownloadedPlansSizes()
private fun startDownloadService() {
val i = Intent(activity, DownloadManagerService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
......
......@@ -63,7 +63,7 @@ class DownloadUseCase @Inject constructor(
else Okio.buffer(Okio.sink(fileStorage))
val buffer = sink.buffer()
var read = 0L
val step = 4096
val step = 8192
val source = response.source()
var stop: Boolean = token.isCancelled
sink.use {
......@@ -161,7 +161,7 @@ class DownloadUseCase @Inject constructor(
val jlist = db.getTourFilesJunction(tour.id).toList()
val junctionList = fileEntities
.map {file ->
val entity = jlist.firstOrNull{it.tour == tour.id && it.file == file.uri}
val entity = jlist.firstOrNull{it.tour == tour.id && it.file.uri() == file.uri.uri()}
?: TourFileJunctionEntity()
entity.setTour(tour.id)
entity.setFile(file.uri)
......
package com.biganto.visual.roompark.domain.use_case
import com.biganto.visual.roompark.domain.contract.DealContract
import com.biganto.visual.roompark.domain.contract.FlatPlanContract
import javax.inject.Inject
/**
......@@ -8,7 +9,8 @@ import javax.inject.Inject
*/
class EstateUseCase @Inject constructor(
private val contract: DealContract
private val contract: DealContract,
private val planContract: FlatPlanContract
) {
fun fetchFavorites() = contract.getFavorites()
......@@ -19,12 +21,9 @@ class EstateUseCase @Inject constructor(
contract.fetchEstate(building, number)
fun getEstatePlanPresets(estateId: Int) =
contract.getPlanTypes(estateId)
planContract.getPlanTypes(estateId)
fun getPlan(estateId: Int, planId: Int) =
contract.getEmptyPlan(estateId, planId)
fun getPlan(
estateId: Int
, planId: Int
......@@ -32,7 +31,7 @@ class EstateUseCase @Inject constructor(
, sizes: Boolean?
, walls: Boolean?
, electric: Boolean?
) = contract.getPlan(
) = planContract.getPlan(
estateId
, planId
, furniture
......
package com.biganto.visual.roompark.domain.use_case
import com.biganto.visual.roompark.data.data_provider.PlanFeaturesVariant
import com.biganto.visual.roompark.data.data_provider.featuresVariants
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.domain.contract.AuthContract
import com.biganto.visual.roompark.domain.contract.FlatPlanContract
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 20.04.2020.
*/
class PlanTypesUseCase @Inject constructor(
private val planContract: FlatPlanContract,
private val auth:AuthContract
) {
private val fetchAllPlanTypes =
auth.currentUser()
.map { user ->
val list =
user.deals?.map { it.estate as EstateEntity }?.toMutableList()
?: mutableListOf()
list.addAll(user.estates?.map { it as EstateEntity } ?: arrayListOf())
list
}
fun fetchNotDownloadedPlansSizes(): Observable<Int> =
fetchAllPlanTypes
.flatMapIterable { it }
.flatMap {
planContract.getPlanTypes(it.id)
.map { plantypes -> plantypes.flatMap { plan -> plan.featuresVariants } }
}
.map { list -> list.sumBy { if (planContract.getPlanFile(it).exists()) 0 else 1 } }
.scan { t1: Int, t2: Int -> t1 + t2 }
fun downloadAllPlanTypes(cancellationToken: DownloadUseCase.CancellationToken) =
fetchAllPlanTypes
.flatMapSingle {
Observable.fromIterable(it)
.flatMap { estateEntity ->
planContract.getPlanTypes(estateEntity.id)
.map { plantypes -> plantypes.flatMap { plan -> plan.featuresVariants } }
}
.toList()
}
.map { it.flatten() }
.filter { !cancellationToken.isCancelled }
.flatMap { downloadPlan(it, cancellationToken) }
private fun downloadPlan(
list: List<PlanFeaturesVariant>
, cancellationToken: DownloadUseCase.CancellationToken
)
: Observable<DownloadProgress> {
var completedThreads = 0
return Observable.fromIterable(list)
.filter { !cancellationToken.isCancelled }
.flatMap {
planContract.getPlan(it)
.subscribeOn(Schedulers.io())
.map {
completedThreads++
DownloadProgress(completedThreads, list.size)
}
}
}
}
data class DownloadProgress(
val currentProgress:Int,
val totalProgress:Int
)
\ No newline at end of file
......@@ -33,12 +33,16 @@ class TourPreviewsUseCase @Inject constructor(
,private val tours:TourContract
) {
fun getTourById(tourId:String):Observable<DownloadTourByIdResult> {
fun getTourById(tourId: String): Observable<DownloadTourByIdResult> {
val tour = retrieveTour(tourId)
Timber.d("retrieveTour $tourId / $tour")
if (tour != null) {
if (tour.downloadState == DownloadState.Downloaded)
return Observable.just<DownloadTourByIdResult>(DownloadTourByIdResult.ReadyToPlay(tour))
return Observable.just<DownloadTourByIdResult>(
DownloadTourByIdResult.ReadyToPlay(
tour
)
)
return setTourState(tour.tour_id, DownloadState.DownloadQueue)
.map<DownloadTourByIdResult> { DownloadTourByIdResult.DownloadStarted(tour) }
......@@ -50,21 +54,21 @@ class TourPreviewsUseCase @Inject constructor(
}
fun retrieveTour(tourId: String) =
db.getTourPreview(tourId)
.asSequence()
.map ( ::fromEntity)
.firstOrNull()
db.getTourPreview(tourId)
.asSequence()
.map(::fromEntity)
.firstOrNull()
private fun setTourState(tourId:String, state: DownloadState): Observable<Boolean> =
db.getTourPreview(tourId)
.observable()
.doOnNext{ it.isDownloaded=state }
.flatMap { db.upsertTourPreview(it).map{true} }
private fun setTourState(tourId: String, state: DownloadState): Observable<Boolean> =
db.getTourPreview(tourId)
.observable()
.doOnNext { it.isDownloaded = state }
.flatMap { db.upsertTourPreview(it).map { true } }
private fun syncTour(tourId:String, parentId:Int):Observable<TourModel> =
syncTour(tourId,parentId,calcTargetResolution())
private fun syncTour(tourId: String, parentId: Int): Observable<TourModel> =
syncTour(tourId, parentId, calcTargetResolution())
private fun syncTour(tourId:String, estateId: Int, targetResolution:Int)
private fun syncTour(tourId: String, estateId: Int, targetResolution: Int)
: Observable<TourModel> {
val request = ToursRemoteByIdRequestModel(
arrayListOf(tourId)
......@@ -76,15 +80,16 @@ class TourPreviewsUseCase @Inject constructor(
.map {
if (it.isNullOrEmpty())
throw CustomApiException.TourByIdNotFoundException()
it.first() }
it.first()
}
.doOnNext { it.isDownloaded = DownloadState.DownloadQueue }
.flatMap {db.upsertTourPreview(it)}
.flatMap { db.upsertTourPreview(it) }
.map(::fromEntity)
.doOnError(Timber::e)
}
private fun calcTargetResolution():Int{
private fun calcTargetResolution(): Int {
val w = Resources.getSystem().displayMetrics.widthPixels
val h = Resources.getSystem().displayMetrics.heightPixels
return w.coerceAtLeast(h)
......@@ -93,10 +98,13 @@ class TourPreviewsUseCase @Inject constructor(
private fun forceTourUpdate(requestModel: ToursRemoteByIdRequestModel): Observable<List<TourPreviewEntity>> =
api.getToursPreviewById(requestModel.ids)
.subscribeOn(Schedulers.io())
.map { fromRaw(it
, requestModel.estateId
, api.provideHttpUrl().toString()
, requestModel.targetResolution)
.map {
fromRaw(
it
, requestModel.estateId
, api.provideHttpUrl().toString()
, requestModel.targetResolution
)
}
......@@ -104,56 +112,60 @@ class TourPreviewsUseCase @Inject constructor(
db.getEstateTourPreviews(estateId)
.filter { !it.isNullOrEmpty() }
private fun fetchApiTourList(multitourId:Int,estateId: Int) =
private fun fetchApiTourList(multitourId: Int, estateId: Int) =
auth.currentUser()
.doOnNext { Timber.d("user on next: $it") }
.map { it.targetResolution }
.flatMap { res ->
Timber.d("user: $res")
api.getOfferTours(multitourId)
.doOnError { Timber.e(it) }
.map {
mergeRaw(it, TourRemoteRequestModel(
estateId,
api.provideHttpUrl().toString(),
res
api.getOfferTours(multitourId)
.doOnError { Timber.e(it) }
.map {
mergeRaw(
it, TourRemoteRequestModel(
estateId,
api.provideHttpUrl().toString(),
res
)
)
)
}
.map { it }
.doOnNext { db.blockingUpsert(it) }
}
}
.map { it }
.doOnNext { db.blockingUpsert(it) }
}
fun fetchTourOffer(multitourId:Int,parent:Int) =
fun fetchTourOffer(multitourId: Int, parent: Int): Observable<List<TourModel>> =
Observable.mergeDelayError(
arrayListOf(fetchDbTourList(parent),fetchApiTourList(multitourId,parent))
arrayListOf(fetchDbTourList(parent), fetchApiTourList(multitourId, parent))
)
.take(1)
.doOnError { Timber.e(it) }
.map (::fromEntity)
.map(::fromEntity)
.subscribeOn(Schedulers.io())
private fun mergeRaw(raw: List<TourPreviewRaw>, requestRequestModel: TourRemoteRequestModel) =
raw.map{ mergeRaw(it, requestRequestModel) }
raw.map { mergeRaw(it, requestRequestModel) }
private fun mergeRaw(raw: TourPreviewRaw, requestRequestModel: TourRemoteRequestModel)
: TourPreviewEntity {
Timber.d("got raw: $raw")
val entity = db.getTourPreview(raw.id.toString()).firstOrNull()
?: return fromRaw(raw
?: return fromRaw(
raw
, requestRequestModel.estateId
, api.provideHttpUrl().toString()
, requestRequestModel.targetResolution)
, requestRequestModel.targetResolution
)
val fromApi = fromRaw(raw
val fromApi = fromRaw(
raw
, requestRequestModel.estateId
, api.provideHttpUrl().toString()
, requestRequestModel.targetResolution)
, requestRequestModel.targetResolution
)
if (fromApi.updated > entity.updated
&& entity.isDownloaded == DownloadState.Downloaded) {
&& entity.isDownloaded == DownloadState.Downloaded
) {
entity.isDownloaded =
DownloadState.NotSynced//maxState(entity.isDownloaded, fromApi.isDownloaded)
return entity
......@@ -163,19 +175,18 @@ class TourPreviewsUseCase @Inject constructor(
// if (fromApi.estate.id!=requestRequestModel.estateId)
// entity.estate=fromApi.estate
fromApi.setMetaFileEntityId(entity.metaFileEntityId)
fromApi.targetResolution=entity.targetResolution
fromApi.isDownloaded=entity.isDownloaded
fromApi.overallSize=entity.overallSize
fromApi.downloadedSize=entity.downloadedSize
fromApi.tempSize=entity.tempSize
fromApi.overallFiles=entity.overallFiles
fromApi.downloadedFiles=entity.downloadedFiles
fromApi.targetResolution = entity.targetResolution
fromApi.isDownloaded = entity.isDownloaded
fromApi.overallSize = entity.overallSize
fromApi.downloadedSize = entity.downloadedSize
fromApi.tempSize = entity.tempSize
fromApi.overallFiles = entity.overallFiles
fromApi.downloadedFiles = entity.downloadedFiles
return fromApi
}
private fun getUserEstates(user:UserEntity)
= user.deals?.map { it.estate }?.plus(
private fun getUserEstates(user: UserEntity) = user.deals?.map { it.estate }?.plus(
user.estates
?.asSequence()
?.filter { it.favorite }
......@@ -185,18 +196,15 @@ class TourPreviewsUseCase @Inject constructor(
fun fetchToursSizes(): Observable<Long> =
auth.currentUser()
.map (::getUserEstates)
.map { it.filter { estates -> estates.multitourId!=null }.toList() }
.map(::getUserEstates)
.map { it.filter { estates -> estates.multitourId != null }.toList() }
.map { estates ->
val toursOffersToLoad = mutableListOf<Int>()
estates.forEach{
estates.forEach {
if (it.tours.isNullOrEmpty())
toursOffersToLoad.add(it.multitourId!!)
}
Timber.d(" targer estates : " +
"${estates.map { it.tours?.size }} " )
Timber.d(" targer toursOffersToLoad : $toursOffersToLoad " )
Pair(estates,toursOffersToLoad)
Pair(estates, toursOffersToLoad)
}
.flatMap { pair ->
if (pair.second.isNotEmpty()) {
......@@ -206,51 +214,45 @@ class TourPreviewsUseCase @Inject constructor(
fetchTourOffer(
it, pair.first.first { estete -> estete.multitourId == it }.id
).doOnError { Timber.e(it) }
.doOnNext { tours -> loadedIds.addAll(tours.map{ t -> t.tour_id }) }
.doOnNext { tours -> loadedIds.addAll(tours.map { t -> t.tour_id }) }
}
).takeLast(1)
.doOnNext { db.refreshEntities(pair.first) }
.map { Pair(pair.first,loadedIds )}
}
else return@flatMap Observable.just(Pair(pair.first,null))
.map { Pair(pair.first, loadedIds) }
} else return@flatMap Observable.just(Pair(pair.first, null))
}
.doOnNext { Timber.d(" HERER ") }
.flatMap { pair ->
val estates = pair.first
for (estate in estates) {
Timber.d(" targer estates : ${estate.user} " )
}
val r = (estates.first().user as UserEntity).targetResolution
Timber.d(" targer R : $r " )
var knownSize =
estates.flatMap {
it.tours?.map {t -> t as TourPreviewEntity}
?.asIterable()?: arrayListOf()
}.filter{t -> t.overallSize > 0L }
.onEach { Timber.d("it.overallSize - it.downloadedSize " +
"${it.overallSize}/${it.downloadedSize}") }
it.tours?.map { t -> t as TourPreviewEntity }
?.asIterable() ?: arrayListOf()
}
.filter { t -> t.overallSize > 0L }
.sumByLong { it.overallSize - it.downloadedSize }
Timber.d(" targer knownSize : $knownSize " )
val toursToLoadSize =
estates.flatMap {
it.tours?.map {t -> t as TourPreviewEntity}
?.asIterable()?: arrayListOf()
}.filter{t -> t.overallSize < 1L }
Timber.d(" targer toursToLoadSize : $toursToLoadSize " )
val listIds = toursToLoadSize.map { it.id }.toMutableList()
pair.second?.let { listIds.addAll(it)}
Timber.d(" targer estates : ${listIds} " )
if (listIds.isNullOrEmpty()) return@flatMap Observable.just(knownSize)
it.tours?.map { t -> t as TourPreviewEntity }
?.asIterable() ?: arrayListOf()
}
.filter { t -> t.overallSize < 1L }
.map { it.id }.toMutableList()
pair.second?.let { toursToLoadSize.addAll(it) }
if (toursToLoadSize.isNullOrEmpty()) return@flatMap Observable.just(knownSize)
else return@flatMap api.getTourFilesSizes(
listIds,r.toString()
toursToLoadSize, r.toString()
)
.flatMapIterable { it }
.flatMap { rawSize ->
db.getTourPreview(rawSize.id.toString())
.observable()
.map {entity ->
.map { entity ->
knownSize += rawSize.total_size
entity.apply {
overallSize = rawSize.total_size
......@@ -258,7 +260,7 @@ class TourPreviewsUseCase @Inject constructor(
}
entity
}
.doOnNext{ db.upsert(it).blockingGet() }
.doOnNext { db.upsert(it).blockingGet() }
}.map { knownSize }
}
.subscribeOn(Schedulers.io())
......@@ -276,7 +278,6 @@ class TourPreviewsUseCase @Inject constructor(
auth.currentUser()
.map { user ->
val estatesList = getUserEstates(user)
estatesList
?.asSequence()
?.filter { it.multitourId != null }
......@@ -291,26 +292,17 @@ class TourPreviewsUseCase @Inject constructor(
)
}
}
.doOnNext { Timber.d("merged list:${it?.size}") }
.map { list ->
list.map { pair ->
api.getOfferTours(pair.first)
.doOnNext { Timber.d(" gonna merge") }
.map {
mergeRaw(
it
, pair.second
)
mergeRaw(it, pair.second)
}
.map { it }
.doOnNext { Timber.d("merged list:${it.size}") }
.blockingFirst()
}
}
.flatten()
}
// .map { it.flatten() }
.doOnNext { Timber.d("merged flatten list:${it.size}") }
.flatMap { db.upsertTourPreview(it) }
.subscribeOn(Schedulers.io())
}
......
......@@ -7,11 +7,14 @@ import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import butterknife.OnClick
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.HeaderToolbarModel
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.conductor.BigantoBaseController
import com.biganto.visual.roompark.conductor.dialogs.DownloadPlansDialogController
import com.biganto.visual.roompark.conductor.dialogs.change_handler.DialogChangeHandler
import com.biganto.visual.roompark.domain.model.SubscriptionModel
import com.biganto.visual.roompark.presentation.screen.settings.util.CahcedListAdapter
import com.biganto.visual.roompark.presentation.screen.settings.util.PushListAdapter
......@@ -105,6 +108,14 @@ class SettingsScreenController :
@BindView(R.id.progress_lock_background)
lateinit var progressShame:View
@OnClick(R.id.downloadFlatCardsIcon)
fun onStartTourPlans(){
router.pushController(RouterTransaction.with(DownloadPlansDialogController())
.pushChangeHandler(DialogChangeHandler())
.popChangeHandler(DialogChangeHandler())
)
}
private fun setToolbar(){
toolBar.appBar.liftOnScrollTargetViewId = R.id.nestedScrollContainer
pushRecycler.isNestedScrollingEnabled = false
......@@ -133,6 +144,15 @@ class SettingsScreenController :
override fun onViewBound(v: View) {
bottomNavigationController.show()
toursDownloaderTitle.text =
resources
?.getString(R.string.download_all_tours_settings_with_size, "-")
flatDownloaderTitle.text =
resources
?.getString(R.string.download_all_plan_types_settings_with_sizes, "-")
setToolbar()
bindRecycler()
}
......@@ -146,6 +166,7 @@ class SettingsScreenController :
is SettingsScreenViewState.SomeError -> render(viewState)
is SettingsScreenViewState.SignOut -> render(viewState)
is SettingsScreenViewState.OnCacheDeleting -> render(viewState)
is SettingsScreenViewState.OnPlanTypesPrefetch -> render(viewState)
is SettingsScreenViewState.LoadCachInfo -> render(viewState)
is SettingsScreenViewState.LoadSubscriptions -> render(viewState)
is SettingsScreenViewState.SubscriptionStatus -> render(viewState)
......@@ -170,6 +191,14 @@ class SettingsScreenController :
)
}
private fun render(viewState: SettingsScreenViewState.OnPlanTypesPrefetch) {
flatDownloaderTitle.text =
resources?.getString(
R.string.download_all_plan_types_settings_with_sizes
, viewState.size.bytesToSize()
)
}
private fun render(viewState: SettingsScreenViewState.LoadSubscriptions){
(pushRecycler.adapter as PushListAdapter).setItems(viewState.list)
}
......@@ -208,11 +237,6 @@ class SettingsScreenController :
@SuppressLint("SetTextI18n")
private fun render(viewState: SettingsScreenViewState.LoadSettingsList){
toursDownloaderTitle.text = viewState.settings.offlineStoreData[0].title +
"(${viewState.settings.offlineStoreData[0].amountBytes.bytesToSize()})"
flatDownloaderTitle.text = viewState.settings.offlineStoreData[1].title +
"(${viewState.settings.offlineStoreData[1].amountBytes.bytesToSize()})"
}
private fun getComponent() = DaggerSettingsScreenComponent.factory()
......
......@@ -41,6 +41,10 @@ class SettingsScreenPresenter @Inject constructor(
val fetchToursSize = interactor.fetchToursSizes()
.map { SettingsScreenViewState.OnSizePrefetch(it) }
val fetchPlansSize = interactor.fetchPlanTypesSizes()
.map { it*1024L*1024L }
.map { SettingsScreenViewState.OnPlanTypesPrefetch(it) }
val onDownloadTours = intent(SettingsScreen::downloadAllTours)
.flatMap { interactor.startToursDownloading()
.andThen(Observable.just(SettingsScreenViewState.Idle()))
......@@ -116,6 +120,7 @@ class SettingsScreenPresenter @Inject constructor(
val state = Observable.mergeDelayError(
arrayListOf(
restoreStateObservable,
fetchPlansSize,
fetchSettings,
onSignOut,
onClearCache,
......
......@@ -19,6 +19,7 @@ sealed class SettingsScreenViewState : BigantoBaseViewState() {
class SignOut() : SettingsScreenViewState()
class OnCacheDeleting(val progress:Float) : SettingsScreenViewState()
class OnSizePrefetch(val size:Long) : SettingsScreenViewState()
class OnPlanTypesPrefetch(val size:Long) : SettingsScreenViewState()
class LoadSubscriptions(val list:List<TitledSubscriptionModel>) : SettingsScreenViewState()
class LoadCachInfo(val list: List<CachedDataModel>) : SettingsScreenViewState()
class SubscriptionStatus(val subId:Int,val subState: Boolean) : SettingsScreenViewState()
......
......@@ -123,5 +123,7 @@
<string name="game_view_content_description" />
<string name="download_all_tours_settings_with_size">Скачать виртуальные туры моих квартир из избранного и сделок (%s)</string>
<string name="download_all_plan_types_settings_with_sizes">Скачать карточки моих квартир из избранного и сделок (%s)</string>
<string name="plan_types_download_completed">Загрузка планов завершена.</string>
<string name="download_plan_types_screen_title">Cкачиваются планировки</string>
</resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment