Enable strict mode and remove some stuff from the main thread

This commit is contained in:
ganfra 2020-09-01 19:36:50 +02:00 committed by Benoit Marty
parent bf0b6d738a
commit f1d902b9ad
19 changed files with 225 additions and 87 deletions

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 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 org.matrix.android.sdk.internal.database
import io.realm.Realm
import java.io.Closeable
class RealmInstanceWrapper(private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable {
override fun close() {
if (closeRealmOnClose) {
realm.close()
}
}
fun <R> withRealm(block: (Realm) -> R): R {
return use {
block(it.realm)
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 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 org.matrix.android.sdk.internal.database
import android.os.Looper
import androidx.annotation.MainThread
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import javax.inject.Inject
import kotlin.concurrent.getOrSet
/**
* This class keeps an instance of realm open in the main thread so you can grab it whenever you want to get a realm
* instance. This does check each time if you are on the main thread or not and returns the appropriate realm instance.
*/
@SessionScope
class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy)
: SessionLifecycleObserver {
private val realmThreadLocal = ThreadLocal<Realm>()
/**
* Allow you to execute a block with an opened realm. It automatically closes it if necessary (ie. when not in main thread)
*/
fun <R> withRealm(block: (Realm) -> R): R {
return getRealmWrapper().withRealm(block)
}
@MainThread
override fun onStart() {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
}
@MainThread
override fun onStop() {
realmThreadLocal.get()?.close()
realmThreadLocal.remove()
}
private fun getRealmWrapper(): RealmInstanceWrapper {
val isOnMainThread = isOnMainThread()
val realm = if (isOnMainThread) {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
} else {
Realm.getInstance(monarchy.realmConfiguration)
}
return RealmInstanceWrapper(realm, closeRealmOnClose = !isOnMainThread)
}
private fun isOnMainThread() = Looper.myLooper() == Looper.getMainLooper()
}

View File

@ -18,26 +18,24 @@
package org.matrix.android.sdk.internal.database.mapper package org.matrix.android.sdk.internal.database.mapper
import org.matrix.android.sdk.api.session.room.model.ReadReceipt import org.matrix.android.sdk.api.session.room.model.ReadReceipt
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
import org.matrix.android.sdk.internal.database.model.UserEntity import org.matrix.android.sdk.internal.database.model.UserEntity
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import io.realm.Realm
import io.realm.RealmConfiguration
import javax.inject.Inject import javax.inject.Inject
internal class ReadReceiptsSummaryMapper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) { internal class ReadReceiptsSummaryMapper @Inject constructor(private val realmSessionProvider: RealmSessionProvider) {
fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> { fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List<ReadReceipt> {
if (readReceiptsSummaryEntity == null) { if (readReceiptsSummaryEntity == null) {
return emptyList() return emptyList()
} }
return Realm.getInstance(realmConfiguration).use { realm -> return realmSessionProvider.withRealm { realm ->
val readReceipts = readReceiptsSummaryEntity.readReceipts val readReceipts = readReceiptsSummaryEntity.readReceipts
readReceipts readReceipts
.mapNotNull { .mapNotNull {
val user = UserEntity.where(realm, it.userId).findFirst() val user = UserEntity.where(realm, it.userId).findFirst()
?: return@mapNotNull null ?: return@mapNotNull null
ReadReceipt(user.asDomain(), it.originServerTs.toLong()) ReadReceipt(user.asDomain(), it.originServerTs.toLong())
} }
} }

View File

@ -47,6 +47,7 @@ import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorage
import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor
import org.matrix.android.sdk.internal.database.DatabaseCleaner import org.matrix.android.sdk.internal.database.DatabaseCleaner
import org.matrix.android.sdk.internal.database.EventInsertLiveObserver import org.matrix.android.sdk.internal.database.EventInsertLiveObserver
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory
import org.matrix.android.sdk.internal.di.Authenticated import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.DeviceId
@ -343,6 +344,10 @@ internal abstract class SessionModule {
@IntoSet @IntoSet
abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindRealmSessionProvider(observer: RealmSessionProvider): SessionLifecycleObserver
@Binds @Binds
abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService

View File

@ -17,17 +17,16 @@
package org.matrix.android.sdk.internal.session.room package org.matrix.android.sdk.internal.session.room
import com.zhuinden.monarchy.Monarchy import io.realm.Realm
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import io.realm.Realm
import javax.inject.Inject import javax.inject.Inject
internal interface RoomGetter { internal interface RoomGetter {
@ -38,18 +37,18 @@ internal interface RoomGetter {
@SessionScope @SessionScope
internal class DefaultRoomGetter @Inject constructor( internal class DefaultRoomGetter @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, private val realmSessionProvider: RealmSessionProvider,
private val roomFactory: RoomFactory private val roomFactory: RoomFactory
) : RoomGetter { ) : RoomGetter {
override fun getRoom(roomId: String): Room? { override fun getRoom(roomId: String): Room? {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> return realmSessionProvider.withRealm { realm ->
createRoom(realm, roomId) createRoom(realm, roomId)
} }
} }
override fun getDirectRoomWith(otherUserId: String): Room? { override fun getDirectRoomWith(otherUserId: String): Room? {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> return realmSessionProvider.withRealm { realm ->
RoomSummaryEntity.where(realm) RoomSummaryEntity.where(realm)
.equalTo(RoomSummaryEntityFields.IS_DIRECT, true) .equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) .equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)

View File

@ -20,24 +20,26 @@ package org.matrix.android.sdk.internal.session.room.state
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.query.process
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.kotlin.where
import javax.inject.Inject import javax.inject.Inject
internal class StateEventDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy) { internal class StateEventDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider) {
fun getStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue): Event? { fun getStateEvent(roomId: String, eventType: String, stateKey: QueryStringValue): Event? {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> return realmSessionProvider.withRealm { realm ->
buildStateEventQuery(realm, roomId, setOf(eventType), stateKey).findFirst()?.root?.asDomain() buildStateEventQuery(realm, roomId, setOf(eventType), stateKey).findFirst()?.root?.asDomain()
} }
} }
@ -53,7 +55,7 @@ internal class StateEventDataSource @Inject constructor(@SessionDatabase private
} }
fun getStateEvents(roomId: String, eventTypes: Set<String>, stateKey: QueryStringValue): List<Event> { fun getStateEvents(roomId: String, eventTypes: Set<String>, stateKey: QueryStringValue): List<Event> {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> return realmSessionProvider.withRealm { realm ->
buildStateEventQuery(realm, roomId, eventTypes, stateKey) buildStateEventQuery(realm, roomId, eventTypes, stateKey)
.findAll() .findAll()
.mapNotNull { .mapNotNull {

View File

@ -39,6 +39,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.util.CancelableBag import org.matrix.android.sdk.api.util.CancelableBag
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.ChunkEntityFields import org.matrix.android.sdk.internal.database.model.ChunkEntityFields
@ -76,7 +77,8 @@ internal class DefaultTimeline(
private val settings: TimelineSettings, private val settings: TimelineSettings,
private val hiddenReadReceipts: TimelineHiddenReadReceipts, private val hiddenReadReceipts: TimelineHiddenReadReceipts,
private val eventBus: EventBus, private val eventBus: EventBus,
private val eventDecryptor: TimelineEventDecryptor private val eventDecryptor: TimelineEventDecryptor,
private val realmSessionProvider: RealmSessionProvider
) : Timeline, TimelineHiddenReadReceipts.Delegate { ) : Timeline, TimelineHiddenReadReceipts.Delegate {
data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>) data class OnNewTimelineEvents(val roomId: String, val eventIds: List<String>)
@ -136,13 +138,13 @@ internal class DefaultTimeline(
} }
override fun pendingEventCount(): Int { override fun pendingEventCount(): Int {
return Realm.getInstance(realmConfiguration).use { return realmSessionProvider.withRealm {
RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0 RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0
} }
} }
override fun failedToDeliverEventCount(): Int { override fun failedToDeliverEventCount(): Int {
return Realm.getInstance(realmConfiguration).use { return realmSessionProvider.withRealm {
TimelineEventEntity.findAllInRoomWithSendStates(it, roomId, SendState.HAS_FAILED_STATES).count() TimelineEventEntity.findAllInRoomWithSendStates(it, roomId, SendState.HAS_FAILED_STATES).count()
} }
} }
@ -239,7 +241,7 @@ internal class DefaultTimeline(
return eventId return eventId
} }
// Otherwise, we should check if the event is in the db, but is hidden because of filters // Otherwise, we should check if the event is in the db, but is hidden because of filters
return Realm.getInstance(realmConfiguration).use { localRealm -> return realmSessionProvider.withRealm { localRealm ->
val nonFilteredEvents = buildEventQuery(localRealm) val nonFilteredEvents = buildEventQuery(localRealm)
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING)
.findAll() .findAll()

View File

@ -22,6 +22,9 @@ import androidx.lifecycle.Transformations
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Sort
import io.realm.kotlin.where
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.session.events.model.isImageMessage import org.matrix.android.sdk.api.session.events.model.isImageMessage
import org.matrix.android.sdk.api.session.events.model.isVideoMessage import org.matrix.android.sdk.api.session.events.model.isVideoMessage
import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.Timeline
@ -30,7 +33,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineService
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.crypto.store.db.doWithRealm import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper import org.matrix.android.sdk.internal.database.mapper.ReadReceiptsSummaryMapper
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
@ -38,13 +41,10 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.fetchCopyMap
import io.realm.Sort
import io.realm.kotlin.where
import org.greenrobot.eventbus.EventBus
internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String, internal class DefaultTimelineService @AssistedInject constructor(@Assisted private val roomId: String,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider,
private val eventBus: EventBus, private val eventBus: EventBus,
private val taskExecutor: TaskExecutor, private val taskExecutor: TaskExecutor,
private val contextOfEventTask: GetContextOfEventTask, private val contextOfEventTask: GetContextOfEventTask,
@ -73,17 +73,17 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings), hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings),
eventBus = eventBus, eventBus = eventBus,
eventDecryptor = eventDecryptor, eventDecryptor = eventDecryptor,
fetchTokenAndPaginateTask = fetchTokenAndPaginateTask fetchTokenAndPaginateTask = fetchTokenAndPaginateTask,
realmSessionProvider = realmSessionProvider
) )
} }
override fun getTimeLineEvent(eventId: String): TimelineEvent? { override fun getTimeLineEvent(eventId: String): TimelineEvent? {
return monarchy return realmSessionProvider.withRealm { realm ->
.fetchCopyMap({ TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let {
TimelineEventEntity.where(it, roomId = roomId, eventId = eventId).findFirst() timelineEventMapper.map(it)
}, { entity, _ -> }
timelineEventMapper.map(entity) }
})
} }
override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> { override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
@ -98,7 +98,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
override fun getAttachmentMessages(): List<TimelineEvent> { override fun getAttachmentMessages(): List<TimelineEvent> {
// TODO pretty bad query.. maybe we should denormalize clear type in base? // TODO pretty bad query.. maybe we should denormalize clear type in base?
return doWithRealm(monarchy.realmConfiguration) { realm -> return realmSessionProvider.withRealm { realm ->
realm.where<TimelineEventEntity>() realm.where<TimelineEventEntity>()
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId) .equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)

View File

@ -23,9 +23,11 @@ import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Case
import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.IgnoredUserEntity import org.matrix.android.sdk.internal.database.model.IgnoredUserEntity
import org.matrix.android.sdk.internal.database.model.IgnoredUserEntityFields import org.matrix.android.sdk.internal.database.model.IgnoredUserEntityFields
@ -33,11 +35,10 @@ import org.matrix.android.sdk.internal.database.model.UserEntity
import org.matrix.android.sdk.internal.database.model.UserEntityFields import org.matrix.android.sdk.internal.database.model.UserEntityFields
import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.util.fetchCopied
import io.realm.Case
import javax.inject.Inject import javax.inject.Inject
internal class UserDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy) { internal class UserDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider) {
private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy { private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy {
monarchy.createDataSourceFactory { realm -> monarchy.createDataSourceFactory { realm ->
@ -58,10 +59,10 @@ internal class UserDataSource @Inject constructor(@SessionDatabase private val m
} }
fun getUser(userId: String): User? { fun getUser(userId: String): User? {
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() } return realmSessionProvider.withRealm {
?: return null val userEntity = UserEntity.where(it, userId).findFirst()
userEntity?.asDomain()
return userEntity.asDomain() }
} }
fun getUserLive(userId: String): LiveData<Optional<User>> { fun getUserLive(userId: String): LiveData<Optional<User>> {

View File

@ -20,18 +20,20 @@ package org.matrix.android.sdk.internal.session.user.accountdata
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.Transformations
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmQuery
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.mapper.AccountDataMapper import org.matrix.android.sdk.internal.database.mapper.AccountDataMapper
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
import io.realm.Realm
import io.realm.RealmQuery
import javax.inject.Inject import javax.inject.Inject
internal class AccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy, internal class AccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider,
private val accountDataMapper: AccountDataMapper) { private val accountDataMapper: AccountDataMapper) {
fun getAccountDataEvent(type: String): UserAccountDataEvent? { fun getAccountDataEvent(type: String): UserAccountDataEvent? {
@ -45,10 +47,9 @@ internal class AccountDataDataSource @Inject constructor(@SessionDatabase privat
} }
fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent> { fun getAccountDataEvents(types: Set<String>): List<UserAccountDataEvent> {
return monarchy.fetchAllMappedSync( return realmSessionProvider.withRealm {
{ accountDataEventsQuery(it, types) }, accountDataEventsQuery(it, types).findAll().map(accountDataMapper::map)
accountDataMapper::map }
)
} }
fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>> { fun getLiveAccountDataEvents(types: Set<String>): LiveData<List<UserAccountDataEvent>> {

View File

@ -23,17 +23,15 @@ import org.matrix.android.sdk.api.session.room.sender.SenderInfo
import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper
import org.matrix.android.sdk.internal.session.user.UserDataSource import org.matrix.android.sdk.internal.session.user.UserDataSource
import io.realm.Realm
import io.realm.RealmConfiguration
import java.net.URLEncoder import java.net.URLEncoder
import javax.inject.Inject import javax.inject.Inject
internal class WidgetFactory @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration, internal class WidgetFactory @Inject constructor(private val userDataSource: UserDataSource,
private val userDataSource: UserDataSource, private val realmSessionProvider: RealmSessionProvider,
@UserId private val userId: String) { @UserId private val userId: String) {
fun create(widgetEvent: Event): Widget? { fun create(widgetEvent: Event): Widget? {
@ -44,7 +42,7 @@ internal class WidgetFactory @Inject constructor(@SessionDatabase private val re
val senderInfo = if (widgetEvent.senderId == null || widgetEvent.roomId == null) { val senderInfo = if (widgetEvent.senderId == null || widgetEvent.roomId == null) {
null null
} else { } else {
Realm.getInstance(realmConfiguration).use { realmSessionProvider.withRealm {
val roomMemberHelper = RoomMemberHelper(it, widgetEvent.roomId) val roomMemberHelper = RoomMemberHelper(it, widgetEvent.roomId)
val roomMemberSummaryEntity = roomMemberHelper.getLastRoomMember(widgetEvent.senderId) val roomMemberSummaryEntity = roomMemberHelper.getLastRoomMember(widgetEvent.senderId)
SenderInfo( SenderInfo(

View File

@ -21,6 +21,7 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.os.StrictMode
import androidx.core.provider.FontRequest import androidx.core.provider.FontRequest
import androidx.core.provider.FontsContractCompat import androidx.core.provider.FontsContractCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
@ -92,6 +93,13 @@ class VectorApplication :
private var fontThreadHandler: Handler? = null private var fontThreadHandler: Handler? = null
override fun onCreate() { override fun onCreate() {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyFlashScreen()
.penaltyLog()
.build())
}
super.onCreate() super.onCreate()
appContext = this appContext = this
vectorComponent = DaggerVectorComponent.factory().create(this) vectorComponent = DaggerVectorComponent.factory().create(this)

View File

@ -100,7 +100,7 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) { override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
Timber.v("Load data: $data") Timber.v("Load data: $data")
if (data.isLocalFile() && data.url != null) { if (data.isLocalFile && data.url != null) {
val initialFile = File(data.url) val initialFile = File(data.url)
callback.onDataReady(initialFile.inputStream()) callback.onDataReady(initialFile.inputStream())
return return

View File

@ -896,13 +896,15 @@ class RoomDetailViewModel @AssistedInject constructor(
} }
private fun handleEventVisible(action: RoomDetailAction.TimelineEventTurnsVisible) { private fun handleEventVisible(action: RoomDetailAction.TimelineEventTurnsVisible) {
if (action.event.root.sendState.isSent()) { // ignore pending/local events viewModelScope.launch(Dispatchers.Default) {
visibleEventsObservable.accept(action) if (action.event.root.sendState.isSent()) { // ignore pending/local events
} visibleEventsObservable.accept(action)
// We need to update this with the related m.replace also (to move read receipt) }
action.event.annotations?.editSummary?.sourceEvents?.forEach { // We need to update this with the related m.replace also (to move read receipt)
room.getTimeLineEvent(it)?.let { event -> action.event.annotations?.editSummary?.sourceEvents?.forEach {
visibleEventsObservable.accept(RoomDetailAction.TimelineEventTurnsVisible(event)) room.getTimeLineEvent(it)?.let { event ->
visibleEventsObservable.accept(RoomDetailAction.TimelineEventTurnsVisible(event))
}
} }
} }
} }

View File

@ -50,7 +50,7 @@ abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Hold
super.bind(holder) super.bind(holder)
imageContentRenderer.render(mediaData, mode, holder.imageView) imageContentRenderer.render(mediaData, mode, holder.imageView)
if (!attributes.informationData.sendState.hasFailed()) { if (!attributes.informationData.sendState.hasFailed()) {
contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, mediaData.isLocalFile(), holder.progressLayout) contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, mediaData.isLocalFile, holder.progressLayout)
} else { } else {
holder.progressLayout.isVisible = false holder.progressLayout.isVisible = false
} }

View File

@ -36,9 +36,9 @@ import im.vector.app.core.glide.GlideRequest
import im.vector.app.core.ui.model.Size import im.vector.app.core.ui.model.Size
import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.DimensionConverter
import im.vector.app.core.utils.isLocalFile import im.vector.app.core.utils.isLocalFile
import kotlinx.android.parcel.Parcelize
import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
import kotlinx.android.parcel.Parcelize
import org.matrix.android.sdk.api.extensions.tryThis import org.matrix.android.sdk.api.extensions.tryThis
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
@ -69,12 +69,10 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
val maxHeight: Int, val maxHeight: Int,
val width: Int?, val width: Int?,
val maxWidth: Int, val maxWidth: Int,
val isLocalFile: Boolean = url.isLocalFile(),
// If true will load non mxc url, be careful to set it only for images sent by you // If true will load non mxc url, be careful to set it only for images sent by you
override val allowNonMxcUrls: Boolean = false override val allowNonMxcUrls: Boolean = false
) : AttachmentData { ) : AttachmentData
fun isLocalFile() = url.isLocalFile()
}
enum class Mode { enum class Mode {
FULL_SIZE, FULL_SIZE,
@ -268,7 +266,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
private fun resolveUrl(data: Data) = private fun resolveUrl(data: Data) =
(activeSessionHolder.getActiveSession().contentUrlResolver().resolveFullSize(data.url) (activeSessionHolder.getActiveSession().contentUrlResolver().resolveFullSize(data.url)
?: data.url?.takeIf { data.isLocalFile() && data.allowNonMxcUrls }) ?: data.url?.takeIf { data.isLocalFile && data.allowNonMxcUrls })
private fun processSize(data: Data, mode: Mode): Size { private fun processSize(data: Data, mode: Mode): Size {
val maxImageWidth = data.maxWidth val maxImageWidth = data.maxWidth

View File

@ -19,6 +19,9 @@ package im.vector.app.features.rageshake
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.PrintWriter import java.io.PrintWriter
@ -85,12 +88,14 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
} }
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (sFileHandler == null) return GlobalScope.launch(Dispatchers.IO) {
if (skipLog(priority)) return if (sFileHandler == null) return@launch
if (t != null) { if (skipLog(priority)) return@launch
logToFile(t) if (t != null) {
logToFile(t)
}
logToFile(prioPrefixes[priority] ?: "$priority ", tag ?: "Tag", message)
} }
logToFile(prioPrefixes[priority] ?: "$priority ", tag ?: "Tag", message)
} }
private fun skipLog(priority: Int): Boolean { private fun skipLog(priority: Int): Boolean {
@ -174,7 +179,8 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
companion object { companion object {
private val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n" private val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n"
// private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
// private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss*SSSZZZZ", Locale.US) private val DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss*SSSZZZZ", Locale.US)
private var mIsTimeZoneSet = false private var mIsTimeZoneSet = false
@ -201,7 +207,6 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
if (null == sCacheDirectory) { if (null == sCacheDirectory) {
return return
} }
val b = StringBuilder() val b = StringBuilder()
b.append(Thread.currentThread().id) b.append(Thread.currentThread().id)
b.append(" ") b.append(" ")

View File

@ -22,6 +22,14 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
@ -39,14 +47,6 @@ import org.matrix.android.sdk.rx.asObservable
import org.matrix.android.sdk.rx.mapOptional import org.matrix.android.sdk.rx.mapOptional
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap import org.matrix.android.sdk.rx.unwrap
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState,

View File

@ -28,6 +28,7 @@ import androidx.core.graphics.drawable.DrawableCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import im.vector.app.R import im.vector.app.R
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.atomic.AtomicReference
/** /**
* Util class for managing themes. * Util class for managing themes.
@ -42,6 +43,8 @@ object ThemeUtils {
private const val THEME_BLACK_VALUE = "black" private const val THEME_BLACK_VALUE = "black"
private const val THEME_STATUS_VALUE = "status" private const val THEME_STATUS_VALUE = "status"
private var currentTheme = AtomicReference<String>(null)
private val mColorByAttr = HashMap<Int, Int>() private val mColorByAttr = HashMap<Int, Int>()
// init the theme // init the theme
@ -68,8 +71,16 @@ object ThemeUtils {
* @return the selected application theme * @return the selected application theme
*/ */
fun getApplicationTheme(context: Context): String { fun getApplicationTheme(context: Context): String {
return PreferenceManager.getDefaultSharedPreferences(context) val currentTheme = this.currentTheme.get()
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE return if (currentTheme == null) {
val themeFromPref = PreferenceManager
.getDefaultSharedPreferences(context)
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE
this.currentTheme.set(themeFromPref)
themeFromPref
} else {
currentTheme
}
} }
/** /**
@ -78,6 +89,7 @@ object ThemeUtils {
* @param aTheme the new theme * @param aTheme the new theme
*/ */
fun setApplicationTheme(context: Context, aTheme: String) { fun setApplicationTheme(context: Context, aTheme: String) {
currentTheme.set(aTheme)
when (aTheme) { when (aTheme) {
THEME_DARK_VALUE -> context.setTheme(R.style.AppTheme_Dark) THEME_DARK_VALUE -> context.setTheme(R.style.AppTheme_Dark)
THEME_BLACK_VALUE -> context.setTheme(R.style.AppTheme_Black) THEME_BLACK_VALUE -> context.setTheme(R.style.AppTheme_Black)