Commit 2bd8fbc6 authored by Vladislav Bogdashkin's avatar Vladislav Bogdashkin 🎣

Merge branch 'feature/tours_player' into develop

parents 9030fa1c 3392e114
......@@ -13,3 +13,10 @@
/captures
.externalNativeBuild
.cxx
/app/libs
/app/src/main/assets
/app/src/main/jniLibs
/app/release
......@@ -21,7 +21,7 @@ android {
defaultConfig {
applicationId $APPLICATION_ID
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
minSdkVersion minSdkVersion_RoomPark
targetSdkVersion targetSdkVersion_RoomPark
......
......@@ -26,6 +26,12 @@
</activity>
<activity android:name=".player.BigantoPlayerActivity" android:screenOrientation="fullSensor"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
android:largeHeap="true"
android:hardwareAccelerated="true" android:process=":UnityKillsMe"> </activity>
<service
android:name=".data.RoomParkMessageService"
android:enabled="true"
......
......@@ -30,12 +30,11 @@ abstract class BigantoBasePresenter<V : MvpView, VS>
open fun parseError(t: Throwable) : VS =
when (t) {
is CustomApiException ->{
is CustomApiException -> {
Timber.e("CustomApiException ${t.messageStringId} / ${t.customMessage}")
parse(t)
}
is NoNetworkException -> parse(t)
is NoNetworkException -> {parse(t)}
else -> {Timber.e(t);parse(t)}
}
......
......@@ -25,7 +25,7 @@ import timber.log.Timber
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
internal const val PHOTOS_KEY = "CHHOSE_PHOTO_LIST_KEY"
internal const val PHOTOS_KEY = "CHOOSE_PHOTO_LIST_KEY"
private fun formBundle(photos: ArrayList<PhotoResolutionModel>): Bundle {
val b = Bundle()
......@@ -66,7 +66,6 @@ class ChooseResolutionDialogController : Controller {
detachDisposable.add(
(recyclerView.adapter as PhotoSizeAdapter).onItemClicked.subscribe {
Timber.d("gonna shit : $it")
router.replaceTopController(RouterTransaction.with(
PhotoDialogController(
it.url
......
package com.biganto.visual.roompark.conductor.dialogs
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.os.Bundle
......@@ -37,6 +38,7 @@ class PhotoDialogController : Controller {
lateinit var recyclerView : RecyclerView
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
......@@ -85,6 +87,7 @@ class PhotoDialogController : Controller {
@LayoutRes
fun getLayoutId() = R.layout.photo_viewer
@SuppressLint("SourceLockedOrientationActivity")
override fun handleBack(): Boolean {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
return router.popCurrentController()
......
package com.biganto.visual.roompark.conductor.dialogs.tour_chooser
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 androidx.core.os.bundleOf
import com.biganto.visual.roompark.R
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.ToursInteractor
import com.biganto.visual.roompark.domain.model.TourModel
import com.biganto.visual.roompark.domain.use_case.DownloadUseCase
import com.biganto.visual.roompark.util.view_utils.snackbar.ISnackBarProvider
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreviewEntity
import com.bluelinelabs.conductor.Controller
import com.bumptech.glide.Glide
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.
*/
internal const val DOWNLOAD_TOUR_ID_KEY = "DOWNLOAD_TOUR_ID_KEY"
class DownloadTourDialogController : Controller {
constructor(args: Bundle) : super(args)
constructor(tourArg: TourModel) : super(bundleOf(DOWNLOAD_TOUR_ID_KEY to tourArg))
lateinit var progressBarDownload: ProgressBar
lateinit var downloaderBg: ImageView
lateinit var downloadTourTitleText: MaterialTextView
lateinit var cancelDownloadText: MaterialTextView
override fun onContextAvailable(context: Context) {
super.onContextAvailable(context)
DaggerDownloaderScreenComponent.factory()
.create(RoomParkApplication.component,activity as RoomParkMainActivity)
.inject(this)
}
private val tour:TourModel by lazy {
args.getParcelable<TourModel>(DOWNLOAD_TOUR_ID_KEY)
}
private var downloadToken = DownloadUseCase.CancellationToken(false)
@Inject
lateinit var downloader: ToursInteractor
@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)
// progress.visibility = View.VISIBLE
downloadToken = DownloadUseCase.CancellationToken(false)
downloadTourTitleText.text = tour.title
Glide.with(view)
.load(tour.previewUrl)
.transform(BlurTransformation(13, 8))
.transform(ColorFilterTransformation(0x99000000.toInt()))
.into(downloaderBg)
disposables.add(
downloader.downloadTour(tour.tour_id, downloadToken)
.onErrorReturn { Timber.e(it);TourPreviewEntity() }
// .onErrorReturn { parseError(it);TourPreviewEntity() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe (
{ model ->
updateProgressBar(model.downloadedFiles, model.overallFiles)
if (model.overallFiles == model.downloadedFiles)
activity?.let{ startPlayer(it,tour) }
}
,{error ->
Timber.e(error)
snackbar.showSnackBar(error.localizedMessage)
}
))
cancelDownloadText.setOnClickListener {
downloadToken.isCancelled = true;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 {
return router.popCurrentController()
}
}
@PerScreen
@Component(
modules = [DownloaderScreenModule::class],
dependencies = [AppComponent::class])
interface DownloaderScreenComponent {
@Component.Factory
interface Factory{
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
): DownloaderScreenComponent
}
//
// val presenter: ArticlesScreenPresenter
fun inject(controller: DownloadTourDialogController)
}
@Module
abstract class DownloaderScreenModule{
@PerScreen
@Binds
abstract fun provideContext(activity: RoomParkMainActivity): Context
}
package com.biganto.visual.roompark.conductor.dialogs.tour_chooser
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 androidx.annotation.LayoutRes
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.conductor.dialogs.change_handler.DialogChangeHandler
import com.biganto.visual.roompark.domain.model.TourModel
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonRecyclerAdapter
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonViewHolder
import com.biganto.visual.roompark.util.extensions.setGone
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.DownloadState
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.RouterTransaction
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.android.material.textview.MaterialTextView
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
internal const val TOUR_MODEL_LIST = "CHOOSE_TOUR_LIST_KEY"
private fun formBundle(photos: ArrayList<TourModel>): Bundle {
val b = Bundle()
b.putParcelableArrayList(TOUR_MODEL_LIST,photos)
return b
}
class ChooseTourDialogController : Controller {
constructor(args: Bundle) : super(args)
constructor(items: ArrayList<TourModel>) : super(formBundle(items))
private lateinit var recyclerView : RecyclerView
private val detachDisposable = CompositeDisposable()
override fun onDetach(view: View) {
detachDisposable.clear()
super.onDetach(view)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
recyclerView = view.findViewById(R.id.toursList)
recyclerView.layoutManager =
LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
recyclerView.adapter = TourChooserAdapter()
recyclerView.itemAnimator = null
recyclerView.addItemDecoration(
DividerItemDecoration(activity,DividerItemDecoration.VERTICAL)
)
args.getParcelableArrayList<TourModel>(TOUR_MODEL_LIST)?.let {
(recyclerView.adapter as TourChooserAdapter).setItems(it)
}
detachDisposable.add(
(recyclerView.adapter as TourChooserAdapter)
.onItemClicked
.debounce(300L,TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::onTourClicked)
)
//view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
view.setOnClickListener { handleBack() }
return view
}
private fun onTourClicked(tour: TourModel) {
when (tour.downloadState) {
DownloadState.Downloaded -> {
activity?.let { startPlayer(it, tour) }
}
else -> {
router.replaceTopController(
RouterTransaction.with(DownloadTourDialogController(tour))
.popChangeHandler(DialogChangeHandler())
.pushChangeHandler(DialogChangeHandler())
)
}
}
}
@LayoutRes
fun getLayoutId() = R.layout.tours_chooser_screen
override fun handleBack(): Boolean {
return router.popCurrentController()
}
}
internal class TourChooserAdapter:CommonRecyclerAdapter<TourChooserViewHolder,TourModel>(){
override val vhKlazz = TourChooserViewHolder::class
override fun getVhLayout(): Int = R.layout.tour_chooser_viewholder
}
internal class TourChooserViewHolder(itemView: View) :CommonViewHolder<TourModel>(itemView){
@BindView(R.id.tour_preview_imageView)
lateinit var tourPreview : ImageView
@BindView(R.id.tour_name)
lateinit var tourName : MaterialTextView
@BindView(R.id.tour_status_imageView)
lateinit var tourStatus : ImageView
@Inject
lateinit var activity:Context
override fun onViewBound(model: TourModel) {
tourName.text = model.title
Glide.with(tourPreview)
.load(model.previewUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(tourPreview)
tourStatus.setGone(model.downloadState == DownloadState.Downloaded)
tourStatus.setImageDrawable(
itemView.context.resources.getDrawable(
when(model.downloadState){
DownloadState.Downloaded -> R.drawable.ic_download
DownloadState.Suspended -> R.drawable.ic_download
DownloadState.DownloadQueue -> R.drawable.ic_download
else -> R.drawable.ic_download
}
,null)
)
}
}
package com.biganto.visual.roompark.conductor.dialogs.tour_chooser
import android.content.Context
import android.content.Intent
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roompark.domain.model.TourModel
import com.biganto.visual.roompark.player.BigantoPlayerActivity
import com.biganto.visual.roompark.player.unity_utils.LoadTourConfig
/**
* Created by Vladislav Bogdashkin on 07.04.2020.
*/
const val PLAY_TOUR_DATA_EXTRAS="BIGANTO_PLAYER_TOUR_DATA"
fun startPlayer(context:Context,tour: TourModel)
{
// hideToursList()
val playerIntent= Intent(context, BigantoPlayerActivity::class.java)
val tourConfig = LoadTourConfig(
tour.tour_id.toInt(),
tour.targetResolution,
FileModule.assetsDirectory(context),
tour.metaPredict,
tour.previewUrl,
context.resources?.getBoolean(R.bool.isTablet)?.not()?:true,
true
)
playerIntent.putExtra(PLAY_TOUR_DATA_EXTRAS,tourConfig)
context.startActivity(playerIntent)
}
\ No newline at end of file
package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
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.ImageAlbumJunctionEntity
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
......
......@@ -2,7 +2,7 @@ package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.androidplayer.data.repository.local.ILocalStore
import com.biganto.visual.roompark.data.local.UserState
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
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.UserEntity
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
......
......@@ -2,7 +2,7 @@ package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.androidplayer.data.repository.local.ILocalStore
import com.biganto.visual.roompark.data.local.UserState
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
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
......@@ -232,7 +232,7 @@ class EstateRepository @Inject constructor(
override fun getDeals(): Observable<List<DealModel>> {
return Observable.mergeDelayError(
arrayListOf(
getDealsDb,
// getDealsDb,
getDealsApi
)
)
......
package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
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.ArticleEntity
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
......
package com.biganto.visual.roompark.data.data_provider
import android.app.Application
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
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.file.FileModule
import com.biganto.visual.roompark.domain.contract.FilesContract
......
package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
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.SubscriptionEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.UserEntity
......
package com.biganto.visual.roompark.data.repository.api.biganto
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourFilesDataRaw
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourPreviewRaw
import io.reactivex.*
import io.reactivex.schedulers.Schedulers
import okhttp3.HttpUrl
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.Retrofit
import timber.log.Timber
import timber.log.Timber.e
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
/**
* Created by Vladislav Bogdashkin on 13.06.2018.
*/
@Singleton
class BigantoRetrofitRepository @Inject constructor(@Named("bigantoApi") retrofit:Retrofit)
: IBigantoApi{
private val api = retrofit.create(IBigantoMobileApi::class.java)
override fun provideHttpUrl(): HttpUrl {
return HttpUrl.parse(IBigantoMobileApi.BASE_URL)!!
}
override fun getAppVersion() = api
.getAppVersion()
.compose(RetrofitResponseValidation())
override fun getToursPreviewById(tourIds: List<String>): Observable<List<TourPreviewRaw>> {
return api.getToursPreviewById(ids = tourIds.joinToString(","))
.compose(RetrofitResponseValidation())
.subscribeOn(Schedulers.io())
}
override fun getTourMetaAsString(tour_id: String): Observable<String> = api
.getTourMetaAsString(ids = tour_id)
.map { it.toString() }
.doOnError { e(it) }
.subscribeOn(Schedulers.io())
override fun getTourFiles(tour_id: String, resolution: String): Observable<List<TourFilesDataRaw>> = api
.getTourFiles(ids = tour_id, resolution = resolution)
.compose(RetrofitResponseValidation())
.doOnError { e(it) }
.subscribeOn(Schedulers.io())
override fun downloadFile(uri: String, headers: Map<String, String>?): Flowable<ResponseBody> = api
.requestFile(headers ?: HashMap<String, String>(), uri)
.compose(FlowableRetrofitResponseValidation())
.doOnError(::e)
override fun getOfferTours(multiTourId:Int): Observable<List<TourPreviewRaw>> =
api
.getOfferTours(offerId = multiTourId)
.compose(RetrofitResponseValidation())
.map { it[multiTourId.toString()]?.toList()?: error("No tours avaliable")}
.doOnError { e(it) }
.subscribeOn(Schedulers.io())
}
internal class RetrofitResponseValidation<T> : ObservableTransformer<Response<T>, T> {
override fun apply(responseObservable: Observable<Response<T>>): ObservableSource<T> {
return responseObservable.switchMap { resp ->
// Timber.d("reutrned code: %s",resp.code())
if (resp.code() == 200 || resp.code() == 206)
if (resp.body() == null)
return@switchMap Completable.complete().toObservable<T>().doOnNext{Timber.d("Completed responseBody")}
else return@switchMap Observable.just(resp.body())
Timber.d("Returning Exception!")
return@switchMap Observable.error<T>(retrofit2.HttpException(resp))
}
}
}
internal class RetrofitResponseValidationString<String> : ObservableTransformer<Response<String>, String> {
override fun apply(responseObservable: Observable<Response<String>>): ObservableSource<String> {
return responseObservable.switchMap<String> { resp ->
// Timber.d("reutrned code: %s",resp.code())
if (resp.code() == 200)
if (resp.body() == null)
return@switchMap Completable.complete().toObservable<String>().doOnNext{Timber.d("Completed responseBody")}
else return@switchMap Observable.just<String>(resp.body())
Timber.d("Returning Exception!")
return@switchMap Observable.error<String>(retrofit2.HttpException(resp))
}
}
}
internal class FlowableRetrofitResponseValidation<T> : FlowableTransformer<Response<T>, T> {
override fun apply(responseObservable: Flowable<Response<T>>): Flowable<T> {
return responseObservable.switchMap { resp ->
if (resp.code() == 200 || resp.code() == 206)
if (resp.body() == null)
return@switchMap Completable.complete().toFlowable<T>().doOnNext{Timber.d("Completed responseBody")}
else return@switchMap Flowable.just(resp.body())
Timber.d("Returning Exception!")
return@switchMap Flowable.error<T>(retrofit2.HttpException(resp))
}
}
}
//Allow to override HttpException with custom exception output, example:
//sealed class CustomApiException(val code:Int,val biganto_message:String):RuntimeException()
//{
// class RedirectException():CustomApiException(code = 300,biganto_message = "Redirect page..")
// class WrongAuthDataException():CustomApiException(code = 401,biganto_message = "Login or password not allowed")
// class BlockedUserException():CustomApiException(code = 403,biganto_message = "User has been banned")
// class ServerException():CustomApiException(code = 500,biganto_message = "Error server: undefined")
//
//}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.api.biganto
import com.biganto.visual.roompark.data.repository.api.biganto.raw.AppVersionRaw
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourFilesDataRaw
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourPreviewRaw
import io.reactivex.Flowable
import io.reactivex.Observable
import okhttp3.HttpUrl
import okhttp3.ResponseBody
/**
* Created by Vladislav Bogdashkin on 13.06.2018.
*/
interface IBigantoApi {
fun provideHttpUrl():HttpUrl
fun downloadFile(uri: String, headers: Map<String, String>?): Flowable<ResponseBody>
// fun getToursFiles(tour_ids: List<Int>, resolution: String): Flowable<Map<String, List<TourFileRaw>>>?
fun getTourMetaAsString(tour_id: String): Observable<String>?
fun getTourFiles(tour_id: String, resolution: String): Observable<List<TourFilesDataRaw>>
fun getAppVersion(): Observable<AppVersionRaw>
fun getToursPreviewById(tourIds: List<String>): Observable<List<TourPreviewRaw>>
fun getOfferTours(multiTourId:Int): Observable<List<TourPreviewRaw>>
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.api.biganto
import com.biganto.visual.roompark.data.repository.api.biganto.raw.AppVersionRaw
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourFilesDataRaw
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourPreviewRaw
import com.google.gson.JsonArray
import io.reactivex.Flowable
import io.reactivex.Observable
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.*
import java.util.*
/**
* Created by Vladislav Bogdashkin on 13.06.2018.
*/
interface IBigantoMobileApi {
companion object{
//const val BASE_URL="http://local.biganto.com"
const val BASE_URL="https://biganto.com"
//const val API_URL="api-novus/"
const val API_URL="api/"
const val DELIMITER="?"
const val CLIENT_TYPE_PARAM="client"
const val CLIENT_VERSION_PARAM="client_version"
const val API_VERSION_PARAM="v"
const val AUTH_TOKEN="auth_token"
const val LANG_PARAM="lang"
const val DEFAULT_LANG_VALUE="en"
const val DEFAULT_CLIENT_TYPE="androidplayer"
//const val DEFAULT_CLIENT_TYPE="iosplayer"
const val OLD_CLIENT_TYPE="mobileplayer"
const val DEFAULT_CLIENT_VERSION="3.0"
const val DEFAULT_API_VERSION="2.0"
//region AppInfo
const val GET_APP_VERSION="misc.appVersion"
//endregion
//region Authentication
const val AUTH_METHOD="users/login/"
const val AUTH_METHOD_2_0="users.authorize"
const val AUTH_LOGIN_PARAM="email"
const val AUTH_PASSWORD_PARAM="password"
//endregion
//region GetEstates
const val GET_ESTATES_METHOD="estates.getList"
//endregion
//region Portfolio
const val GET_PORTFOLIO_ESTATES_METHOD="portfolio.getEstates"
const val GET_PORTFOLIO_TOURS_METHOD="portfolio.getTours"
//endregion
//region GetToursPreview
const val GET_TOURS_METHOD="tours.getBadges"//"tours.getList"
//const val PARENT_ESTATE_PARAM="id"
const val PARENT_ESTATE_PARAM="estate_id"
const val TOURS_BY_ID_PARAM="id"
const val TOURS_TYPES_PARAM="types"
const val DEFAULT_SUPPORTED_TOURS_TYPES = "virtual,real"
//endregion
//region GetToursMeta
const val GET_TOURS_META_METHOD="tours.getMeta"
const val GET_TOURS_META_ID="id"
//endregion
//region GetFiles
const val GET_TOURS_FILES_METHOD="tours.getFiles"
const val GET_TOURS_FILES_ID="id"
const val GET_TOURS_FILES_RESOLUTION="resolution"
//endregion
//region offers.GetTours
const val OFFER_GET_TOURS_METHOD="offers.getTours"
const val OFFER_GET_TOURS_ID="id"
//endregion
}
@GET("$API_URL$GET_TOURS_METHOD$DELIMITER")
fun getToursPreviewById(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
@Query(CLIENT_VERSION_PARAM) clientVersion: String = DEFAULT_CLIENT_VERSION,
@Query(API_VERSION_PARAM) apiVersion: String = DEFAULT_API_VERSION,
@Query(LANG_PARAM) languageCode: String = Locale.getDefault().language,
@Query(TOURS_TYPES_PARAM) toursTypes: String = DEFAULT_SUPPORTED_TOURS_TYPES,
@Query(TOURS_BY_ID_PARAM) ids: String
): Observable<Response<List<TourPreviewRaw>>>
@GET("$API_URL$GET_TOURS_META_METHOD$DELIMITER")
fun getTourMetaAsString(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
@Query(CLIENT_VERSION_PARAM) clientVersion: String = DEFAULT_CLIENT_VERSION,
@Query(API_VERSION_PARAM) apiVersion: String = DEFAULT_API_VERSION,
@Query(LANG_PARAM) languageCode: String = Locale.getDefault().language,
@Query(GET_TOURS_META_ID) ids: String
): Observable<JsonArray>
@GET("$API_URL$GET_TOURS_FILES_METHOD$DELIMITER")
fun getTourFiles(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
@Query(CLIENT_VERSION_PARAM) clientVersion: String = DEFAULT_CLIENT_VERSION,
@Query(API_VERSION_PARAM) apiVersion: String = DEFAULT_API_VERSION,
@Query(LANG_PARAM) languageCode: String = Locale.getDefault().language,
@Query(GET_TOURS_FILES_ID) ids: String,
@Query(GET_TOURS_FILES_RESOLUTION) resolution: String
): Observable<Response<List<TourFilesDataRaw>>>
@GET("$API_URL$GET_APP_VERSION$DELIMITER")
fun getAppVersion(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
@Query(CLIENT_VERSION_PARAM) clientVersion: String = DEFAULT_CLIENT_VERSION,
@Query(API_VERSION_PARAM) apiVersion: String = DEFAULT_API_VERSION,
@Query(LANG_PARAM) languageCode: String = Locale.getDefault().language
): Observable<Response<AppVersionRaw>>
@GET("$API_URL$OFFER_GET_TOURS_METHOD$DELIMITER")
fun getOfferTours(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
@Query(CLIENT_VERSION_PARAM) clientVersion: String = DEFAULT_CLIENT_VERSION,
@Query(API_VERSION_PARAM) apiVersion: String = DEFAULT_API_VERSION,
@Query(LANG_PARAM) languageCode: String = Locale.getDefault().language,
@Query(OFFER_GET_TOURS_ID) offerId: Int
): Observable<Response<Map<String,List<TourPreviewRaw>>>>
@Streaming
@GET
fun requestFile(
@HeaderMap headers: Map<String, String>?,
@Url fileUrl: String
): Flowable<Response<ResponseBody>>
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.api.biganto.raw
import java.util.*
/**
* Created by Vladislav Bogdashkin on 09.06.2018.
*/
data class ErrorRaw(
val code:Int,
val message:String
)
data class EstatesRaw(
val estates:List<EstateRaw>,
val errors:List<ErrorRaw>?
)
data class EstatesRoomParkRaw(
val estates:Map<String, EstateRoomParkRaw>,
val errors:List<ErrorRaw>?
)
data class EstateRoomParkRaw(
val avaliable:Boolean,
val long: String, //->long name
val short: String //->short name
)
data class TourOverviewsRoomParkRaw(
val tours:List<TourOverviewRoomParkRaw>,
val errors:List<ErrorRaw>?
)
data class TourOverviewRoomParkRaw(
val hidden:Boolean,
val id:String,
val preview: String,
val title: String,
val url:String
)
data class EstateRaw(
val id:Int,
val created:Date,
val title:String,
val preview:String?,
val cnt_tours:Int,
val errors:List<ErrorRaw>?
)
data class ToursPreviewRaw(
val list:List<TourPreviewRaw>,
val errors:List<ErrorRaw>?
)
data class TourPreviewRaw(
val id:Int,
val created:Date,
val updated:Date,
val type:String,
val title:String,
val preview:String,
val screen:String,
val hidden:Boolean,
val resolutions:List<Int>?,
val features:List<String>?,
val errors:List<ErrorRaw>?
)
data class TourFileRaw(
val size:Long,
val url:String,
val errors:List<ErrorRaw>?
)
data class TourFilesDataRaw(
val files:List<TourFileRaw>,
val id:Int,
val resolution:Int,
val total_size:Long,
val errors:List<ErrorRaw>?
)
data class TourGetFilesRaw(
val data: Map<String,List<TourFileRaw>>,
val errors:List<ErrorRaw>?
)
data class OfferTours(
val data: Map<String,List<TourPreviewRaw>>,
val errors:List<ErrorRaw>?
)
data class AppVersionRaw(
val current_version : String,
val download_url : String?,
val message : String?,
val critical : Boolean,
val errors : List<ErrorRaw>?
)
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.api.retrofit.di
import android.app.Application
import com.biganto.visual.roompark.data.repository.api.retrofit.IRoomParkMobileApi
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.ErrorRaw
import com.biganto.visual.roompark.data.repository.api.biganto.IBigantoMobileApi
import com.biganto.visual.roompark.data.repository.api.retrofit.util.*
import com.biganto.visual.roompark.data.repository.api.room_park.IRoomParkMobileApi
import com.biganto.visual.roompark.data.repository.api.room_park.raw.ErrorRaw
import com.biganto.visual.roompark.data.service.network.INetworkMonitor
import com.biganto.visual.roompark.data.service.network.LiveNetworkMonitor
import com.biganto.visual.roompark.data.service.network.NoNetworkException
......@@ -22,6 +23,7 @@ import retrofit2.converter.scalars.ScalarsConverterFactory
import timber.log.Timber
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Named
import javax.inject.Singleton
/**
......@@ -102,7 +104,7 @@ class RetrofitModule{
@Provides
@Singleton
// @Named("roomParkApi")
@Named("roomParkApi")
fun provideRetrofitRoomParkApi(context: Application): Retrofit =
Retrofit.Builder()
.baseUrl(IRoomParkMobileApi.BASE_URL)
......@@ -113,4 +115,18 @@ class RetrofitModule{
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
@Provides
@Singleton
@Named("bigantoApi")
fun provideRetrofitBigantoApi(context: Application): Retrofit =
Retrofit.Builder()
.baseUrl(IBigantoMobileApi.BASE_URL)
.client(client(AppVersionManager(context),LiveNetworkMonitor(context)))
.addConverterFactory(NullOnEmptyConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(gsonConverterFactory())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.api.retrofit.util
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.ErrorRaw
import com.biganto.visual.roompark.data.repository.api.room_park.raw.ErrorRaw
import com.biganto.visual.roompark.domain.custom_exception.parseException
import com.google.gson.*
import timber.log.Timber
......
package com.biganto.visual.roompark.data.repository.api.retrofit.util
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.ErrorRaw
import com.biganto.visual.roompark.data.repository.api.room_park.raw.ErrorRaw
import com.biganto.visual.roompark.domain.custom_exception.parseException
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
......
package com.biganto.visual.roompark.data.repository.api
package com.biganto.visual.roompark.data.repository.api.room_park
import com.biganto.visual.roompark.data.repository.api.retrofit.DEFAULT_ARTICLE_PAGE_SIZE
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.*
import com.biganto.visual.roompark.data.repository.api.room_park.raw.*
import io.reactivex.Observable
/**
......@@ -51,6 +50,3 @@ interface IRoomParkApi {
fun getWebCamsList(): Observable<List<WebCamRaw>>
}
interface IBigantoApi {
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.api.retrofit
package com.biganto.visual.roompark.data.repository.api.room_park
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.*
import com.biganto.visual.roompark.data.repository.api.room_park.raw.*
import io.reactivex.Observable
import retrofit2.Response
import retrofit2.http.*
......@@ -142,7 +142,7 @@ interface IRoomParkMobileApi{
@Field(PASSWORD_AUTH_PARAM) pwd: String
): Observable<Response<AuthRaw>>
@POST("$API_URL${SUBSCRIBE_METHOD}$DELIMITER")
@POST("$API_URL$SUBSCRIBE_METHOD$DELIMITER")
@FormUrlEncoded
fun subscribe(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
......@@ -154,7 +154,7 @@ interface IRoomParkMobileApi{
@Field(TOPIC_SUBSCRIBTION_TOPIC_ID_PARAM) estateId: String?
): Observable<Response<StatusResponse>>
@POST("$API_URL${UNSUBSCRIBE_METHOD}$DELIMITER")
@POST("$API_URL$UNSUBSCRIBE_METHOD$DELIMITER")
@FormUrlEncoded
fun unsubscribe(
@Query(CLIENT_TYPE_PARAM) clientType: String = DEFAULT_CLIENT_TYPE,
......
package com.biganto.visual.roompark.data.repository.api.retrofit
package com.biganto.visual.roompark.data.repository.api.room_park
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.*
import com.biganto.visual.roompark.data.repository.api.room_park.raw.*
import com.biganto.visual.roompark.util.extensions.asInt
import io.reactivex.Completable
import io.reactivex.Observable
......@@ -12,6 +11,7 @@ import retrofit2.Response
import retrofit2.Retrofit
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
/**
* Created by Vladislav Bogdashkin on 29.10.2019.
......@@ -19,7 +19,8 @@ import javax.inject.Inject
const val DEFAULT_ARTICLE_PAGE_SIZE = 10
class RetrofitRepository @Inject constructor(retrofit: Retrofit) : IRoomParkApi {
class RetrofitRepository @Inject constructor(@Named("roomParkApi") retrofit: Retrofit) :
IRoomParkApi {
private val api = retrofit.create(IRoomParkMobileApi::class.java)
......
package com.biganto.visual.roompark.data.repository.api.retrofit.raw
package com.biganto.visual.roompark.data.repository.api.room_park.raw
import java.util.*
......
package com.biganto.visual.roompark.data.repository.db
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString
import com.biganto.visual.roompark.data.repository.db.requrey.model.*
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.DownloadState
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreviewEntity
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Observable
import io.reactivex.Single
import io.requery.Persistable
import io.requery.reactivex.ReactiveResult
import io.requery.reactivex.ReactiveScalar
/**
* Created by Vladislav Bogdashkin on 29.10.2019.
......@@ -36,4 +41,32 @@ interface IDb {
fun setDealReadState(id: String, state: Boolean): Completable
fun saveSubscription(subscription: SubscriptionEntity): Single<SubscriptionEntity>
fun getSubscription(id: Int): ReactiveResult<SubscriptionEntity>
fun deleteTourPreview(id: String): Int?
fun deleteTourPreview(entity: TourPreviewEntity)
fun deleteFiles(entity: List<FileEntity>)
fun deleteFile(entity: FileEntity)
fun deleteTourFilesJunction(tourId: String): ReactiveScalar<Int>
fun deleteTourFilesJunction(entity: List<TourFileJunctionEntity>)
fun getTourFilesJunctionUniqueFiles(tourId: String): List<TourFileJunctionEntity?>
fun getTourFilesJunction(tourId: String): ReactiveResult<TourFileJunctionEntity>
fun upsertFileEntity(entity: List<FileEntity>): Observable<Iterable<FileEntity>>
fun upsertFileEntity(entity: FileEntity): Observable<FileEntity>
fun flowableFileEntityes(uris: List<RevisionString>): Flowable<FileEntity>
fun getDownloadedSumFileEntityes(uris: List<RevisionString>): Long
fun fetchTouresWithStates(states: List<DownloadState>): MutableList<TourPreviewEntity>?
fun fetchFileEntityes(uris: List<RevisionString>): MutableList<FileEntity>
fun pushFileEntities(uris: List<FileEntity>): Observable<Iterable<FileEntity>>
fun getFileEntity(uri: RevisionString): ReactiveResult<FileEntity>
fun getTourFiles(entity: TourPreviewEntity): MutableList<TourFileJunctionEntity>
fun getTourFiles(tourId: String): MutableList<TourFileJunctionEntity>
fun getTourFilesObservable(tourId: String): ReactiveResult<TourFileJunctionEntity>
fun upsertTourPreview(list: List<TourPreviewEntity>): Observable<Iterable<TourPreviewEntity>>
fun upsertTourPreview(entity: TourPreviewEntity): Observable<TourPreviewEntity>
fun upsertTourFileJunction(entity: List<TourFileJunctionEntity>): Observable<Iterable<TourFileJunctionEntity>>
fun upsertTourFileJunction(entity: TourFileJunctionEntity): Observable<TourFileJunctionEntity>
fun observableTourDownloadState(): Flowable<TourPreviewEntity>
fun getFileEntitysOrDefault(uri: RevisionString): FileEntity
fun getTourPreview(tourId: String): ReactiveResult<TourPreviewEntity>
fun getTourPreviewsObservableResult(estateId: Int): Observable<ReactiveResult<TourPreviewEntity>>
fun getEstateTourPreviews(estateId: Int): Observable<List<TourPreviewEntity>>
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.db.requrey
import android.app.Application
import com.biganto.visual.roompark.Models
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.db.requrey.model.*
import com.biganto.visual.roompark.di.dagger.DATABASE_VERSION
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.DownloadState
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreview
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreviewEntity
import dagger.Module
import dagger.Provides
import io.reactivex.BackpressureStrategy
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import io.requery.Persistable
import io.requery.android.sqlite.DatabaseSource
import io.requery.reactivex.KotlinReactiveEntityStore
......@@ -51,7 +57,6 @@ class RequeryRepository @Inject constructor(
override fun upsertUser(entity: UserEntity): Observable<UserEntity> =
store.upsert(entity).toObservable()
override fun <T : Persistable> upsert(entity: T): Single<T> = store.upsert(entity)
override fun <T : List<Persistable>> upsert(entity: T): Single<Iterable<Persistable>> =
......@@ -185,4 +190,204 @@ class RequeryRepository @Inject constructor(
.where(SubscriptionEntity.ID.eq(id))
.get()
///region tours files
override fun getTourPreviewsObservableResult(estateId: Int) = store
.select(TourPreviewEntity::class)
// .join(UserEntity::class).on(UserEntity::uuid.eq(estateId))
.where(TourPreviewEntity.ESTATE_ID.eq(estateId))
.get()
.observableResult()
override fun getTourPreview(tourId: String) = store
.select(TourPreviewEntity::class)
.where(TourPreviewEntity.ID.eq(tourId))
.get()
override fun getEstateTourPreviews(estateId: Int): Observable<List<TourPreviewEntity>> = store
.select(TourPreviewEntity::class)
.where(TourPreviewEntity.ESTATE_ID.eq(estateId))
.get()
.observable()
.toList()
.toObservable()
.map { it.toList() }
override fun upsertTourPreview(list: List<TourPreviewEntity>) = store
.upsert(list)
.doOnSuccess { Timber.d("Upsertd succses %s", it.count()) }
.toObservable()
override fun upsertTourPreview(entity: TourPreviewEntity) = store
.upsert(entity)
.toObservable()
override fun upsertTourFileJunction(entity: List<TourFileJunctionEntity>)
: Observable<Iterable<TourFileJunctionEntity>> = store.upsert(entity).toObservable()
override fun upsertTourFileJunction(entity: TourFileJunctionEntity) = store
.upsert(entity)
.toObservable()
override fun observableTourDownloadState() = store
.select(TourPreviewEntity::class)
.where(TourPreviewEntity.DOWNLOADED.notIn(arrayListOf(DownloadState.NotDownloaded, DownloadState.MetaPreparation)))
.get()
// .flowable()
// .
.observableResult()
.flatMap { it.observable() }
.toFlowable(BackpressureStrategy.BUFFER)
.subscribeOn(Schedulers.io())
override fun getFileEntitysOrDefault(uri: RevisionString) = store
.select(FileEntity::class)
.where(FileEntity.URI.eq(uri))
.get()
.firstOr {
val entity = FileEntity()
entity.setUri(uri)
entity.setDownloadedSize(0)
entity.setTotalSize(0)
entity.setDownloaded(false)
entity
}
override fun getFileEntity(uri: RevisionString) = store
.select(FileEntity::class)
.where(FileEntity.URI.eq(uri))
.get()
override fun getTourFiles(entity : TourPreviewEntity ) =store
.select(TourFileJunctionEntity::class)
.where(TourFileJunctionEntity.TOUR.eq(entity.id))
.get()
.toList()
override fun getTourFiles(tourId : String) =store
.select(TourFileJunctionEntity::class)
.where(TourFileJunctionEntity.TOUR.eq(tourId))
.get()
.toList()
override fun getTourFilesObservable(tourId : String) =store
.select(TourFileJunctionEntity::class)
.where(TourFileJunctionEntity.TOUR.eq(tourId))
.get()
override fun pushFileEntities(uris: List<FileEntity>) = store
.select(FileEntity::class)
.where(FileEntity.URI.`in`(uris.map { it.uri }.toList()))
.get()
.observable()
.toList()
.map {existedUris-> uris.filter{!existedUris.map {file -> file.uri.revisionUri() }.contains(it.uri.revisionUri()) }.toList()}
.flatMapObservable{ store.insert(it).toObservable() }
override fun fetchFileEntityes(uris: List<RevisionString>) = store
.select(FileEntity::class)
.where(FileEntity.URI.`in`(uris))
.get()
.toList()
override fun fetchTouresWithStates(states: List<DownloadState>) = store
.select(TourPreviewEntity::class)
.where(TourPreviewEntity.DOWNLOADED.`in`(states))
.get()
.toList()
override fun getDownloadedSumFileEntityes(uris: List<RevisionString>) = store
.select(FileEntity::class)
.where(FileEntity.URI.`in`(uris))
.get()
.map{ it.downloadedSize }
.sum()
// .observable()
override fun flowableFileEntityes(uris: List<RevisionString>) = store
.select(FileEntity::class)
.where(FileEntity.URI.`in`(uris))
.get()
.flowable()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
override fun upsertFileEntity(entity: FileEntity) = store
.upsert(entity)
.toObservable()
override fun upsertFileEntity(entity: List<FileEntity>) = store
.upsert(entity)
.toObservable()
override fun getTourFilesJunction(tourId:String) = store
.select(TourFileJunctionEntity::class)
.where(TourFileJunctionEntity.TOUR.eq(tourId))
.get()
override fun getTourFilesJunctionUniqueFiles(tourId:String):
List<TourFileJunctionEntity?> =
store
.raw(TourFileJunctionEntity::class,
"SELECT tfj.* from TourFileJunction tfj " +
"join TourFileJunction tfj2 on tfj2.file = tfj.File " +
"WHERE tfj2.tour = $tourId " +
"GROUP BY tfj.file " +
"HAVING COUNT(tfj.file) < 2"
)
.toList()
private fun <E:Persistable> deleteBlocking(entity:E) = store
.delete(entity)
.blockingAwait()
private fun <E:List<Persistable>> deleteBlocking(entity:E) = store
.delete(entity)
.blockingAwait()
override fun deleteTourFilesJunction(entity: List<TourFileJunctionEntity>) =
deleteBlocking(entity)
override fun deleteTourFilesJunction(tourId:String) = store
.delete(TourFileJunctionEntity::class)
.where(TourFileJunctionEntity.TOUR.eq(tourId))
.get()
override fun deleteFile(entity:FileEntity) = deleteBlocking(entity)
override fun deleteFiles(entity:List<FileEntity>) = deleteBlocking(entity)
override fun deleteTourPreview(entity: TourPreviewEntity) = deleteBlocking(entity)
override fun deleteTourPreview(id:String) = store
.delete(TourPreview::class)
.where(TourPreviewEntity.ID.eq(id))
.get()
.single()
.blockingGet()
///endregion
}
package com.biganto.visual.roompark.data.repository.db.requrey.model
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreview
import io.requery.*
/**
......@@ -38,6 +39,10 @@ interface Estate : Persistable {
@get:Nullable
val multitourId: Int?
@get:Nullable
@get:OneToMany(mappedBy = "estate")
val tours: Set<TourPreview>?
@get:Nullable
val multitourPreview: String?
......
package com.biganto.visual.roompark.data.repository.db.requrey.model
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourFileRaw
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString
import com.biganto.visual.roompark.data.repository.db.requrey.utils.RevisionStringConverter
import io.requery.*
/**
* Created by Vladislav Bogdashkin on 15.06.2018.
*/
@Entity
interface File :Persistable {
// @get:Key
// var id: String
// @get:Convert(URIConverter::class)
@get:Key
@get:Convert(RevisionStringConverter::class)
val uri: RevisionString
val totalSize: Long
val downloadedSize: Long
@get:Nullable
val isDownloaded: Boolean
// @get:ForeignKey(references = Skybox::class)
// @get:ManyToOne(cascade = arrayOf(CascadeAction.NONE))
// var skyboxId:Skybox?
}
fun fromRaw(raw: TourFileRaw):FileEntity {
val entity = FileEntity()
entity.setDownloaded(false)
entity.setDownloadedSize(0L)
entity.setTotalSize(raw.size)
entity.setUri(RevisionString(raw.url.substring(raw.url.indexOf("asset"))))
return entity
}
fun fromRaw(raw: List<TourFileRaw>): List<FileEntity> =List(raw.size) { index-> fromRaw(raw[index]) }
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.db.requrey.model
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString
import com.biganto.visual.roompark.data.repository.db.requrey.utils.RevisionStringConverter
import io.requery.*
/**
* Created by Vladislav Bogdashkin on 15.06.2018.
*/
@Entity
interface TourFileJunction :Persistable {
@get:Key
@get:Generated
val id: Int
// @get:OneToOne( cascade = [CascadeAction.NONE])
// val tour: TourPreview
//+@get:ForeignKey(delete = ReferentialAction.NO_ACTION, update = ReferentialAction.NO_ACTION, references = TourPreviewEntity::class)
val tour: String
// @get:OneToOne( cascade = [CascadeAction.NONE])
// val file: File
//@get:ForeignKey(delete = ReferentialAction.NO_ACTION, update = ReferentialAction.NO_ACTION, references = FileEntity::class)
@get:Convert(RevisionStringConverter::class)
val file: RevisionString
}
package com.biganto.visual.roomparkvr.data.repository.db.requery.model
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourPreviewRaw
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString
import com.biganto.visual.roompark.data.repository.db.requrey.model.Estate
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.data.repository.db.requrey.utils.IntListConverter
import com.biganto.visual.roompark.data.repository.db.requrey.utils.IsoDateConverter
import com.biganto.visual.roompark.data.repository.db.requrey.utils.RevisionStringConverter
import io.requery.*
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.abs
/**
* Created by Vladislav Bogdashkin on 15.06.2018.
*/
@Entity
interface TourPreview :Persistable {
@get:Key
val id: String
@get:ForeignKey(references = Estate::class)
@get:ManyToOne
var estate: Estate
//Obsolete
// @get:ForeignKey(references = Tour::class)
// @get:OneToOne(mappedBy = "id", cascade = arrayOf(CascadeAction.SAVE))
// var tour: Tour?
@get:Convert(IsoDateConverter::class)
val created: Date
@get:Convert(IsoDateConverter::class)
val updated: Date
val type: String
val screen: String
val title: String
val preview: String?
val hidden: Boolean
var isDownloaded: DownloadState
var overallSize:Long
var tempSize:Long
var downloadedSize:Long
var overallFiles:Int
var downloadedFiles:Int
var targetResolution : Int
@get:Convert(IntListConverter::class)
var resolutions:ArrayList<Int>
// @get:JunctionTable(name= "TourFilesRule")
// @get:ManyToMany( cascade = arrayOf(CascadeAction.NONE))
// @get:Convert(RevisionStringListConverter::class)
// var tourFiles: ArrayList<RevisionString>?
//
@get:Convert(RevisionStringConverter::class)
val metaFileEntityId: RevisionString?
}
fun fromRaw(raw: TourPreviewRaw, parentId:Int, baseUrl:String, userResolution:Int):TourPreviewEntity{
val tour=TourPreviewEntity()
tour.setId(raw.id.toString())
tour.setCreated(raw.created)
tour.setUpdated(raw.updated)
tour.setType(raw.type)
tour.setTitle(raw.title)
tour.setPreview("$baseUrl${raw.preview}")
tour.setScreen("$baseUrl${raw.screen}")
tour.setHidden(raw.hidden)
tour.resolutions= if (raw.resolutions != null && !raw.resolutions.isNullOrEmpty()) ArrayList(raw.resolutions)
else ArrayList(1024)
tour.targetResolution=raw.resolutions?.nearestResolution(userResolution)?:1024
val estate= EstateEntity()
estate.setId(parentId)
tour.estate=estate
tour.isDownloaded=DownloadState.NotDownloaded
tour.overallSize=0L
tour.downloadedSize=0L
tour.tempSize=0L
tour.overallFiles=0
tour.downloadedFiles=0
// val tourMeta= TourEntity()
// tourMeta.setId(raw.id.toString())
// tour.tour=tourMeta
return tour
}
fun List<Int>.nearestResolution(targetResolution:Int ):Int{
var lastScore=Int.MAX_VALUE
var lastRes=0
for (res in this){
var d = targetResolution-res
if (d>0) d*=2
d=abs(d)
if (d<lastScore){lastScore=d;lastRes=res}
}
// Timber.d("Resolutions: $this / t=$targetResolution final=$lastRes score=$lastScore")
return lastRes
}
fun fromRaw(raw: List<TourPreviewRaw>, parentId:Int, url:String, userResolution:Int):List<TourPreviewEntity> =List(raw.size) { index-> fromRaw(raw[index],parentId,url,userResolution) }
enum class DownloadState(val i:Int):Comparable<DownloadState>{
NotDownloaded(0),
MetaPreparation(1),
DownloadQueue(2),
Downloading(3),
Suspended(4),
Downloaded(5),
Crushed(6),
Deleting(7),
NotSynced(20);
}
package com.biganto.visual.roompark.data.repository.db.requrey.utils;
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString;
import io.requery.Converter;
/**
* Created by Vladislav Bogdashkin on 04.07.2018.
*/
public class RevisionStringConverter implements Converter<RevisionString, String> {
@SuppressWarnings("unchecked")
@Override
public Class<RevisionString> getMappedType() {
return RevisionString.class;
}
@Override
public Class<String> getPersistedType() {
return String.class;
}
@Override
public Integer getPersistedSize() {
return null;
}
@Override
public String convertToPersisted(RevisionString value) {
return value == null ? null : value.revisionUri();
}
@Override
public RevisionString convertToMapped(Class<? extends RevisionString> type,
String value) {
return value == null ? null : new RevisionString(value);
}
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.db.requrey.utils;
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString;
import java.util.ArrayList;
import io.requery.Converter;
public class RevisionStringListConverter implements Converter<ArrayList<RevisionString>, String> {
private static final String stringDelimeter=">RS<";
@SuppressWarnings("unchecked")
@Override
public Class<ArrayList<RevisionString>> getMappedType() {
return (Class)ArrayList.class;
}
@Override
public Class<String> getPersistedType() {
return String.class;
}
@Override
public Integer getPersistedSize() {
return null;
}
@Override
public String convertToPersisted(ArrayList<RevisionString> value) {
if (value == null) {
return "";
}
StringBuilder sb = new StringBuilder();
int index = 0;
for (RevisionString str: value) {
if (index > 0) {
sb.append(stringDelimeter);
}
sb.append(str.revisionUri());
index++;
}
return sb.toString();
}
@Override
public ArrayList<RevisionString> convertToMapped(Class<? extends ArrayList<RevisionString>> type,
String value) {
ArrayList<RevisionString> list = new ArrayList<>();
if (value != null && !value.isEmpty())
for (String s : value.split(stringDelimeter))
list.add(new RevisionString(s));
return list;
}
}
package com.biganto.visual.roompark.data.repository.mapper
import android.content.res.Resources
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.*
import com.biganto.visual.roompark.data.repository.api.room_park.raw.*
import com.biganto.visual.roompark.data.repository.db.requrey.PhotoResolutions
import com.biganto.visual.roompark.data.repository.db.requrey.TitledPhoto
import com.biganto.visual.roompark.data.repository.db.requrey.model.*
......
......@@ -4,7 +4,7 @@ import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import com.biganto.visual.roompark.BuildConfig
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.AppVersionRaw
import com.biganto.visual.roompark.data.repository.api.room_park.raw.AppVersionRaw
import com.jakewharton.rxrelay2.BehaviorRelay
import dagger.Module
import dagger.Provides
......
......@@ -4,8 +4,9 @@ import android.app.Application
import com.biganto.visual.androidplayer.data.repository.local.ILocalStore
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.data.local.LocalStorage
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
import com.biganto.visual.roompark.data.repository.api.biganto.IBigantoApi
import com.biganto.visual.roompark.data.repository.api.retrofit.di.RetrofitModule
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.DbModule
import com.biganto.visual.roompark.data.repository.file.FileModule
......@@ -17,10 +18,6 @@ import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
/**
* Created by Vladislav Bogdashkin on 13.06.2018.
*/
......@@ -57,7 +54,9 @@ interface AppComponent : AndroidInjector<RoomParkApplication>{
fun provideLocal():ILocalStore
fun provideApi():IRoomParkApi
fun provideApi(): IRoomParkApi
fun provideBigantoApi(): IBigantoApi
fun providedb():IDb
......@@ -75,41 +74,5 @@ interface AppComponent : AndroidInjector<RoomParkApplication>{
@BindsInstance app:RoomParkApplication
):AppComponent
// @BindsInstance
// fun context(application: Application): Factory
// @BindsInstance
// fun retrofitModule(retrofit: RetrofitModule):Builder
// @BindsInstance
// @Named("roomParkApi")
// fun retrofit(
// retorfitModule: RetrofitModule
// ): DaggerDataComponent.Builder
//
// fun build(): AppComponent
}
// retorfitModule: RetrofitModule
// fun cache(): ICachedStore
// fun context(): Context
// fun db(): IDb
// fun api(): IApi
// fun roomApi(): IRoomParkApi
// fun fileModule(): FilesModule
//// fun appLifeCycle(): AppLifecycleListener
// fun networkMonitor(): INetworkMonitor
// fun versionMonitor(): IAppVersionControl
// fun versionNotifier(): IAppVersionNotifier
//
// fun estateRepo() : IEstateRepository
// fun tourRepo() : ITourRepository
// fun userRepo() : IUserRepository
}
\ No newline at end of file
......@@ -9,19 +9,7 @@ import dagger.Module
* Created by Vladislav Bogdashkin on 13.06.2018.
*/
const val USER_CACHE_LIMIT_SIZE = 3
const val USER_CACHE_LIMIT_SECONDS_INACTIVE = 30L
const val TOURS_CACHE_LIMIT_SIZE = 500
const val TOURS_CACHE_LIMIT_SECONDS_INACTIVE = 200L
const val ESTATES_CACHE_LIMIT_SIZE = 100
const val ESTATES_CACHE_LIMIT_SECONDS_INACTIVE = 200L
const val FILES_CACHE_LIMIT_SIZE = 10000
const val FILES_CACHE_LIMIT_SECONDS_INACTIVE = 60L
const val DATABASE_VERSION = 12
const val DATABASE_VERSION = 13
@Module
abstract class AppModule{
......
......@@ -2,9 +2,11 @@ package com.biganto.visual.roompark.di.dagger
import com.biganto.visual.roompark.data.data_provider.*
import com.biganto.visual.roompark.data.local.LocalStorage
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
import com.biganto.visual.roompark.data.repository.api.retrofit.RetrofitRepository
import com.biganto.visual.roompark.data.repository.api.biganto.BigantoRetrofitRepository
import com.biganto.visual.roompark.data.repository.api.biganto.IBigantoApi
import com.biganto.visual.roompark.data.repository.api.retrofit.di.RetrofitModule
import com.biganto.visual.roompark.data.repository.api.room_park.IRoomParkApi
import com.biganto.visual.roompark.data.repository.api.room_park.RetrofitRepository
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.db.requrey.DbModule
import com.biganto.visual.roompark.data.repository.db.requrey.RequeryRepository
......@@ -56,93 +58,17 @@ abstract class ContractRepositoryModule {
@Module
abstract class DataModule {
// @Singleton
// @Binds
// abstract fun provideAuthContract(contract: AuthContractModule) : AuthContract
// @Binds
// abstract fun provideModule(m:RetrofitModule) : RetrofitModule
// @Binds
// abstract fun provideRpRetrofit(retrofit:Retrofit) : Retrofit
//
@Singleton
@Binds
abstract fun provideRoomParkApi(api: RetrofitRepository) : IRoomParkApi
abstract fun provideBigantoApi(bigantoApi:BigantoRetrofitRepository): IBigantoApi
// @Binds
// abstract fun provideStore(store: KotlinReactiveEntityStore<Persistable>) : KotlinReactiveEntityStore<Persistable>
@Singleton
@Binds
abstract fun provideDb(db: RequeryRepository) : IDb
// @Singleton
// @Binds
// abstract fun provideLocalStorage(local: UserHolder) : ILocalStore
/*
@Provides
@Singleton
fun provieApi(@Named("bigantoApi") retorfit:Retrofit): IApi {
return RetrofitRepository(retorfit)
}
abstract fun provideRoomParkApi(roomParkApi:RetrofitRepository): IRoomParkApi
@Provides
@Singleton
fun provieRoomParkApi(@Named("roomParkApi") retorfit:Retrofit): IRoomParkApi {
return RoomParkRetrofitRepository(retorfit)
}
@Provides
@Singleton
fun provideDb(context:Application): IDb {
return RequeryRepository(getRequeryDataStore(context))
}
@Provides
@Singleton
fun provideFileModule(context:Application): FilesModule {
return FilesModule(context)
}
@Provides
@Singleton
fun providesNetworkListener(context:Application): INetworkMonitor {
return LiveNetworkMonitor(context)
}
@Binds
abstract fun provideDb(db: RequeryRepository) : IDb
@Provides
@Singleton
fun getRequeryDataStore(context:Application): KotlinReactiveEntityStore<Persistable> {
Timber.d("Kotlin store creating..")
val source = DatabaseSource(context, Models.DEFAULT, "BigantoPerfect", DATABASE_VERSION)
source.setLoggingEnabled(false)
// if ( BuildConfig.DEBUG) {
// // use this in development mode to drop and recreate the tables on every upgrade
// source.setTableCreationMode(TableCreationMode.DROP_CREATE)
// }
val store = KotlinEntityDataStore<Persistable>(source.configuration)
Timber.d("Kotlin store %s",source)
return KotlinReactiveEntityStore(store)
// // override onUpgrade to handle migrating to a new version
// val configuration = source.configuration
// return ReactiveSupport.toReactiveStore(
// EntityDataStore<Persistable>(configuration))
}
*/
}
\ No newline at end of file
......@@ -2,7 +2,7 @@ package com.biganto.visual.roompark.domain.custom_exception
import androidx.annotation.StringRes
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.ErrorRaw
import com.biganto.visual.roompark.data.repository.api.room_park.raw.ErrorRaw
/**
......
package com.biganto.visual.roompark.domain.interactor
import com.biganto.visual.roompark.domain.model.EstateModel
import com.biganto.visual.roompark.domain.use_case.DownloadUseCase
import com.biganto.visual.roompark.domain.use_case.TourPreviewsUseCase
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 03.04.2020.
*/
class ToursInteractor @Inject constructor(
private val downloadUseCase: DownloadUseCase,
private val tourUseCase:TourPreviewsUseCase
){
fun getEstateTourList(estate:EstateModel) =
tourUseCase.fetchTourOffer(estate.multitourId
?: error("Отсутсвуют виртуальные туры для данного объекта"),estate.id)
fun downloadTour(tourId:String,cancellationToken: DownloadUseCase.CancellationToken) =
downloadUseCase.startTourDownloading(tourId,cancellationToken)
}
\ No newline at end of file
package com.biganto.visual.roompark.domain.model
import android.os.Parcel
import android.os.Parcelable
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.DownloadState
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreviewEntity
import java.text.DecimalFormat
import java.util.*
import kotlin.math.max
/**
* Created by Vladislav Bogdashkin on 30.05.2018.
*/
data class TourModel (
val tour_id:String="-1",
val created : Date=Date(),
val updated:Date=Date(),
val type: TourType = TourType.VIRTUAL,
val title:String="",
val previewUrl:String?="",
val screen:String?=null,
val hidden:Boolean=false,
val metaUri:String?,
val parentId:Int=-1,
var downloadState: DownloadState =DownloadState.MetaPreparation,
val downloadedSize: Long=-1L,
val totalTourSize : Long=-1L,
val targetResolution : Int
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readSerializable() as Date,
parcel.readSerializable() as Date,
TourType.valueOf(parcel.readString()),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readByte() != 0.toByte(),
parcel.readString(),
parcel.readInt(),
DownloadState.valueOf(parcel.readString()),
parcel.readLong(),
parcel.readLong(),
parcel.readInt())
val metaPredict : String
get() = metaUri?.removeSuffix("$tour_id.json") ?: ""
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(tour_id)
parcel.writeSerializable(created)
parcel.writeSerializable(updated)
parcel.writeString(type.name)
parcel.writeString(title)
parcel.writeString(previewUrl)
parcel.writeString(screen)
parcel.writeByte(if (hidden) 1 else 0)
parcel.writeString(metaUri)
parcel.writeInt(parentId)
parcel.writeString(downloadState.name)
parcel.writeLong(downloadedSize)
parcel.writeLong(totalTourSize)
parcel.writeInt(targetResolution)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<TourModel> {
override fun createFromParcel(parcel: Parcel): TourModel {
return TourModel(parcel)
}
override fun newArray(size: Int): Array<TourModel?> {
return arrayOfNulls(size)
}
}
}
fun Long.bytesToSize():String{
if (this==-1L) return "~"
var scale=0
var rest:Long=this
var least=0f
// Timber.d("size to gb: %s",this)
while (rest>=1024) {
least=(rest%1024).toFloat()
rest /= 1024
scale++
}
least/=102.4f
// least*=100f
return when(scale)
{
0->"$rest.${least.format(0)}B"
1->"$rest.${least.format(0)}Kb"
2->"$rest.${least.format(0)}Mb"
3->"$rest.${least.format(0)}Gb"
4->"$rest.${least.format(0)}Tb"
else ->"$rest${least.format(0)}Bb" //BigantoByte
}
}
fun bytesToString(bytes:Long)=bytes.bytesToSize()
fun Float.format(fracDigits: Int): String {
val df = DecimalFormat()
df.maximumFractionDigits = fracDigits
return df.format(this)
}
enum class TourType(val i:Int)
{
VIRTUAL(0),
REAL(1)
}
fun fromEntity(entity: TourPreviewEntity) = TourModel(
tour_id = entity.id,
parentId = entity.estate.id,
created= entity.created,
updated = entity.created,
// updated = entity.updated,
type = when(entity.type){
"virtual"-> TourType.VIRTUAL
"real"-> TourType.REAL
else-> TourType.REAL
},
title = entity.title,
previewUrl = entity.preview,
metaUri= entity.metaFileEntityId?.uri(),
screen = entity.screen,
hidden = entity.hidden,
downloadState = entity.isDownloaded,//calcDownloadState(entity.tour as TourEntity?)
downloadedSize = entity.downloadedSize?:-1L,
totalTourSize = max(entity.overallSize?:0L,entity.tempSize?:0L),
targetResolution = entity.targetResolution
)
fun fromEntity(raw: List<TourPreviewEntity>):List<TourModel> =List(raw.size) { index-> fromEntity(raw[index])}
data class TourLoadProgressModel(
val downloadState: DownloadState,
val downloadedSize: Long=-1L,
val totalTourSize : Long=-1L
)
......@@ -5,6 +5,6 @@ package com.biganto.visual.roompark.domain.model
*/
data class TourRequestModel(val building:Int, val flat:Int)
data class TourResponse(val response:List<TourModel>)
data class TourModel(val tourId:String, val title:Int, val preview:String?)
//data class TourRequestModel(val building:Int, val flat:Int)
//data class TourResponse(val response:List<TourModel>)
//data class TourModel(val tourId:String, val title:Int, val preview:String?)
package com.biganto.visual.roompark.domain.model
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.StreamRaw
import com.biganto.visual.roompark.data.repository.api.retrofit.raw.WebCamRaw
import com.biganto.visual.roompark.data.repository.api.room_park.raw.StreamRaw
import com.biganto.visual.roompark.data.repository.api.room_park.raw.WebCamRaw
/**
* Created by Vladislav Bogdashkin on 23.09.2019.
......
package com.biganto.visual.roompark.domain.use_case
import android.app.Application
import android.media.MediaScannerConnection
import com.biganto.visual.roompark.data.repository.api.biganto.IBigantoApi
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.db.requrey.RevisionString
import com.biganto.visual.roompark.data.repository.db.requrey.model.FileEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.TourFileJunctionEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.fromRaw
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.DownloadState
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreviewEntity
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import okio.Okio
import timber.log.Timber
import java.io.File
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 02.08.2019.
*/
private const val META_PREDICTION="/tourMeta_"
private const val META_FILE_TYPE=".json"
class DownloadUseCase @Inject constructor(
private val db: IDb,
private val api: IBigantoApi,
private val fileModule: FileModule,
private val context: Application
) {
private fun writeFile(
response: ResponseBody,
model: TourFileData,
token: CancellationToken
): Observable<TourFileData> {
return Observable.create<TourFileData> { sub ->
try {
if (token.isCancelled) {
sub.onNext(model)
sub.onComplete()
}
val fileStorage = fileModule.getFile(model.fileUrl.uri())
val sink =
if (model.fileDownloadedSize>0)
Okio.buffer(Okio.appendingSink(fileStorage))
else Okio.buffer(Okio.sink(fileStorage))
val buffer = sink.buffer()
var read = 0L
val step = 4096
val source = response.source()
var stop: Boolean = token.isCancelled
sink.use {
while (!stop && { read = source.read(buffer, step.toLong());read }() != -1L) {
model.tempDownloadedSize += read
model.fileDownloadedSize += read
if (model.tempOverallFileSize == 0L)
model.tempTourTotalDiff += model.tempDownloadedSize
if (token.isCancelled) {
model.isDownloaded = false
model.fileDownloadedSize = 0
sub.onComplete()
stop = true
source.buffer.flush()
}
}
}
model.isDownloaded = (source.exhausted()
&& (model.fileDownloadedSize == model.tempOverallFileSize
|| model.tempOverallFileSize == 0L))
sub.onNext(model.copy())
model.tempTourTotalDiff = 0
model.tempDownloadedSize = 0
sub.onComplete()
sink.close()
} catch (e: Throwable) {
Timber.e(e)
if (!sub.isDisposed)
sub.onError(e)
token.isCancelled = true
}
}
}
private fun mergeFiles(files: List<FileEntity>) {
Timber.d("Nerge files")
files.forEach { file ->
val entity: FileEntity? = db.getFileEntity(file.uri).firstOrNull()
entity?.let {
file.setDownloaded(it.isDownloaded)
file.setDownloadedSize(it.downloadedSize)
file.setTotalSize(it.totalSize)
}
}
Timber.d("save files: ${files.size}")
db.upsertFileEntity(files)?.blockingSubscribe { Timber.d("file saved") }
}
@Volatile
private var tourDbModel: TourPreviewEntity? = null
private fun setDownloadInfo(
id: String,
downloadedSize: Long? = null
, downloadedDiffSize: Long? = null
, totalSize: Long? = null
, resolution: Int? = null
, filesCount: Int? = null
, tempLoadedFiles: Int? = null
, totalSizedDiffSize: Long? = null
) {
if (tourDbModel?.id != id)
throw error("Wrong tour id")
tourDbModel?.let { entity ->
downloadedSize?.let { entity.downloadedSize = it }
downloadedDiffSize?.let { entity.downloadedSize += it }
totalSize?.let { entity.overallSize = it }
resolution?.let { entity.targetResolution = it }
filesCount?.let { entity.overallFiles = it }
tempLoadedFiles?.let { entity.downloadedFiles += it }
totalSizedDiffSize?.let { entity.overallSize += it }
if (entity.downloadedFiles == entity.overallFiles)
entity.isDownloaded = DownloadState.Downloaded
}
Timber.d(" tour: ${tourDbModel?.downloadedFiles} / ${tourDbModel?.overallFiles}")
}
private fun observableTourDownloading(tour: TourPreviewEntity, token: CancellationToken) =
api.getTourFiles(tour.id, tour.targetResolution.toString())
.map { tourDbModel = tour;it.first() }
.map { raw ->
var downloadedSize = 0L
var totalSize = 0L
val fileEntities = raw.files.map(::fromRaw)
mergeFiles(fileEntities)
val jlist = db.getTourFilesJunction(tour.id).toList()
val junctionList = fileEntities
.map {file ->
val entity = jlist.firstOrNull{it.tour == tour.id && it.file == file.uri}
?: TourFileJunctionEntity()
entity.setTour(tour.id)
entity.setFile(file.uri)
downloadedSize += file.downloadedSize
totalSize += file.totalSize
entity
}
setDownloadInfo(
raw.id.toString()
, downloadedSize = downloadedSize
, totalSize = totalSize
, resolution = raw.resolution
, filesCount = raw.files.count()
)
junctionList
}
.doOnNext { junctionList ->
db.upsertTourFileJunction(junctionList)?.subscribe { Timber.d("junction upserted") }
}
.doOnNext { _ ->
tourDbModel?.let {
db.upsertTourPreview(it)?.subscribe { Timber.d("tour upserted") }
}
}
.flatMapIterable { it }
.flatMap { junction ->
db.getFileEntity(junction.file)
.observable()
.map { entity ->
TourFileData(
fileUrl = junction.file
, tourId = junction.tour
, tempDownloadedSize = 0L
, tempOverallFileSize = entity.totalSize
, fileDownloadedSize = entity.downloadedSize
, tempTourTotalDiff = 0L
, isDownloaded = entity.isDownloaded
)
}
}
.toFlowable(BackpressureStrategy.BUFFER)
.parallel(4)
.runOn(Schedulers.io())
.filter { !token.isCancelled }
.flatMap { model ->
if (model.isDownloaded)
return@flatMap Flowable.just(model)
var header: HashMap<String, String>? = null
if (model.fileDownloadedSize > 0){
header = hashMapOf(Pair("Range", "bytes=${model.fileDownloadedSize}-"))
Timber.w("trying to continue download file " +
"url by: ${model.fileUrl}" +
"size is: ${model.fileDownloadedSize}/${model.tempOverallFileSize}" +
"and header is: $header")
}
api.downloadFile(model.fileUrl.revisionUri(), header)
.doOnError {
Timber.e(it)
}
.flatMap<TourFileData> {
writeFile(it, model, token)
.toFlowable(BackpressureStrategy.BUFFER)
.doOnCancel { Timber.d("CANCELLED") }
}
.flatMap { downloadInfo ->
db.upsertFileEntity(
FileEntity().also{
it.setUri(downloadInfo.fileUrl)
it.setDownloadedSize(downloadInfo.fileDownloadedSize)
it.setTotalSize(downloadInfo.tempOverallFileSize)
it.setDownloaded(downloadInfo.isDownloaded)})
.toFlowable(BackpressureStrategy.BUFFER)
.map { downloadInfo }
}
}
.sequential()
.toObservable()
// .buffer(15L,TimeUnit.MILLISECONDS)
// .flatMapIterable { it }
.map { model ->
setDownloadInfo(
model.tourId
, totalSizedDiffSize = model.tempTourTotalDiff
, downloadedDiffSize = model.tempDownloadedSize
, tempLoadedFiles = if (model.isDownloaded) 1 else null
)
model.tempDownloadedSize = 0
model.tourId
}
.delay(14L, TimeUnit.MILLISECONDS)
// .sample(37L, TimeUnit.MILLISECONDS)
.flatMap { db.upsertTourPreview(tourDbModel!!) }
fun startTourDownloading(tourId: String, cancellataionToken: CancellationToken)
: Observable<TourPreviewEntity> = db.getTourPreview(tourId).observable()
.doOnNext {
it.isDownloaded = DownloadState.Downloading
it.downloadedFiles = 0
it.downloadedSize = 0L
it.tempSize = it.overallSize // <- overall changes due downloading!!
it.overallSize = 0L
}
.flatMap { db.upsertTourPreview(it) }
.flatMap(::getMeta)
.doOnError(Timber::e)
.flatMap { observableTourDownloading(it, cancellataionToken) }
private fun getMeta(tour: TourPreviewEntity) =
api.getTourMetaAsString(tour.id)
?.doOnNext { meta ->
tour.let {
val metaUri = RevisionString("$META_PREDICTION${tour.id}$META_FILE_TYPE")
it.setMetaFileEntityId(metaUri)
fileModule.saveFileToDisk(fileModule.getFile(metaUri.uri()), meta)
}
}
?.map { tour }
?.onErrorReturn {
tour.isDownloaded = DownloadState.Crushed
db.upsertTourPreview(tour)?.blockingSubscribe()
tour
}
//#endregion oldMethod
private fun refreshGallery(file: File) {
MediaScannerConnection.scanFile(
context, arrayOf(file.path), null
)
{ path, uri ->
{}//Timber.d("Scanned $path")
}
}
data class TourFileData(
val fileUrl: RevisionString,
val tourId: String,
var tempDownloadedSize: Long = 0L,
var tempOverallFileSize: Long = 0L,
var fileDownloadedSize: Long = 0L,
var tempTourTotalDiff: Long = 0L,
var isDownloaded: Boolean = false
)
data class CancellationToken(var isCancelled: Boolean)
}
\ No newline at end of file
package com.biganto.visual.roompark.domain.use_case
import android.content.res.Resources
import com.biganto.visual.roompark.data.repository.api.biganto.IBigantoApi
import com.biganto.visual.roompark.data.repository.api.biganto.raw.TourPreviewRaw
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.domain.contract.AuthContract
import com.biganto.visual.roompark.domain.custom_exception.CustomApiException
import com.biganto.visual.roompark.domain.model.TourModel
import com.biganto.visual.roompark.domain.model.fromEntity
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.DownloadState
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreviewEntity
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.fromRaw
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 19.06.2018.
*/
const val EMPTY_PARENT = -778
class TourPreviewsUseCase @Inject constructor(
private val api:IBigantoApi
,private val db: IDb
,private val auth:AuthContract
) {
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 setTourState(tour.tour_id, DownloadState.DownloadQueue)
.map<DownloadTourByIdResult> { DownloadTourByIdResult.DownloadStarted(tour) }
}
return syncTour(tourId, parentId = EMPTY_PARENT)
.map { it }
.map<DownloadTourByIdResult> { DownloadTourByIdResult.DownloadStarted(it) }
}
fun retrieveTour(tourId: String) =
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 syncTour(tourId:String, parentId:Int):Observable<TourModel> =
syncTour(tourId,parentId,calcTargetResolution())
private fun syncTour(tourId:String, estateId: Int, targetResolution:Int)
: Observable<TourModel> {
val request = ToursRemoteByIdRequestModel(
arrayListOf(tourId)
, estateId
, null
, targetResolution
)
return forceTourUpdate(request)
.map {
if (it.isNullOrEmpty())
throw CustomApiException.TourByIdNotFoundException()
it.first() }
.doOnNext { it.isDownloaded = DownloadState.DownloadQueue }
.flatMap {db.upsertTourPreview(it)}
.map(::fromEntity)
.doOnError(Timber::e)
}
private fun calcTargetResolution():Int{
val w = Resources.getSystem().displayMetrics.widthPixels
val h = Resources.getSystem().displayMetrics.heightPixels
return w.coerceAtLeast(h)
}
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)
}
private fun fetchDbTourList(estateId: Int) =
db.getEstateTourPreviews(estateId)
.filter { !it.isNullOrEmpty() }
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
)
)
}
.map { it }
.doOnNext { db.blockingUpsert(it) }
}
fun fetchTourOffer(multitourId:Int,parent:Int) =
Observable.mergeDelayError(
arrayListOf(fetchDbTourList(parent),fetchApiTourList(multitourId,parent))
)
.take(1)
.doOnError { Timber.e(it) }
.map (::fromEntity)
.subscribeOn(Schedulers.io())
private fun mergeRaw(raw: List<TourPreviewRaw>, requestRequestModel: TourRemoteRequestModel) =
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
, requestRequestModel.estateId
, api.provideHttpUrl().toString()
, requestRequestModel.targetResolution)
val fromApi = fromRaw(raw
, requestRequestModel.estateId
, api.provideHttpUrl().toString()
, requestRequestModel.targetResolution)
if (fromApi.updated > entity.updated
&& entity.isDownloaded == DownloadState.Downloaded) {
entity.isDownloaded =
DownloadState.NotSynced//maxState(entity.isDownloaded, fromApi.isDownloaded)
return entity
}
// In case< where we need to update tour parent, when it was downloaded by link
// 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
return fromApi
}
}
data class TourRemoteRequestModel(val estateId: Int, val token: String? = null, val targetResolution: Int)
data class ToursRemoteByIdRequestModel(val ids: List<String>, val estateId: Int,
val token: String? = null, val targetResolution: Int)
sealed class DownloadTourByIdResult{
class ReadyToPlay(val tour:TourModel): DownloadTourByIdResult()
class DownloadStarted(val tour:TourModel): DownloadTourByIdResult()
class TourError(val id:String): DownloadTourByIdResult()
}
package com.biganto.visual.roompark.player
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
import android.view.OrientationEventListener
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.Animation.AnimationListener
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.player.unity_utils.IBigantoUnityPlugin
import com.biganto.visual.roompark.player.unity_utils.LoadTourConfig
import com.biganto.visual.roompark.player.unity_utils.UnityOrientation
import com.biganto.visual.roompark.player.unity_utils.requests_raw.UPBaseRequest
import com.biganto.visual.roompark.player.unity_utils.requests_raw.UPBuildTourRequest
import com.biganto.visual.roompark.player.unity_utils.requests_raw.UPChangeOrientationRequest
import com.bumptech.glide.Glide
import com.google.gson.GsonBuilder
import com.unity3d.player.UnityPlayer
import kotlinx.android.synthetic.main.activity_biganto_player.*
//import kotlinx.android.synthetic.main.activity_biganto_player.*
import timber.log.Timber
const val PLAY_TOUR_DATA_EXTRAS="BIGANTO_PLAYER_TOUR_DATA"
const val ERROR_TOUR_BUILD_FINISH_CODE=8601
const val UNITY_SCENE_LOADED_FADE_MILLS=600L
private fun UPBaseRequest.requestToJson():String{
return GsonBuilder().create().toJson(this, javaClass)
}
class BigantoPlayerActivity : Activity(), UnityResponseListener {
lateinit var preview: ImageView
lateinit var previewBackground: FrameLayout
lateinit var biganto: ImageView
lateinit var progress: ProgressBar
lateinit var container: FrameLayout
lateinit var unityContainer: FrameLayout
private var mUnityPlayer: UnityPlayer? = null
private var bigantoPluginListener: IBigantoUnityPlugin? = null
private lateinit var orientationListener: OrientationListener
// @Inject
// lateinit var appLifeCycle: AppLifecycleListener
private lateinit var tourObj: LoadTourConfig
override fun onCreate(savedInstanceState: Bundle?) {
Timber.d("CREATING PLAYER ACTIVITY")
super.onCreate(savedInstanceState)
orientationListener = OrientationListener(this)
// appLifeCycle = BigantoApplication.component.appLifeCycle()
//
// ProcessLifecycleOwner.get().lifecycle
// .addObserver(appLifeCycle)
setContentView(R.layout.activity_biganto_player)
container = this.frameContainer
unityContainer = this.unityFrameContainer
preview = this.playerPreview
previewBackground = this.previewBack
biganto = this.bigantoLogo
progress = this.loadingBar
if (mUnityPlayer == null) {
mUnityPlayer = UnityPlayer(this)
}
tourObj = intent.getParcelableExtra(PLAY_TOUR_DATA_EXTRAS)
val animated = AnimatedVectorDrawableCompat.create(this, R.drawable.biganto_splash_anim)
animated?.registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
biganto.post { animated.start() }
}
})
biganto.setImageDrawable(animated)
animated?.start()
Glide.with(this)
.load(tourObj.previewUri)
// .centerCrop()
.into(preview)
.also {
fadeOutAndHideImage(1f, 0f, 1500, biganto)
Handler().postDelayed(
{
fadeOutAndHideImage(0f, 1f, 1500, preview)
}
, 1200
)
}
}
private fun fadeOutAndHideImage(from: Float, to: Float, duration: Long, img: ImageView) {
val fadeOut = AlphaAnimation(from, to)
fadeOut.interpolator = AccelerateInterpolator()
fadeOut.duration = duration
fadeOut.setAnimationListener(object : AnimationListener {
override fun onAnimationEnd(animation: Animation) {
if (from > to) img.visibility = View.GONE
}
override fun onAnimationRepeat(animation: Animation) {}
override fun onAnimationStart(animation: Animation) {
if (from < to) img.visibility = View.VISIBLE
}
})
img.startAnimation(fadeOut)
}
private fun quitPlayer(){
mUnityPlayer?.stop()
mUnityPlayer?.quit()
}
override fun onBackPressed() {
finish()
}
@SuppressLint("SourceLockedOrientationActivity")
override fun onDestroy() {
this.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
quitPlayer()
super.onDestroy()
}
/**
* invokes from unity by AndroidJavaObject mechanics
*/
fun setUnityPlayerListener(listener: IBigantoUnityPlugin) {
this.bigantoPluginListener = listener
// Timber.d(" UNITY SETTING LISTENER THROGH ACTIVITY")
}
override fun unitySceneLoaded() {
runOnUiThread {
preview.visibility = View.VISIBLE
}
mUnityPlayer?.view?.visibility = View.VISIBLE
bigantoPluginListener?.sendUnityTourBuildRequest(UPBuildTourRequest(tourObj).requestToJson())
}
override fun unityTourBuilded() {
runOnUiThread {
//preview.visibility = View.GONE
fadeOutAndHideImage(1f, 0f, UNITY_SCENE_LOADED_FADE_MILLS, preview)
progress.visibility = View.GONE
previewBackground.visibility = View.GONE
biganto.visibility = View.GONE
}
mUnityPlayer?.view?.visibility = View.VISIBLE
}
override fun unityTourBuildFailed() {
Timber.d("Tour build failed! Finishing activity...")
finish()
}
override fun unityTourBuildProgress(progress: Int, total: Int) {
runOnUiThread {
preview.visibility = View.VISIBLE
}
mUnityPlayer?.view?.visibility = View.VISIBLE
}
override fun unityCrushed(message: String) {
runOnUiThread {
preview.visibility = View.VISIBLE
}
mUnityPlayer?.view?.visibility = View.VISIBLE
}
override fun onNewIntent(intent: Intent) {
Timber.d("PLAYER ACTIVITY GOT INTNET")
// To support deep linking, we need to make sure that the client can get access to
// the last sent intent. The clients access this through a JNI api that allows them
// to get the intent set on launch. To update that after launch we have to manually
// replace the intent with the one caught here.
setIntent(intent)
}
// Pause Unity
override fun onPause() {
super.onPause()
mUnityPlayer?.pause()
}
// Resume Unity
override fun onResume() {
Timber.d("PLAYER ACTIVITY RESUME")
super.onResume()
mUnityPlayer?.resume()
}
override fun onStart() {
super.onStart()
orientationListener.enable()
if (mUnityPlayer?.view?.parent != null) return
mUnityPlayer?.start()
unityContainer.addView(mUnityPlayer?.view)
}
override fun onStop() {
orientationListener.disable()
bigantoPluginListener = null
mUnityPlayer?.stop()
super.onStop()
}
// Low Memory Unity
override fun onLowMemory() {
super.onLowMemory()
mUnityPlayer?.lowMemory()
}
// Trim Memory Unity
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
if (level == TRIM_MEMORY_RUNNING_CRITICAL) {
mUnityPlayer?.lowMemory()
}
}
// This ensures the layout will be correct.
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
mUnityPlayer?.configurationChanged(newConfig)
}
// Notify Unity of the focus change.
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
mUnityPlayer?.windowFocusChanged(hasFocus)
}
//Uncomment this, if you want control backstack inside Unity engine
// // For some reason the multiple keyevent type is not supported by the ndk.
// // Force event injection by overriding dispatchKeyEvent().
// override public boolean dispatchKeyEvent(KeyEvent event)
// {
// if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
// return mUnityPlayer.injectEvent(event);
// return super.dispatchKeyEvent(event);
// }
//
// Pass any events not handled by (unfocused) views straight to UnityPlayer
// override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
// return mUnityPlayer?.injectEvent(event) ?: false;
// }
//
// override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
// return mUnityPlayer?.injectEvent(event) ?: false
// }
//
// override fun onTouchEvent(event: MotionEvent): Boolean {
// return mUnityPlayer?.injectEvent(event) ?: false
// }
// /*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
inner class OrientationListener(context: Context) : OrientationEventListener(context) {
private val ROTATION_O = 1
private val ROTATION_90 = 2
private val ROTATION_180 = 3
private val ROTATION_270 = 4
var rotation = 0
override fun onOrientationChanged(orientation: Int) {
var newRotation = rotation
if ((orientation < 35 || orientation > 325) && rotation != ROTATION_O) { // PORTRAIT
newRotation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
} else if (orientation in 146..214 && rotation != ROTATION_180) { // REVERSE PORTRAIT
newRotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
} else if (orientation in 56..124 && rotation != ROTATION_270) { // REVERSE LANDSCAPE
newRotation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
} else if (orientation in 236..304 && rotation != ROTATION_90) { //LANDSCAPE
newRotation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
}
if (newRotation != rotation) {
rotation = newRotation
// UnityBridge.runOnUnityThread {
Timber.d("Unity change orientation request")
// UnityBridge.runOnUnityThread { bigantoPluginListener?.sendUnityChangeOrientationRequest(rotation.toUnityOrientationId().requestToJson()) }
// }
}
}
fun Int.toUnityOrientationId(): UPChangeOrientationRequest {
val orId = when (this) {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT -> UnityOrientation.Portrait
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE -> UnityOrientation.LandscapeLeft
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE -> UnityOrientation.LandscapeRight
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT -> UnityOrientation.PortraitUpsideDown
else -> UnityOrientation.Portrait
}
return UPChangeOrientationRequest(orId)
}
}
}
interface UnityResponseListener{
fun unitySceneLoaded()
fun unityTourBuilded()
fun unityTourBuildProgress(progress:Int, total:Int)
fun unityCrushed(message:String)
fun unityTourBuildFailed()
}
\ No newline at end of file
package com.biganto.visual.roompark.player.unity_utils
import android.content.Context
import com.unity3d.player.UnityPlayer
/**
* Created by Vladislav Bogdashkin on 08.08.2018.
*/
class BigantoTourPlayer(p0: Context) : UnityPlayer(p0)
{
// val title = "Bigant Visual Tour Player"
// override fun kill() {
//// }
}
\ No newline at end of file
package com.biganto.visual.roompark.player.unity_utils;
public interface IBigantoUnityPlugin{
public void sendUnitySceneLoadRequest(String request);
public void sendUnityTourBuildRequest(String request);
public void sendUnityFinishRequest(String request);
public void sendUnityPauseRequest(String request);
public void sendUnityErrorRequest(String request);
public void sendUnityChangeOrientationRequest(String request);
// void onTourSceneLoaded();
// void onTourScenePrepared(TourPlayerCallback callback);
// void onTourLoaded(TourReadyCallback callback);
}
package com.biganto.visual.roompark.player.unity_utils
import android.os.Parcel
import android.os.Parcelable
/**
* Created by Vladislav Bogdashkin on 08.05.2019.
*/
data class LoadTourConfig(
val tourId:Int,
val targetResolution:Int,
val cacheFolderUri:String,
val metaUri:String,
val previewUri:String?,
val AllowVR:Boolean,
val AllowDollhouse:Boolean) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readInt(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readInt()!= 0,
parcel.readInt() == 1
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(tourId)
parcel.writeInt(targetResolution)
parcel.writeString(cacheFolderUri)
parcel.writeString(metaUri)
parcel.writeString(previewUri)
parcel.writeInt(if (AllowVR)1 else 0 )
parcel.writeInt(if (AllowDollhouse)1 else 0)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<LoadTourConfig> {
override fun createFromParcel(parcel: Parcel): LoadTourConfig {
return LoadTourConfig(parcel)
}
override fun newArray(size: Int): Array<LoadTourConfig?> {
return arrayOfNulls(size)
}
}
}
\ No newline at end of file
package com.biganto.visual.roompark.player.unity_utils;
import android.os.Handler;
import timber.log.Timber;
/**
* Created by Vladislav Bogdashkin on 23.08.2018.
*/
public final class UnityBridge {
// Содержит ссылку на C# реализацию интерфейса
// Перенаправляет вызов в Unity поток
private static Handler unityMainThreadHandler;
// Функция перевода выполнения в Unity поток, потребуется в дальнейшем
public static void runOnUnityThread(Runnable runnable) {
if (unityMainThreadHandler != null && runnable != null) {
unityMainThreadHandler.post(runnable);
}
}
public static void unRegisterHandler() {
if(unityMainThreadHandler != null)
unityMainThreadHandler.getLooper().quit();
unityMainThreadHandler=null;
}
public static void registerHandler() {
Timber.d(" handler: %s",unityMainThreadHandler );
if(unityMainThreadHandler == null) {
// Так как эту функцию вызываем всегда на старте Unity,
// этот вызов идет из нужного нам в дальнейшем потока,
// создадим для него Handler
unityMainThreadHandler = new Handler();
}
Timber.d(" handler: %s",unityMainThreadHandler );
}
}
\ No newline at end of file
package com.biganto.visual.roompark.player.unity_utils;
import com.google.gson.annotations.SerializedName;
public enum UnityLayoutViewState{
@SerializedName("0")
BINDING (0),
@SerializedName("1")
READY (1),
@SerializedName("2")
DETACHED (2),
@SerializedName("3")
DESTROYED (3);
private final int value;
public int getValue() {
return value;
}
private UnityLayoutViewState(int value) {
this.value = value;
}
}
package com.biganto.visual.roompark.player.unity_utils;
/**
* Created by Vladislav Bogdashkin on 28.08.2018.
*/
public enum UnityOrientation {
Unknown (0),
// Portrait orientation.
Portrait (1),
// Portrait orientation, upside down.
PortraitUpsideDown (2),
// Landscape orientation, counter-clockwise from the portrait orientation.
LandscapeLeft (3),
LandscapeRight (4),
// Auto-rotates the screen as necessary toward any of the enabled orientations.
AutoRotation (5);
int id;
private UnityOrientation(int i){id = i;}
public int GetID(){return id;}
public boolean IsEmpty(){return this.equals(UnityOrientation.Unknown);}
public boolean Compare(int i){return id == i;}
public static UnityOrientation GetValue(int _id)
{
UnityOrientation[] As = UnityOrientation.values();
for (UnityOrientation A : As) {
if (A.Compare(_id))
return A;
}
return UnityOrientation.Unknown;
}
}
package com.biganto.visual.roompark.player.unity_utils;
import com.google.gson.annotations.SerializedName;
public enum UnityPlayerViewState{
@SerializedName("0")
NotInitialized(0),
@SerializedName("1")
PreLoading(1),
@SerializedName("2")
Ready(2),
@SerializedName("3")
Error(3);
private final int value;
public int getValue() {
return value;
}
private UnityPlayerViewState(int value) {
this.value = value;
}
}
package com.biganto.visual.roompark.player.unity_utils.requests_raw;
/**
* Created by Vladislav Bogdashkin on 20.08.2018.
*/
//Unity Player base communication request
public class UPBaseRequest {
}
package com.biganto.visual.roompark.player.unity_utils.requests_raw;
import com.biganto.visual.roompark.player.unity_utils.LoadTourConfig;
/**
* Created by Vladislav Bogdashkin on 20.08.2018.
*/
public class UPBuildTourRequest extends UPBaseRequest{
public int TourId;
public int TargetResolution;
public String AssetsDirectory;
public String MetaPredict;
public String tour_preview;
public Boolean AllowVR=true;
public Boolean AllowDollhouse=true;
public UPBuildTourRequest(){}
public UPBuildTourRequest(
int tour_id,
int target_tex_resolution,
String sd_core_folder_uri,
String meta_file_path,
String tour_preview,
boolean allow_vr,
boolean allow_dollhouse
) {
this.TourId = tour_id;
this.TargetResolution = target_tex_resolution;
this.AssetsDirectory = sd_core_folder_uri;
this.MetaPredict = meta_file_path;
this.tour_preview = tour_preview;
this.AllowVR = allow_vr;
this.AllowDollhouse = allow_dollhouse;
}
public UPBuildTourRequest(LoadTourConfig tour) {
this(tour.getTourId(), tour.getTargetResolution(), tour.getCacheFolderUri(),
tour.getMetaUri(), tour.getPreviewUri(),tour.getAllowVR(),tour.getAllowDollhouse());
}
}
package com.biganto.visual.roompark.player.unity_utils.requests_raw;
import com.biganto.visual.roompark.player.unity_utils.UnityOrientation;
/**
* Created by Vladislav Bogdashkin on 20.08.2018.
*/
public class UPChangeOrientationRequest extends UPBaseRequest {
UnityOrientation orientation;
public UPChangeOrientationRequest(){orientation=UnityOrientation.Portrait;}
public UPChangeOrientationRequest(UnityOrientation orientation){this.orientation=orientation;}
}
package com.biganto.visual.roompark.player.unity_utils.requests_raw;
/**
* Created by Vladislav Bogdashkin on 20.08.2018.
*/
public class UPFinishRequest extends UPBaseRequest{
}
package com.biganto.visual.roompark.player.unity_utils.requests_raw;
/**
* Created by Vladislav Bogdashkin on 20.08.2018.
*/
public class UPPauseRequest extends UPBaseRequest {
}
package com.biganto.visual.roompark.player.unity_utils.requests_raw;
/**
* Created by Vladislav Bogdashkin on 20.08.2018.
*/
public class UPSceneLoadingRequest extends UPBaseRequest{
}
......@@ -300,6 +300,7 @@ class AlbumsScreenController :
.debounce (300L,TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { (albumsRecyclerView.adapter as AlbumListAdapter).setItems(arrayListOf())}
.share()
override fun onPhotoSelected(): Observable<PhotoModel>
......
......@@ -69,8 +69,9 @@ class AlbumsScreenPresenter @Inject constructor(
val headerItemSelected = intent(AlbumsScreen::onAlbumSelected)
.doOnNext { selectedIndex = it.albumId }
.doOnNext { restoreModel.currentIndex = it.albumId }
.flatMap<AlbumsScreenViewState> { model ->
.switchMap<AlbumsScreenViewState> { model ->
requestAlbum(model.albumId)
.onErrorReturn(::parseError)
.startWith(Observable.just<AlbumsScreenViewState>(AlbumsScreenViewState.HeaderAlbumChoosed(item = model))
)
}
......
......@@ -4,6 +4,7 @@ import com.biganto.visual.roompark.conductor.BigantoBasePresenter
import com.biganto.visual.roompark.domain.interactor.DealsInteractor
import com.biganto.visual.roompark.domain.model.DealPreviewModel
import com.biganto.visual.roompark.util.monades.ExceptionString
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
......@@ -20,7 +21,7 @@ class DealsScreenPresenter @Inject constructor(
: BigantoBasePresenter<DealsScreen, DealsScreenViewState>() {
override fun defaultErrorViewStateHandler() =
{e:ExceptionString -> DealsScreenViewState.SomeError(e)}
{ e: ExceptionString -> DealsScreenViewState.SomeError(e) }
override fun bindIntents() {
......@@ -29,22 +30,29 @@ class DealsScreenPresenter @Inject constructor(
val fetchDeals = interactor.fetchDeals()
.flatMap { deals ->
getStatusList
.map { List(deals.size) { index ->
.map{
List(deals.size) { index ->
DealPreviewModel(
Pair(deals[index], it)
)
} }
}
.map(DealsScreenViewState::DealsLoaded)
}
.doOnError { Timber.e(" ERORORO ") }
}
.map<DealsScreenViewState>(DealsScreenViewState::DealsLoaded)
.onErrorReturn(::parseError)
val state = restoreStateObservable
.mergeWith(fetchDeals)
.doOnError{ Timber.e(it)}
val state = Observable.mergeDelayError(
arrayListOf(
restoreStateObservable,
fetchDeals
)
)
.doOnError { Timber.e(it) }
.onErrorReturn(::parseError)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
subscribeViewState(state.cast(DealsScreenViewState::class.java), DealsScreen::render)
}
}
\ No newline at end of file
......@@ -15,5 +15,6 @@ interface EstateScreen : BigantoBaseContract<EstateScreenViewState> {
fun switchElectric(): Observable<Pair<Int,Boolean>>
fun showCommonInfo(): Observable<Int>
fun showExplication(): Observable<Int>
fun tourCardClicked(): Observable<Int>
}
......@@ -18,11 +18,14 @@ import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.base.StatusState
import com.biganto.visual.roompark.base.StatusToolbarModel
import com.biganto.visual.roompark.conductor.BigantoBaseController
import com.biganto.visual.roompark.conductor.dialogs.change_handler.DialogChangeHandler
import com.biganto.visual.roompark.conductor.dialogs.tour_chooser.ChooseTourDialogController
import com.biganto.visual.roompark.domain.model.*
import com.biganto.visual.roompark.presentation.screen.estate.util.FlatInfoAdapter
import com.biganto.visual.roompark.util.extensions.setGone
import com.biganto.visual.roompark.util.extensions.startUrl
import com.biganto.visual.roompark.util.view_utils.image_view.RoundedImageView
import com.bluelinelabs.conductor.RouterTransaction
import com.bumptech.glide.Glide
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.tabs.TabLayout
......@@ -70,6 +73,12 @@ class EstateScreenController :
.map { planTypesTabLayout.selectedTabPosition }
.observeOn(AndroidSchedulers.mainThread())
override fun tourCardClicked(): Observable<Int> =
tourScreen.clicks()
.map { 1 }
.debounce(320L, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
private fun ViewGroup.switchMatch() =
this.findViewById<SwitchMaterial>(R.id.switch1)
.checkedChanges()
......@@ -110,6 +119,7 @@ class EstateScreenController :
@Inject
override lateinit var injectedPresenter: EstateScreenPresenter
@BindView(R.id.flatTypesCustomView)
lateinit var flatTypeView: ViewGroup
......@@ -251,6 +261,8 @@ class EstateScreenController :
is EstateScreenViewState.PlanTypeSelected -> render(viewState)
is EstateScreenViewState.SomeError -> render(viewState)
is EstateScreenViewState.ShowEstateInfo -> render(viewState)
is EstateScreenViewState.ToursLoaded -> render(viewState)
}
}
......@@ -302,12 +314,17 @@ class EstateScreenController :
}
private fun render(viewState: EstateScreenViewState.ShowEstateInfo) {
(flatInfoRecyclerView.adapter as FlatInfoAdapter).setItems(viewState.info)
}
private fun render(viewState: EstateScreenViewState.ToursLoaded) {
Timber.d("Wtf")
router.pushController(RouterTransaction.with(
ChooseTourDialogController(ArrayList(viewState.tours)))
.popChangeHandler(DialogChangeHandler())
.pushChangeHandler(DialogChangeHandler())
)
}
private fun render(viewState: EstateScreenViewState.LoadPlanTypes) {
planTypesTabLayout.removeAllTabs()
......
......@@ -5,6 +5,7 @@ import androidx.annotation.StringRes
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.conductor.BigantoBasePresenter
import com.biganto.visual.roompark.domain.interactor.EstateInteractor
import com.biganto.visual.roompark.domain.interactor.ToursInteractor
import com.biganto.visual.roompark.domain.model.*
import com.biganto.visual.roompark.presentation.screen.estate.util.DisplayInfoModel
import com.biganto.visual.roompark.util.extensions.toRubly
......@@ -23,6 +24,7 @@ import javax.inject.Named
class EstateScreenPresenter @Inject constructor(
private val interactor: EstateInteractor,
private val toursInteractor: ToursInteractor,
private val context: Context,
@Named(SELECTED_ESTATE_ID_KEY) private val estateId:Int
)
......@@ -53,7 +55,7 @@ class EstateScreenPresenter @Inject constructor(
val fetchPlan = intent(EstateScreen::planTypesTabSelected)
.map { planList?.get(it) }
.flatMap {planPreset ->
.flatMap { planPreset ->
interactor.getPlan(planPreset)
.map<EstateScreenViewState> { plan -> EstateScreenViewState.LoadPlan(plan) }
.startWith(
......@@ -66,7 +68,7 @@ class EstateScreenPresenter @Inject constructor(
EstateScreenViewState.ShowEstateInfo(
showType
,if (showType== InfoShowType.COMMON_INFO)
, if (showType == InfoShowType.COMMON_INFO)
mapCommonInfo(estate?.commonInfo)
else mapCommonInfo(planPreset.explication)
)
......@@ -105,26 +107,39 @@ class EstateScreenPresenter @Inject constructor(
val showInfo = intent(EstateScreen::showCommonInfo)
.doOnNext { showType = InfoShowType.COMMON_INFO }
.map { estate?.commonInfo}
.map (::mapCommonInfo)
.map { EstateScreenViewState.ShowEstateInfo(showType,it) }
.map { estate?.commonInfo }
.map(::mapCommonInfo)
.map { EstateScreenViewState.ShowEstateInfo(showType, it) }
val showExplication = intent(EstateScreen::showExplication)
.doOnNext { showType = InfoShowType.EXPLICATIONS }
.map { planList?.get(it)?.explication}
.map (::mapCommonInfo)
.map { EstateScreenViewState.ShowEstateInfo(showType,it) }
val state = restoreStateObservable
.mergeWith(prefetchCards)
.mergeWith(fetchPlans)
.mergeWith(fetchPlan)
.mergeWith(switchElectric)
.mergeWith(switchFurn)
.mergeWith(switchSizes)
.mergeWith(switchWalls)
.mergeWith(showInfo)
.mergeWith(showExplication)
.map { planList?.get(it)?.explication }
.map(::mapCommonInfo)
.map { EstateScreenViewState.ShowEstateInfo(showType, it) }
val onStartTours = intent(EstateScreen::tourCardClicked)
.map { estate }
.flatMap {estate -> toursInteractor.getEstateTourList(estate)
.map { EstateScreenViewState.ToursLoaded(it) }
}
val state = Observable.mergeDelayError(
arrayListOf(
restoreStateObservable,
prefetchCards,
fetchPlans,
fetchPlan,
switchElectric,
switchFurn,
switchSizes,
switchWalls,
showInfo,
onStartTours,
showExplication
)
)
.doOnError { Timber.e(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
......
......@@ -3,6 +3,7 @@ package com.biganto.visual.roompark.presentation.screen.estate
import com.biganto.visual.roompark.conductor.BigantoBaseViewState
import com.biganto.visual.roompark.domain.model.EstateModel
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import com.biganto.visual.roompark.domain.model.TourModel
import com.biganto.visual.roompark.presentation.screen.estate.util.DisplayInfoModel
import com.biganto.visual.roompark.util.monades.ExceptionString
......@@ -19,4 +20,5 @@ sealed class EstateScreenViewState : BigantoBaseViewState() {
class LoadPlan(val planBody:String) : EstateScreenViewState()
class SomeError(val exception: ExceptionString) : EstateScreenViewState()
class ShowEstateInfo(val showType: InfoShowType, val info:List<DisplayInfoModel>) : EstateScreenViewState()
class ToursLoaded(val tours:List<TourModel>) : EstateScreenViewState()
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ package com.biganto.visual.roompark.presentation.screen.favorites
import com.biganto.visual.roompark.conductor.BigantoBasePresenter
import com.biganto.visual.roompark.domain.interactor.FavoritesInteractor
import com.biganto.visual.roompark.util.monades.ExceptionString
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
......@@ -19,21 +20,28 @@ class FavoritesScreenPresenter @Inject constructor(
: BigantoBasePresenter<FavoritesScreen, FavoritesScreenViewState>() {
override fun defaultErrorViewStateHandler() =
{e: ExceptionString -> FavoritesScreenViewState.SomeError(e)}
{ e: ExceptionString -> FavoritesScreenViewState.SomeError(e) }
override fun bindIntents() {
val prefetchCards = interactor.getFavoritesForCurrentUser()
.map { FavoritesScreenViewState.FavoriteEstatesLoaded(it) }
val state = restoreStateObservable
.mergeWith(prefetchCards)
.doOnError{ Timber.e(it)}
val state = Observable.mergeDelayError(
arrayListOf(
restoreStateObservable,
prefetchCards
)
)
.doOnError { Timber.e(it) }
.onErrorReturn(::parseError)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
subscribeViewState(state.cast(FavoritesScreenViewState::class.java), FavoritesScreen::render)
subscribeViewState(
state.cast(FavoritesScreenViewState::class.java),
FavoritesScreen::render
)
}
}
\ No newline at end of file
......@@ -7,18 +7,14 @@ import android.view.Menu
import android.view.View
import android.view.ViewGroup
import androidx.annotation.NonNull
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.contains
import androidx.core.view.get
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import com.biganto.visual.roompark.base.ICollapsingToolBar
import com.biganto.visual.roompark.base.IConductorActivity
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.Router
import com.bluelinelabs.conductor.RouterTransaction
import com.google.android.material.bottomnavigation.BottomNavigationView
import timber.log.Timber
/**
* Created by Vladislav Bogdashkin on 11.10.2019.
......@@ -119,53 +115,8 @@ class BNVRouterPagerAdapter(
router.backstack.forEach { it.controller().setOptionsMenuHidden(false) }
currentPrimaryRouter = router
}
drawToolbar(position)
}
fun drawToolbar(position: Int){
return
val toolBar = currentPrimaryRouter?.activity as ICollapsingToolBar
toolBar.showAll()
toolBar.appBar.setExpanded(false,true)
toolBar.appBar.setLiftable(true)
toolBar.appBar.setLifted(true)
toolBar.appBarScrollable(false)
toolBar.topAppBar.title = when(position){
0 -> "dsfsdf"
1 -> "ИЗБРАННОЕ"
2 -> "МОИ СДЕЛКИ"
3 -> "СМОТРЕТЬ\nКВАРТИРУ"
4 -> "НАСТРОЙКИ"
else -> ""
}
// toolBar.appBar.visibility=View.VISIBLE
val cc = (currentPrimaryRouter?.activity as IConductorActivity)
Timber.d(" posicione $position")
when(position){
0 -> {
Timber.d(" IN POSOIITION 0 ")
// toolBar.appBar.visibility = Toolbar.GONE;
// toolBar.topAppBar.visibility = View.GONE;
val params: CoordinatorLayout.LayoutParams = cc.conductorContainer.layoutParams as CoordinatorLayout.LayoutParams
params.behavior = null
}
1 ->{}
2 ->{}
3 -> {
toolBar.appBar.setLifted(false)
toolBar.appBar.setLiftable(false)
}
4 ->{}
else -> {}
}
}
override fun isViewFromObject(view: View, router: Any): Boolean {
require(router is Router) { "Non-router object in router stack!" }
......
......@@ -101,8 +101,6 @@ class SettingsScreenPresenter @Inject constructor(
}
.startWith(SettingsScreenViewState.OnCacheDeleting(0f))
.doOnError { Timber.e(it) }
.onErrorReturn(::parseError)
}
val state = Observable.mergeDelayError(
......
......@@ -5,8 +5,10 @@ import android.view.View
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.IBottomNavigation
import com.biganto.visual.roompark.util.view_utils.snackbar.SnackBarMessageType.*
import com.google.android.material.snackbar.Snackbar
import timber.log.Timber
import javax.inject.Inject
/**
......@@ -69,8 +71,26 @@ class SnackBarProvider @Inject constructor(val activity: Activity) : ISnackBarPr
snack?.dismiss()
snack = Snackbar
.make(parentView, message, length)
.setAction(actionText(type)) {}
.setActionTextColor(color(type))
// .setAction(actionText(type)) {}
// .setActionTextColor(color(type))
Timber.w(" act - ${activity} ")
Timber.w(" act - ${(activity as? IBottomNavigation)} ")
// (activity as? IBottomNavigation)?.let {bNAv ->
// Timber.w(" act $bNAv" )
// snack?.apply {
// Timber.w(" sna")
// view.layoutParams = (view.layoutParams as CoordinatorLayout.LayoutParams).apply {
// Timber.w(" fusc $this")
// setMargins(
// leftMargin,
// topMargin,
// rightMargin,
// 300
// )
// }
// }?.show()
// }
snack?.show()
}
......
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="128dp"
android:height="148dp"
android:viewportWidth="128"
android:viewportHeight="148">
<path
android:name="path"
android:pathData="M 51.2 125.075 L 51.2 81.005 L 12.8 58.925 L 12.8 102.995 L 51.2 125.075 Z M 64 73.6 L 64 147.2 L 0 110.4 L 0 0 L 12.8 7.36 L 12.8 44.152 L 64 73.6 Z"
android:fillColor="#b4272d"
android:fillAlpha="0"
android:strokeWidth="1"/>
<path
android:name="path_1"
android:pathData="M 85.28 61.28 L 85.28 134.88 L 72.48 127.52 L 72.48 68.692 L 21.28 39.492 L 21.28 24.164 Z"
android:fillColor="#b4272d"
android:fillAlpha="0"
android:strokeWidth="1"/>
<path
android:name="path_2"
android:pathData="M 106.404 48.557 L 106.404 122.157 L 93.604 114.797 L 93.604 55.969 L 42.404 26.77 L 42.404 11.441"
android:fillColor="#b4272d"
android:fillAlpha="0"
android:strokeWidth="1"/>
<path
android:name="path_3"
android:pathData="M 127.37 36.24 L 127.37 109.84 L 114.57 102.48 L 114.57 43.652 L 63.37 14.452 L 63.37 -0.876 Z"
android:fillColor="#b4272d"
android:fillAlpha="0"
android:strokeWidth="1"/>
</vector>
</aapt:attr>
<target android:name="path">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="fillAlpha"
android:duration="250"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:anim/accelerate_interpolator"/>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="1000"
android:duration="250"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
</aapt:attr>
</target>
<target android:name="path_1">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="178"
android:duration="343"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:anim/linear_interpolator"/>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="1250"
android:duration="250"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
</aapt:attr>
</target>
<target android:name="path_2">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="453"
android:duration="378"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:anim/linear_interpolator"/>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="1500"
android:duration="250"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
</aapt:attr>
</target>
<target android:name="path_3">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="646"
android:duration="438"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:anim/linear_interpolator"/>
<objectAnimator
android:propertyName="fillAlpha"
android:startOffset="1750"
android:duration="250"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:anim/decelerate_interpolator"/>
</set>
</aapt:attr>
</target>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="2dip" />
<solid android:color="#33000000" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="2dip" />
<solid android:color="#ccFFFFFF" />
</shape>
</clip>
</item>
<item
android:id="@android:id/progress"
>
<clip>
<shape>
<corners
android:radius="2dip" />
<solid android:color="#FFFFFFFF" />
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/download_container"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/backgroundDownloader"
android:scaleType="centerCrop"
android:background="#A62B2727"
/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/downloadingTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="32dp" android:layout_marginStart="32dp" android:gravity="center_horizontal"
android:textAlignment="center"
style="@style/NoticeTextDownloader"
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="32dp" android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="@+id/backgroundDownloader"
app:layout_constraintVertical_bias="0.32999998"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tourToDownloadTitle"
app:layout_constraintTop_toBottomOf="@+id/downloadingTitle" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="32dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="32dp" style="@style/TitleTextDownloader"/>
<ProgressBar
android:id="@+id/downloadProgress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="4dp"
android:layout_marginStart="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="32dp"
android:progressDrawable="@drawable/horizontal_progress_downloader"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tourToDownloadTitle" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/cancelDownloadButton"
app:layout_constraintTop_toBottomOf="@+id/downloadProgress" android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="32dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="32dp"
android:layout_marginBottom="32dp" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.24000001" style="@style/CancelTextDownloader" android:padding="4dp"
android:paddingStart="8dp" android:paddingEnd="8dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:id="@+id/frameContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".player.BigantoPlayerActivity">
<FrameLayout
android:id="@+id/unityFrameContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
</FrameLayout>
<FrameLayout
android:id="@+id/previewBack"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
</FrameLayout>
<ImageView
android:id="@+id/playerPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@color/colorPrimary"
android:visibility="invisible" />
<ImageView
android:id="@+id/bigantoLogo"
android:layout_width="148dp"
android:layout_height="128dp"
android:scaleType="fitCenter"
android:layout_gravity="center"
/>
<ProgressBar
android:id="@+id/loadingBar"
style="@style/BigantoProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_margin="16dp" />
</FrameLayout>
\ No newline at end of file
......@@ -120,13 +120,14 @@
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:background="@color/colorOpacityCardBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="@dimen/bottom_navigation_height"
android:layout_gravity="bottom"
android:fitsSystemWindows="true"
android:visibility="gone"
app:elevation="0dp"
app:itemBackground="@color/colorCommonBackground"
app:itemBackground="@color/colorOpacityBackgroundInv"
app:itemHorizontalTranslationEnabled="false"
app:itemIconTint="@drawable/bottom_navigation_icon_selector"
app:labelVisibilityMode="unlabeled"
......
......@@ -21,6 +21,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:minHeight="80dp"
android:orientation="vertical"
tools:itemCount="1"
tools:listitem="@layout/photo_preview_viewholder" />
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/tour_preview_imageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_bell_on" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tour_name"
style="@style/Common_Text.Notice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:text="Тур"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tour_status_imageView"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/tour_preview_imageView"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/tour_status_imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@+id/tour_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tour_name"
app:srcCompat="@drawable/ic_back" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/playTourCardOpacityLight">
<com.google.android.material.card.MaterialCardView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
app:cardElevation="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.32999998">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/toursList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_marginBottom="8dp"
android:orientation="vertical"
tools:itemCount="3"
tools:listitem="@layout/tour_chooser_viewholder">
</androidx.recyclerview.widget.RecyclerView>
</com.google.android.material.card.MaterialCardView>
<ImageView
android:id="@+id/close_current_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:clickable="true"
android:contentDescription="@string/content_description_close"
android:focusable="true"
android:focusableInTouchMode="true"
android:scaleType="fitXY"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_close_circled" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -18,5 +18,6 @@
<item name="colorControlNormal">@color/colorAccent</item>
<item name="android:statusBarColor">@color/colorPrimary</item>
<item name="android:windowLightStatusBar">true</item>
<item name="snackbarStyle">@style/Widget.RoomPark.Snackbar</item>
</style>
</resources>
\ No newline at end of file
......@@ -96,4 +96,11 @@
<string name="subscribe_error_message">Ошибка! Подписаться не удалось!</string>
<!--endregion-->
<string name="download_tour_title_text">Скачивается тур</string>
<string name="download_tour_cancel_text">Отмена</string>
<string name="game_view_content_description" />
</resources>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
......@@ -15,6 +15,17 @@
<item name="colorControlHighlight">@color/colorAccent</item>
<item name="colorControlNormal">@color/colorAccent</item>
<item name="android:statusBarColor">@color/colorPrimary</item>
<item name="snackbarStyle">@style/Widget.RoomPark.Snackbar</item>
</style>
<style name="Widget.RoomPark.Snackbar" parent="Widget.MaterialComponents.Snackbar" tools:override="true">
<item name="android:layout_margin">@null</item>
<!-- Use default Snackbar margins for top/left/right -->
<item name="android:layout_marginTop" tools:ignore="PrivateResource">@dimen/mtrl_snackbar_margin</item>
<item name="android:layout_marginLeft" tools:ignore="PrivateResource">@dimen/mtrl_snackbar_margin</item>
<item name="android:layout_marginRight" tools:ignore="PrivateResource">@dimen/mtrl_snackbar_margin</item>
<!-- Custom bottom margin, this could work for top/left/right too -->
<item name="android:layout_marginBottom">@dimen/bottom_navigation_height</item>
</style>
<style name="Widget.MainTextInputStyle" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
......@@ -424,4 +435,28 @@
//endregion
<style name="NoticeTextDownloader" parent="Default_TextView">
<item name="android:textColor">@color/colorNoticeText</item>
<item name="android:text">@string/download_tour_title_text</item>
</style>
<style name="TitleTextDownloader" parent="Default_TextView">
<item name="android:textColor">@color/colorInvertedText</item>
</style>
<style name="BubbleTitleText" parent="Header_TextView.Main_Header">
</style>
<style name="CancelTextDownloader" parent="Default_TextView">
<item name="android:textColor">@color/colorError</item>
<item name="android:text">@string/download_tour_cancel_text</item>
</style>
<style name="BigantoProgressBar">
<item name="android:layout_width">@dimen/default_icon_width</item>
<item name="android:layout_height">@dimen/default_icon_height</item>
<item name="android:indeterminateTint">@color/colorPrimary</item>
</style>
</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