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

merge actual develop

parents 0e5dd9ee 7db1a09f
......@@ -13,3 +13,10 @@
/captures
.externalNativeBuild
.cxx
/app/libs
/app/src/main/assets
/app/src/main/jniLibs
/app/release
......@@ -10,14 +10,28 @@ apply from: '../dependencies.gradle'
apply plugin: 'kotlinx-serialization'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
//apply plugin: 'io.fabric'
// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
// Initialize a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()
// Load your keystore.properties file into the keystoreProperties object.
android {
compileSdkVersion compileSdkVersion_RoomPark
defaultConfig {
applicationId $APPLICATION_ID
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86'
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
minSdkVersion minSdkVersion_RoomPark
targetSdkVersion targetSdkVersion_RoomPark
......@@ -26,19 +40,57 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
def keystorePropertiesFile = rootProject.file("../roomParkKeystore.properties")
if ( keystorePropertiesFile.exists() ){
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
// else throw new GradleException('Plesase, provide keystore properties file! ')
// You need to specify either an absolute path or include the
// keystore file in the same directory as the build.gradle file.
}
}
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
aaptOptions.cruncherEnabled = false
ext.alwaysUpdateBuildId = false
}
release {
minifyEnabled false
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
debuggable false
}
}
aaptOptions {
noCompress '.unity3d', '.ress', '.resource', '.obb'
}
task renameBundle(type: Copy) {
from "$buildDir/outputs/bundle/release"
into "../../"
String name = findProperty('newBundleName')
rename 'app-release.aab', "${name}.aab"
}
task publishRelease(type: GradleBuild) {
tasks = ['bundleRelease']
}
androidExtensions {
experimental = true
}
......@@ -56,6 +108,10 @@ android {
targetCompatibility 1.8
sourceCompatibility 1.8
}
lintOptions {
disable 'BinaryOperationInTimber'
}
}
kapt {
......@@ -80,11 +136,6 @@ dependencies {
//Logger: Timber
implementation "com.jakewharton.timber:timber:$timberVersion"
//Crashlytics
implementation('com.crashlytics.sdk.android:crashlytics:2.10.0@aar') {
transitive = true;
}
//RxJava
implementation "io.reactivex.rxjava3:rxjava:$rxJavaVersion"
......@@ -162,6 +213,19 @@ dependencies {
implementation 'jp.wasabeef:glide-transformations:4.0.0'
kapt "com.github.bumptech.glide:compiler:$glideVersion"
//Firebase
implementation "com.google.firebase:firebase-analytics:$fireBaseVersion"
implementation 'com.google.firebase:firebase-messaging:20.1.3'
//CrshLytics
implementation "com.google.firebase:firebase-crashlytics:$firebaseCrashlyticsVersion"
//RxKotlin
implementation("io.reactivex.rxjava2:rxkotlin:$rxKotlinVersion")
//Arch Lifecycle
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
//Tests
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
......
{
"project_info": {
"project_number": "740988198336",
"firebase_url": "https://room-park-9cc38.firebaseio.com",
"project_id": "room-park-9cc38",
"storage_bucket": "room-park-9cc38.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:740988198336:android:0fd03bfcfa85b78dae3c25",
"android_client_info": {
"package_name": "com.biganto.visual.roompark"
}
},
"oauth_client": [
{
"client_id": "740988198336-e2vel467jbici47mkokmmtuhg07gfl8k.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.biganto.visual.roompark",
"certificate_hash": "f23a8c722c050ddb5e6015b9f254535ddf5387ff"
}
},
{
"client_id": "740988198336-8c08r8mikgulg2s08p0fgi5j028rhn4u.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBvrvu2Iz2FGEf7S_DVaSwQmhuijIuW2SU"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "740988198336-8c08r8mikgulg2s08p0fgi5j028rhn4u.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
......@@ -19,3 +19,202 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
-dontwarn com.franmontiel.persistentcookiejar.**
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-dontwarn com.google.vr.ndk.base.DaydreamAp
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
# prevent Crashlytics obfuscation
-dontwarn com.crashlytics.**
-dontwarn org.kobjects.**
-dontwarn org.ksoap2.**
-dontwarn org.kxml2.**
-dontwarn org.xmlpull.v1.**
-keep class com.biganto.visual.roompark.data.repository.db.requrey.model** { *; }
-keep class com.biganto.visual.roompark.player.unity_utils** { *; }
-keepclassmembers class com.biganto.visual.roompark.player.unity_utils** { *; }
-keep class com.biganto.visual.roompark.player.BigantoPlayerActivity { *; }
-keepclassmembers class com.biganto.visual.roompark.player.BigantoPlayerActivity** { *; }
-keep public class * implements android.os.Parcelable
# keep CREATOR for referenced parcelables since it is accessed via reflection
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
}
-dontobfuscate -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
# could be refined, but if it works you got the idea
-keepnames class * implements android.os.Parcelable { *; }
-keepclassmembers class * implements android.os.Parcelable { *; }
-keepnames class * implements com.bluelinelabs.conductor** { *; }
-keepclassmembers class * implements com.bluelinelabs.conductor** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keep class com.biganto.visual.roompark.data.repository.api.biganto.raw** { *; }
-keepclassmembers class com.biganto.visual.roompark.data.repository.api.biganto.raw** { *; }
-keep class com.biganto.visual.roompark.data.repository.api.room_park.raw** { *; }
-keepclassmembers class com.biganto.visual.roompark.data.repository.api.room_park.raw** { *; }
# prevent Requery obfuscation
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontoptimize
-dontpreverify
-dontwarn java.lang.FunctionalInterface
-dontwarn java.util.**
-dontwarn java.time.**
-dontwarn javax.annotation.**
-dontwarn javax.cache.**
-dontwarn javax.naming.**
-dontwarn javax.transaction.**
-dontwarn java.sql.**
-dontwarn javax.sql.**
-dontwarn androidx.**
-dontwarn io.requery.cache.**
-dontwarn io.requery.rx.**
-dontwarn io.requery.reactivex.**
-dontwarn io.requery.reactor.**
-dontwarn io.requery.query.**
-dontwarn io.requery.android.sqlcipher.**
-dontwarn io.requery.android.sqlitex.**
-keepclassmembers enum io.requery** {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# google vr sdk
# Don't obfuscate any NDK/SDK code. This makes the debugging of stack traces
# in release builds easier.
-keepnames class com.google.vr.ndk** { *; }
-keepnames class com.google.vr.sdk** { *; }
# These are part of the Java <-> native interfaces for GVR.
-keepclasseswithmembernames,includedescriptorclasses class com.google.vr** {
native <methods>;
}
# The SDK configuration protos use reflection.
-keep class com.google.vr.sdk.proto** {
*;
}
-keep class com.google.common.logging.Vr$VREvent$SdkConfigurationParams** {
*;
}
-keep class com.google.common.logging.nano.Vr$VREvent$SdkConfigurationParams** {
*;
}
-keep class com.google.vr.cardboard.UsedByNative
-keep @com.google.vr.cardboard.UsedByNative class *
-keepclassmembers class * {
@com.google.vr.cardboard.UsedByNative *;
}
-keep class com.google.vr.cardboard.annotations.UsedByNative
-keep @com.google.vr.cardboard.annotations.UsedByNative class *
-keepclassmembers class * {
@com.google.vr.cardboard.annotations.UsedByNative *;
}
-keep class com.google.vr.cardboard.annotations.UsedByReflection
-keep @com.google.vr.cardboard.annotations.UsedByReflection class *
-keepclassmembers class * {
@com.google.vr.cardboard.annotations.UsedByReflection *;
}
-dontwarn sun.misc.Unsafe
-dontwarn libcore.io.Memory
# end googlevr sdk
-dontusemixedcaseclassnames
-dontwarn android.support.**
-verbose
-dontoptimize
-dontpreverify
-keep class com.unity3d** { *; }
-keep class org.fmod** { *; }
-keep class bitter.jnibridge** { *; }
-keepclassmembers class com.tms.rarus.videoserver.* { *; }
-keepclassmembers class com.unity3d.player** { *; }
-keepclassmembers class org.fmod** { *; }
-keepclasseswithmembernames class * {
native <methods>;
}
-keep class com.google.firebase.crashlytics** { *; }
-dontwarn com.google.firebase.crashlytics.**
-keep class com.google.android.gms** { *; }
-dontwarn com.google.android.gms.**
-keep class com.biganto.androidplayer** { *; }
-keepclassmembers class com.biganto.androidplayer.* { *; }
-libraryjars ./libs/unity-classes.jar
#-libraryjars ./libs/libprotobuf-java-nano.jar
-keep interface kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader
-keep class kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl
-keep class com.biganto.visual.roompark.presentation.screen.settings.util** { *; }
-keepclassmembers class com.biganto.visual.roompark.presentation.screen.settings.util** { *; }
-dontwarn kotlin.reflect.jvm.internal.**
-keep class kotlin** {
public protected *;
}
-keepclassmembers class * { public <init>(...); }
\ No newline at end of file
......@@ -2,19 +2,26 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.biganto.visual.roompark">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".base.RoomParkApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:screenOrientation="userPortrait"
android:supportsRtl="true"
android:theme="@style/AppTheme.Launch">
<activity android:name=".base.RoomParkMainActivity"
<activity
android:name=".base.RoomParkMainActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
......@@ -22,6 +29,47 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</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"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
<!--
Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more.-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_bell_on" />
<!--
Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. See README(https://goo.gl/6BKBk7) for more.
-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
</service>
<service
android:name=".data.service.download.DownloadManagerService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
\ No newline at end of file
......@@ -4,10 +4,13 @@ import android.util.Log
import com.biganto.visual.roompark.BuildConfig
import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.DaggerAppComponent
import com.crashlytics.android.Crashlytics
import dagger.android.AndroidInjector
import dagger.android.DaggerApplication
import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins
import kotlinx.io.IOException
import timber.log.Timber
import java.net.SocketException
/**
* Created by Vladislav Bogdashkin on 03.09.2019.
......@@ -34,15 +37,46 @@ class RoomParkApplication : DaggerApplication() {
if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
else Timber.plant(CrashlyticsTree())
RxJavaPlugins.setErrorHandler { e ->
when (e) {
is UndeliverableException -> {
}
// fine, irrelevant network problem or API that throws on cancellation
is IOException -> return@setErrorHandler
is SocketException -> return@setErrorHandler
// fine, some blocking code was interrupted by a dispose call
is InterruptedException ->
return@setErrorHandler
// that's likely a bug in the application
is NullPointerException -> {
Thread.currentThread().uncaughtExceptionHandler
.uncaughtException(Thread.currentThread(), e)
return@setErrorHandler
}
is IllegalArgumentException -> {
Thread.currentThread().uncaughtExceptionHandler
.uncaughtException(Thread.currentThread(), e)
return@setErrorHandler
}
// that's a bug in RxJava or in a custom operator
is IllegalStateException -> {
Thread.currentThread().uncaughtExceptionHandler
.uncaughtException(Thread.currentThread(), e)
return@setErrorHandler
}
}
Timber.w(e, "Undeliverable exception received, not sure what to do")
}
}
}
private class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) return
Crashlytics.log(priority, tag, message)
throwable?.let { Crashlytics.logException(it) }
}
}
\ No newline at end of file
......@@ -9,7 +9,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isGone
import butterknife.BindView
import butterknife.ButterKnife
import com.biganto.visual.roompark.BuildConfig
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.presentation.screen.splash.SplashScreenController
import com.biganto.visual.roompark.util.extensions.setGone
......@@ -17,12 +16,11 @@ import com.biganto.visual.roompark.util.view_utils.app_bar.DragControlAppBarLayo
import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Router
import com.bluelinelabs.conductor.RouterTransaction
import com.crashlytics.android.Crashlytics
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.textview.MaterialTextView
import io.fabric.sdk.android.Fabric
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.bell_switch_view.view.*
import kotlinx.android.synthetic.main.status_layout_toolbar.view.*
import kotlinx.android.synthetic.main.switch_toolbar.view.*
import timber.log.Timber
......@@ -53,7 +51,7 @@ class RoomParkMainActivity(
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
if (!BuildConfig.DEBUG) Fabric.with(this, Crashlytics())
// if (!BuildConfig.DEBUG) Fabric.with(this, Crashlytics())
setContentView(R.layout.activity_main)
......@@ -108,7 +106,7 @@ class RoomParkMainActivity(
headerToolbarBack.text = it.backTitle?:""
headerToolbar.toolbar_title.text = it.title?:""
headerToolbar.include13.setGone(!(it.switcher?:false))
headerToolbar.bell_container.setGone(!(it.switcher?:false))
}
status?.let {
statusToolbar.status_icon.setGone(it.state == null)
......
......@@ -102,6 +102,7 @@ abstract class BigantoBaseController<VS : BigantoBaseViewState,V: BigantoBaseCon
}
override fun handleBack(): Boolean {
detachDisposable.clear()
router.popController(this)
return true
// return super.handleBack()
......
......@@ -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.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.annotation.LayoutRes
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.BaseRoomParkActivity
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.di.dagger.ActivityModule
import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.PerScreen
import com.biganto.visual.roompark.domain.interactor.SettingsInteractor
import com.biganto.visual.roompark.domain.use_case.DownloadUseCase
import com.biganto.visual.roompark.util.view_utils.snackbar.ISnackBarProvider
import com.bluelinelabs.conductor.Controller
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.FitCenter
import com.google.android.material.textview.MaterialTextView
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import jp.wasabeef.glide.transformations.BlurTransformation
import jp.wasabeef.glide.transformations.ColorFilterTransformation
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
class DownloadPlansDialogController : Controller {
constructor():super()
constructor(args: Bundle) : super(args)
lateinit var progressBarDownload: ProgressBar
lateinit var downloaderBg: ImageView
lateinit var downloadTourTitleText: MaterialTextView
lateinit var cancelDownloadText: MaterialTextView
lateinit var downloadingContentText: MaterialTextView
override fun onContextAvailable(context: Context) {
super.onContextAvailable(context)
DaggerPlansDownloaderScreenComponent.factory()
.create(RoomParkApplication.component,activity as RoomParkMainActivity)
.inject(this)
}
private var downloadToken = DownloadUseCase.CancellationToken(false)
@Inject
lateinit var downloader: SettingsInteractor
@Inject
lateinit var rpActivity: RoomParkMainActivity
lateinit var snackbar: ISnackBarProvider
private val disposables = CompositeDisposable()
override fun onDetach(view: View) {
disposables.clear()
super.onDetach(view)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
snackbar = ActivityModule.provideSnackBar(rpActivity)
progressBarDownload = view.findViewById(R.id.downloadProgress)
downloaderBg = view.findViewById(R.id.backgroundDownloader)
cancelDownloadText = view.findViewById(R.id.cancelDownloadButton)
downloadTourTitleText = view.findViewById(R.id.tourToDownloadTitle)
downloadingContentText = view.findViewById(R.id.downloadingTitle)
downloadingContentText.text = resources?.getString(R.string.download_plan_types_screen_title)
// progress.visibility = View.VISIBLE
downloadToken = DownloadUseCase.CancellationToken(false)
downloadTourTitleText.text = ""
Glide.with(view)
.load(R.drawable.flat_placeholder)
.transform(BlurTransformation(13, 4)
,ColorFilterTransformation(0x99000000.toInt())
,FitCenter())
.into(downloaderBg)
disposables.add(
downloader.downloadTourPlans(downloadToken)
// .onErrorReturn { parseError(it);TourPreviewEntity() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe (
{ model ->
downloadTourTitleText.text = "${model.currentProgress}/${model.totalProgress}"
updateProgressBar(model.currentProgress, model.totalProgress)
if (model.currentProgress == model.totalProgress){
snackbar.showSnackBar(R.string.plan_types_download_completed)
handleBack()
}
}
,{error ->
Timber.e(error)
snackbar.showSnackBar(error.localizedMessage)
handleBack()
}
))
cancelDownloadText.setOnClickListener {handleBack() }
// downloadTour(it.tour.tour_id, downloadToken)
// view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
return view
}
private fun updateProgressBar(curr:Int, max:Int){
activity?.runOnUiThread {
progressBarDownload.max = max
progressBarDownload.progress = curr
}
}
@LayoutRes
fun getLayoutId() = R.layout.download_tour_layout
override fun handleBack(): Boolean {
downloadToken.isCancelled = true
return router.popController(this)
}
}
@PerScreen
@Component(
modules = [PlansDownloaderScreenModule::class],
dependencies = [AppComponent::class])
interface PlansDownloaderScreenComponent {
@Component.Factory
interface Factory{
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
): PlansDownloaderScreenComponent
}
//
// val presenter: ArticlesScreenPresenter
fun inject(controller: DownloadPlansDialogController)
}
@Module
abstract class PlansDownloaderScreenModule{
@PerScreen
@Binds
abstract fun provideActivity(activity: RoomParkMainActivity): BaseRoomParkActivity
}
package com.biganto.visual.roompark.conductor.dialogs
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
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 androidx.recyclerview.widget.RecyclerView
import com.biganto.visual.roompark.R
import com.bluelinelabs.conductor.Controller
import com.bumptech.glide.Glide
......@@ -20,6 +21,7 @@ import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.github.chrisbanes.photoview.PhotoView
import com.google.android.material.snackbar.Snackbar
import timber.log.Timber
/**
......@@ -35,15 +37,14 @@ class PhotoDialogController : Controller {
constructor(photoUrl: String) : super(bundleOf(PHOTO_URL_KEY to photoUrl))
lateinit var recyclerView : RecyclerView
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
val progress = view.findViewById<ProgressBar>(R.id.photo_load_progress_bar)
progress.visibility = View.VISIBLE
view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
......@@ -79,12 +80,20 @@ class PhotoDialogController : Controller {
})
.into(photoView)
}
view.findViewById<ImageView>(R.id.close_current_button).setOnClickListener {
Timber.d("Clicked")
handleBack()
}
return view
}
@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
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import androidx.annotation.LayoutRes
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.BaseRoomParkActivity
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.conductor.dialogs.change_handler.DialogChangeHandler
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roompark.data.service.network.INetworkMonitor
import com.biganto.visual.roompark.di.dagger.ActivityModule
import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.PerScreen
import com.biganto.visual.roompark.domain.interactor.SettingsInteractor
import com.biganto.visual.roompark.domain.model.bytesToSize
import com.biganto.visual.roompark.util.view_utils.snackbar.ISnackBarProvider
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.RouterTransaction
import com.google.android.material.textview.MaterialTextView
import com.jakewharton.rxbinding3.view.clicks
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 timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
private const val TYPICAL_PLAN_SIZE = (1.2f*1024L*1024L).toLong()
class StartPlansDownloadingDialogController : Controller {
constructor() : super()
constructor(args: Bundle) : super(args)
private val detachDisposable = CompositeDisposable()
override fun onDetach(view: View) {
detachDisposable.clear()
super.onDetach(view)
}
@Inject
lateinit var interactor: SettingsInteractor
@Inject
lateinit var file: FileModule
lateinit var snackbar: ISnackBarProvider
@Inject
lateinit var rpActivity: RoomParkMainActivity
@Inject
lateinit var networkMonitor: INetworkMonitor
override fun onContextAvailable(context: Context) {
super.onContextAvailable(context)
DaggerStartPlansDownloadingDialogScreenComponent.factory()
.create(RoomParkApplication.component,activity as RoomParkMainActivity)
.inject(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
snackbar = ActivityModule.provideSnackBar(rpActivity)
view.findViewById<MaterialTextView>(R.id.download_dialog_title)
.text = resources?.getString(R.string.download_all_plans_notice_text)
view.findViewById<MaterialTextView>(R.id.all_tours_size_textView)
.text = resources?.getString(R.string.download_all_plans_size_title
,"0")
file.freeSpace.bytesToSize().let {
view.findViewById<MaterialTextView>(R.id.disk_size_textView)
.text = resources?.getString(R.string.download_all_tours_disk_size_title,it)
}
view.findViewById<Button>(R.id.alert_dismiss_button)
.setOnClickListener { handleBack() }
view.findViewById<ImageView>(R.id.close_current_button)
.setOnClickListener { handleBack() }
detachDisposable.add(
interactor.fetchPlanTypesSizes()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({
view.findViewById<MaterialTextView>(R.id.all_tours_size_textView)
.text = resources?.getString(R.string.download_all_plans_size_title
,(it* TYPICAL_PLAN_SIZE).bytesToSize())
},
{err ->
Timber.e(err)
snackbar.showSnackBar(R.string.download_all_plans_errors_snackbar_message)
})
)
detachDisposable.add(
view.findViewById<Button>(R.id.alert_ok_button)
.clicks()
.filter{
if (!networkMonitor.isConnected){
snackbar.showSnackBar(R.string.no_network_error)
}
networkMonitor.isConnected
}
.debounce(200, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
router.replaceTopController(
RouterTransaction.with(DownloadPlansDialogController())
.pushChangeHandler(DialogChangeHandler())
.popChangeHandler(DialogChangeHandler())
)
}
)
view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
// view.setOnClickListener { handleBack() }
return view
}
@LayoutRes
fun getLayoutId() = R.layout.tours_download_dialog_screen
override fun handleBack(): Boolean {
return router.popCurrentController()
}
}
@PerScreen
@Component(
modules = [StartPlansDownloadingDialogScreenModule::class],
dependencies = [AppComponent::class])
interface StartPlansDownloadingDialogScreenComponent {
@Component.Factory
interface Factory{
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
): StartPlansDownloadingDialogScreenComponent
}
fun inject(controller: StartPlansDownloadingDialogController)
}
@Module
abstract class StartPlansDownloadingDialogScreenModule{
@PerScreen
@Binds
abstract fun provideActivity(activity: RoomParkMainActivity): BaseRoomParkActivity
}
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.IBottomNavigation
import com.biganto.visual.roompark.base.ICollapsingToolBar
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.model.fromEntity
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)
}
private val bottomNavigationController: IBottomNavigation by lazy {rpActivity}
private val toolbar: ICollapsingToolBar by lazy {rpActivity}
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)
,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, fromEntity(model))
handleBack()
}
}
,{error ->
Timber.e(error)
snackbar.showSnackBar(error.localizedMessage)
}
))
cancelDownloadText.setOnClickListener {handleBack() }
// downloadTour(it.tour.tour_id, downloadToken)
// view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
return view
}
private fun updateProgressBar(curr:Int, max:Int){
activity?.runOnUiThread {
progressBarDownload.max = max
progressBarDownload.progress = curr
}
}
@LayoutRes
fun getLayoutId() = R.layout.download_tour_layout
override fun handleBack(): Boolean {
downloadToken.isCancelled = true
return router.popController(this)
}
}
@PerScreen
@Component(
modules = [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.Button
import android.widget.ImageView
import androidx.annotation.LayoutRes
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.BaseRoomParkActivity
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roompark.data.service.network.INetworkMonitor
import com.biganto.visual.roompark.data.service.network.NoNetworkException
import com.biganto.visual.roompark.di.dagger.ActivityModule
import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.PerScreen
import com.biganto.visual.roompark.domain.interactor.SettingsInteractor
import com.biganto.visual.roompark.domain.model.bytesToSize
import com.biganto.visual.roompark.util.view_utils.snackbar.ISnackBarProvider
import com.bluelinelabs.conductor.Controller
import com.google.android.material.textview.MaterialTextView
import com.jakewharton.rxbinding3.view.clicks
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 timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 09.04.2019.
*/
class StartToursDownloadingDialogController : Controller {
constructor():super()
constructor(args: Bundle):super(args)
private val detachDisposable = CompositeDisposable()
override fun onDetach(view: View) {
detachDisposable.clear()
super.onDetach(view)
}
@Inject
lateinit var downloader: SettingsInteractor
@Inject
lateinit var file: FileModule
lateinit var snackbar: ISnackBarProvider
@Inject
lateinit var rpActivity: RoomParkMainActivity
@Inject
lateinit var networkMonitor: INetworkMonitor
override fun onContextAvailable(context: Context) {
super.onContextAvailable(context)
DaggerStartToursDownloadingDialogScreenComponent.factory()
.create(RoomParkApplication.component,activity as RoomParkMainActivity)
.inject(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(getLayoutId(), container, false)
view.findViewById<MaterialTextView>(R.id.download_dialog_title)
.text = resources?.getString(R.string.download_all_tours_notice_text)
view.findViewById<MaterialTextView>(R.id.all_tours_size_textView)
.text = resources?.getString(R.string.download_all_tours_size_title
,"0")
snackbar = ActivityModule.provideSnackBar(rpActivity)
file.freeSpace.bytesToSize().let {
view.findViewById<MaterialTextView>(R.id.disk_size_textView)
.text = resources?.getString(R.string.download_all_tours_disk_size_title,it)
}
view.findViewById<Button>(R.id.alert_dismiss_button)
.setOnClickListener { handleBack() }
view.findViewById<ImageView>(R.id.close_current_button)
.setOnClickListener { handleBack() }
detachDisposable.add(
downloader.fetchToursSizes()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
view.findViewById<MaterialTextView>(R.id.all_tours_size_textView)
.text = resources?.getString(R.string.download_all_tours_size_title
,it.bytesToSize())
}
)
detachDisposable.add(
view.findViewById<Button>(R.id.alert_ok_button)
.clicks()
.filter{
if (!networkMonitor.isConnected){
snackbar.showSnackBar(R.string.no_network_error)
}
networkMonitor.isConnected
}
.debounce(200, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.io())
.flatMap{ downloader.startToursDownloading()}
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({
snackbar.showSnackBar(R.string.download_all_tours_start_snackbar_message)
handleBack()
},{
Timber.e(it)
snackbar.showSnackBar(R.string.download_all_tours_errors_snackbar_message)
handleBack()
}
)
)
view.findViewById<View>(R.id.close_current_button).setOnClickListener { handleBack() }
// view.setOnClickListener { handleBack() }
return view
}
@LayoutRes
fun getLayoutId() = R.layout.tours_download_dialog_screen
override fun handleBack(): Boolean {
return router.popCurrentController()
}
}
@PerScreen
@Component(
modules = [StartToursDownloadingDialogScreenModule::class],
dependencies = [AppComponent::class])
interface StartToursDownloadingDialogScreenComponent {
@Component.Factory
interface Factory{
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
): StartToursDownloadingDialogScreenComponent
}
fun inject(controller: StartToursDownloadingDialogController)
}
@Module
abstract class StartToursDownloadingDialogScreenModule{
@PerScreen
@Binds
abstract fun provideActivity(activity: RoomParkMainActivity): BaseRoomParkActivity
}
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) }
handleBack()
}
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
import java.io.File
/**
* 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).plus(File.separator).plus(tour.footageUri),
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
......@@ -60,7 +60,7 @@ class BigantoMviConductorLifecycleListener<V : MvpView, P : MviPresenter<V, *>>
}
val viewMpv = callback?.mvpView ?: throw NullPointerException(
"MvpView returned from getMvpView() is null. Returned by " + controller.activity!!)
"MvpView returned from getMvpView() is null. Returned by ${controller.activity}")
if (viewStateWillBeRestored) {
callback!!.setRestoringViewState(true)
......
package com.biganto.visual.roompark.data
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.data.service.notification.INotificationCenter
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import timber.log.Timber
import javax.inject.Inject
class RoomParkMessageService : FirebaseMessagingService() {
@Inject
lateinit var notyCenter: INotificationCenter
init {
notyCenter = RoomParkApplication.component.provideNotifivations()
}
override fun onNewToken(p0: String) {
super.onNewToken(p0)
Timber.d("NEW TOKEN REGISTERED: ${p0}")
}
override fun onMessageReceived(remoteMessage: RemoteMessage) { // ...
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Timber.d("From: ${remoteMessage.from}")
// Check if message contains a data payload.
if (remoteMessage.data.size > 0) {
Timber.d("Message data payload: %s", remoteMessage.data)
if ( /* Check if data needs to be processed by long running job */true) { // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
// scheduleJob()
} else { // Handle message within 10 seconds
// handleNow()
}
}
// Check if message contains a notification payload.
if (remoteMessage.notification != null) {
Timber.d("Message Notification Body: %s", remoteMessage.notification?.body)
notyCenter.showPushNotifyMessage(remoteMessage.notification?.body?:"Уведомление")
}
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
}
\ 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
......@@ -73,7 +73,9 @@ class AlbumsContractModule @Inject constructor(
arrayListOf(fetchTopLevelAlbumsDb,fetchTopLevelAlbumsApi)
)
.doOnNext { Timber.d("got entity $it") }
.map { fromEntity(it,::fromEntity) }
.map { fromEntity(it,::fromEntity).sortedByDescending{ album -> album.published } }
//endregion allAlbums
//region concrete Albums
......
......@@ -2,11 +2,15 @@ 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.SubscriptionEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.UserEntity
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
import com.biganto.visual.roompark.domain.contract.AuthContract
import com.biganto.visual.roompark.domain.custom_exception.CustomApiException
import com.biganto.visual.roompark.domain.model.AuthInfoModel
import com.biganto.visual.roompark.domain.model.SubscriptionTopic
import com.biganto.visual.roompark.domain.model.fromEntity
import io.reactivex.Completable
import io.reactivex.Observable
......@@ -35,6 +39,19 @@ class AuthContractModule @Inject constructor(
api.authenticate(email,password)
.map ( ::fromRaw )
.flatMap{ db.upsertUser(it) }
.flatMap {user ->
val userSubs = user.subscriptions?.map { it as SubscriptionEntity }
var newSubs = defaultSubsList(user)
if (!userSubs.isNullOrEmpty()){
newSubs = newSubs.filter {defSub ->
!userSubs.map { it.topic }.contains(defSub.topic)
}.toList()
}
if (newSubs.isNullOrEmpty()) return@flatMap Observable.just(user)
return@flatMap db.upsert(newSubs)
.toObservable()
.flatMap { db.refreshUser(user) }
}
.doOnNext{ Timber.d("user id: ${it.uuid}")}
.doOnNext { local.setRecentUser(it.uuid.toString()).blockingAwait() }
.map(::fromEntity)
......@@ -46,4 +63,53 @@ class AuthContractModule @Inject constructor(
else ->false
} }
override fun currentUser(): Observable<UserEntity> =
local.recentUser()
.doOnNext { Timber.w("recentUSer: $it") }
.flatMap{ when(it){
is UserState.NotAuthenticated -> throw CustomApiException.NotAuthorizedException()
is UserState.Authenticated -> db.fetchUser(it.uuid.toInt())
.doOnError { Timber.e(it) }
.doOnNext {u-> "got usr:$u" }
else -> throw CustomApiException.NotAuthorizedException()
} }
}
private fun defaultSubsList(owner:UserEntity) : List<SubscriptionEntity> = arrayListOf(
SubscriptionEntity().apply {
setTopic(SubscriptionTopic.Progress_1().topicName)
setOwner(owner)
setState(false)
}
,SubscriptionEntity().apply {
setTopic(SubscriptionTopic.Progress_2().topicName)
setOwner(owner)
setState(false)
}
,SubscriptionEntity().apply {
setTopic(SubscriptionTopic.Progress_3().topicName)
setOwner(owner)
setState(false)
}
,SubscriptionEntity().apply {
setTopic(SubscriptionTopic.Progress_kindergarden().topicName)
setOwner(owner)
setState(false)
}
,SubscriptionEntity().apply {
setTopic(SubscriptionTopic.News().topicName)
setOwner(owner)
setState(false)
}
,SubscriptionEntity().apply {
setTopic(SubscriptionTopic.Blog().topicName)
setOwner(owner)
setState(false)
}
,SubscriptionEntity().apply {
setTopic(SubscriptionTopic.Construction().topicName)
setOwner(owner)
setState(false)
}
)
\ No newline at end of file
package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.roompark.domain.contract.DeviceUtilsContract
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.iid.FirebaseInstanceId
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 26.03.2020.
*/
class DeviceUtilsRepository @Inject constructor(
) : DeviceUtilsContract {
override fun getDeviceId(): Observable<String> =
Observable.create<String> {emitter ->
Timber.d("Creat observer")
FirebaseInstanceId.getInstance().instanceId
.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Timber.w(task.exception, "getInstanceId failed")
emitter.onError(task.exception!!)
return@OnCompleteListener
}
// Get new Instance ID token
task.result?.token?.let { emitter.onNext(it) }
emitter.onComplete()
// Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
})
}.subscribeOn(Schedulers.computation())
}
\ 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.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.androidplayer.data.repository.local.ILocalStore
import com.biganto.visual.roompark.data.repository.api.room_park.IRoomParkApi
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.PlanPresetEntity
import com.biganto.visual.roompark.data.repository.file.FileModule
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
import com.biganto.visual.roompark.data.repository.mapper.fromRawList
import com.biganto.visual.roompark.domain.contract.FlatPlanContract
import com.biganto.visual.roompark.domain.model.FeatureModel
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import com.biganto.visual.roompark.domain.model.fromEntity
import com.biganto.visual.roompark.domain.use_case.DownloadUseCase
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import timber.log.Timber
import java.util.*
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 20.04.2020.
*/
class PlanRepository @Inject constructor(
private val local: ILocalStore,
private val api: IRoomParkApi,
private val db: IDb,
private val file: FileModule
)
: FlatPlanContract {
private fun getPlanTypesApi(estateId: Int): Observable<List<PlanPresetEntity>> =
api.getEstatePlanTypes(estateId)
.doOnNext { Timber.d("raw0 $it") }
.map { fromRawList(it, ::fromRaw) }
.map {
it.onEach { plan ->
val e = EstateEntity()
e.setId(estateId)
plan.estateId = e
}.toList()
}
.doOnNext(db::blockingUpsert)
.subscribeOn(Schedulers.io())
override fun getPlansObservable(list:List<PlanFeaturesVariant>
,cancellationToken: DownloadUseCase.CancellationToken)
: Observable<String> {
return Observable.create { emitter ->
list
.asSequence()
.chunked(12)
.also { System.gc() }
.flatten()
.filter { !cancellationToken.isCancelled }
.forEach { variant->
val file = FileModule.getDirectory(
file.context
, FileModule.FileDirectory.PlanTypeDir(
estateId = variant.estateId,
planId = variant.planId,
furniture = variant.furniture,
walls = variant.walls,
sizes = variant.sizes,
electric = variant.electric
))
if (file.exists())
emitter.onNext(file.absolutePath)
else{
try {
Timber.w("Cancellation is: ${cancellationToken.isCancelled}")
if (cancellationToken.isCancelled){
emitter.onComplete()
}
file.parentFile.mkdirs()
file.writeText(
api.getDirectPlan(variant.estateId, variant.planId,
variant.furniture?:false
,variant.sizes?:false
,variant.walls?:false
,variant.electric?:false).blockingFirst()
)
emitter.onNext(file.absolutePath)
}
catch (e:Throwable){emitter.onError(e)}
}
}
emitter.onComplete()
}
}
override fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>> =
Observable.mergeDelayError(
arrayListOf(getPlanTypesApi(estateId))
).map { l -> List(l.size) { fromEntity(l[it]) } }
private fun getPlanApi(estateId: Int
, planId:Int
, furniture:Boolean? = null
, sizes:Boolean? = null
, walls:Boolean? = null
, electric:Boolean? = null) =
api.getDirectPlan(estateId, planId,
furniture?:false
,sizes?:false
,walls?:false
,electric?:false)
.map {
val sFile = getPlanFile(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric
)
file.saveFileToDisk(sFile, it)
sFile.absolutePath
}
.subscribeOn(Schedulers.io())
override fun getPlanFile(featuresVariant: PlanFeaturesVariant)
= getPlanFile(
estateId = featuresVariant.estateId,
planId = featuresVariant.planId,
furniture = featuresVariant.furniture,
sizes = featuresVariant.sizes,
walls = featuresVariant.walls,
electric = featuresVariant.electric
)
private fun getPlanFile(estateId: Int
, planId:Int
, furniture:Boolean? = null
, sizes:Boolean? = null
, walls:Boolean? = null
, electric:Boolean? = null)
= FileModule.getDirectory(file.context
, FileModule.FileDirectory.PlanTypeDir(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric
))
// override fun getEmptyPlan(estateId: Int
// ,planId:Int): Observable<String> =
// Observable.mergeDelayError(
// arrayListOf(getPlanApi(estateId,planId))
// )
override fun getPlan(featuresVariant: PlanFeaturesVariant)
= getPlan(
estateId = featuresVariant.estateId,
planId = featuresVariant.planId,
furniture = featuresVariant.furniture,
sizes = featuresVariant.sizes,
walls = featuresVariant.walls,
electric = featuresVariant.electric
)
override fun getPlan(estateId: Int
,planId:Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String> =
Observable.fromCallable { getPlanFile(
estateId = estateId,
planId = planId,
furniture = furniture,
walls = walls,
sizes = sizes,
electric = electric) }.flatMap {
if (it.exists()) Observable.just(it.path)
else getPlanApi(estateId
,planId
, furniture
, sizes
, walls
, electric
)
}
// fun getPlanRequestString(estateId: Int
// , planId:Int
// , furniture:Boolean
// , sizes:Boolean
// , walls:Boolean
// , electric:Boolean
// ) = api.getDirectPlan(estateId,planId,furniture,sizes,electric).
}
private fun BitSet.getOrNull(index:Int) =
if (index<0 || index>=this.size()) null
else get(index)
private fun Int.getBit(index: Int): Boolean? {
if (index<0) return null
return Integer.toBinaryString(shr(index)).last()=='1'
}
val PlanPresetModel.featuresVariants : List<PlanFeaturesVariant>
get() {
if (this.features.isNullOrEmpty()) return arrayListOf()
val maxInd = this.features.size
val f = features.indexOfFirst { it is FeatureModel.Furniture }
val s = features.indexOfFirst { it is FeatureModel.Sizes }
val e = features.indexOfFirst { it is FeatureModel.Electric }
val w = features.indexOfFirst { it is FeatureModel.Walls }
val resList = mutableListOf<PlanFeaturesVariant>()
var increment = 0
var allTrue: Boolean
do{
val v = PlanFeaturesVariant(
estateId,planId,
furniture = increment.getBit(f),
sizes = increment.getBit(s),
electric = increment.getBit(e),
walls = increment.getBit(w)
)
allTrue = v.furniture?:true && v.electric?:true && v.sizes?:true && v.walls?:true
resList.add(v)
increment++
}while (!allTrue)
return resList
}
data class PlanFeaturesVariant(
val estateId: Int,
val planId:Int,
val furniture:Boolean?,
val sizes:Boolean?,
val walls:Boolean?,
val electric:Boolean?
)
\ No newline at end of file
package com.biganto.visual.roompark.data.data_provider
import com.biganto.visual.roompark.data.repository.api.room_park.IRoomParkApi
import com.biganto.visual.roompark.data.repository.api.room_park.raw.SubscriptionStatusRaw
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
import com.biganto.visual.roompark.domain.contract.SubscriptionContract
import com.biganto.visual.roompark.domain.model.SubscriptionModel
import com.biganto.visual.roompark.domain.model.fromEntity
import io.reactivex.Completable
import io.reactivex.Observable
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 25.03.2020.
*/
private const val SUBSCRIPTION_RESULT_STATUS = "OK"
class SubscriptionRepository @Inject constructor(
private val api: IRoomParkApi,
private val db: IDb
): SubscriptionContract{
override fun saveSubscribeState(sub:SubscriptionEntity): Observable<SubscriptionEntity> {
return saveSubscribeState(
sub.owner as UserEntity,
sub.id,
sub.topic,
sub.number,
sub.state
)
}
override fun saveSubscribeState(
userEntity: UserEntity,
subInnerId:Int?,
topic: String,
topic_id: String?,
newState:Boolean
): Observable<SubscriptionEntity> {
val userSubs = userEntity.subscriptions
val sub = subInnerId?.let { id ->
userSubs?.firstOrNull { sub -> sub.id == id } as SubscriptionEntity?
}?:SubscriptionEntity()
.apply {
setOwner(userEntity)
setTopic(topic)
setNumber(topic_id)
}
sub.setState(newState)
return db.saveSubscription(sub)
}
fun saveSubscribtions(
userEntity: UserEntity,
apiSubs: List<SubscriptionStatusRaw>
): Observable<List<SubscriptionModel>>? {
val userSubs = userEntity.subscriptions
val newSubList = mutableListOf<SubscriptionEntity>()
apiSubs.forEach { apiSub ->
val cachedSub =
userSubs?.firstOrNull { s ->
s.topic == apiSub.topic && s.number == apiSub.estate_id
}
as SubscriptionEntity?
?: SubscriptionEntity()
.apply {
setOwner(userEntity)
setTopic(apiSub.topic)
setNumber(apiSub.estate_id)
}
cachedSub.setState(apiSub.active)
newSubList.add(cachedSub)
}
userSubs?.filter { !newSubList.map { s ->s.id }.contains(it.id) }?.forEach {
(it as SubscriptionEntity).setState(false)
}
userSubs?.forEach {s ->
if (newSubList.firstOrNull { s.topic == it.topic && s.number == it.number} == null)
newSubList.add(s as SubscriptionEntity)
}
return db.upsert(newSubList)
.map {list -> list.map {
fromEntity(
it as SubscriptionEntity)
}}
.toObservable()
}
override fun subscribeTopicResult(
user:UserEntity,
subInnerId:Int,
deviceToken: String,
topic: String,
topic_id: String?
): Observable<List<SubscriptionModel>> =
api.subscribeTopic(
userToken = user.authToken,
deviceToken = deviceToken
,topicName = topic
,topicId = topic_id)
.flatMap { saveSubscribtions(user,it.subscriptions?: arrayListOf()) }
.doOnNext { db.refreshUser(user).blockingFirst() }
override fun unSubscribeTopicResult(
user:UserEntity,
subInnerId:Int,
deviceToken: String,
topic: String,
topic_id: String?
): Observable<List<SubscriptionModel>> =
api.unSuubscribeTopic(
userToken = user.authToken,
deviceToken = deviceToken
,topicName = topic
,topicId = topic_id)
.flatMap { saveSubscribtions(user,it.subscriptions?: arrayListOf()) }
.doOnNext { db.refreshUser(user) }
override fun subscribeTopic(
user:UserEntity,
subInnerId:Int,
deviceToken: String,
topic: String,
topic_id: String?
): Completable = api.subscribeTopic(
userToken = user.authToken,
deviceToken = deviceToken
,topicName = topic
,topicId = topic_id)
.flatMapCompletable {
if (it.status == SUBSCRIPTION_RESULT_STATUS){
saveSubscribeState(user,subInnerId,topic,topic_id,true)
.ignoreElements()
}
else error("Error subscription state!")
}
override fun unSubscribeTopic(
user:UserEntity,
subInnerId:Int,
deviceToken: String,
topic: String,
topic_id: String?
): Completable =
api.unSuubscribeTopic(
userToken = user.authToken,
deviceToken = deviceToken
,topicName = topic
,topicId = topic_id)
.doOnNext { Timber.d("$it") }
.flatMapCompletable {
if (it.status == SUBSCRIPTION_RESULT_STATUS){
saveSubscribeState(user,subInnerId,topic,topic_id,false)
.ignoreElements()
}
else error("Error subscription state!")
}
}
\ No newline at end of file
package com.biganto.visual.roompark.data.data_provider
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.TourContract
import com.biganto.visual.roompark.domain.model.AuthInfoModel
import io.reactivex.Completable
import io.reactivex.Observable
import timber.log.Timber
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 29.10.2019.
*/
//@Singleton
class ToursRepository @Inject constructor(
private val files: FileModule,
private val db:IDb
): TourContract {
override fun getMultiTourId(building: Int, number: Int): Observable<AuthInfoModel> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun getOffer(offerId: Int): Observable<AuthInfoModel> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun deleteToursDbInfo(): Completable =
Completable.merge(
arrayListOf(
db.dropTourFileJuncTable(),
db.dropFileTable(),
db.dropTourTable(),
db.refreshEstatesWithTours()
)
)
// .concatWith { }
.doOnComplete { Timber.w("Completed --") }
.doOnError { Timber.e(it) }
}
package com.biganto.visual.roompark.data.memcache
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import com.jakewharton.rxrelay2.Relay
import com.jakewharton.rxrelay2.ReplayRelay
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
/**
* Created by Vladislav Bogdashkin on 19.10.2018.
*/
/**
* @param[maxSize] max entries count before automatic save && flush
* @param[flushInterval] time without any actions with cache in SECONDS before automatic save && flush
* */
abstract class EntityCache<K,V>
constructor(
private val maxSize: Int = 300,
private val flushInterval: Long = TimeUnit.MINUTES.toSeconds(1)
) : Cache<K,V> {
private val disposable = CompositeDisposable()
protected abstract fun saveDelegate (values:List<V>)
protected abstract fun readDelegate (key:K) : V?
protected abstract fun deleteDelegate (value:V)
protected val notifier: Relay<V> = ReplayRelay.create<V>().toSerialized()
init {
disposable.addAll(
Observable.interval(flushInterval, flushInterval, TimeUnit.SECONDS)
.doOnTerminate { clear() }
.doOnDispose { clear() }
.subscribe { clear() }
)
}
override fun contains(key: K): Boolean = keyMap.containsKey(key)
protected val locker = Any()
private var lastFlushTime = System.nanoTime()
private val keyMap = ConcurrentHashMap<K, V>()
override val size: Int
get() = keyMap.size
override val toList
get() = keyMap.toList()
fun deleteEntity(key: K){
synchronized(locker) {
deleteItem(key)
}
}
private fun deleteItem(key:K){
synchronized(locker) {
lastFlushTime = System.nanoTime()
var toDelete = keyMap.remove(key)
if (toDelete == null)
toDelete = readDelegate(key)
if (toDelete == null) return // -> нет в хранилище
deleteDelegate(value = toDelete)
}
}
fun deleteEntitys(keys: List<K>){
synchronized(locker) {
keys.forEach { deleteItem(it)}
}
}
override fun set(key: K, value: V?) {
synchronized(locker) {
lastFlushTime = System.nanoTime()
if (keyMap.size > maxSize)
clear()
value?.let {
keyMap[key]=it
notifier.accept(it)
}
}
}
override fun remove(key: K) = keyMap.remove(key)
override fun get(key: K): V? {
synchronized(locker) {
lastFlushTime = System.nanoTime()
return keyMap[key] ?: readDelegate(key)
}
}
override fun saveAll(){
synchronized(locker)
{
if (keyMap.size > 0) {
Timber.d("Going to save items: ${keyMap.values.size}")
saveDelegate(keyMap.values.toList())
}
}
}
override fun clear() {
if (keyMap.size == 0) return
synchronized(locker)
{
saveAll()
keyMap.clear()
}
}
override fun removeAll(): List<V>? {
val values= keyMap.values.toList()
keyMap.clear()
return values
}
private fun recycle() {
if (keyMap.size<=0) return
val shouldRecycle = System.nanoTime() - lastFlushTime >= TimeUnit.MILLISECONDS.toNanos(flushInterval)
if (!shouldRecycle) return
keyMap.clear()
}
}
interface Cache<K,V> {
val size: Int
val toList:List<Pair<K,V>>
operator fun set(key: K, value: V?)
fun contains(key: K): Boolean
operator fun get(key: K): V?
fun remove(key: K): V?
fun removeAll(): List<V>?
fun clear()
fun saveAll()
}
/**
* Suppress warning Leaking This as we sure to make singletone instance of object in single-thread
* more info and
* @see <a href="https://stackoverflow.com/questions/3921616/leaking-this-in-constructor-warning">discussion</a>
*/
@Suppress("LeakingThis")
abstract class LifeCycleCache<K,V>(size:Int, secondsToFlush:Long)
: EntityCache<K, V>(maxSize=size,flushInterval = secondsToFlush)
, LifecycleObserver
{
init {
ProcessLifecycleOwner.get().lifecycle
.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onAppPaused() {
saveAll()
}
}
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.TourFilesSimpleDataRaw
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 getTourFilesSizes(ids: List<String>, resolution: String)
: Observable<List<TourFilesSimpleDataRaw>> = api
.getTourFilesSize(ids = ids.joinToString(separator = ","), 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(multiTourIds:List<Int>): Observable<List<TourPreviewRaw>> =
api
.getOfferTours(offerId = multiTourIds.joinToString(separator = ","))
.compose(RetrofitResponseValidation())
.map { it.values.flatten() }
.doOnError { e(it) }
.subscribeOn(Schedulers.io())
override fun getOfferTours(multiTourId:Int): Observable<List<TourPreviewRaw>> =
api
.getOfferTours(offerId = multiTourId.toString())
.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.TourFilesSimpleDataRaw
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>>
fun getOfferTours(multiTourIds: List<Int>): Observable<List<TourPreviewRaw>>
fun getTourFilesSizes(
ids: List<String>,
resolution: String
): Observable<List<TourFilesSimpleDataRaw>>
}
\ 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.TourFilesSimpleDataRaw
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_TOURS_FILES_METHOD$DELIMITER")
fun getTourFilesSize(
@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<TourFilesSimpleDataRaw>>>
@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: String
): 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 LiteTourMetaRaw(
val baseurl:String,
val tour_baseurl:String
)
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 TourFilesSimpleDataRaw(
val id:Int,
val resolution:Int,
val total_size:Long,
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
/**
......@@ -15,13 +14,15 @@ interface IRoomParkApi {
fun subscribeTopic(
userToken: String,
deviceToken: String,
topicName: String
topicName: String,
topicId:String?
): Observable<StatusResponse>
fun unSuubscribeTopic(
userToken: String,
deviceToken: String,
topicName: String
topicName: String,
topicId:String?
): Observable<StatusResponse>
fun getDeals(userToken: String): Observable<List<DealRaw>>
......@@ -49,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.*
......@@ -26,8 +26,8 @@ interface IRoomParkMobileApi{
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 DEFAULT_CLIENT_TYPE="android"
//const val DEFAULT_CLIENT_TYPE="ios"
const val OLD_CLIENT_TYPE="mobileplayer"
const val DEFAULT_CLIENT_VERSION="3.0"
......@@ -48,6 +48,7 @@ interface IRoomParkMobileApi{
const val UNSUBSCRIBE_METHOD="users.unsubscribe"
const val DEVICE_TOKEN_SUBSCRIBTION_PARAM="deviceToken"
const val TOPIC_SUBSCRIBTION_PARAM="topic"
const val TOPIC_SUBSCRIBTION_TOPIC_ID_PARAM="estate_id"
val topicTypes = arrayOf(
"deals",
"progress-1",
......@@ -141,24 +142,28 @@ interface IRoomParkMobileApi{
@Field(PASSWORD_AUTH_PARAM) pwd: String
): Observable<Response<AuthRaw>>
@POST("$API_URL${SUBSCRIBE_METHOD}OD$DELIMITER")
@POST("$API_URL$SUBSCRIBE_METHOD$DELIMITER")
@FormUrlEncoded
fun subscribe(
@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(AUTH_TOKEN) token: String,
@Query(DEVICE_TOKEN_SUBSCRIBTION_PARAM) deviceToken: String,
@Query(TOPIC_SUBSCRIBTION_PARAM) topic: String
@Field(DEVICE_TOKEN_SUBSCRIBTION_PARAM) deviceToken: String,
@Field(TOPIC_SUBSCRIBTION_PARAM) topic: String,
@Field(TOPIC_SUBSCRIBTION_TOPIC_ID_PARAM) estateId: String?
): Observable<Response<StatusResponse>>
@POST("$API_URL${UNSUBSCRIBE_METHOD}OD$DELIMITER")
@POST("$API_URL$UNSUBSCRIBE_METHOD$DELIMITER")
@FormUrlEncoded
fun unsubscribe(
@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(AUTH_TOKEN) token: String,
@Query(DEVICE_TOKEN_SUBSCRIBTION_PARAM) deviceToken: String,
@Query(TOPIC_SUBSCRIBTION_PARAM) topic: String
@Field(DEVICE_TOKEN_SUBSCRIBTION_PARAM) deviceToken: String,
@Field(TOPIC_SUBSCRIBTION_PARAM) topic: String,
@Field(TOPIC_SUBSCRIBTION_TOPIC_ID_PARAM) estateId: String?
): Observable<Response<StatusResponse>>
@GET("$API_URL$DEALS_METHOD$DELIMITER")
......
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)
......@@ -35,17 +36,20 @@ class RetrofitRepository @Inject constructor(retrofit: Retrofit) : IRoomParkApi
override fun subscribeTopic(
userToken: String,
deviceToken: String,
topicName: String
topicName: String,
topicId:String?
): Observable<StatusResponse> =
api.subscribe(token = userToken, deviceToken = deviceToken, topic = topicName)
api.subscribe(token = userToken, deviceToken = deviceToken, topic = topicName,estateId = topicId)
.doOnError { Timber.w(" WTFF ???") }
.compose(RetrofitResponseValidation())
override fun unSuubscribeTopic(
userToken: String,
deviceToken: String,
topicName: String
topicName: String,
topicId:String?
): Observable<StatusResponse> =
api.unsubscribe(token = userToken, deviceToken = deviceToken, topic = topicName)
api.unsubscribe(token = userToken, deviceToken = deviceToken, topic = topicName,estateId = topicId)
.compose(RetrofitResponseValidation())
......
package com.biganto.visual.roompark.data.repository.api.retrofit.raw
package com.biganto.visual.roompark.data.repository.api.room_park.raw
import com.google.gson.annotations.Expose
import java.util.*
/**
......@@ -13,7 +14,17 @@ data class AuthRaw(
val name:String
)
data class StatusResponse(val status:String)
data class StatusResponse(
val status:String,
val subscriptions:List<SubscriptionStatusRaw>?
)
data class SubscriptionStatusRaw(
val topic:String,
val estate_id: String,
@Expose
val active: Boolean = true
)
data class DealRaw(
val id:String,
......@@ -30,6 +41,7 @@ data class EstateRaw(
val id:Int,
val type:String,
val number:String,
val available:Boolean,
val common_info: CommonInfoRaw,
val plan_png:PlanRaw?,
val plan_jpg:PlanRaw?,
......
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.
*/
interface IDb {
fun upsertUser(entity: UserEntity): Observable<UserEntity>?
fun upsertUser(entity: UserEntity): Observable<UserEntity>
fun <T : Persistable> upsert(entity: T): Single<T>
fun <T : List<Persistable>> upsert(entity: T): Single<Iterable<Persistable>>
fun fetchFeeds(): Observable<FeedEntity>
......@@ -27,11 +32,55 @@ interface IDb {
fun getPhotos(albumId: Int): Observable<GalleryPhotoEntity>
fun getPhoto(photoId: Int): Observable<GalleryPhotoEntity>
fun getAlbum(albumId: Int): Observable<ImageAlbumEntity>
fun getUserFavorites(uuid: Int): Observable<EstateEntity>
fun getUserFavorites(uuid: Int): Observable<EstateEntity?>
fun fetchAllUsers(): Observable<List<UserEntity>>
fun getEstate(estateId: Int): Observable<EstateEntity>
fun upsertEstate(entity: EstateEntity)
fun fetchEstateByNumber(building: Int, number: String): ReactiveResult<EstateEntity>
fun setArticleReadState(id: Int, state: Boolean): Completable
fun setDealReadState(id: String, state: Boolean): Completable
fun saveSubscription(subscription: SubscriptionEntity): Observable<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>>
fun dropFileTable(): Completable
fun dropTourFileJuncTable(): Completable
fun dropTourTable(): Completable
fun refreshUser(userEntity: UserEntity): Observable<UserEntity>
fun refreshEntities(entities: List<Persistable>): Observable<Iterable<Persistable>>
fun refreshEstatesWithTours(): Completable
fun upsertEstates(entity: List<EstateEntity>): Observable<Iterable<EstateEntity>>?
fun upsertDeals(entity: List<DealEntity>): Observable<Iterable<DealEntity>>
fun deleteSubscriptions(entities: List<Subscription>)
fun deleteEstate(entity: List<EstateEntity>)
fun deleteEstate(entity: EstateEntity)
fun getUserDeals(uuid: Int): Observable<DealEntity?>
fun deleteDeal(entity: DealEntity)
fun deleteDeal(entity: List<DealEntity>)
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.db.requrey.model
import com.biganto.visual.roomparkvr.data.repository.db.requery.model.TourPreview
import io.requery.*
/**
......@@ -14,6 +15,7 @@ interface Estate : Persistable {
val id: Int
val type: String
val number: String
val available: Boolean
@get:Nullable
val sectionBegin: Int?
@get:Nullable
......@@ -38,6 +40,11 @@ interface Estate : Persistable {
@get:Nullable
val multitourId: Int?
@get:Nullable
@get:OneToMany(mappedBy = "estate")
val tours: Set<TourPreview>?
@get:Nullable
val multitourPreview: String?
......@@ -73,6 +80,6 @@ interface Estate : Persistable {
@get:Nullable
@get:Column(name = "UserContainer")
@get:ForeignKey(references = User::class )
@get:OneToOne(mappedBy = "uuid",cascade = [CascadeAction.NONE])
@get:ManyToOne(cascade = [CascadeAction.NONE])
var user:User?
}
\ No newline at end of file
package com.biganto.visual.roompark.data.repository.db.requrey.model
import com.biganto.visual.roompark.data.repository.api.biganto.IBigantoMobileApi.Companion.BASE_URL
import com.biganto.visual.roompark.data.repository.api.biganto.raw.LiteTourMetaRaw
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 com.biganto.visual.roompark.data.repository.file.FileModule
import io.requery.*
import java.io.File
import java.math.BigInteger
import java.security.MessageDigest
/**
* Created by Vladislav Bogdashkin on 15.06.2018.
*/
val domainPredicthMatcher = "^(http|https)://".toRegex()
fun toInitialName(fullPath: String) = String.format(
"%032x"
, BigInteger(
1
, MessageDigest.getInstance("MD5").digest(fullPath.toByteArray(Charsets.UTF_8))
)
)
@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:Nullable
val destination: String
}
fun fromRaw(raw: TourFileRaw, meta: LiteTourMetaRaw):FileEntity {
val entity = FileEntity()
entity.setDownloaded(false)
entity.setDownloadedSize(0L)
entity.setTotalSize(raw.size)
var fileUri=raw.url
domainPredicthMatcher.containsMatchIn(raw.url)
val bui = raw.url.indexOf(meta.baseurl)
if (bui>=0) fileUri = toInitialName(meta.baseurl)
.plus(File.separator)
.plus(raw.url.removeRange(0,bui+meta.baseurl.length))
val tui = raw.url.indexOf(meta.tour_baseurl)
if (tui>=0) fileUri = toInitialName(meta.tour_baseurl)
.plus(File.separator)
.plus(raw.url.removeRange(0,tui+meta.tour_baseurl.length))
entity.setUri(RevisionString(FileModule.assetsLocalPath(fileUri)))
entity.setDestination(
if (domainPredicthMatcher.containsMatchIn(raw.url)) raw.url
else BASE_URL.plus(raw.url)
)
return entity
}
fun fromRaw(raw: List<TourFileRaw>, meta: LiteTourMetaRaw): List<FileEntity> =
List(raw.size) { index-> fromRaw(raw[index],meta) }
......@@ -15,8 +15,9 @@ interface Subscription : Persistable {
@get:ForeignKey(references = User::class, referencedColumn = "uuid")
@get:ManyToOne
val owner: User
val deviceToken: String
val topic: String
val number: String
@get:Nullable
val number: String? //estateId or smth same, depends on subscription case
val state: Boolean
}
\ 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.model.domainPredicthMatcher
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?
val tourBaseUrl: String?
val footageBaseUrl: String?
}
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(
if (domainPredicthMatcher.containsMatchIn(raw.preview)) raw.preview
else "$baseUrl${raw.preview}"
)
tour.setScreen(
if (domainPredicthMatcher.containsMatchIn(raw.screen)) raw.screen
else "$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);
}
......@@ -24,5 +24,14 @@ interface User : Persistable {
@get:Nullable
@get:OneToMany(cascade = [CascadeAction.DELETE])
val deals:List<Deal>?
@get:Nullable
@get:OneToMany(cascade = [CascadeAction.DELETE])
val estates:List<Estate>?
@get:Nullable
@get:OneToMany(cascade = [CascadeAction.DELETE])
val subscriptions:List<Subscription>?
}
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;
}
}
......@@ -8,6 +8,7 @@ import com.google.gson.JsonElement
import dagger.Module
import io.reactivex.Observable
import kotlinx.io.IOException
import okio.Okio
import timber.log.Timber
import java.io.File
import javax.inject.Inject
......@@ -33,8 +34,13 @@ class FileModule @Inject constructor(val context: Application) {
fun getFile(fileUri: String): File {
try {
val fileName =if (fileUri.contains("/")) fileUri.substring(fileUri.lastIndexOf("/")) else fileUri
val fileDir = if (fileUri.contains("/")) fileUri.substring(0, fileUri.lastIndexOf("/")) else ""
val fileName =
if (fileUri.contains("/")) fileUri.substring(fileUri.lastIndexOf("/"))
else fileUri
val fileDir =
if (fileUri.contains("/"))
fileUri.substring(0, fileUri.lastIndexOf("/"))
else ""
val directory = File(rootFolder, fileDir)
directory.mkdirs()
......@@ -53,15 +59,45 @@ class FileModule @Inject constructor(val context: Application) {
file.writeText("[$jsonElement]") //to json array because core unity method parse data like TourData[] Estate[] etc..
}
fun saveFileToDiskObservable(file:File,content: String):Observable<Long> {
file.parentFile.mkdirs()
return Observable.create { emitter ->
val fileStorage = file
val sink = Okio.buffer(Okio.sink(fileStorage))
val buffer = sink.buffer()
var read = 0L
val step = 8192
val source =
content.byteInputStream()
var bytesRead = 0L
Timber.w("start read ")
file.writeText(content)
Timber.w("butes read ")
// sink.flush()
System.gc()
emitter.onNext(bytesRead)
// emitter.onComplete()
}
}
fun saveFileToDisk(file:File,content: String){
Timber.d("write to : $file")
Timber.d("write to : ${file.name}")
// file.createNewFile()
file.parentFile.mkdirs()
file.writeText(content) //to json array because core unity method parse data like TourData[] Estate[] etc..
}
fun deleteFile(uri:String)= getFile(uri).delete()
// fun deleteFile(uri:String)= getFile(uri).delete()
fun deleteAssetFile(uri:String) =
getAssetFile(uri).delete()
fun getAssetFile(uri:String) =
File(assetsDirectory(context).plus(uri))
fun deleteAllCacheObservable() =
Observable.create<Pair<Int, Int>> {emitter ->
......@@ -80,18 +116,26 @@ class FileModule @Inject constructor(val context: Application) {
}
val getCoreCacheDirectory:String
get(){
return rootFolder.absolutePath
}
val freeSpace = rootFolder.freeSpace
companion object {
fun getDirectory(context: Context, dirType: FileDirectory): File =
File(context.filesDir.absolutePath.plus(dirType.dir))
fun assetsDirectory(context: Context): String = context.filesDir.absolutePath
fun assetsDirectory(context: Context): String =
getDirectory(context,FileDirectory.ToursDir(BIGANTO_TOURS_FOLDER)).absolutePath
fun assetsFile(context: Context): File =
getDirectory(context,FileDirectory.ToursDir(BIGANTO_TOURS_FOLDER))
fun assetsLocalPath(uri:String): String =
FileDirectory.ToursDir(BIGANTO_TOURS_FOLDER).dir.plus(File.separator).plus(uri)
fun assetsFile(context: Context): File = context.filesDir.absoluteFile
private const val BIGANTO_TOURS_FOLDER = "biganto"
}
......
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.*
......@@ -113,11 +113,11 @@ fun fromRaw(raw:DealRaw):DealEntity {
}
fun fromRaw(raw:DealRaw,user:UserEntity):DealEntity {
val entity = fromRaw(raw)
entity.user = user
return entity
}
fun fromRaw(raw:DealRaw,user:UserEntity):DealEntity =
fromRaw(raw).apply {
this.user = user
this.estate.user = user
}
......@@ -126,6 +126,7 @@ fun fromRaw(raw:EstateRaw):EstateEntity{
entity.setId(raw.id)
entity.setType(raw.type)
entity.setNumber(raw.number)
entity.setAvailable(raw.available)
entity.setSectionBegin(raw.common_info.section_begin)
entity.setSectionEnd(raw.common_info.section_end)
entity.setPlanJpgUrl(raw.plan_jpg?.url)
......
package com.biganto.visual.roompark.data.service.download
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.roomparkvr.data.repository.db.requery.model.DownloadState
/**
* Created by Vladislav Bogdashkin on 14.04.2020.
*/
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,
val fatalState: DownloadState? = null,
var destination: String
) {
constructor(entity: FileEntity, junction: TourFileJunctionEntity) : this(
fileUrl = junction.file
, tourId = junction.tour
, tempDownloadedSize = 0L
, tempOverallFileSize = entity.totalSize
, fileDownloadedSize = entity.downloadedSize
, tempTourTotalDiff = 0L
, isDownloaded = entity.isDownloaded
, destination = entity.destination
)
}
package com.biganto.visual.roompark.data.service.lifecycle
/**
* Created by Vladislav Bogdashkin on 14.04.2020.
*/
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AppLifecycleListener @Inject constructor(): ForegroundLifecycleObserver {
private var isForeground=false
override val isAppForeground : Boolean
get() = isForeground
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
Timber.d("Returning to foreground…")
isForeground=true
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
Timber.d("Moving to background…")
isForeground=false
}
}
interface ForegroundLifecycleObserver : LifecycleObserver{
val isAppForeground : Boolean
}
\ No newline at end of file
package com.biganto.visual.roompark.data.service.network
import android.app.Application
import android.content.Context
import android.net.ConnectivityManager
import javax.inject.Inject
import javax.inject.Singleton
......@@ -9,7 +11,7 @@ import javax.inject.Singleton
* Created by Vladislav Bogdashkin on 12.10.2018.
*/
@Singleton
class LiveNetworkMonitor(val context: Context) : INetworkMonitor {
class LiveNetworkMonitor @Inject constructor(val context: Application) : INetworkMonitor {
override val isConnected: Boolean
......@@ -23,7 +25,8 @@ class LiveNetworkMonitor(val context: Context) : INetworkMonitor {
override val isWifiConnected: Boolean
get() {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return cm.activeNetworkInfo != null && (cm.activeNetworkInfo.type == ConnectivityManager.TYPE_WIFI);
return cm.activeNetworkInfo != null
&& (cm.activeNetworkInfo.type == ConnectivityManager.TYPE_WIFI);
}
}
......
......@@ -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,11 +4,15 @@ 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
import com.biganto.visual.roompark.data.service.lifecycle.ForegroundLifecycleObserver
import com.biganto.visual.roompark.data.service.network.INetworkMonitor
import com.biganto.visual.roompark.data.service.notification.INotificationCenter
import com.biganto.visual.roompark.domain.contract.*
import dagger.BindsInstance
import dagger.Component
......@@ -17,10 +21,6 @@ import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
/**
* Created by Vladislav Bogdashkin on 13.06.2018.
*/
......@@ -53,13 +53,29 @@ interface AppComponent : AndroidInjector<RoomParkApplication>{
fun filesRep(): FilesContract
fun subsRep(): SubscriptionContract
fun provideLocal():ILocalStore
fun provideApi():IRoomParkApi
fun provideApi(): IRoomParkApi
fun provideBigantoApi(): IBigantoApi
fun providedb():IDb
fun provideUtils():DeviceUtilsContract
fun provideTour():TourContract
fun providePlan():FlatPlanContract
fun provideLifeCycle(): ForegroundLifecycleObserver
fun provideNotifivations(): INotificationCenter
fun provideNetMonitor(): INetworkMonitor
fun provideAppContext():Application
fun provideFileSystem(): FileModule
......@@ -72,41 +88,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,6 @@ 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 = 11
@Module
abstract class AppModule{
......
package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.data.repository.db.requrey.model.UserEntity
import com.biganto.visual.roompark.domain.model.AuthInfoModel
import io.reactivex.Completable
import io.reactivex.Observable
......@@ -12,4 +13,5 @@ interface AuthContract {
fun signIn(email:String,password:String) : Observable<AuthInfoModel>
fun signOut() : Completable
fun validateAuthState(): Observable<Boolean>
fun currentUser(): Observable<UserEntity>
}
\ No newline at end of file
package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.domain.model.DealModel
import com.biganto.visual.roompark.data.repository.db.requrey.model.DealEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.EstateEntity
import com.biganto.visual.roompark.data.repository.db.requrey.model.UserEntity
import com.biganto.visual.roompark.domain.model.EstateModel
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import io.reactivex.Completable
import io.reactivex.Observable
......@@ -13,20 +14,11 @@ import io.reactivex.Observable
interface DealContract{
fun getFavorites() : Observable<List<EstateModel>>
// fun getFavorites() : Observable<List<EstateModel>>
fun getEstate(estateId: Int): Observable<EstateModel>
fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>>
fun getEmptyPlan(estateId: Int, planId: Int): Observable<String>
fun getPlan(
estateId: Int
, planId: Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String>
fun getDeals(): Observable<List<DealModel>>
fun fetchEstate(building: Int, number: Int): Observable<EstateModel>
fun setDealRead(dealId: String): Completable
fun fetchDeals(user: UserEntity): Observable<List<DealEntity>>
fun fetchFavorites(user: UserEntity): Observable<List<EstateEntity>>
fun getDealsApi(user: UserEntity): Observable<List<DealEntity>>
}
\ No newline at end of file
package com.biganto.visual.roompark.domain.contract
import com.biganto.visual.roompark.data.data_provider.PlanFeaturesVariant
import com.biganto.visual.roompark.domain.model.PlanPresetModel
import com.biganto.visual.roompark.domain.use_case.DownloadUseCase
import io.reactivex.Observable
import java.io.File
/**
* Created by Vladislav Bogdashkin on 20.04.2020.
*/
interface FlatPlanContract{
fun getPlanTypes(estateId: Int): Observable<List<PlanPresetModel>>
fun getPlan(
estateId: Int
, planId: Int
, furniture:Boolean?
, sizes:Boolean?
, walls:Boolean?
, electric:Boolean?): Observable<String>
fun getPlanFile(featuresVariant: PlanFeaturesVariant): File
fun getPlan(featuresVariant: PlanFeaturesVariant): Observable<String>
fun getPlansObservable(
list: List<PlanFeaturesVariant>,
cancellationToken: DownloadUseCase.CancellationToken
): Observable<String>
}
\ 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
/**
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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