Merge pull request #2075 from vector-im/feature/strict_mode_tracking

Feature/strict mode tracking
This commit is contained in:
Benoit Marty 2020-09-10 13:46:44 +02:00 committed by GitHub
commit b9e8d7187c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 315 additions and 137 deletions

View File

@ -20,7 +20,7 @@ Build 🧱:
- -
Other changes: Other changes:
- - Performance: share Realm instance used on UI thread and improve SharedPreferences reading time.
Changes in Element 1.0.6 (2020-09-08) Changes in Element 1.0.6 (2020-09-08)
=================================================== ===================================================

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
internal 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
internal 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
@ -325,23 +326,27 @@ internal abstract class SessionModule {
@Binds @Binds
@IntoSet @IntoSet
abstract fun bindIntegrationManager(observer: IntegrationManager): SessionLifecycleObserver abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver
@Binds @Binds
@IntoSet @IntoSet
abstract fun bindWidgetUrlFormatter(observer: DefaultWidgetURLFormatter): SessionLifecycleObserver abstract fun bindWidgetUrlFormatter(formatter: DefaultWidgetURLFormatter): SessionLifecycleObserver
@Binds @Binds
@IntoSet @IntoSet
abstract fun bindShieldTrustUpdated(observer: ShieldTrustUpdater): SessionLifecycleObserver abstract fun bindShieldTrustUpdated(updater: ShieldTrustUpdater): SessionLifecycleObserver
@Binds @Binds
@IntoSet @IntoSet
abstract fun bindIdentityService(observer: DefaultIdentityService): SessionLifecycleObserver abstract fun bindIdentityService(service: DefaultIdentityService): SessionLifecycleObserver
@Binds @Binds
@IntoSet @IntoSet
abstract fun bindDatabaseCleaner(observer: DatabaseCleaner): SessionLifecycleObserver abstract fun bindDatabaseCleaner(cleaner: DatabaseCleaner): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindRealmSessionProvider(provider: 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

@ -18,6 +18,7 @@
package org.matrix.android.sdk.internal.session.room.send package org.matrix.android.sdk.internal.session.room.send
import com.zhuinden.monarchy.Monarchy 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.Content
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.session.events.model.EventType 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.send.SendState
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.internal.crypto.MXEventDecryptionResult 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.helper.nextId
import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.ContentMapper
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper 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.summary.RoomSummaryUpdater
import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimeline
import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.awaitTransaction
import io.realm.Realm
import org.greenrobot.eventbus.EventBus
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy, internal class LocalEchoRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider,
private val roomSummaryUpdater: RoomSummaryUpdater, private val roomSummaryUpdater: RoomSummaryUpdater,
private val eventBus: EventBus, private val eventBus: EventBus,
private val timelineEventMapper: TimelineEventMapper) { private val timelineEventMapper: TimelineEventMapper) {
@ -59,7 +60,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
if (event.eventId == null) { if (event.eventId == null) {
throw IllegalStateException("You should have set an eventId for your event") 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 eventEntity = event.toEntity(roomId, SendState.UNSENT, System.currentTimeMillis())
val roomMemberHelper = RoomMemberHelper(realm, roomId) val roomMemberHelper = RoomMemberHelper(realm, roomId)
val myUser = roomMemberHelper.getLastRoomMember(senderId) val myUser = roomMemberHelper.getLastRoomMember(senderId)
@ -150,7 +151,7 @@ internal class LocalEchoRepository @Inject constructor(@SessionDatabase private
} }
fun getAllEventsWithStates(roomId: String, states : List<SendState>): List<TimelineEvent> { fun getAllEventsWithStates(roomId: String, states : List<SendState>): List<TimelineEvent> {
return Realm.getInstance(monarchy.realmConfiguration).use { realm -> return realmSessionProvider.withRealm { realm ->
TimelineEventEntity TimelineEventEntity
.findAllInRoomWithSendStates(realm, roomId, states) .findAllInRoomWithSendStates(realm, roomId, states)
.sortedByDescending { it.displayIndex } .sortedByDescending { it.displayIndex }

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

@ -172,3 +172,6 @@ import org.matrix.androidsdk.crypto.data===2
### Use `Context#getSystemService` extension function provided by `core-ktx` ### Use `Context#getSystemService` extension function provided by `core-ktx`
getSystemService\(Context getSystemService\(Context
### Use DefaultSharedPreferences.getInstance() instead for better performance
PreferenceManager\.getDefaultSharedPreferences==2

View File

@ -190,6 +190,8 @@ android {
resValue "bool", "debug_mode", "true" resValue "bool", "debug_mode", "true"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false" 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 signingConfig signingConfigs.debug
} }
@ -199,6 +201,7 @@ android {
resValue "bool", "debug_mode", "false" resValue "bool", "debug_mode", "false"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false" buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
postprocessing { postprocessing {
removeUnusedCode true removeUnusedCode true

View File

@ -22,7 +22,7 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.core.utils.lsFiles import im.vector.app.core.utils.lsFiles
import timber.log.Timber import timber.log.Timber
@ -44,7 +44,7 @@ class DebugReceiver : BroadcastReceiver() {
} }
private fun dumpPreferences(context: Context) { private fun dumpPreferences(context: Context) {
logPrefs("DefaultSharedPreferences", PreferenceManager.getDefaultSharedPreferences(context)) logPrefs("DefaultSharedPreferences", DefaultSharedPreferences.getInstance(context))
} }
private fun logPrefs(name: String, sharedPreferences: SharedPreferences?) { private fun logPrefs(name: String, sharedPreferences: SharedPreferences?) {
@ -58,7 +58,7 @@ class DebugReceiver : BroadcastReceiver() {
} }
private fun alterScalarToken(context: Context) { 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") // putString("SCALAR_TOKEN_PREFERENCE_KEY" + Matrix.getInstance(context).defaultSession.myUserId, "bad_token")
} }
} }

View File

@ -19,7 +19,6 @@ package im.vector.app.push.fcm
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import androidx.preference.PreferenceManager
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit import androidx.core.content.edit
import com.google.android.gms.common.ConnectionResult 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 com.google.firebase.iid.FirebaseInstanceId
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder 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.core.pushers.PushersManager
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber import timber.log.Timber
@ -46,7 +46,7 @@ object FcmHelper {
* @return the FCM token or null if not received from FCM * @return the FCM token or null if not received from FCM
*/ */
fun getFcmToken(context: Context): String? { 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, fun storeFcmToken(context: Context,
token: String?) { token: String?) {
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putString(PREFS_KEY_FCM_TOKEN, token) putString(PREFS_KEY_FCM_TOKEN, token)
} }
} }

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,7 @@ class VectorApplication :
private var fontThreadHandler: Handler? = null private var fontThreadHandler: Handler? = null
override fun onCreate() { override fun onCreate() {
enableStrictModeIfNeeded()
super.onCreate() super.onCreate()
appContext = this appContext = this
vectorComponent = DaggerVectorComponent.factory().create(this) vectorComponent = DaggerVectorComponent.factory().create(this)
@ -163,6 +165,15 @@ class VectorApplication :
// initKnownEmojiHashSet(appContext) // 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 providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)
override fun getWorkManagerConfiguration(): WorkConfiguration { override fun getWorkManagerConfiguration(): WorkConfiguration {

View File

@ -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.applicationContext).also { INSTANCE = it }
}
}

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

@ -23,11 +23,11 @@ import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.preference.PreferenceManager
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import butterknife.OnClick import butterknife.OnClick
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
import timber.log.Timber import timber.log.Timber
/** /**
@ -57,7 +57,7 @@ class KeysBackupBanner @JvmOverloads constructor(
init { init {
setupView() setupView()
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false) putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)
putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, "") putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, "")
} }
@ -105,17 +105,17 @@ class KeysBackupBanner @JvmOverloads constructor(
state.let { state.let {
when (it) { when (it) {
is State.Setup -> { is State.Setup -> {
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, true) putBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, true)
} }
} }
is State.Recover -> { is State.Recover -> {
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, it.version) putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, it.version)
} }
} }
is State.Update -> { is State.Update -> {
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, it.version) putString(BANNER_UPDATE_DO_NOT_SHOW_FOR_VERSION, it.version)
} }
} }
@ -150,7 +150,7 @@ class KeysBackupBanner @JvmOverloads constructor(
private fun renderSetup(nbOfKeys: Int) { private fun renderSetup(nbOfKeys: Int) {
if (nbOfKeys == 0 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 // Do not display the setup banner if there is no keys to backup, or if the user has already closed it
isVisible = false isVisible = false
} else { } else {
@ -164,7 +164,7 @@ class KeysBackupBanner @JvmOverloads constructor(
} }
private fun renderRecover(version: String) { 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 isVisible = false
} else { } else {
isVisible = true isVisible = true
@ -177,7 +177,7 @@ class KeysBackupBanner @JvmOverloads constructor(
} }
private fun renderUpdate(version: String) { 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 isVisible = false
} else { } else {
isVisible = true 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 * 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) { fun onRecoverDoneForVersion(context: Context, version: String) {
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, version) putString(BANNER_RECOVER_DO_NOT_SHOW_FOR_VERSION, version)
} }
} }

View File

@ -21,7 +21,7 @@ import android.media.Ringtone
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
/** /**
@ -40,7 +40,7 @@ import im.vector.app.features.settings.VectorPreferences
* @see Ringtone * @see Ringtone
*/ */
fun getCallRingtoneUri(context: Context): Uri? { 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) .getString(VectorPreferences.SETTINGS_CALL_RINGTONE_URI_PREFERENCE_KEY, null)
callRingtone?.let { callRingtone?.let {
@ -94,7 +94,7 @@ fun getCallRingtoneName(context: Context): String? {
* @see Ringtone * @see Ringtone
*/ */
fun setCallRingtoneUri(context: Context, ringtoneUri: Uri) { fun setCallRingtoneUri(context: Context, ringtoneUri: Uri) {
PreferenceManager.getDefaultSharedPreferences(context) DefaultSharedPreferences.getInstance(context)
.edit { .edit {
putString(VectorPreferences.SETTINGS_CALL_RINGTONE_URI_PREFERENCE_KEY, ringtoneUri.toString()) 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 * Set using Riot default ringtone
*/ */
fun useRiotDefaultRingtone(context: Context): Boolean { 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 * Ask if default Riot ringtone has to be used
*/ */
fun setUseRiotDefaultRingtone(context: Context, useRiotDefault: Boolean) { fun setUseRiotDefaultRingtone(context: Context, useRiotDefault: Boolean) {
PreferenceManager.getDefaultSharedPreferences(context) DefaultSharedPreferences.getInstance(context)
.edit { .edit {
putBoolean(VectorPreferences.SETTINGS_CALL_RINGTONE_USE_RIOT_PREFERENCE_KEY, useRiotDefault) putBoolean(VectorPreferences.SETTINGS_CALL_RINGTONE_USE_RIOT_PREFERENCE_KEY, useRiotDefault)
} }

View File

@ -20,8 +20,8 @@ import android.app.Activity
import android.content.Context import android.content.Context
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.features.settings.VectorSettingsUrls 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" private const val SHARED_PREF_KEY = "LAST_DISCLAIMER_VERSION_VALUE"
fun showDisclaimerDialog(activity: Activity) { fun showDisclaimerDialog(activity: Activity) {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(activity) val sharedPrefs = DefaultSharedPreferences.getInstance(activity)
if (sharedPrefs.getInt(SHARED_PREF_KEY, 0) < CURRENT_DISCLAIMER_VALUE) { if (sharedPrefs.getInt(SHARED_PREF_KEY, 0) < CURRENT_DISCLAIMER_VALUE) {
sharedPrefs.edit { sharedPrefs.edit {
@ -52,7 +52,7 @@ fun showDisclaimerDialog(activity: Activity) {
} }
fun doNotShowDisclaimerDialog(context: Context) { fun doNotShowDisclaimerDialog(context: Context) {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) val sharedPrefs = DefaultSharedPreferences.getInstance(context)
sharedPrefs.edit { sharedPrefs.edit {
putInt(SHARED_PREF_KEY, CURRENT_DISCLAIMER_VALUE) putInt(SHARED_PREF_KEY, CURRENT_DISCLAIMER_VALUE)

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

@ -18,8 +18,8 @@ package im.vector.app.features.homeserver
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
/** /**
* Object to store and retrieve home and identity server urls * 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 * Save home and identity sever urls received by the Referrer receiver
*/ */
fun setDefaultUrlsFromReferrer(context: Context, homeServerUrl: String, identityServerUrl: String) { fun setDefaultUrlsFromReferrer(context: Context, homeServerUrl: String, identityServerUrl: String) {
PreferenceManager.getDefaultSharedPreferences(context) DefaultSharedPreferences.getInstance(context)
.edit { .edit {
if (homeServerUrl.isNotEmpty()) { if (homeServerUrl.isNotEmpty()) {
putString(DEFAULT_REFERRER_HOME_SERVER_URL_PREF, homeServerUrl) 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 * Save home and identity sever urls entered by the user. May be custom or default value
*/ */
fun saveServerUrls(context: Context, homeServerUrl: String, identityServerUrl: String) { fun saveServerUrls(context: Context, homeServerUrl: String, identityServerUrl: String) {
PreferenceManager.getDefaultSharedPreferences(context) DefaultSharedPreferences.getInstance(context)
.edit { .edit {
putString(HOME_SERVER_URL_PREF, homeServerUrl) putString(HOME_SERVER_URL_PREF, homeServerUrl)
putString(IDENTITY_SERVER_URL_PREF, identityServerUrl) 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 * Return last used home server url, or the default one from referrer or the default one from resources
*/ */
fun getLastHomeServerUrl(context: Context): String { fun getLastHomeServerUrl(context: Context): String {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = DefaultSharedPreferences.getInstance(context)
return prefs.getString(HOME_SERVER_URL_PREF, return prefs.getString(HOME_SERVER_URL_PREF,
prefs.getString(DEFAULT_REFERRER_HOME_SERVER_URL_PREF, prefs.getString(DEFAULT_REFERRER_HOME_SERVER_URL_PREF,

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

@ -19,7 +19,7 @@ package im.vector.app.features.rageshake
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import androidx.core.content.edit 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.core.resources.VersionCodeProvider
import im.vector.app.features.version.VersionProvider import im.vector.app.features.version.VersionProvider
import org.matrix.android.sdk.api.Matrix 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) { override fun uncaughtException(thread: Thread, throwable: Throwable) {
Timber.v("Uncaught exception: $throwable") Timber.v("Uncaught exception: $throwable")
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
putBoolean(PREFS_CRASH_KEY, true) putBoolean(PREFS_CRASH_KEY, true)
} }
val b = StringBuilder() val b = StringBuilder()
@ -115,7 +115,7 @@ class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter
* @return true if the application crashed * @return true if the application crashed
*/ */
fun didAppCrash(context: Context): Boolean { fun didAppCrash(context: Context): Boolean {
return PreferenceManager.getDefaultSharedPreferences(context) return DefaultSharedPreferences.getInstance(context)
.getBoolean(PREFS_CRASH_KEY, false) .getBoolean(PREFS_CRASH_KEY, false)
} }
@ -123,7 +123,7 @@ class VectorUncaughtExceptionHandler @Inject constructor(private val bugReporter
* Clear the crash status * Clear the crash status
*/ */
fun clearAppCrashStatus(context: Context) { fun clearAppCrashStatus(context: Context) {
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
remove(PREFS_CRASH_KEY) remove(PREFS_CRASH_KEY)
} }
} }

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

@ -19,8 +19,8 @@ package im.vector.app.features.settings
import android.content.Context import android.content.Context
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
/** /**
* Object to manage the Font Scale choice of the user * Object to manage the Font Scale choice of the user
@ -56,7 +56,7 @@ object FontScale {
* @return the font scale value * @return the font scale value
*/ */
fun getFontScaleValue(context: Context): FontScaleValue { fun getFontScaleValue(context: Context): FontScaleValue {
val preferences = PreferenceManager.getDefaultSharedPreferences(context) val preferences = DefaultSharedPreferences.getInstance(context)
return if (APPLICATION_FONT_SCALE_KEY !in preferences) { return if (APPLICATION_FONT_SCALE_KEY !in preferences) {
val fontScale = context.resources.configuration.fontScale val fontScale = context.resources.configuration.fontScale
@ -81,7 +81,7 @@ object FontScale {
* @param fontScaleValue the font scale value to store * @param fontScaleValue the font scale value to store
*/ */
private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) { private fun saveFontScaleValue(context: Context, fontScaleValue: FontScaleValue) {
PreferenceManager.getDefaultSharedPreferences(context) DefaultSharedPreferences.getInstance(context)
.edit { putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue) } .edit { putString(APPLICATION_FONT_SCALE_KEY, fontScaleValue.preferenceValue) }
} }
} }

View File

@ -19,9 +19,9 @@ package im.vector.app.features.settings
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
@ -59,7 +59,7 @@ object VectorLocale {
*/ */
fun init(context: Context) { fun init(context: Context) {
this.context = context this.context = context
val preferences = PreferenceManager.getDefaultSharedPreferences(context) val preferences = DefaultSharedPreferences.getInstance(context)
if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) { if (preferences.contains(APPLICATION_LOCALE_LANGUAGE_KEY)) {
applicationLocale = Locale(preferences.getString(APPLICATION_LOCALE_LANGUAGE_KEY, "")!!, applicationLocale = Locale(preferences.getString(APPLICATION_LOCALE_LANGUAGE_KEY, "")!!,
@ -85,7 +85,7 @@ object VectorLocale {
fun saveApplicationLocale(locale: Locale) { fun saveApplicationLocale(locale: Locale) {
applicationLocale = locale applicationLocale = locale
PreferenceManager.getDefaultSharedPreferences(context).edit { DefaultSharedPreferences.getInstance(context).edit {
val language = locale.language val language = locale.language
if (language.isEmpty()) { if (language.isEmpty()) {
remove(APPLICATION_LOCALE_LANGUAGE_KEY) remove(APPLICATION_LOCALE_LANGUAGE_KEY)

View File

@ -22,10 +22,10 @@ import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.provider.MediaStore import android.provider.MediaStore
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.squareup.seismic.ShakeDetector import com.squareup.seismic.ShakeDetector
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.features.homeserver.ServerUrlsRepository import im.vector.app.features.homeserver.ServerUrlsRepository
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import org.matrix.android.sdk.api.extensions.tryThis 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. * Clear the preferences.

View File

@ -25,9 +25,10 @@ import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.preference.PreferenceManager
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
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,15 @@ 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 = DefaultSharedPreferences.getInstance(context)
.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE
this.currentTheme.set(themeFromPref)
themeFromPref
} else {
currentTheme
}
} }
/** /**
@ -78,6 +88,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)