From f1d902b9ad27a785112ba22f8f770796b472758d Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 1 Sep 2020 19:36:50 +0200 Subject: [PATCH 1/8] Enable strict mode and remove some stuff from the main thread --- .../internal/database/RealmInstanceWrapper.kt | 35 +++++++++ .../internal/database/RealmSessionProvider.kt | 72 +++++++++++++++++++ .../mapper/ReadReceiptsSummaryMapper.kt | 10 ++- .../sdk/internal/session/SessionModule.kt | 5 ++ .../sdk/internal/session/room/RoomGetter.kt | 11 ++- .../room/state/StateEventDataSource.kt | 14 ++-- .../session/room/timeline/DefaultTimeline.kt | 10 +-- .../room/timeline/DefaultTimelineService.kt | 26 +++---- .../internal/session/user/UserDataSource.kt | 15 ++-- .../user/accountdata/AccountDataDataSource.kt | 15 ++-- .../session/widgets/helper/WidgetFactory.kt | 10 ++- .../java/im/vector/app/VectorApplication.kt | 8 +++ .../app/core/glide/VectorGlideModelLoader.kt | 2 +- .../home/room/detail/RoomDetailViewModel.kt | 16 +++-- .../timeline/item/MessageImageVideoItem.kt | 2 +- .../features/media/ImageContentRenderer.kt | 10 ++- .../features/rageshake/VectorFileLogger.kt | 19 +++-- .../members/RoomMemberListViewModel.kt | 16 ++--- .../vector/app/features/themes/ThemeUtils.kt | 16 ++++- 19 files changed, 225 insertions(+), 87 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt new file mode 100644 index 0000000000..851605437f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt @@ -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 withRealm(block: (Realm) -> R): R { + return use { + block(it.realm) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt new file mode 100644 index 0000000000..a232d83b6d --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt @@ -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() + + /** + * Allow you to execute a block with an opened realm. It automatically closes it if necessary (ie. when not in main thread) + */ + fun 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() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt index 188ca4937c..6b9c0e7a45 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/ReadReceiptsSummaryMapper.kt @@ -18,26 +18,24 @@ package org.matrix.android.sdk.internal.database.mapper 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.UserEntity 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 -internal class ReadReceiptsSummaryMapper @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration) { +internal class ReadReceiptsSummaryMapper @Inject constructor(private val realmSessionProvider: RealmSessionProvider) { fun map(readReceiptsSummaryEntity: ReadReceiptsSummaryEntity?): List { if (readReceiptsSummaryEntity == null) { return emptyList() } - return Realm.getInstance(realmConfiguration).use { realm -> + return realmSessionProvider.withRealm { realm -> val readReceipts = readReceiptsSummaryEntity.readReceipts readReceipts .mapNotNull { val user = UserEntity.where(realm, it.userId).findFirst() - ?: return@mapNotNull null + ?: return@mapNotNull null ReadReceipt(user.asDomain(), it.originServerTs.toLong()) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index d404cecc51..e2042bfeac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -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.database.DatabaseCleaner 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.di.Authenticated import org.matrix.android.sdk.internal.di.DeviceId @@ -343,6 +344,10 @@ internal abstract class SessionModule { @IntoSet abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver + @Binds + @IntoSet + abstract fun bindRealmSessionProvider(observer: RealmSessionProvider): SessionLifecycleObserver + @Binds abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt index 38dcad2311..985cf80e97 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt @@ -17,17 +17,16 @@ 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.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.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields 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.room.membership.RoomMemberHelper -import io.realm.Realm import javax.inject.Inject internal interface RoomGetter { @@ -38,18 +37,18 @@ internal interface RoomGetter { @SessionScope internal class DefaultRoomGetter @Inject constructor( - @SessionDatabase private val monarchy: Monarchy, + private val realmSessionProvider: RealmSessionProvider, private val roomFactory: RoomFactory ) : RoomGetter { override fun getRoom(roomId: String): Room? { - return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + return realmSessionProvider.withRealm { realm -> createRoom(realm, roomId) } } override fun getDirectRoomWith(otherUserId: String): Room? { - return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + return realmSessionProvider.withRealm { realm -> RoomSummaryEntity.where(realm) .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) .equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt index e8dc2ddf40..65d30868d8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/StateEventDataSource.kt @@ -20,24 +20,26 @@ package org.matrix.android.sdk.internal.session.room.state import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations 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.session.events.model.Event import org.matrix.android.sdk.api.util.Optional 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.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase 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 -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? { - return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + return realmSessionProvider.withRealm { realm -> 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, stateKey: QueryStringValue): List { - return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + return realmSessionProvider.withRealm { realm -> buildStateEventQuery(realm, roomId, eventTypes, stateKey) .findAll() .mapNotNull { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index 421cd1b063..52651af881 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -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.TimelineSettings 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.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.ChunkEntityFields @@ -76,7 +77,8 @@ internal class DefaultTimeline( private val settings: TimelineSettings, private val hiddenReadReceipts: TimelineHiddenReadReceipts, private val eventBus: EventBus, - private val eventDecryptor: TimelineEventDecryptor + private val eventDecryptor: TimelineEventDecryptor, + private val realmSessionProvider: RealmSessionProvider ) : Timeline, TimelineHiddenReadReceipts.Delegate { data class OnNewTimelineEvents(val roomId: String, val eventIds: List) @@ -136,13 +138,13 @@ internal class DefaultTimeline( } override fun pendingEventCount(): Int { - return Realm.getInstance(realmConfiguration).use { + return realmSessionProvider.withRealm { RoomEntity.where(it, roomId).findFirst()?.sendingTimelineEvents?.count() ?: 0 } } override fun failedToDeliverEventCount(): Int { - return Realm.getInstance(realmConfiguration).use { + return realmSessionProvider.withRealm { TimelineEventEntity.findAllInRoomWithSendStates(it, roomId, SendState.HAS_FAILED_STATES).count() } } @@ -239,7 +241,7 @@ internal class DefaultTimeline( return eventId } // 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) .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.DESCENDING) .findAll() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt index db675f69f5..c60a944409 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimelineService.kt @@ -22,6 +22,9 @@ import androidx.lifecycle.Transformations import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject 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.isVideoMessage 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.util.Optional 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.TimelineEventMapper 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.di.SessionDatabase 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, @SessionDatabase private val monarchy: Monarchy, + private val realmSessionProvider: RealmSessionProvider, private val eventBus: EventBus, private val taskExecutor: TaskExecutor, private val contextOfEventTask: GetContextOfEventTask, @@ -73,17 +73,17 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv hiddenReadReceipts = TimelineHiddenReadReceipts(readReceiptsSummaryMapper, roomId, settings), eventBus = eventBus, eventDecryptor = eventDecryptor, - fetchTokenAndPaginateTask = fetchTokenAndPaginateTask + fetchTokenAndPaginateTask = fetchTokenAndPaginateTask, + realmSessionProvider = realmSessionProvider ) } override fun getTimeLineEvent(eventId: String): TimelineEvent? { - return monarchy - .fetchCopyMap({ - TimelineEventEntity.where(it, roomId = roomId, eventId = eventId).findFirst() - }, { entity, _ -> - timelineEventMapper.map(entity) - }) + return realmSessionProvider.withRealm { realm -> + TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let { + timelineEventMapper.map(it) + } + } } override fun getTimeLineEventLive(eventId: String): LiveData> { @@ -98,7 +98,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv override fun getAttachmentMessages(): List { // TODO pretty bad query.. maybe we should denormalize clear type in base? - return doWithRealm(monarchy.realmConfiguration) { realm -> + return realmSessionProvider.withRealm { realm -> realm.where() .equalTo(TimelineEventEntityFields.ROOM_ID, roomId) .sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt index dd3c6856c0..f6cb86c0ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/UserDataSource.kt @@ -23,9 +23,11 @@ import androidx.paging.DataSource import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList 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.util.Optional 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.model.IgnoredUserEntity 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.query.where import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.util.fetchCopied -import io.realm.Case 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 by lazy { monarchy.createDataSourceFactory { realm -> @@ -58,10 +59,10 @@ internal class UserDataSource @Inject constructor(@SessionDatabase private val m } fun getUser(userId: String): User? { - val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() } - ?: return null - - return userEntity.asDomain() + return realmSessionProvider.withRealm { + val userEntity = UserEntity.where(it, userId).findFirst() + userEntity?.asDomain() + } } fun getUserLive(userId: String): LiveData> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt index d54bfdd63d..a9261eddab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/AccountDataDataSource.kt @@ -20,18 +20,20 @@ package org.matrix.android.sdk.internal.session.user.accountdata import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations 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.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.model.UserAccountDataEntity import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields 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 internal class AccountDataDataSource @Inject constructor(@SessionDatabase private val monarchy: Monarchy, + private val realmSessionProvider: RealmSessionProvider, private val accountDataMapper: AccountDataMapper) { fun getAccountDataEvent(type: String): UserAccountDataEvent? { @@ -45,10 +47,9 @@ internal class AccountDataDataSource @Inject constructor(@SessionDatabase privat } fun getAccountDataEvents(types: Set): List { - return monarchy.fetchAllMappedSync( - { accountDataEventsQuery(it, types) }, - accountDataMapper::map - ) + return realmSessionProvider.withRealm { + accountDataEventsQuery(it, types).findAll().map(accountDataMapper::map) + } } fun getLiveAccountDataEvents(types: Set): LiveData> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt index 2cbc9b23dc..992dbf16df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt @@ -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.WidgetContent 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.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.user.UserDataSource -import io.realm.Realm -import io.realm.RealmConfiguration import java.net.URLEncoder import javax.inject.Inject -internal class WidgetFactory @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration, - private val userDataSource: UserDataSource, +internal class WidgetFactory @Inject constructor(private val userDataSource: UserDataSource, + private val realmSessionProvider: RealmSessionProvider, @UserId private val userId: String) { 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) { null } else { - Realm.getInstance(realmConfiguration).use { + realmSessionProvider.withRealm { val roomMemberHelper = RoomMemberHelper(it, widgetEvent.roomId) val roomMemberSummaryEntity = roomMemberHelper.getLastRoomMember(widgetEvent.senderId) SenderInfo( diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index f64ebf9245..bca0a028e9 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.res.Configuration import android.os.Handler import android.os.HandlerThread +import android.os.StrictMode import androidx.core.provider.FontRequest import androidx.core.provider.FontsContractCompat import androidx.lifecycle.Lifecycle @@ -92,6 +93,13 @@ class VectorApplication : private var fontThreadHandler: Handler? = null override fun onCreate() { + if (BuildConfig.DEBUG) { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyFlashScreen() + .penaltyLog() + .build()) + } super.onCreate() appContext = this vectorComponent = DaggerVectorComponent.factory().create(this) diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt index 2a17c2ca1b..0eba78dbf8 100644 --- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt @@ -100,7 +100,7 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { Timber.v("Load data: $data") - if (data.isLocalFile() && data.url != null) { + if (data.isLocalFile && data.url != null) { val initialFile = File(data.url) callback.onDataReady(initialFile.inputStream()) return diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index eab0007080..a77d50d767 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -896,13 +896,15 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleEventVisible(action: RoomDetailAction.TimelineEventTurnsVisible) { - 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 { - room.getTimeLineEvent(it)?.let { event -> - visibleEventsObservable.accept(RoomDetailAction.TimelineEventTurnsVisible(event)) + viewModelScope.launch(Dispatchers.Default) { + 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 { + room.getTimeLineEvent(it)?.let { event -> + visibleEventsObservable.accept(RoomDetailAction.TimelineEventTurnsVisible(event)) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt index 9ada087207..ffc51f671f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt @@ -50,7 +50,7 @@ abstract class MessageImageVideoItem : AbsMessageItem(null) + private val mColorByAttr = HashMap() // init the theme @@ -68,8 +71,16 @@ object ThemeUtils { * @return the selected application theme */ fun getApplicationTheme(context: Context): String { - return PreferenceManager.getDefaultSharedPreferences(context) - .getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE + val currentTheme = this.currentTheme.get() + 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 */ fun setApplicationTheme(context: Context, aTheme: String) { + currentTheme.set(aTheme) when (aTheme) { THEME_DARK_VALUE -> context.setTheme(R.style.AppTheme_Dark) THEME_BLACK_VALUE -> context.setTheme(R.style.AppTheme_Black) From fa381cc06d4d25faef2ad01279fb818265b4778a Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 8 Sep 2020 12:53:08 +0200 Subject: [PATCH 2/8] Use a singleton for default shared pref --- .../im/vector/app/receivers/DebugReceiver.kt | 6 ++-- .../java/im/vector/app/push/fcm/FcmHelper.kt | 6 ++-- .../app/core/di/DefaultSharedPreferences.kt | 31 +++++++++++++++++++ .../app/core/ui/views/KeysBackupBanner.kt | 18 +++++------ .../im/vector/app/core/utils/RingtoneUtils.kt | 10 +++--- .../features/disclaimer/DisclaimerDialog.kt | 6 ++-- .../homeserver/ServerUrlsRepository.kt | 8 ++--- .../VectorUncaughtExceptionHandler.kt | 8 ++--- .../vector/app/features/settings/FontScale.kt | 6 ++-- .../app/features/settings/VectorLocale.kt | 6 ++-- .../features/settings/VectorPreferences.kt | 4 +-- .../vector/app/features/themes/ThemeUtils.kt | 5 ++- 12 files changed, 72 insertions(+), 42 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt diff --git a/vector/src/debug/java/im/vector/app/receivers/DebugReceiver.kt b/vector/src/debug/java/im/vector/app/receivers/DebugReceiver.kt index 7b64bed08e..0cea9e3d8e 100644 --- a/vector/src/debug/java/im/vector/app/receivers/DebugReceiver.kt +++ b/vector/src/debug/java/im/vector/app/receivers/DebugReceiver.kt @@ -22,7 +22,7 @@ import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences import androidx.core.content.edit -import androidx.preference.PreferenceManager +import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.utils.lsFiles import timber.log.Timber @@ -44,7 +44,7 @@ class DebugReceiver : BroadcastReceiver() { } private fun dumpPreferences(context: Context) { - logPrefs("DefaultSharedPreferences", PreferenceManager.getDefaultSharedPreferences(context)) + logPrefs("DefaultSharedPreferences", DefaultSharedPreferences.getInstance(context)) } private fun logPrefs(name: String, sharedPreferences: SharedPreferences?) { @@ -58,7 +58,7 @@ class DebugReceiver : BroadcastReceiver() { } private fun alterScalarToken(context: Context) { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { // putString("SCALAR_TOKEN_PREFERENCE_KEY" + Matrix.getInstance(context).defaultSession.myUserId, "bad_token") } } diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt index 6c68d6c3b5..84ca392c45 100755 --- a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt @@ -19,7 +19,6 @@ package im.vector.app.push.fcm import android.app.Activity import android.content.Context -import androidx.preference.PreferenceManager import android.widget.Toast import androidx.core.content.edit import com.google.android.gms.common.ConnectionResult @@ -27,6 +26,7 @@ import com.google.android.gms.common.GoogleApiAvailability import com.google.firebase.iid.FirebaseInstanceId import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.pushers.PushersManager import im.vector.app.features.settings.VectorPreferences import timber.log.Timber @@ -46,7 +46,7 @@ object FcmHelper { * @return the FCM token or null if not received from FCM */ fun getFcmToken(context: Context): String? { - return PreferenceManager.getDefaultSharedPreferences(context).getString(PREFS_KEY_FCM_TOKEN, null) + return DefaultSharedPreferences.getInstance(context).getString(PREFS_KEY_FCM_TOKEN, null) } /** @@ -58,7 +58,7 @@ object FcmHelper { */ fun storeFcmToken(context: Context, token: String?) { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putString(PREFS_KEY_FCM_TOKEN, token) } } diff --git a/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt b/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt new file mode 100644 index 0000000000..745af3e16c --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt @@ -0,0 +1,31 @@ +/* + * 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 im.vector.app.core.di + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceManager + +object DefaultSharedPreferences { + + @Volatile private var INSTANCE: SharedPreferences? = null + + fun getInstance(context: Context): SharedPreferences = + INSTANCE ?: synchronized(this) { + INSTANCE ?: PreferenceManager.getDefaultSharedPreferences(context).also { INSTANCE = it } + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt b/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt index 14d9261204..f4890780b5 100755 --- a/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt @@ -23,11 +23,11 @@ import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.edit import androidx.core.view.isVisible -import androidx.preference.PreferenceManager import butterknife.BindView import butterknife.ButterKnife import butterknife.OnClick import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences import timber.log.Timber /** @@ -57,7 +57,7 @@ class KeysBackupBanner @JvmOverloads constructor( init { setupView() - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false) putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, "") } @@ -105,17 +105,17 @@ class KeysBackupBanner @JvmOverloads constructor( state.let { when (it) { is State.Setup -> { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, true) } } is State.Recover -> { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, it.version) } } is State.Update -> { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, it.version) } } @@ -150,7 +150,7 @@ class KeysBackupBanner @JvmOverloads constructor( private fun renderSetup(nbOfKeys: Int) { if (nbOfKeys == 0 - || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) { + || DefaultSharedPreferences.getInstance(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) { // Do not display the setup banner if there is no keys to backup, or if the user has already closed it isVisible = false } else { @@ -164,7 +164,7 @@ class KeysBackupBanner @JvmOverloads constructor( } private fun renderRecover(version: String) { - if (version == PreferenceManager.getDefaultSharedPreferences(context).getString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, null)) { + if (version == DefaultSharedPreferences.getInstance(context).getString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, null)) { isVisible = false } else { isVisible = true @@ -177,7 +177,7 @@ class KeysBackupBanner @JvmOverloads constructor( } private fun renderUpdate(version: String) { - if (version == PreferenceManager.getDefaultSharedPreferences(context).getString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, null)) { + if (version == DefaultSharedPreferences.getInstance(context).getString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, null)) { isVisible = false } else { isVisible = true @@ -258,7 +258,7 @@ class KeysBackupBanner @JvmOverloads constructor( * Inform the banner that a Recover has been done for this version, so do not show the Recover banner for this version */ fun onRecoverDoneForVersion(context: Context, version: String) { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, version) } } diff --git a/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt b/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt index 82b8c3a51a..9b84ea7b2f 100644 --- a/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/RingtoneUtils.kt @@ -21,7 +21,7 @@ import android.media.Ringtone import android.media.RingtoneManager import android.net.Uri import androidx.core.content.edit -import androidx.preference.PreferenceManager +import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.features.settings.VectorPreferences /** @@ -40,7 +40,7 @@ import im.vector.app.features.settings.VectorPreferences * @see Ringtone */ fun getCallRingtoneUri(context: Context): Uri? { - val callRingtone: String? = PreferenceManager.getDefaultSharedPreferences(context) + val callRingtone: String? = DefaultSharedPreferences.getInstance(context) .getString(VectorPreferences.SETTINGS_CALL_RINGTONE_URI_PREFERENCE_KEY, null) callRingtone?.let { @@ -94,7 +94,7 @@ fun getCallRingtoneName(context: Context): String? { * @see Ringtone */ fun setCallRingtoneUri(context: Context, ringtoneUri: Uri) { - PreferenceManager.getDefaultSharedPreferences(context) + DefaultSharedPreferences.getInstance(context) .edit { putString(VectorPreferences.SETTINGS_CALL_RINGTONE_URI_PREFERENCE_KEY, ringtoneUri.toString()) } @@ -104,14 +104,14 @@ fun setCallRingtoneUri(context: Context, ringtoneUri: Uri) { * Set using Riot default ringtone */ fun useRiotDefaultRingtone(context: Context): Boolean { - return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(VectorPreferences.SETTINGS_CALL_RINGTONE_USE_RIOT_PREFERENCE_KEY, true) + return DefaultSharedPreferences.getInstance(context).getBoolean(VectorPreferences.SETTINGS_CALL_RINGTONE_USE_RIOT_PREFERENCE_KEY, true) } /** * Ask if default Riot ringtone has to be used */ fun setUseRiotDefaultRingtone(context: Context, useRiotDefault: Boolean) { - PreferenceManager.getDefaultSharedPreferences(context) + DefaultSharedPreferences.getInstance(context) .edit { putBoolean(VectorPreferences.SETTINGS_CALL_RINGTONE_USE_RIOT_PREFERENCE_KEY, useRiotDefault) } diff --git a/vector/src/main/java/im/vector/app/features/disclaimer/DisclaimerDialog.kt b/vector/src/main/java/im/vector/app/features/disclaimer/DisclaimerDialog.kt index b9568e0b62..c2cd2e11e3 100644 --- a/vector/src/main/java/im/vector/app/features/disclaimer/DisclaimerDialog.kt +++ b/vector/src/main/java/im/vector/app/features/disclaimer/DisclaimerDialog.kt @@ -20,8 +20,8 @@ import android.app.Activity import android.content.Context import androidx.appcompat.app.AlertDialog import androidx.core.content.edit -import androidx.preference.PreferenceManager import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.features.settings.VectorSettingsUrls @@ -31,7 +31,7 @@ private const val CURRENT_DISCLAIMER_VALUE = 2 private const val SHARED_PREF_KEY = "LAST_DISCLAIMER_VERSION_VALUE" fun showDisclaimerDialog(activity: Activity) { - val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(activity) + val sharedPrefs = DefaultSharedPreferences.getInstance(activity) if (sharedPrefs.getInt(SHARED_PREF_KEY, 0) < CURRENT_DISCLAIMER_VALUE) { sharedPrefs.edit { @@ -52,7 +52,7 @@ fun showDisclaimerDialog(activity: Activity) { } fun doNotShowDisclaimerDialog(context: Context) { - val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) + val sharedPrefs = DefaultSharedPreferences.getInstance(context) sharedPrefs.edit { putInt(SHARED_PREF_KEY, CURRENT_DISCLAIMER_VALUE) diff --git a/vector/src/main/java/im/vector/app/features/homeserver/ServerUrlsRepository.kt b/vector/src/main/java/im/vector/app/features/homeserver/ServerUrlsRepository.kt index 683dea43b0..094fc4b3bb 100644 --- a/vector/src/main/java/im/vector/app/features/homeserver/ServerUrlsRepository.kt +++ b/vector/src/main/java/im/vector/app/features/homeserver/ServerUrlsRepository.kt @@ -18,8 +18,8 @@ package im.vector.app.features.homeserver import android.content.Context import androidx.core.content.edit -import androidx.preference.PreferenceManager import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences /** * Object to store and retrieve home and identity server urls @@ -38,7 +38,7 @@ object ServerUrlsRepository { * Save home and identity sever urls received by the Referrer receiver */ fun setDefaultUrlsFromReferrer(context: Context, homeServerUrl: String, identityServerUrl: String) { - PreferenceManager.getDefaultSharedPreferences(context) + DefaultSharedPreferences.getInstance(context) .edit { if (homeServerUrl.isNotEmpty()) { putString(DEFAULT_REFERRER_HOME_SERVER_URL_PREF, homeServerUrl) @@ -54,7 +54,7 @@ object ServerUrlsRepository { * Save home and identity sever urls entered by the user. May be custom or default value */ fun saveServerUrls(context: Context, homeServerUrl: String, identityServerUrl: String) { - PreferenceManager.getDefaultSharedPreferences(context) + DefaultSharedPreferences.getInstance(context) .edit { putString(HOME_SERVER_URL_PREF, homeServerUrl) putString(IDENTITY_SERVER_URL_PREF, identityServerUrl) @@ -65,7 +65,7 @@ object ServerUrlsRepository { * Return last used home server url, or the default one from referrer or the default one from resources */ fun getLastHomeServerUrl(context: Context): String { - val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val prefs = DefaultSharedPreferences.getInstance(context) return prefs.getString(HOME_SERVER_URL_PREF, prefs.getString(DEFAULT_REFERRER_HOME_SERVER_URL_PREF, diff --git a/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt b/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt index 53f10e5d54..6954b9c87b 100644 --- a/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/VectorUncaughtExceptionHandler.kt @@ -19,7 +19,7 @@ package im.vector.app.features.rageshake import android.content.Context import android.os.Build import androidx.core.content.edit -import androidx.preference.PreferenceManager +import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.resources.VersionCodeProvider import im.vector.app.features.version.VersionProvider import org.matrix.android.sdk.api.Matrix @@ -61,7 +61,7 @@ class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter */ override fun uncaughtException(thread: Thread, throwable: Throwable) { Timber.v("Uncaught exception: $throwable") - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { putBoolean(PREFS_CRASH_KEY, true) } val b = StringBuilder() @@ -115,7 +115,7 @@ class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter * @return true if the application crashed */ fun didAppCrash(context: Context): Boolean { - return PreferenceManager.getDefaultSharedPreferences(context) + return DefaultSharedPreferences.getInstance(context) .getBoolean(PREFS_CRASH_KEY, false) } @@ -123,7 +123,7 @@ class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter * Clear the crash status */ fun clearAppCrashStatus(context: Context) { - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { remove(PREFS_CRASH_KEY) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/FontScale.kt b/vector/src/main/java/im/vector/app/features/settings/FontScale.kt index 5fbb021e0a..ad678ec49d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/FontScale.kt +++ b/vector/src/main/java/im/vector/app/features/settings/FontScale.kt @@ -19,8 +19,8 @@ package im.vector.app.features.settings import android.content.Context import androidx.annotation.StringRes import androidx.core.content.edit -import androidx.preference.PreferenceManager import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences /** * Object to manage the Font Scale choice of the user @@ -56,7 +56,7 @@ object FontScale { * @return the font scale value */ fun getFontScaleValue(context: Context): FontScaleValue { - val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val preferences = DefaultSharedPreferences.getInstance(context) return if (APPLICATION_FONT_SCALE_KEY !in preferences) { val fontScale = context.resources.configuration.fontScale @@ -81,7 +81,7 @@ object FontScale { * @param fontScaleValue the font scale value to store */ private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) { - PreferenceManager.getDefaultSharedPreferences(context) + DefaultSharedPreferences.getInstance(context) .edit { putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue) } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt b/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt index b9d81ab005..cff4ca0cb9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorLocale.kt @@ -19,9 +19,9 @@ package im.vector.app.features.settings import android.content.Context import android.content.res.Configuration import androidx.core.content.edit -import androidx.preference.PreferenceManager import im.vector.app.BuildConfig import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber @@ -59,7 +59,7 @@ object VectorLocale { */ fun init(context: Context) { this.context = context - val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val preferences = DefaultSharedPreferences.getInstance(context) if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) { applicationLocale = Locale(preferences.getString(APPLICATION_LOCALE_LANGUAGE_KEY, "")!!, @@ -85,7 +85,7 @@ object VectorLocale { fun saveApplicationLocale(locale: Locale) { applicationLocale = locale - PreferenceManager.getDefaultSharedPreferences(context).edit { + DefaultSharedPreferences.getInstance(context).edit { val language = locale.language if (language.isEmpty()) { remove(APPLICATION_LOCALE_LANGUAGE_KEY) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 69f8540a1d..886395c1f7 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -22,10 +22,10 @@ import android.media.RingtoneManager import android.net.Uri import android.provider.MediaStore import androidx.core.content.edit -import androidx.preference.PreferenceManager import com.squareup.seismic.ShakeDetector import im.vector.app.BuildConfig import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.features.homeserver.ServerUrlsRepository import im.vector.app.features.themes.ThemeUtils import org.matrix.android.sdk.api.extensions.tryThis @@ -227,7 +227,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { ) } - private val defaultPrefs = PreferenceManager.getDefaultSharedPreferences(context) + private val defaultPrefs = DefaultSharedPreferences.getInstance(context) /** * Clear the preferences. diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt index 69a385fc47..18faa07954 100644 --- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt +++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt @@ -25,8 +25,8 @@ import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat -import androidx.preference.PreferenceManager import im.vector.app.R +import im.vector.app.core.di.DefaultSharedPreferences import timber.log.Timber import java.util.concurrent.atomic.AtomicReference @@ -73,8 +73,7 @@ object ThemeUtils { fun getApplicationTheme(context: Context): String { val currentTheme = this.currentTheme.get() return if (currentTheme == null) { - val themeFromPref = PreferenceManager - .getDefaultSharedPreferences(context) + val themeFromPref = DefaultSharedPreferences.getInstance(context) .getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE this.currentTheme.set(themeFromPref) themeFromPref From 979c0832cf3f2e79054c544f6227a846b2bfb8ed Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 8 Sep 2020 15:29:02 +0200 Subject: [PATCH 3/8] Use realmSessionProvider in localEchoRepository --- .../internal/session/room/send/LocalEchoRepository.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt index b3188883c0..00c624a20d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoRepository.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.room.send import com.zhuinden.monarchy.Monarchy +import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -27,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult +import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.helper.nextId import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper @@ -43,12 +45,11 @@ import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline import org.matrix.android.sdk.internal.util.awaitTransaction -import io.realm.Realm -import org.greenrobot.eventbus.EventBus import timber.log.Timber import javax.inject.Inject internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy, + private val realmSessionProvider: RealmSessionProvider, private val roomSummaryUpdater: RoomSummaryUpdater, private val eventBus: EventBus, private val timelineEventMapper: TimelineEventMapper) { @@ -59,7 +60,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private if (event.eventId == null) { throw IllegalStateException("You should have set an eventId for your event") } - val timelineEventEntity = Realm.getInstance(monarchy.realmConfiguration).use { realm -> + val timelineEventEntity = realmSessionProvider.withRealm { realm -> val eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis()) val roomMemberHelper = RoomMemberHelper(realm, roomId) val myUser = roomMemberHelper.getLastRoomMember(senderId) @@ -150,7 +151,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private } fun getAllEventsWithStates(roomId: String, states : List): List { - return Realm.getInstance(monarchy.realmConfiguration).use { realm -> + return realmSessionProvider.withRealm { realm -> TimelineEventEntity .findAllInRoomWithSendStates(realm, roomId, states) .sortedByDescending { it.displayIndex } From c2880a5832ca0d445099a6ca8c0572970a7e9b5c Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 8 Sep 2020 15:38:00 +0200 Subject: [PATCH 4/8] Strict mode: add a build entry to enable whenever we want to check --- vector/build.gradle | 3 +++ .../java/im/vector/app/VectorApplication.kt | 17 ++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index 6b251d5329..e0f401aaf6 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -190,6 +190,8 @@ android { resValue "bool", "debug_mode", "true" buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false" + // Set to true if you want to enable strict mode in debug + buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false" signingConfig signingConfigs.debug } @@ -199,6 +201,7 @@ android { resValue "bool", "debug_mode", "false" buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false" + buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false" postprocessing { removeUnusedCode true diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index bca0a028e9..c9d2c96223 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -93,13 +93,7 @@ class VectorApplication : private var fontThreadHandler: Handler? = null override fun onCreate() { - if (BuildConfig.DEBUG) { - StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyFlashScreen() - .penaltyLog() - .build()) - } + enableStrictModeIfNeeded() super.onCreate() appContext = this vectorComponent = DaggerVectorComponent.factory().create(this) @@ -171,6 +165,15 @@ class VectorApplication : // initKnownEmojiHashSet(appContext) } + private fun enableStrictModeIfNeeded() { + if (BuildConfig.ENABLE_STRICT_MODE_LOGS) { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyLog() + .build()) + } + } + override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION) override fun getWorkManagerConfiguration(): WorkConfiguration { From dc04d2848d194be2a2e380694d6469b006e5dcad Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 8 Sep 2020 15:43:32 +0200 Subject: [PATCH 5/8] Default pref: make sure to use app context --- .../main/java/im/vector/app/core/di/DefaultSharedPreferences.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt b/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt index 745af3e16c..abee0cb2e7 100644 --- a/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt +++ b/vector/src/main/java/im/vector/app/core/di/DefaultSharedPreferences.kt @@ -26,6 +26,6 @@ object DefaultSharedPreferences { fun getInstance(context: Context): SharedPreferences = INSTANCE ?: synchronized(this) { - INSTANCE ?: PreferenceManager.getDefaultSharedPreferences(context).also { INSTANCE = it } + INSTANCE ?: PreferenceManager.getDefaultSharedPreferences(context.applicationContext).also { INSTANCE = it } } } From 8cb7260375a85ce6d426ccf2fb72a27c1edcbda4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 9 Sep 2020 12:10:46 +0200 Subject: [PATCH 6/8] Small changes (PR review) --- .../sdk/internal/database/RealmInstanceWrapper.kt | 2 +- .../sdk/internal/database/RealmSessionProvider.kt | 2 +- .../android/sdk/internal/session/SessionModule.kt | 12 ++++++------ tools/check/forbidden_strings_in_code.txt | 3 +++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt index 851605437f..4d02d696d8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt @@ -19,7 +19,7 @@ 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 { +internal class RealmInstanceWrapper (private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable { override fun close() { if (closeRealmOnClose) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt index a232d83b6d..a7f934ffc0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt @@ -31,7 +31,7 @@ import kotlin.concurrent.getOrSet * 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) +internal class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : SessionLifecycleObserver { private val realmThreadLocal = ThreadLocal() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index e2042bfeac..5397b8d9bd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -326,27 +326,27 @@ internal abstract class SessionModule { @Binds @IntoSet - abstract fun bindIntegrationManager(observer: IntegrationManager): SessionLifecycleObserver + abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver @Binds @IntoSet - abstract fun bindWidgetUrlFormatter(observer: DefaultWidgetURLFormatter): SessionLifecycleObserver + abstract fun bindWidgetUrlFormatter(formatter: DefaultWidgetURLFormatter): SessionLifecycleObserver @Binds @IntoSet - abstract fun bindShieldTrustUpdated(observer: ShieldTrustUpdater): SessionLifecycleObserver + abstract fun bindShieldTrustUpdated(updater: ShieldTrustUpdater): SessionLifecycleObserver @Binds @IntoSet - abstract fun bindIdentityService(observer: DefaultIdentityService): SessionLifecycleObserver + abstract fun bindIdentityService(service: DefaultIdentityService): SessionLifecycleObserver @Binds @IntoSet - abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver + abstract fun bindDatabaseCleaner(cleaner: DatabaseCleaner): SessionLifecycleObserver @Binds @IntoSet - abstract fun bindRealmSessionProvider(observer: RealmSessionProvider): SessionLifecycleObserver + abstract fun bindRealmSessionProvider(provider: RealmSessionProvider): SessionLifecycleObserver @Binds abstract fun bindInitialSyncProgressService(service: DefaultInitialSyncProgressService): InitialSyncProgressService diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index b5438a0c5b..425fc5a6d6 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -172,3 +172,6 @@ import org.matrix.androidsdk.crypto.data===2 ### Use `Context#getSystemService` extension function provided by `core-ktx` getSystemService\(Context + +### Use DefaultSharedPreferences.getInstance() instead for better performance +PreferenceManager\.getDefaultSharedPreferences==2 From 01a4905dc8b70c6c9d34dbff2f2da604587e1f65 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 9 Sep 2020 12:15:27 +0200 Subject: [PATCH 7/8] Changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b1a93209bc..2b8c294a7e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,7 +20,7 @@ Build 🧱: - Other changes: - - + - Performance: share Realm instance used on UI thread and improve SharedPreferences reading time. Changes in Element 1.0.6 (2020-09-08) =================================================== From 11fb2bcdfa1799c4ef9e14ec52bbc219d5843bce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 9 Sep 2020 13:34:57 +0200 Subject: [PATCH 8/8] ktlint... --- .../android/sdk/internal/database/RealmInstanceWrapper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt index 4d02d696d8..e2ddbcbca8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmInstanceWrapper.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.database import io.realm.Realm import java.io.Closeable -internal class RealmInstanceWrapper (private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable { +internal class RealmInstanceWrapper(private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable { override fun close() { if (closeRealmOnClose) {