Clear cache and rework Signout

This commit is contained in:
Benoit Marty
2019-04-17 15:48:59 +02:00
parent b6cbed1c90
commit b1b526a516
19 changed files with 289 additions and 44 deletions

View File

@ -19,7 +19,7 @@ package im.vector.matrix.android.api.failure
import java.io.IOException
/**
* This class allows to expose differents kind of error to be then handled by the application.
* This class allows to expose different kinds of error to be then handled by the application.
* As it is a sealed class, you typically use it like that :
* when(failure) {
* is NetworkConnection -> Unit

View File

@ -33,6 +33,7 @@ data class MatrixError(
const val FORBIDDEN = "M_FORBIDDEN"
const val UNKNOWN = "M_UNKNOWN"
const val UNKNOWN_TOKEN = "M_UNKNOWN_TOKEN"
const val MISSING_TOKEN = "M_MISSING_TOKEN"
const val BAD_JSON = "M_BAD_JSON"
const val NOT_JSON = "M_NOT_JSON"
const val NOT_FOUND = "M_NOT_FOUND"

View File

@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session
import androidx.annotation.MainThread
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.crypto.CryptoService
@ -36,6 +37,7 @@ interface Session :
GroupService,
UserService,
CryptoService,
CacheService,
SignOutService,
FilterService {
@ -93,7 +95,11 @@ interface Session :
/**
* A global session listener to get notified for some events.
*/
// Not used at the moment
interface Listener
interface Listener {
/**
* The access token is not valid anymore
*/
fun onInvalidToken()
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.cache
import im.vector.matrix.android.api.MatrixCallback
/**
* This interface defines a method to sign out. It's implemented at the session level.
*/
interface CacheService {
/**
* Clear the whole cached data, except credentials. Once done, the session is closed and has to be opened again
*/
fun clearCache(callback: MatrixCallback<Unit>)
}

View File

@ -28,7 +28,7 @@ data class RoomSummary(
val displayName: String = "",
val topic: String = "",
val avatarUrl: String = "",
val isDirect: Boolean,
val isDirect: Boolean = false,
val lastMessage: Event? = null,
val otherMemberIds: List<String> = emptyList(),
var notificationCount: Int = 0,

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.util
import im.vector.matrix.android.api.MatrixCallback
/**
* Simple MatrixCallback implementation which delegate its calls to another callback
*/
open class MatrixCallbackDelegate<T>(private val callback: MatrixCallback<T>) : MatrixCallback<T> {
override fun onSuccess(data: T) {
callback.onSuccess(data)
}
override fun onFailure(failure: Throwable) {
callback.onFailure(failure)
}
}

View File

@ -23,6 +23,7 @@ import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.group.Group
@ -36,6 +37,7 @@ import im.vector.matrix.android.api.session.signout.SignOutService
import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.MatrixCallbackDelegate
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.di.MatrixKoinComponent
import im.vector.matrix.android.internal.di.MatrixKoinHolder
@ -65,6 +67,7 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
private val groupService by inject<GroupService>()
private val userService by inject<UserService>()
private val filterService by inject<FilterService>()
private val cacheService by inject<CacheService>()
private val signOutService by inject<SignOutService>()
private val syncThread by inject<SyncThread>()
private val contentUrlResolver by inject<ContentUrlResolver>()
@ -118,17 +121,18 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
@MainThread
override fun signOut(callback: MatrixCallback<Unit>) {
assert(isOpen)
syncThread.kill()
return signOutService.signOut(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
// Close the session
stopSync()
close()
callback.onSuccess(data)
// Clear the cache
cacheService.clearCache(object : MatrixCallbackDelegate<Unit>(callback) {})
}
override fun onFailure(failure: Throwable) {
callback.onFailure(failure)
// Ignore failure
onSuccess(Unit)
// callback.onFailure(failure)
}
})
}
@ -184,6 +188,19 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
return filterService.setFilter(filterPreset)
}
override fun clearCache(callback: MatrixCallback<Unit>) {
assert(isOpen)
syncThread.pause()
cacheService.clearCache(object : MatrixCallbackDelegate<Unit>(callback) {
override fun onSuccess(data: Unit) {
// Restart the sync
syncThread.restart()
super.onSuccess(data)
}
})
}
// USER SERVICE
override fun getUser(userId: String): User? {

View File

@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session
import android.content.Context
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.signout.SignOutService
@ -26,6 +27,9 @@ import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.matrix.android.api.session.user.UserService
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.database.model.SessionRealmModule
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
import im.vector.matrix.android.internal.session.cache.RealmCacheService
import im.vector.matrix.android.internal.session.cache.RealmClearCacheTask
import im.vector.matrix.android.internal.session.filter.*
import im.vector.matrix.android.internal.session.group.DefaultGroupService
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
@ -110,6 +114,14 @@ internal class SessionModule(private val sessionParams: SessionParams) {
DefaultSignOutService(get(), get()) as SignOutService
}
scope(DefaultSession.SCOPE) {
RealmCacheService(get(), get()) as CacheService
}
scope(DefaultSession.SCOPE) {
RealmClearCacheTask(get()) as ClearCacheTask
}
scope(DefaultSession.SCOPE) {
DefaultUserService(get()) as UserService
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.cache
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.cache.CacheService
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
internal class RealmCacheService(private val clearCacheTask: ClearCacheTask,
private val taskExecutor: TaskExecutor) : CacheService {
override fun clearCache(callback: MatrixCallback<Unit>) {
clearCacheTask
.configureWith(Unit)
.dispatchTo(callback)
.executeBy(taskExecutor)
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.cache
import arrow.core.Try
import im.vector.matrix.android.internal.task.Task
import io.realm.Realm
import io.realm.RealmConfiguration
internal interface ClearCacheTask : Task<Unit, Unit>
internal class RealmClearCacheTask(val realmConfiguration: RealmConfiguration) : ClearCacheTask {
override fun execute(params: Unit): Try<Unit> {
return Try {
val realm = Realm.getInstance(realmConfiguration)
realm.executeTransaction {
it.deleteAll()
}
realm.close()
}
}
}

View File

@ -52,7 +52,14 @@ internal class DefaultRoom(
RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
}
Transformations.map(liveRealmData) { results ->
results.map { it.asDomain() }.first()
val roomSummaries = results.map { it.asDomain() }
if (roomSummaries.isEmpty()) {
// Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache
RoomSummary(roomId)
} else {
roomSummaries.first()
}
}
}

View File

@ -31,7 +31,6 @@ internal class DefaultSignOutTask(private val signOutAPI: SignOutAPI,
return executeRequest<Unit> {
apiCall = signOutAPI.signOut()
}.flatMap {
// TODO Clear DB, media cache, etc.
sessionParamsStore.delete()
}
}

View File

@ -56,7 +56,7 @@ internal class SyncModule {
}
scope(DefaultSession.SCOPE) {
DefaultSyncTask(get(), get(), get()) as SyncTask
DefaultSyncTask(get(), get(), get(), get()) as SyncTask
}
scope(DefaultSession.SCOPE) {

View File

@ -17,6 +17,11 @@
package im.vector.matrix.android.internal.session.sync
import arrow.core.Try
import arrow.core.failure
import arrow.core.recoverWith
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.internal.auth.SessionParamsStore
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.filter.FilterRepository
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
@ -30,7 +35,8 @@ internal interface SyncTask : Task<SyncTask.Params, SyncResponse> {
internal class DefaultSyncTask(private val syncAPI: SyncAPI,
private val filterRepository: FilterRepository,
private val syncResponseHandler: SyncResponseHandler
private val syncResponseHandler: SyncResponseHandler,
private val sessionParamsStore: SessionParamsStore
) : SyncTask {
@ -46,6 +52,15 @@ internal class DefaultSyncTask(private val syncAPI: SyncAPI,
return executeRequest<SyncResponse> {
apiCall = syncAPI.sync(requestParams)
}.recoverWith { throwable ->
// Intercept 401
if (throwable is Failure.ServerError
&& throwable.error.code == MatrixError.UNKNOWN_TOKEN) {
sessionParamsStore.delete()
}
// Transmit the throwable
throwable.failure()
}.flatMap { syncResponse ->
syncResponseHandler.handleResponse(syncResponse, params.token, false)
}

View File

@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.session.sync.job
import com.squareup.moshi.JsonEncodingException
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.session.sync.SyncTask
@ -59,7 +60,11 @@ internal class SyncThread(private val syncTask: SyncTask,
if (state != State.PAUSED) {
return@synchronized
}
Timber.v("Unpause sync...")
Timber.v("Resume sync...")
// Retrieve the last token, it may have been deleted in case of a clear cache
nextBatch = syncTokenStore.getLastToken()
state = State.RUNNING
lock.notify()
}
@ -97,7 +102,7 @@ internal class SyncThread(private val syncTask: SyncTask,
lock.wait()
}
} else {
Timber.v("Execute sync request...")
Timber.v("Execute sync request with token $nextBatch")
val latch = CountDownLatch(1)
val params = SyncTask.Params(nextBatch)
cancelableTask = syncTask.configureWith(params)
@ -124,6 +129,13 @@ internal class SyncThread(private val syncTask: SyncTask,
// Wait 10s before retrying
sleep(RETRY_WAIT_TIME_MS)
}
if (failure is Failure.ServerError
&& (failure.error.code == MatrixError.UNKNOWN_TOKEN || failure.error.code == MatrixError.MISSING_TOKEN)) {
// No token or invalid token, stop the thread
state = State.KILLING
}
latch.countDown()
}