Make sync request works as initial sync

This commit is contained in:
ganfra 2018-10-08 17:52:45 +02:00
parent a7eecdffae
commit 058f1704fa
35 changed files with 422 additions and 224 deletions

View File

@ -3,6 +3,7 @@
<words> <words>
<w>coroutine</w> <w>coroutine</w>
<w>moshi</w> <w>moshi</w>
<w>synchronizer</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

View File

@ -3,14 +3,45 @@ package im.vector.riotredesign.features.home
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.Toast
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.internal.events.sync.data.SyncResponse
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.RiotActivity import im.vector.riotredesign.core.platform.RiotActivity
import kotlinx.android.synthetic.main.activity_home.*
import org.koin.android.ext.android.inject
class HomeActivity : RiotActivity() { class HomeActivity : RiotActivity() {
private val matrix by inject<Matrix>()
private val synchronizer = matrix.currentSession?.synchronizer()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home) setContentView(R.layout.activity_home)
synchronizeButton.setOnClickListener { synchronize() }
}
private fun synchronize() {
synchronizeButton.visibility = View.GONE
loadingView.visibility = View.VISIBLE
synchronizer?.synchronize(object : MatrixCallback<SyncResponse> {
override fun onSuccess(data: SyncResponse?) {
synchronizeButton.visibility = View.VISIBLE
loadingView.visibility = View.GONE
Toast.makeText(this@HomeActivity, "Success", Toast.LENGTH_LONG).show()
}
override fun onFailure(failure: Failure) {
synchronizeButton.visibility = View.VISIBLE
loadingView.visibility = View.GONE
Toast.makeText(this@HomeActivity, failure.toString(), Toast.LENGTH_LONG).show()
}
})
} }

View File

@ -5,9 +5,9 @@ import android.view.View
import android.widget.Toast import android.widget.Toast
import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.failure.Failure
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.RiotActivity import im.vector.riotredesign.core.platform.RiotActivity
import im.vector.riotredesign.features.home.HomeActivity import im.vector.riotredesign.features.home.HomeActivity
@ -17,10 +17,8 @@ import org.koin.android.ext.android.inject
class LoginActivity : RiotActivity() { class LoginActivity : RiotActivity() {
private val matrix by inject<Matrix>() private val matrix by inject<Matrix>()
private val homeServerConnectionConfig = HomeServerConnectionConfig("https://matrix.org/") private val homeServerConnectionConfig = HomeServerConnectionConfig("https://matrix.org/")
private val session = matrix.createSession(homeServerConnectionConfig) private val authenticator = matrix.authenticator()
private val authenticator = session.authenticator()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -32,8 +30,9 @@ class LoginActivity : RiotActivity() {
val login = loginField.text.trim().toString() val login = loginField.text.trim().toString()
val password = passwordField.text.trim().toString() val password = passwordField.text.trim().toString()
progressBar.visibility = View.VISIBLE progressBar.visibility = View.VISIBLE
authenticator.authenticate(login, password, object : MatrixCallback<Credentials> { authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback<Session> {
override fun onSuccess(data: Credentials?) { override fun onSuccess(data: Session?) {
matrix.currentSession = data
goToHomeScreen() goToHomeScreen()
} }

View File

@ -7,20 +7,34 @@
android:orientation="vertical" android:orientation="vertical"
tools:context=".features.login.LoginActivity"> tools:context=".features.login.LoginActivity">
<ProgressBar
<TextView android:id="@+id/loadingView"
android:text="activity_home"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/synchronizeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Synchronize"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -57,11 +57,9 @@ dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'com.squareup.okio:okio:1.15.0' implementation 'com.squareup.okio:okio:1.15.0'
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
// Paging // Paging
implementation "android.arch.paging:runtime:1.0.1" implementation "android.arch.paging:runtime:1.0.1"
@ -69,6 +67,10 @@ dependencies {
implementation "org.koin:koin-core:$koin_version" implementation "org.koin:koin-core:$koin_version"
implementation "org.koin:koin-core-ext:$koin_version" implementation "org.koin:koin-core-ext:$koin_version"
// Logging
implementation 'com.jakewharton.timber:timber:4.7.1'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

View File

@ -4,47 +4,64 @@
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [ "entities": [
{ {
"id": "1:637433444018824383", "id": "3:213989653016909134",
"lastPropertyId": "6:2515822585258942903", "lastPropertyId": "6:8192129106205548340",
"name": "Credentials", "name": "Credentials",
"properties": [ "properties": [
{ {
"id": "1:6211403495341530846", "id": "1:3516672544602138732",
"name": "id" "name": "id"
}, },
{ {
"id": "2:1774175862476960436", "id": "2:8457998089049891725",
"indexId": "1:7573100044105293219",
"name": "userId" "name": "userId"
}, },
{ {
"id": "3:5757014528669120452", "id": "3:878535388660167894",
"name": "homeServer" "name": "homeServer"
}, },
{ {
"id": "4:6085081322264805865", "id": "4:7030303501035684102",
"name": "accessToken" "name": "accessToken"
}, },
{ {
"id": "5:5476767963007280768", "id": "5:7193051897929077560",
"name": "refreshToken" "name": "refreshToken"
}, },
{ {
"id": "6:2515822585258942903", "id": "6:8192129106205548340",
"name": "deviceId" "name": "deviceId"
} }
], ],
"relations": [] "relations": []
} }
], ],
"lastEntityId": "1:637433444018824383", "lastEntityId": "3:213989653016909134",
"lastIndexId": "0:0", "lastIndexId": "1:7573100044105293219",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
"modelVersion": 4, "modelVersion": 4,
"modelVersionParserMinimum": 4, "modelVersionParserMinimum": 4,
"retiredEntityUids": [], "retiredEntityUids": [
637433444018824383,
5577202525246066978
],
"retiredIndexUids": [], "retiredIndexUids": [],
"retiredPropertyUids": [], "retiredPropertyUids": [
6211403495341530846,
1774175862476960436,
5757014528669120452,
6085081322264805865,
5476767963007280768,
2515822585258942903,
1374359365665650669,
2615322165288227076,
8258973118557394726,
4167129800901721265,
4654729568692986907,
958528673818813300
],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1
} }

View File

@ -0,0 +1,66 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "3:213989653016909134",
"lastPropertyId": "6:8192129106205548340",
"name": "Credentials",
"properties": [
{
"id": "1:3516672544602138732",
"name": "id"
},
{
"id": "2:8457998089049891725",
"name": "userId"
},
{
"id": "3:878535388660167894",
"name": "homeServer"
},
{
"id": "4:7030303501035684102",
"name": "accessToken"
},
{
"id": "5:7193051897929077560",
"name": "refreshToken"
},
{
"id": "6:8192129106205548340",
"name": "deviceId"
}
],
"relations": []
}
],
"lastEntityId": "3:213989653016909134",
"lastIndexId": "0:0",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 4,
"modelVersionParserMinimum": 4,
"retiredEntityUids": [
637433444018824383,
5577202525246066978
],
"retiredIndexUids": [],
"retiredPropertyUids": [
6211403495341530846,
1774175862476960436,
5757014528669120452,
6085081322264805865,
5476767963007280768,
2515822585258942903,
1374359365665650669,
2615322165288227076,
8258973118557394726,
4167129800901721265,
4654729568692986907,
958528673818813300
],
"retiredRelationUids": [],
"version": 1
}

View File

@ -1,21 +1,42 @@
package im.vector.matrix.android.api package im.vector.matrix.android.api
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig import android.content.Context
import im.vector.matrix.android.internal.DefaultSession import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.thread.MainThreadExecutor
import im.vector.matrix.android.internal.auth.AuthModule
import im.vector.matrix.android.internal.di.MatrixModule import im.vector.matrix.android.internal.di.MatrixModule
import im.vector.matrix.android.internal.di.NetworkModule import im.vector.matrix.android.internal.di.NetworkModule
import org.koin.standalone.KoinComponent
import org.koin.standalone.StandAloneContext.loadKoinModules import org.koin.standalone.StandAloneContext.loadKoinModules
import org.koin.standalone.inject
import timber.log.Timber
import timber.log.Timber.DebugTree
import java.util.concurrent.Executor
class Matrix(matrixOptions: MatrixOptions) {
class Matrix(matrixOptions: MatrixOptions) : KoinComponent {
private val authenticator by inject<Authenticator>()
var currentSession: Session? = null
init { init {
val matrixModule = MatrixModule(matrixOptions) val matrixModule = MatrixModule(matrixOptions)
val networkModule = NetworkModule() val networkModule = NetworkModule()
loadKoinModules(listOf(matrixModule, networkModule)) val authModule = AuthModule(matrixOptions.context)
loadKoinModules(listOf(matrixModule, networkModule, authModule))
if (BuildConfig.DEBUG) {
Timber.plant(DebugTree())
}
} }
fun createSession(homeServerConnectionConfig: HomeServerConnectionConfig): Session { fun authenticator(): Authenticator {
return DefaultSession(homeServerConnectionConfig) return authenticator
} }
} }
data class MatrixOptions(val context: Context,
val mainExecutor: Executor = MainThreadExecutor())

View File

@ -1,8 +0,0 @@
package im.vector.matrix.android.api
import android.content.Context
import im.vector.matrix.android.api.thread.MainThreadExecutor
import java.util.concurrent.Executor
data class MatrixOptions(val context: Context,
val mainExecutor: Executor = MainThreadExecutor())

View File

@ -1,11 +0,0 @@
package im.vector.matrix.android.api
import im.vector.matrix.android.api.auth.Authenticator
interface Session {
fun authenticator(): Authenticator
fun close()
}

View File

@ -1,11 +1,12 @@
package im.vector.matrix.android.api.auth package im.vector.matrix.android.api.auth
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.api.auth.data.Credentials
interface Authenticator { interface Authenticator {
fun authenticate(login: String, password: String, callback: MatrixCallback<Credentials>): Cancelable fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String, callback: MatrixCallback<Session>): Cancelable
} }

View File

@ -1,6 +1,6 @@
package im.vector.matrix.android.api.auth package im.vector.matrix.android.api.auth
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.auth.data.Credentials
interface CredentialsStore { interface CredentialsStore {

View File

@ -1,15 +0,0 @@
package im.vector.matrix.android.api.auth.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
@Entity
@JsonClass(generateAdapter = true)
data class Credentials(@Id var id: Long = 0,
@Json(name = "user_id") val userId: String,
@Json(name = "home_server") val homeServer: String,
@Json(name = "access_token") val accessToken: String,
@Json(name = "refresh_token") val refreshToken: String?,
@Json(name = "device_id") val deviceId: String?)

View File

@ -1,31 +1,17 @@
package im.vector.matrix.android.api.events package im.vector.matrix.android.api.events
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.Moshi import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class Event( data class Event(
@Json(name = "event_id") val eventId: String, @Json(name = "type") val type: String,
@Json(name = "type") val type: EventType, @Json(name = "event_id") val eventId: String? = null,
@Json(name = "content") val content: String, @Json(name = "content") val content: Map<String, Any>? = null,
@Json(name = "origin_server_ts") val originServerTs: Long, @Json(name = "prev_content") val prevContent: Map<String, Any>? = null,
@Json(name = "prev_content") val prevContent: String? = null, @Json(name = "origin_server_ts") val originServerTs: Long? = null,
@Json(name = "sender") val sender: String? = null, @Json(name = "sender") val sender: String? = null,
@Json(name = "state_key") val stateKey: String? = null, @Json(name = "state_key") val stateKey: String? = null,
@Json(name = "room_id") val roomId: String? = null, @Json(name = "room_id") val roomId: String? = null,
@Json(name = "unsigned_data") val unsignedData: UnsignedData? = null @Json(name = "unsigned_data") val unsignedData: UnsignedData? = null
) { )
inline fun <reified T> content(): T? {
val moshi = Moshi.Builder().build()
return moshi.adapter<T>(T::class.java).fromJson(content)
}
inline fun <reified T> prevContent(): T? {
if (prevContent == null) {
return null
}
val moshi = Moshi.Builder().build()
return moshi.adapter<T>(T::class.java).fromJson(prevContent)
}
}

View File

@ -0,0 +1,3 @@
package im.vector.matrix.android.api.events
class EventContent : HashMap<Any, Any>()

View File

@ -40,5 +40,4 @@ enum class EventType {
@Json(name ="m.room.related_groups") STATE_RELATED_GROUPS, @Json(name ="m.room.related_groups") STATE_RELATED_GROUPS,
@Json(name ="m.room.pinned_events") STATE_PINNED_EVENT @Json(name ="m.room.pinned_events") STATE_PINNED_EVENT
} }

View File

@ -1,7 +1,9 @@
package im.vector.matrix.android.api.events package im.vector.matrix.android.api.events
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class UnsignedData( data class UnsignedData(
@Json(name = "age") val age: Int, @Json(name = "age") val age: Int,
@Json(name = "redacted_because") val redactedEvent: Event? = null, @Json(name = "redacted_because") val redactedEvent: Event? = null,

View File

@ -0,0 +1,11 @@
package im.vector.matrix.android.api.session
import im.vector.matrix.android.internal.events.sync.Synchronizer
interface Session {
fun synchronizer(): Synchronizer
fun close()
}

View File

@ -1,37 +0,0 @@
package im.vector.matrix.android.internal
import im.vector.matrix.android.api.Session
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.internal.auth.LoginModule
import org.koin.core.scope.Scope
import org.koin.standalone.KoinComponent
import org.koin.standalone.StandAloneContext
import org.koin.standalone.getKoin
import org.koin.standalone.inject
class DefaultSession(val homeServerConnectionConfig: HomeServerConnectionConfig) : Session, KoinComponent {
private val authenticator by inject<Authenticator>()
private val scope: Scope
init {
val loginModule = LoginModule(homeServerConnectionConfig)
StandAloneContext.loadKoinModules(listOf(loginModule))
scope = getKoin().createScope(SCOPE)
}
override fun authenticator(): Authenticator {
return authenticator
}
override fun close() {
scope.close()
}
companion object {
const val SCOPE: String = "session"
}
}

View File

@ -1,6 +1,6 @@
package im.vector.matrix.android.internal.auth package im.vector.matrix.android.internal.auth
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.auth.data.Credentials
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
import im.vector.matrix.android.internal.network.NetworkConstants import im.vector.matrix.android.internal.network.NetworkConstants
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
@ -11,7 +11,7 @@ import retrofit2.http.POST
/** /**
* The login REST API. * The login REST API.
*/ */
interface LoginApi { interface AuthAPI {
/** /**
* Pass params to the server for the current login phase. * Pass params to the server for the current login phase.

View File

@ -0,0 +1,39 @@
package im.vector.matrix.android.internal.auth
import android.content.Context
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.CredentialsStore
import im.vector.matrix.android.internal.auth.data.Credentials
import im.vector.matrix.android.internal.auth.data.MyObjectBox
import im.vector.matrix.android.internal.auth.db.ObjectBoxCredentialsStore
import io.objectbox.Box
import io.objectbox.BoxStore
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
private const val AUTH_BOX_STORE = "AUTH_BOX_STORE"
class AuthModule(private val context: Context) : Module {
override fun invoke(): ModuleDefinition = module {
single {
DefaultAuthenticator(get(), get(), get(), get()) as Authenticator
}
single(name = AUTH_BOX_STORE) {
MyObjectBox.builder().androidContext(context).build()
}
single {
val boxStore = get(name = AUTH_BOX_STORE) as BoxStore
boxStore.boxFor(Credentials::class.java) as Box<Credentials>
}
single {
ObjectBoxCredentialsStore(get()) as CredentialsStore
}
}.invoke()
}

View File

@ -2,36 +2,49 @@ package im.vector.matrix.android.internal.auth
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.auth.Authenticator import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.CredentialsStore import im.vector.matrix.android.api.auth.CredentialsStore
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.session.DefaultSession
import im.vector.matrix.android.internal.util.map import im.vector.matrix.android.internal.auth.data.Credentials
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.util.CancelableCoroutine import im.vector.matrix.android.internal.util.CancelableCoroutine
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.map
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.Retrofit
class DefaultAuthenticator(private val loginApi: LoginApi, class DefaultAuthenticator(private val retrofitBuilder: Retrofit.Builder,
private val jsonMapper: Moshi, private val jsonMapper: Moshi,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val credentialsStore: CredentialsStore) : Authenticator { private val credentialsStore: CredentialsStore) : Authenticator {
override fun authenticate(login: String, password: String, callback: MatrixCallback<Credentials>): Cancelable { override fun authenticate(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String, callback: MatrixCallback<Session>): Cancelable {
val authAPI = buildAuthAPI(homeServerConnectionConfig)
val job = GlobalScope.launch(coroutineDispatchers.main) { val job = GlobalScope.launch(coroutineDispatchers.main) {
val loginParams = PasswordLoginParams.userIdentifier(login, password, "Mobile") val loginParams = PasswordLoginParams.userIdentifier(login, password, "Mobile")
val loginResult = executeRequest<Credentials> { val loginResult = executeRequest<Credentials> {
apiCall = loginApi.login(loginParams) apiCall = authAPI.login(loginParams)
moshi = jsonMapper moshi = jsonMapper
dispatcher = coroutineDispatchers.io dispatcher = coroutineDispatchers.io
}.map { }.map {
it?.apply { credentialsStore.save(it) } it?.apply { credentialsStore.save(it) }
}.map {
DefaultSession(homeServerConnectionConfig)
} }
loginResult.either({ callback.onFailure(it) }, { callback.onSuccess(it) }) loginResult.either({ callback.onFailure(it) }, { callback.onSuccess(it) })
} }
return CancelableCoroutine(job) return CancelableCoroutine(job)
} }
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
val retrofit = retrofitBuilder.baseUrl(homeServerConnectionConfig.hsUri).build()
return retrofit.create(AuthAPI::class.java)
}
} }

View File

@ -1,33 +0,0 @@
package im.vector.matrix.android.internal.auth
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.internal.DefaultSession
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
import retrofit2.Retrofit
class LoginModule(private val connectionConfig: HomeServerConnectionConfig) : Module {
override fun invoke(): ModuleDefinition = module {
scope(DefaultSession.SCOPE) {
Retrofit.Builder()
.client(get())
.baseUrl(connectionConfig.hsUri)
.addConverterFactory(get())
.addCallAdapterFactory(get())
.build()
}
scope(DefaultSession.SCOPE) {
val retrofit: Retrofit = get()
retrofit.create(LoginApi::class.java)
}
scope(DefaultSession.SCOPE) {
DefaultAuthenticator(get(), get(), get(), get()) as Authenticator
}
}.invoke()
}

View File

@ -0,0 +1,17 @@
package im.vector.matrix.android.internal.auth.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.Index
@Entity
@JsonClass(generateAdapter = true)
data class Credentials(
@Id var id: Long = 0,
@Index @Json(name = "user_id") val userId: String,
@Json(name = "home_server") val homeServer: String,
@Json(name = "access_token") val accessToken: String,
@Json(name = "refresh_token") val refreshToken: String?,
@Json(name = "device_id") val deviceId: String?)

View File

@ -1,18 +0,0 @@
package im.vector.matrix.android.internal.auth.db
import im.vector.matrix.android.api.auth.CredentialsStore
import im.vector.matrix.android.api.auth.data.Credentials
class InMemoryCredentialsStore : CredentialsStore {
var credentials: Credentials? = null
override fun get(): Credentials? {
return credentials
}
override fun save(credentials: Credentials) {
this.credentials = credentials.copy()
}
}

View File

@ -1,7 +1,7 @@
package im.vector.matrix.android.internal.auth.db package im.vector.matrix.android.internal.auth.db
import im.vector.matrix.android.api.auth.CredentialsStore import im.vector.matrix.android.api.auth.CredentialsStore
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.internal.auth.data.Credentials
import io.objectbox.Box import io.objectbox.Box
class ObjectBoxCredentialsStore(private val box: Box<Credentials>) : CredentialsStore { class ObjectBoxCredentialsStore(private val box: Box<Credentials>) : CredentialsStore {
@ -11,7 +11,7 @@ class ObjectBoxCredentialsStore(private val box: Box<Credentials>) : Credentials
} }
override fun get(): Credentials? { override fun get(): Credentials? {
return box.all.firstOrNull() return box.all.lastOrNull()
} }
} }

View File

@ -1,13 +1,7 @@
package im.vector.matrix.android.internal.di package im.vector.matrix.android.internal.di
import im.vector.matrix.android.api.MatrixOptions import im.vector.matrix.android.api.MatrixOptions
import im.vector.matrix.android.api.auth.CredentialsStore
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.auth.data.MyObjectBox
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.auth.db.ObjectBoxCredentialsStore
import io.objectbox.Box
import io.objectbox.BoxStore
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
@ -23,19 +17,5 @@ class MatrixModule(private val options: MatrixOptions) : Module {
single { single {
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = options.mainExecutor.asCoroutineDispatcher()) MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = options.mainExecutor.asCoroutineDispatcher())
} }
single {
MyObjectBox.builder().androidContext(options.context).build()
}
single {
val boxStore = get() as BoxStore
boxStore.boxFor(Credentials::class.java) as Box<Credentials>
}
single {
ObjectBoxCredentialsStore(get()) as CredentialsStore
}
}.invoke() }.invoke()
} }

View File

@ -10,9 +10,12 @@ import org.koin.dsl.module.Module
import org.koin.dsl.module.module import org.koin.dsl.module.module
import retrofit2.CallAdapter import retrofit2.CallAdapter
import retrofit2.Converter import retrofit2.Converter
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.converter.moshi.MoshiConverterFactory
import timber.log.Timber
import java.util.concurrent.TimeUnit
class NetworkModule() : Module { class NetworkModule : Module {
override fun invoke(): ModuleDefinition = module { override fun invoke(): ModuleDefinition = module {
@ -21,17 +24,23 @@ class NetworkModule() : Module {
} }
single { single {
HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val logger = HttpLoggingInterceptor.Logger { message -> Timber.v(message) }
HttpLoggingInterceptor(logger).apply { level = HttpLoggingInterceptor.Level.BODY }
} }
single { single {
OkHttpClient.Builder() OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(get() as AccessTokenInterceptor) .addInterceptor(get() as AccessTokenInterceptor)
.addInterceptor(get() as HttpLoggingInterceptor) .addInterceptor(get() as HttpLoggingInterceptor)
.build() .build()
} }
single { Moshi.Builder().build() } single {
Moshi.Builder().build()
}
single { single {
MoshiConverterFactory.create(get()) as Converter.Factory MoshiConverterFactory.create(get()) as Converter.Factory
@ -41,6 +50,12 @@ class NetworkModule() : Module {
CoroutineCallAdapterFactory() as CallAdapter.Factory CoroutineCallAdapterFactory() as CallAdapter.Factory
} }
factory {
Retrofit.Builder()
.client(get())
.addConverterFactory(get())
.addCallAdapterFactory(get())
}
}.invoke() }.invoke()
} }

View File

@ -0,0 +1,20 @@
package im.vector.matrix.android.internal.di
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.internal.session.DefaultSession
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
import retrofit2.Retrofit
class SessionModule(private val connectionConfig: HomeServerConnectionConfig) : Module {
override fun invoke(): ModuleDefinition = module {
scope(DefaultSession.SCOPE) {
val retrofitBuilder = get() as Retrofit.Builder
retrofitBuilder
.baseUrl(connectionConfig.hsUri)
.build()
}
}.invoke()
}

View File

@ -1,7 +1,7 @@
package im.vector.matrix.android.internal.events.sync package im.vector.matrix.android.internal.events.sync
import im.vector.matrix.android.internal.network.NetworkConstants
import im.vector.matrix.android.internal.events.sync.data.SyncResponse import im.vector.matrix.android.internal.events.sync.data.SyncResponse
import im.vector.matrix.android.internal.network.NetworkConstants
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import retrofit2.Response import retrofit2.Response
import retrofit2.http.GET import retrofit2.http.GET
@ -10,6 +10,6 @@ import retrofit2.http.QueryMap
interface SyncAPI { interface SyncAPI {
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync") @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "sync")
fun sync(@QueryMap params: Map<String, Any>): Deferred<Response<SyncResponse>> fun sync(@QueryMap params: Map<String, String>): Deferred<Response<SyncResponse>>
} }

View File

@ -0,0 +1,23 @@
package im.vector.matrix.android.internal.events.sync
import im.vector.matrix.android.internal.session.DefaultSession
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
import retrofit2.Retrofit
class SyncModule : Module {
override fun invoke(): ModuleDefinition = module {
scope(DefaultSession.SCOPE) {
val retrofit: Retrofit = get()
retrofit.create(SyncAPI::class.java)
}
scope(DefaultSession.SCOPE) {
Synchronizer(get(), get(), get())
}
}.invoke()
}

View File

@ -1,11 +1,32 @@
package im.vector.matrix.android.internal.events.sync package im.vector.matrix.android.internal.events.sync
class Synchronizer(private val syncAPI: SyncAPI) { import com.squareup.moshi.Moshi
import im.vector.matrix.android.api.MatrixCallback
fun synchronize() { import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.events.sync.data.SyncResponse
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.util.CancelableCoroutine
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class Synchronizer(private val syncAPI: SyncAPI,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val jsonMapper: Moshi) {
fun synchronize(callback: MatrixCallback<SyncResponse>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) {
val params = HashMap<String, String>()
params["timeout"] = "0"
params["filter"] = "{}"
val syncResponse = executeRequest<SyncResponse> {
apiCall = syncAPI.sync(params)
moshi = jsonMapper
dispatcher = coroutineDispatchers.io
}
syncResponse.either({ callback.onFailure(it) }, { callback.onSuccess(it) })
}
return CancelableCoroutine(job)
} }
} }

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.events.sync.data package im.vector.matrix.android.internal.events.sync.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
// SyncResponse represents the request response for server sync v2. // SyncResponse represents the request response for server sync v2.
@ -9,36 +10,36 @@ data class SyncResponse(
/** /**
* The user private data. * The user private data.
*/ */
val accountData: Map<String, Any> = emptyMap(), @Json(name = "account_data") val accountData: Map<String, Any>? = emptyMap(),
/** /**
* The opaque token for the end. * The opaque token for the end.
*/ */
val nextBatch: String? = null, @Json(name = "next_batch") val nextBatch: String? = null,
/** /**
* The updates to the presence status of other users. * The updates to the presence status of other users.
*/ */
val presence: PresenceSyncResponse? = null, @Json(name = "presence") val presence: PresenceSyncResponse? = null,
/* /*
* Data directly sent to one of user's devices. * Data directly sent to one of user's devices.
*/ */
val toDevice: ToDeviceSyncResponse? = null, @Json(name = "to_device") val toDevice: ToDeviceSyncResponse? = null,
/** /**
* List of rooms. * List of rooms.
*/ */
val rooms: RoomsSyncResponse? = null, @Json(name = "rooms") val rooms: RoomsSyncResponse? = null,
/** /**
* Devices list update * Devices list update
*/ */
val deviceLists: DeviceListResponse? = null, @Json(name = "device_lists") val deviceLists: DeviceListResponse? = null,
/** /**
* One time keys management * One time keys management
*/ */
val deviceOneTimeKeysCount: DeviceOneTimeKeysCountSyncResponse? = null @Json(name = "device_one_time_keys_count") val deviceOneTimeKeysCount: DeviceOneTimeKeysCountSyncResponse? = null
) )

View File

@ -0,0 +1,38 @@
package im.vector.matrix.android.internal.session
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.internal.di.SessionModule
import im.vector.matrix.android.internal.events.sync.SyncModule
import im.vector.matrix.android.internal.events.sync.Synchronizer
import org.koin.core.scope.Scope
import org.koin.standalone.KoinComponent
import org.koin.standalone.StandAloneContext
import org.koin.standalone.getKoin
import org.koin.standalone.inject
class DefaultSession(homeServerConnectionConfig: HomeServerConnectionConfig) : Session, KoinComponent {
private val synchronizer by inject<Synchronizer>()
private val scope: Scope
init {
val sessionModule = SessionModule(homeServerConnectionConfig)
val syncModule = SyncModule()
StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule))
scope = getKoin().createScope(SCOPE)
}
override fun synchronizer(): Synchronizer {
return synchronizer
}
override fun close() {
scope.close()
}
companion object {
const val SCOPE: String = "session"
}
}