Enable strict mode and remove some stuff from the main thread

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

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database
import io.realm.Realm
import java.io.Closeable
class RealmInstanceWrapper(private val realm: Realm, private val closeRealmOnClose: Boolean) : Closeable {
override fun close() {
if (closeRealmOnClose) {
realm.close()
}
}
fun <R> withRealm(block: (Realm) -> R): R {
return use {
block(it.realm)
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database
import android.os.Looper
import androidx.annotation.MainThread
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import javax.inject.Inject
import kotlin.concurrent.getOrSet
/**
* This class keeps an instance of realm open in the main thread so you can grab it whenever you want to get a realm
* instance. This does check each time if you are on the main thread or not and returns the appropriate realm instance.
*/
@SessionScope
class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy)
: SessionLifecycleObserver {
private val realmThreadLocal = ThreadLocal<Realm>()
/**
* Allow you to execute a block with an opened realm. It automatically closes it if necessary (ie. when not in main thread)
*/
fun <R> withRealm(block: (Realm) -> R): R {
return getRealmWrapper().withRealm(block)
}
@MainThread
override fun onStart() {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
}
@MainThread
override fun onStop() {
realmThreadLocal.get()?.close()
realmThreadLocal.remove()
}
private fun getRealmWrapper(): RealmInstanceWrapper {
val isOnMainThread = isOnMainThread()
val realm = if (isOnMainThread) {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
} else {
Realm.getInstance(monarchy.realmConfiguration)
}
return RealmInstanceWrapper(realm, closeRealmOnClose = !isOnMainThread)
}
private fun isOnMainThread() = Looper.myLooper() == Looper.getMainLooper()
}

View File

@ -18,26 +18,24 @@
package org.matrix.android.sdk.internal.database.mapper
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<ReadReceipt> {
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())
}
}

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.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

View File

@ -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)

View File

@ -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<String>, stateKey: QueryStringValue): List<Event> {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
return realmSessionProvider.withRealm { realm ->
buildStateEventQuery(realm, roomId, eventTypes, stateKey)
.findAll()
.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.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<String>)
@ -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()

View File

@ -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<Optional<TimelineEvent>> {
@ -98,7 +98,7 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
override fun getAttachmentMessages(): List<TimelineEvent> {
// TODO pretty bad query.. maybe we should denormalize clear type in base?
return doWithRealm(monarchy.realmConfiguration) { realm ->
return realmSessionProvider.withRealm { realm ->
realm.where<TimelineEventEntity>()
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)

View File

@ -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<UserEntity> 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<Optional<User>> {

View File

@ -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<String>): List<UserAccountDataEvent> {
return monarchy.fetchAllMappedSync(
{ accountDataEventsQuery(it, types) },
accountDataMapper::map
)
return realmSessionProvider.withRealm {
accountDataEventsQuery(it, types).findAll().map(accountDataMapper::map)
}
}
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.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(

View File

@ -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)

View File

@ -100,7 +100,7 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
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

View File

@ -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))
}
}
}
}

View File

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

View File

@ -36,9 +36,9 @@ import im.vector.app.core.glide.GlideRequest
import im.vector.app.core.ui.model.Size
import im.vector.app.core.utils.DimensionConverter
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.internal.crypto.attachments.ElementToDecrypt
import kotlinx.android.parcel.Parcelize
import org.matrix.android.sdk.api.extensions.tryThis
import timber.log.Timber
import java.io.File
@ -69,12 +69,10 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
val maxHeight: Int,
val width: 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
override val allowNonMxcUrls: Boolean = false
) : AttachmentData {
fun isLocalFile() = url.isLocalFile()
}
) : AttachmentData
enum class Mode {
FULL_SIZE,
@ -268,7 +266,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
private fun resolveUrl(data: Data) =
(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 {
val maxImageWidth = data.maxWidth

View File

@ -19,6 +19,9 @@ package im.vector.app.features.rageshake
import android.content.Context
import android.util.Log
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
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?) {
if (sFileHandler == null) return
if (skipLog(priority)) return
if (t != null) {
logToFile(t)
GlobalScope.launch(Dispatchers.IO) {
if (sFileHandler == null) return@launch
if (skipLog(priority)) return@launch
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 {
@ -174,7 +179,8 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
companion object {
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 var mIsTimeZoneSet = false
@ -201,7 +207,6 @@ class VectorFileLogger @Inject constructor(val context: Context, private val vec
if (null == sCacheDirectory) {
return
}
val b = StringBuilder()
b.append(Thread.currentThread().id)
b.append(" ")

View File

@ -22,6 +22,14 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
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.crypto.RoomEncryptionTrustLevel
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.rx
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
class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState,

View File

@ -28,6 +28,7 @@ import androidx.core.graphics.drawable.DrawableCompat
import androidx.preference.PreferenceManager
import im.vector.app.R
import timber.log.Timber
import java.util.concurrent.atomic.AtomicReference
/**
* Util class for managing themes.
@ -42,6 +43,8 @@ object ThemeUtils {
private const val THEME_BLACK_VALUE = "black"
private const val THEME_STATUS_VALUE = "status"
private var currentTheme = AtomicReference<String>(null)
private val mColorByAttr = HashMap<Int, Int>()
// 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)