Clean code

This commit is contained in:
Ganard 2020-02-03 16:14:36 +01:00
parent 88755a79b4
commit f454078c6b
32 changed files with 430 additions and 489 deletions

View File

@ -1,5 +1,6 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="160" />
<AndroidXmlCodeStyleSettings> <AndroidXmlCodeStyleSettings>
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" /> <option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings> </AndroidXmlCodeStyleSettings>

View File

@ -1,6 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<state> <state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state> </state>
</component> </component>

View File

@ -70,7 +70,6 @@ import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationService import im.vector.matrix.android.internal.crypto.verification.DefaultVerificationService
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.database.query.whereType import im.vector.matrix.android.internal.database.query.whereType
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.extensions.foldToCallback import im.vector.matrix.android.internal.extensions.foldToCallback

View File

@ -320,7 +320,6 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM
// al devices = // al devices =
val models = response.deviceKeys?.get(userId)?.mapValues { entry -> CryptoInfoMapper.map(entry.value) } val models = response.deviceKeys?.get(userId)?.mapValues { entry -> CryptoInfoMapper.map(entry.value) }
Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $models") Timber.v("## doKeyDownloadForUsers() : Got keys for $userId : $models")
if (!models.isNullOrEmpty()) { if (!models.isNullOrEmpty()) {
val workingCopy = models.toMutableMap() val workingCopy = models.toMutableMap()

View File

@ -36,7 +36,6 @@ import im.vector.matrix.android.internal.session.room.timeline.PaginationDirecti
import io.realm.Realm import io.realm.Realm
import io.realm.Sort import io.realm.Sort
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
import kotlinx.coroutines.coroutineScope
import timber.log.Timber import timber.log.Timber
internal fun ChunkEntity.deleteOnCascade() { internal fun ChunkEntity.deleteOnCascade() {
@ -110,7 +109,6 @@ internal fun ChunkEntity.addTimelineEvent(roomId: String,
eventEntity: EventEntity, eventEntity: EventEntity,
direction: PaginationDirection, direction: PaginationDirection,
roomMemberContentsByUser: HashMap<String, RoomMemberContent?>) { roomMemberContentsByUser: HashMap<String, RoomMemberContent?>) {
val eventId = eventEntity.eventId val eventId = eventEntity.eventId
if (timelineEvents.find(eventId) != null) { if (timelineEvents.find(eventId) != null) {
return return
@ -134,8 +132,7 @@ internal fun ChunkEntity.addTimelineEvent(roomId: String,
this.senderName = roomMemberContent?.displayName this.senderName = roomMemberContent?.displayName
if (roomMemberContent?.displayName != null) { if (roomMemberContent?.displayName != null) {
val isHistoricalUnique = roomMemberContentsByUser.values.find { val isHistoricalUnique = roomMemberContentsByUser.values.find {
roomMemberContent != it && it != roomMemberContent && it?.displayName == roomMemberContent.displayName
it?.displayName == roomMemberContent.displayName
} == null } == null
isUniqueDisplayName = if (isLastForward) { isUniqueDisplayName = if (isLastForward) {
val isLiveUnique = RoomMemberSummaryEntity val isLiveUnique = RoomMemberSummaryEntity

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.database.model
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Index import io.realm.annotations.Index
import io.realm.annotations.PrimaryKey
internal open class CurrentStateEventEntity(var eventId: String = "", internal open class CurrentStateEventEntity(var eventId: String = "",
var root: EventEntity? = null, var root: EventEntity? = null,

View File

@ -21,9 +21,7 @@ import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.Index import io.realm.annotations.Index
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
internal open class EventEntity(@PrimaryKey var eventId: String = "", internal open class EventEntity(@PrimaryKey var eventId: String = "",

View File

@ -20,7 +20,6 @@ import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.annotations.Index import io.realm.annotations.Index
import io.realm.annotations.LinkingObjects import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
internal open class TimelineEventEntity(var localId: Long = 0, internal open class TimelineEventEntity(var localId: Long = 0,
@Index var eventId: String = "", @Index var eventId: String = "",

View File

@ -29,7 +29,8 @@ internal fun CurrentStateEventEntity.Companion.where(realm: Realm, roomId: Strin
.equalTo(CurrentStateEventEntityFields.TYPE, type) .equalTo(CurrentStateEventEntityFields.TYPE, type)
} }
internal fun CurrentStateEventEntity.Companion.whereStateKey(realm: Realm, roomId: String, type: String, stateKey: String): RealmQuery<CurrentStateEventEntity> { internal fun CurrentStateEventEntity.Companion.whereStateKey(realm: Realm, roomId: String, type: String, stateKey: String)
: RealmQuery<CurrentStateEventEntity> {
return where(realm = realm, roomId = roomId, type = type) return where(realm = realm, roomId = roomId, type = type)
.equalTo(CurrentStateEventEntityFields.STATE_KEY, stateKey) .equalTo(CurrentStateEventEntityFields.STATE_KEY, stateKey)
} }
@ -49,5 +50,3 @@ private fun create(realm: Realm, roomId: String, stateKey: String, type: String)
this.stateKey = stateKey this.stateKey = stateKey
} }
} }

View File

@ -35,7 +35,8 @@ internal fun TimelineEventEntity.Companion.where(realm: Realm, roomId: String, e
internal fun TimelineEventEntity.Companion.whereRoomId(realm: Realm, internal fun TimelineEventEntity.Companion.whereRoomId(realm: Realm,
roomId: String): RealmQuery<TimelineEventEntity> { roomId: String): RealmQuery<TimelineEventEntity> {
return realm.where<TimelineEventEntity>().equalTo(TimelineEventEntityFields.ROOM_ID, roomId) return realm.where<TimelineEventEntity>()
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
} }
internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm: Realm, senderMembershipEventId: String): List<TimelineEventEntity> { internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm: Realm, senderMembershipEventId: String): List<TimelineEventEntity> {

View File

@ -26,7 +26,6 @@ import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.internal.SessionManager import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.auth.AuthModule import im.vector.matrix.android.internal.auth.AuthModule
import im.vector.matrix.android.internal.auth.SessionParamsStore import im.vector.matrix.android.internal.auth.SessionParamsStore
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers

View File

@ -21,6 +21,7 @@ import android.content.Context
import android.content.IntentFilter import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Network import android.net.Network
import android.os.Build
import javax.inject.Inject import javax.inject.Inject
internal interface NetworkCallbackStrategy { internal interface NetworkCallbackStrategy {
@ -47,7 +48,7 @@ internal class FallbackNetworkCallbackStrategy @Inject constructor(private val c
} }
} }
@TargetApi(android.os.Build.VERSION_CODES.N) @TargetApi(Build.VERSION_CODES.N)
internal class PreferredNetworkCallbackStrategy @Inject constructor(context: Context) : NetworkCallbackStrategy { internal class PreferredNetworkCallbackStrategy @Inject constructor(context: Context) : NetworkCallbackStrategy {
private var hasChangedCallback: (() -> Unit)? = null private var hasChangedCallback: (() -> Unit)? = null

View File

@ -42,10 +42,10 @@ interface NetworkConnectivityChecker {
} }
@SessionScope @SessionScope
internal class DefaultNetworkConnectivityChecker @Inject constructor(private val coroutineDispatchers: MatrixCoroutineDispatchers, internal class DefaultNetworkConnectivityChecker @Inject constructor(private val homeServerPinger: HomeServerPinger,
private val homeServerPinger: HomeServerPinger,
private val backgroundDetectionObserver: BackgroundDetectionObserver, private val backgroundDetectionObserver: BackgroundDetectionObserver,
private val networkCallbackStrategy: NetworkCallbackStrategy) : NetworkConnectivityChecker { private val networkCallbackStrategy: NetworkCallbackStrategy)
: NetworkConnectivityChecker {
private val hasInternetAccess = AtomicBoolean(true) private val hasInternetAccess = AtomicBoolean(true)
private val listeners = Collections.synchronizedSet(LinkedHashSet<NetworkConnectivityChecker.Listener>()) private val listeners = Collections.synchronizedSet(LinkedHashSet<NetworkConnectivityChecker.Listener>())

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session package im.vector.matrix.android.internal.session
import android.content.Context import android.content.Context
import android.os.Build
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.Binds import dagger.Binds
import dagger.Lazy import dagger.Lazy
@ -194,7 +195,7 @@ internal abstract class SessionModule {
fun providesNetworkCallbackStrategy(fallbackNetworkCallbackStrategy: Provider<FallbackNetworkCallbackStrategy>, fun providesNetworkCallbackStrategy(fallbackNetworkCallbackStrategy: Provider<FallbackNetworkCallbackStrategy>,
preferredNetworkCallbackStrategy: Provider<PreferredNetworkCallbackStrategy> preferredNetworkCallbackStrategy: Provider<PreferredNetworkCallbackStrategy>
): NetworkCallbackStrategy { ): NetworkCallbackStrategy {
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
preferredNetworkCallbackStrategy.get() preferredNetworkCallbackStrategy.get()
} else { } else {
fallbackNetworkCallbackStrategy.get() fallbackNetworkCallbackStrategy.get()
@ -241,4 +242,3 @@ internal abstract class SessionModule {
@Binds @Binds
abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
} }

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.homeserver
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject

View File

@ -24,7 +24,6 @@ import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomTopicContent import im.vector.matrix.android.api.session.room.model.RoomTopicContent
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity import im.vector.matrix.android.internal.database.model.CurrentStateEventEntity
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
@ -36,7 +35,6 @@ import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.database.query.getOrNull import im.vector.matrix.android.internal.database.query.getOrNull
import im.vector.matrix.android.internal.database.query.isEventRead import im.vector.matrix.android.internal.database.query.isEventRead
import im.vector.matrix.android.internal.database.query.latestEvent import im.vector.matrix.android.internal.database.query.latestEvent
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.database.query.whereType import im.vector.matrix.android.internal.database.query.whereType
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver import im.vector.matrix.android.internal.session.room.membership.RoomDisplayNameResolver

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.session.room.draft
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
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 im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.room.send.DraftService import im.vector.matrix.android.api.session.room.send.DraftService
import im.vector.matrix.android.api.session.room.send.UserDraft import im.vector.matrix.android.api.session.room.send.UserDraft

View File

@ -122,5 +122,4 @@ internal class DefaultReadService @AssistedInject constructor(
private fun ReadService.MarkAsReadParams.forceReadReceipt(): Boolean { private fun ReadService.MarkAsReadParams.forceReadReceipt(): Boolean {
return this == ReadService.MarkAsReadParams.READ_RECEIPT || this == ReadService.MarkAsReadParams.BOTH return this == ReadService.MarkAsReadParams.READ_RECEIPT || this == ReadService.MarkAsReadParams.BOTH
} }
} }

View File

@ -25,7 +25,6 @@ import im.vector.matrix.android.internal.database.query.isReadMarkerMoreRecent
import im.vector.matrix.android.internal.database.query.latestEvent import im.vector.matrix.android.internal.database.query.latestEvent
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.RoomAPI
import im.vector.matrix.android.internal.session.sync.ReadReceiptHandler import im.vector.matrix.android.internal.session.sync.ReadReceiptHandler

View File

@ -16,12 +16,10 @@
package im.vector.matrix.android.internal.session.room.send package im.vector.matrix.android.internal.session.room.send
import android.content.Context
import androidx.work.BackoffPolicy import androidx.work.BackoffPolicy
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.Operation import androidx.work.Operation
import androidx.work.WorkManager
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

View File

@ -48,7 +48,6 @@ import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultC
import im.vector.matrix.android.api.session.room.model.relation.ReplyToContent import im.vector.matrix.android.api.session.room.model.relation.ReplyToContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
import im.vector.matrix.android.internal.di.UserId import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.session.content.ThumbnailExtractor import im.vector.matrix.android.internal.session.content.ThumbnailExtractor
import im.vector.matrix.android.internal.session.room.send.pills.TextPillsUtils import im.vector.matrix.android.internal.session.room.send.pills.TextPillsUtils
@ -57,7 +56,6 @@ import im.vector.matrix.android.internal.util.StringProvider
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.commonmark.parser.Parser import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer import org.commonmark.renderer.html.HtmlRenderer
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
/** /**

View File

@ -27,7 +27,6 @@ import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
import im.vector.matrix.android.api.session.room.timeline.hasBeenEdited
import im.vector.matrix.android.api.util.CancelableBag import im.vector.matrix.android.api.util.CancelableBag
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
@ -665,7 +664,6 @@ internal class DefaultTimeline(
val params = GetContextOfEventTask.Params(roomId, eventId) val params = GetContextOfEventTask.Params(roomId, eventId)
cancelableBag += contextOfEventTask.configureWith(params) { cancelableBag += contextOfEventTask.configureWith(params) {
callback = object : MatrixCallback<TokenChunkEventPersistor.Result> { callback = object : MatrixCallback<TokenChunkEventPersistor.Result> {
override fun onSuccess(data: TokenChunkEventPersistor.Result) { override fun onSuccess(data: TokenChunkEventPersistor.Result) {
postSnapshot() postSnapshot()
} }

View File

@ -28,7 +28,6 @@ import im.vector.matrix.android.internal.database.helper.deleteOnCascade
import im.vector.matrix.android.internal.database.helper.merge import im.vector.matrix.android.internal.database.helper.merge
import im.vector.matrix.android.internal.database.mapper.toEntity import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.CurrentStateEventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity import im.vector.matrix.android.internal.database.model.TimelineEventEntity
@ -168,13 +167,19 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
private fun handleReachEnd(realm: Realm, roomId: String, direction: PaginationDirection, currentChunk: ChunkEntity) { private fun handleReachEnd(realm: Realm, roomId: String, direction: PaginationDirection, currentChunk: ChunkEntity) {
Timber.v("Reach end of $roomId") Timber.v("Reach end of $roomId")
roomId.isBlank()
if (direction == PaginationDirection.FORWARDS) { if (direction == PaginationDirection.FORWARDS) {
val currentLiveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) val currentLiveChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
if (currentChunk != currentLiveChunk) { if (currentChunk != currentLiveChunk) {
currentChunk.isLastForward = true currentChunk.isLastForward = true
currentLiveChunk?.deleteOnCascade() currentLiveChunk?.deleteOnCascade()
RoomSummaryEntity.where(realm, roomId).findFirst()?.apply { RoomSummaryEntity.where(realm, roomId).findFirst()?.apply {
latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = RoomSummaryUpdater.PREVIEWABLE_TYPES) latestPreviewableEvent = TimelineEventEntity.latestEvent(
realm,
roomId,
includesSending = true,
filterTypes = RoomSummaryUpdater.PREVIEWABLE_TYPES
)
} }
} }
} else { } else {
@ -237,7 +242,12 @@ internal class TokenChunkEventPersistor @Inject constructor(private val monarchy
} }
if (shouldUpdateSummary) { if (shouldUpdateSummary) {
val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId) val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = RoomSummaryUpdater.PREVIEWABLE_TYPES) val latestPreviewableEvent = TimelineEventEntity.latestEvent(
realm,
roomId,
includesSending = true,
filterTypes = RoomSummaryUpdater.PREVIEWABLE_TYPES
)
roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent roomSummaryEntity.latestPreviewableEvent = latestPreviewableEvent
} }
RoomEntity.where(realm, roomId).findFirst()?.addOrUpdate(currentChunk) RoomEntity.where(realm, roomId).findFirst()?.addOrUpdate(currentChunk)

View File

@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.session.sync.job
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.os.IBinder import android.os.IBinder
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.failure.isTokenError import im.vector.matrix.android.api.failure.isTokenError
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session

View File

@ -158,4 +158,5 @@ Formatter\.formatFileSize===1
Formatter\.formatShortFileSize===1 Formatter\.formatShortFileSize===1
### Use kotlin stdlib to test or compare strings ### Use kotlin stdlib to test or compare strings
android\.text\.TextUtils # DISABLED
# android\.text\.TextUtils

View File

@ -1,445 +0,0 @@
/*
* Copyright 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.riotx.core.platform;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.text.Layout;
import android.text.Layout.Alignment;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatTextView;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* A {@link android.widget.TextView} that ellipsizes more intelligently.
* This class supports ellipsizing multiline text through setting {@code android:ellipsize}
* and {@code android:maxLines}.
* <p/>
* Note: {@link android.text.TextUtils.TruncateAt#MARQUEE} ellipsizing type is not supported.
* This as to be used to get rid of the StaticLayout issue with maxLines and ellipsize causing some performance issues.
*/
public class EllipsizingTextView extends AppCompatTextView {
public static final int ELLIPSIZE_ALPHA = 0x88;
private SpannableString ELLIPSIS = new SpannableString("\u2026");
private static final Pattern DEFAULT_END_PUNCTUATION
= Pattern.compile("[\\.!?,;:\u2026]*$", Pattern.DOTALL);
private final List<EllipsizeListener> mEllipsizeListeners = new ArrayList<>();
private EllipsizeStrategy mEllipsizeStrategy;
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private CharSequence mFullText;
private int mMaxLines;
private float mLineSpacingMult = 1.0f;
private float mLineAddVertPad = 0.0f;
/**
* The end punctuation which will be removed when appending {@link #ELLIPSIS}.
*/
private Pattern mEndPunctPattern;
public EllipsizingTextView(Context context) {
this(context, null);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
new int[]{android.R.attr.maxLines, android.R.attr.ellipsize}, defStyle, 0);
setMaxLines(a.getInt(0, Integer.MAX_VALUE));
a.recycle();
setEndPunctuationPattern(DEFAULT_END_PUNCTUATION);
final int currentTextColor = getCurrentTextColor();
final int ellipsizeColor = Color.argb(ELLIPSIZE_ALPHA, Color.red(currentTextColor), Color.green(currentTextColor), Color.blue(currentTextColor));
ELLIPSIS.setSpan(new ForegroundColorSpan(ellipsizeColor), 0, ELLIPSIS.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
public void setEndPunctuationPattern(Pattern pattern) {
mEndPunctPattern = pattern;
}
@SuppressWarnings("unused")
public void addEllipsizeListener(@NonNull EllipsizeListener listener) {
mEllipsizeListeners.add(listener);
}
@SuppressWarnings("unused")
public void removeEllipsizeListener(@NonNull EllipsizeListener listener) {
mEllipsizeListeners.remove(listener);
}
@SuppressWarnings("unused")
public boolean isEllipsized() {
return isEllipsized;
}
/**
* @return The maximum number of lines displayed in this {@link android.widget.TextView}.
*/
public int getMaxLines() {
return mMaxLines;
}
@Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
mMaxLines = maxLines;
isStale = true;
}
/**
* Determines if the last fully visible line is being ellipsized.
*
* @return {@code true} if the last fully visible line is being ellipsized;
* otherwise, returns {@code false}.
*/
public boolean ellipsizingLastFullyVisibleLine() {
return mMaxLines == Integer.MAX_VALUE;
}
@Override
public void setLineSpacing(float add, float mult) {
mLineAddVertPad = add;
mLineSpacingMult = mult;
super.setLineSpacing(add, mult);
}
@Override
public void setText(CharSequence text, BufferType type) {
if (!programmaticChange) {
mFullText = text instanceof Spanned ? (Spanned) text : text;
isStale = true;
}
super.setText(text, type);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (ellipsizingLastFullyVisibleLine()) {
isStale = true;
}
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
super.setPadding(left, top, right, bottom);
if (ellipsizingLastFullyVisibleLine()) {
isStale = true;
}
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
if (isStale) {
resetText();
}
super.onDraw(canvas);
}
/**
* Sets the ellipsized text if appropriate.
*/
private void resetText() {
int maxLines = getMaxLines();
CharSequence workingText = mFullText;
boolean ellipsized = false;
if (maxLines != -1) {
if (mEllipsizeStrategy == null) setEllipsize(null);
workingText = mEllipsizeStrategy.processText(mFullText);
ellipsized = !mEllipsizeStrategy.isInLayout(mFullText);
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : mEllipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
/**
* Causes words in the text that are longer than the view is wide to be ellipsized
* instead of broken in the middle. Use {@code null} to turn off ellipsizing.
* <p/>
* Note: Method does nothing for {@link android.text.TextUtils.TruncateAt#MARQUEE}
* ellipsizing type.
*
* @param where part of text to ellipsize
*/
@Override
public void setEllipsize(TruncateAt where) {
if (where == null) {
mEllipsizeStrategy = new EllipsizeNoneStrategy();
return;
}
switch (where) {
case END:
mEllipsizeStrategy = new EllipsizeEndStrategy();
break;
case START:
mEllipsizeStrategy = new EllipsizeStartStrategy();
break;
case MIDDLE:
mEllipsizeStrategy = new EllipsizeMiddleStrategy();
break;
case MARQUEE:
default:
mEllipsizeStrategy = new EllipsizeNoneStrategy();
break;
}
}
/**
* A listener that notifies when the ellipsize state has changed.
*/
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
/**
* A base class for an ellipsize strategy.
*/
private abstract class EllipsizeStrategy {
/**
* Returns ellipsized text if the text does not fit inside of the layout;
* otherwise, returns the full text.
*
* @param text text to process
* @return Ellipsized text if the text does not fit inside of the layout;
* otherwise, returns the full text.
*/
public CharSequence processText(CharSequence text) {
return !isInLayout(text) ? createEllipsizedText(text) : text;
}
/**
* Determines if the text fits inside of the layout.
*
* @param text text to fit
* @return {@code true} if the text fits inside of the layout;
* otherwise, returns {@code false}.
*/
public boolean isInLayout(CharSequence text) {
Layout layout = createWorkingLayout(text);
return layout.getLineCount() <= getLinesCount();
}
/**
* Creates a working layout with the given text.
*
* @param workingText text to create layout with
* @return {@link android.text.Layout} with the given text.
*/
protected Layout createWorkingLayout(CharSequence workingText) {
return new StaticLayout(workingText, getPaint(),
getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight(),
Alignment.ALIGN_NORMAL, mLineSpacingMult,
mLineAddVertPad, false /* includepad */);
}
/**
* Get how many lines of text we are allowed to display.
*/
protected int getLinesCount() {
if (ellipsizingLastFullyVisibleLine()) {
int fullyVisibleLinesCount = getFullyVisibleLinesCount();
return fullyVisibleLinesCount == -1 ? 1 : fullyVisibleLinesCount;
} else {
return mMaxLines;
}
}
/**
* Get how many lines of text we can display so their full height is visible.
*/
protected int getFullyVisibleLinesCount() {
Layout layout = createWorkingLayout("");
int height = getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
int lineHeight = layout.getLineBottom(0);
return height / lineHeight;
}
/**
* Creates ellipsized text from the given text.
*
* @param fullText text to ellipsize
* @return Ellipsized text
*/
protected abstract CharSequence createEllipsizedText(CharSequence fullText);
}
/**
* An {@link EllipsizingTextView.EllipsizeStrategy} that
* does not ellipsize text.
*/
private class EllipsizeNoneStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
return fullText;
}
}
/**
* An {@link EllipsizingTextView.EllipsizeStrategy} that
* ellipsizes text at the end.
*/
private class EllipsizeEndStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
CharSequence workingText = TextUtils.substring(fullText, 0, textLength - cutOffLength).trim();
while (!isInLayout(TextUtils.concat(stripEndPunctuation(workingText), ELLIPSIS))) {
int lastSpace = TextUtils.lastIndexOf(workingText, ' ');
if (lastSpace == -1) {
break;
}
workingText = TextUtils.substring(workingText, 0, lastSpace).trim();
}
workingText = TextUtils.concat(stripEndPunctuation(workingText), ELLIPSIS);
SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, 0, workingText.length(), null, dest, 0);
}
return dest;
}
/**
* Strips the end punctuation from a given text according to {@link #mEndPunctPattern}.
*
* @param workingText text to strip end punctuation from
* @return Text without end punctuation.
*/
public String stripEndPunctuation(CharSequence workingText) {
return mEndPunctPattern.matcher(workingText).replaceFirst("");
}
}
/**
* An {@link EllipsizingTextView.EllipsizeStrategy} that
* ellipsizes text at the start.
*/
private class EllipsizeStartStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
CharSequence workingText = TextUtils.substring(fullText, cutOffLength, textLength).trim();
while (!isInLayout(TextUtils.concat(ELLIPSIS, workingText))) {
int firstSpace = TextUtils.indexOf(workingText, ' ');
if (firstSpace == -1) {
break;
}
workingText = TextUtils.substring(workingText, firstSpace, workingText.length()).trim();
}
workingText = TextUtils.concat(ELLIPSIS, workingText);
SpannableStringBuilder dest = new SpannableStringBuilder(workingText);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, textLength - workingText.length(),
textLength, null, dest, 0);
}
return dest;
}
}
/**
* An {@link EllipsizingTextView.EllipsizeStrategy} that
* ellipsizes text in the middle.
*/
private class EllipsizeMiddleStrategy extends EllipsizeStrategy {
@Override
protected CharSequence createEllipsizedText(CharSequence fullText) {
Layout layout = createWorkingLayout(fullText);
int cutOffIndex = layout.getLineEnd(mMaxLines - 1);
int textLength = fullText.length();
int cutOffLength = textLength - cutOffIndex;
if (cutOffLength < ELLIPSIS.length()) cutOffLength = ELLIPSIS.length();
cutOffLength += cutOffIndex % 2; // Make it even.
String firstPart = TextUtils.substring(
fullText, 0, textLength / 2 - cutOffLength / 2).trim();
String secondPart = TextUtils.substring(
fullText, textLength / 2 + cutOffLength / 2, textLength).trim();
while (!isInLayout(TextUtils.concat(firstPart, ELLIPSIS, secondPart))) {
int lastSpaceFirstPart = firstPart.lastIndexOf(' ');
int firstSpaceSecondPart = secondPart.indexOf(' ');
if (lastSpaceFirstPart == -1 || firstSpaceSecondPart == -1) break;
firstPart = firstPart.substring(0, lastSpaceFirstPart).trim();
secondPart = secondPart.substring(firstSpaceSecondPart, secondPart.length()).trim();
}
SpannableStringBuilder firstDest = new SpannableStringBuilder(firstPart);
SpannableStringBuilder secondDest = new SpannableStringBuilder(secondPart);
if (fullText instanceof Spanned) {
TextUtils.copySpansFrom((Spanned) fullText, 0, firstPart.length(),
null, firstDest, 0);
TextUtils.copySpansFrom((Spanned) fullText, textLength - secondPart.length(),
textLength, null, secondDest, 0);
}
return TextUtils.concat(firstDest, ELLIPSIS, secondDest);
}
}
}

View File

@ -0,0 +1,398 @@
/*
* Copyright 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.riotx.core.platform
import android.R
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.text.Layout
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.StaticLayout
import android.text.TextUtils.*
import android.text.style.ForegroundColorSpan
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import java.util.ArrayList
import java.util.regex.Pattern
/**
* A [android.widget.TextView] that ellipsizes more intelligently.
* This class supports ellipsizing multiline text through setting `android:ellipsize`
* and `android:maxLines`.
*
*
* Note: [TruncateAt.MARQUEE] ellipsizing type is not supported.
* This as to be used to get rid of the StaticLayout issue with maxLines and ellipsize causing some performance issues.
*/
class EllipsizingTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = R.attr.textViewStyle)
: AppCompatTextView(context, attrs, defStyle) {
private val ELLIPSIS = SpannableString("\u2026")
private val ellipsizeListeners: MutableList<EllipsizeListener> = ArrayList()
private var ellipsizeStrategy: EllipsizeStrategy? = null
var isEllipsized = false
private set
private var isStale = false
private var programmaticChange = false
private var fullText: CharSequence? = null
private var maxLines = 0
private var lineSpacingMult = 1.0f
private var lineAddVertPad = 0.0f
/**
* The end punctuation which will be removed when appending [.ELLIPSIS].
*/
private var mEndPunctPattern: Pattern? = null
fun setEndPunctuationPattern(pattern: Pattern?) {
mEndPunctPattern = pattern
}
fun addEllipsizeListener(listener: EllipsizeListener) {
ellipsizeListeners.add(listener)
}
fun removeEllipsizeListener(listener: EllipsizeListener) {
ellipsizeListeners.remove(listener)
}
/**
* @return The maximum number of lines displayed in this [android.widget.TextView].
*/
override fun getMaxLines(): Int {
return maxLines
}
override fun setMaxLines(maxLines: Int) {
super.setMaxLines(maxLines)
this.maxLines = maxLines
isStale = true
}
/**
* Determines if the last fully visible line is being ellipsized.
*
* @return `true` if the last fully visible line is being ellipsized;
* otherwise, returns `false`.
*/
fun ellipsizingLastFullyVisibleLine(): Boolean {
return maxLines == Int.MAX_VALUE
}
override fun setLineSpacing(add: Float, mult: Float) {
lineAddVertPad = add
lineSpacingMult = mult
super.setLineSpacing(add, mult)
}
override fun setText(text: CharSequence, type: BufferType) {
if (!programmaticChange) {
fullText = if (text is Spanned) text else text
isStale = true
}
super.setText(text, type)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (ellipsizingLastFullyVisibleLine()) {
isStale = true
}
}
override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
super.setPadding(left, top, right, bottom)
if (ellipsizingLastFullyVisibleLine()) {
isStale = true
}
}
override fun onDraw(canvas: Canvas) {
if (isStale) {
resetText()
}
super.onDraw(canvas)
}
/**
* Sets the ellipsized text if appropriate.
*/
private fun resetText() {
val maxLines = maxLines
var workingText = fullText
var ellipsized = false
if (maxLines != -1) {
if (ellipsizeStrategy == null) setEllipsize(null)
workingText = ellipsizeStrategy!!.processText(fullText)
ellipsized = !ellipsizeStrategy!!.isInLayout(fullText)
}
if (workingText != text) {
programmaticChange = true
text = try {
workingText
} finally {
programmaticChange = false
}
}
isStale = false
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized
for (listener in ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized)
}
}
}
/**
* Causes words in the text that are longer than the view is wide to be ellipsized
* instead of broken in the middle. Use `null` to turn off ellipsizing.
*
*
* Note: Method does nothing for [TruncateAt.MARQUEE]
* ellipsizing type.
*
* @param where part of text to ellipsize
*/
override fun setEllipsize(where: TruncateAt?) {
if (where == null) {
ellipsizeStrategy = EllipsizeNoneStrategy()
return
}
ellipsizeStrategy = when (where) {
TruncateAt.END -> EllipsizeEndStrategy()
TruncateAt.START -> EllipsizeStartStrategy()
TruncateAt.MIDDLE -> EllipsizeMiddleStrategy()
TruncateAt.MARQUEE -> EllipsizeNoneStrategy()
else -> EllipsizeNoneStrategy()
}
}
/**
* A listener that notifies when the ellipsize state has changed.
*/
interface EllipsizeListener {
fun ellipsizeStateChanged(ellipsized: Boolean)
}
/**
* A base class for an ellipsize strategy.
*/
private abstract inner class EllipsizeStrategy {
/**
* Returns ellipsized text if the text does not fit inside of the layout;
* otherwise, returns the full text.
*
* @param text text to process
* @return Ellipsized text if the text does not fit inside of the layout;
* otherwise, returns the full text.
*/
fun processText(text: CharSequence?): CharSequence? {
return if (!isInLayout(text)) createEllipsizedText(text) else text
}
/**
* Determines if the text fits inside of the layout.
*
* @param text text to fit
* @return `true` if the text fits inside of the layout;
* otherwise, returns `false`.
*/
fun isInLayout(text: CharSequence?): Boolean {
val layout = createWorkingLayout(text)
return layout.lineCount <= linesCount
}
/**
* Creates a working layout with the given text.
*
* @param workingText text to create layout with
* @return [android.text.Layout] with the given text.
*/
@Suppress("DEPRECATION")
protected fun createWorkingLayout(workingText: CharSequence?): Layout {
return StaticLayout(
workingText,
paint,
width - compoundPaddingLeft - compoundPaddingRight,
Layout.Alignment.ALIGN_NORMAL,
lineSpacingMult,
lineAddVertPad,
false
)
}
/**
* Get how many lines of text we are allowed to display.
*/
protected val linesCount: Int
get() = if (ellipsizingLastFullyVisibleLine()) {
val fullyVisibleLinesCount = fullyVisibleLinesCount
if (fullyVisibleLinesCount == -1) 1 else fullyVisibleLinesCount
} else {
maxLines
}
/**
* Get how many lines of text we can display so their full height is visible.
*/
protected val fullyVisibleLinesCount: Int
get() {
val layout = createWorkingLayout("")
val height = height - compoundPaddingTop - compoundPaddingBottom
val lineHeight = layout.getLineBottom(0)
return height / lineHeight
}
/**
* Creates ellipsized text from the given text.
*
* @param fullText text to ellipsize
* @return Ellipsized text
*/
protected abstract fun createEllipsizedText(fullText: CharSequence?): CharSequence?
}
/**
* An [EllipsizingTextView.EllipsizeStrategy] that
* does not ellipsize text.
*/
private inner class EllipsizeNoneStrategy : EllipsizeStrategy() {
override fun createEllipsizedText(fullText: CharSequence?): CharSequence? {
return fullText
}
}
/**
* An [EllipsizingTextView.EllipsizeStrategy] that
* ellipsizes text at the end.
*/
private inner class EllipsizeEndStrategy : EllipsizeStrategy() {
override fun createEllipsizedText(fullText: CharSequence?): CharSequence? {
val layout = createWorkingLayout(fullText)
val cutOffIndex = layout.getLineEnd(maxLines - 1)
val textLength = fullText!!.length
var cutOffLength = textLength - cutOffIndex
if (cutOffLength < ELLIPSIS.length) cutOffLength = ELLIPSIS.length
var workingText: CharSequence = substring(fullText, 0, textLength - cutOffLength).trim()
while (!isInLayout(concat(stripEndPunctuation(workingText), ELLIPSIS))) {
val lastSpace = lastIndexOf(workingText, ' ')
if (lastSpace == -1) {
break
}
workingText = substring(workingText, 0, lastSpace).trim()
}
workingText = concat(stripEndPunctuation(workingText), ELLIPSIS)
val dest = SpannableStringBuilder(workingText)
if (fullText is Spanned) {
copySpansFrom(fullText as Spanned?, 0, workingText.length, null, dest, 0)
}
return dest
}
/**
* Strips the end punctuation from a given text according to [.mEndPunctPattern].
*
* @param workingText text to strip end punctuation from
* @return Text without end punctuation.
*/
fun stripEndPunctuation(workingText: CharSequence?): String {
return mEndPunctPattern!!.matcher(workingText).replaceFirst("")
}
}
/**
* An [EllipsizingTextView.EllipsizeStrategy] that
* ellipsizes text at the start.
*/
private inner class EllipsizeStartStrategy : EllipsizeStrategy() {
override fun createEllipsizedText(fullText: CharSequence?): CharSequence? {
val layout = createWorkingLayout(fullText)
val cutOffIndex = layout.getLineEnd(maxLines - 1)
val textLength = fullText!!.length
var cutOffLength = textLength - cutOffIndex
if (cutOffLength < ELLIPSIS.length) cutOffLength = ELLIPSIS.length
var workingText: CharSequence = substring(fullText, cutOffLength, textLength).trim()
while (!isInLayout(concat(ELLIPSIS, workingText))) {
val firstSpace = indexOf(workingText, ' ')
if (firstSpace == -1) {
break
}
workingText = substring(workingText, firstSpace, workingText.length).trim()
}
workingText = concat(ELLIPSIS, workingText)
val dest = SpannableStringBuilder(workingText)
if (fullText is Spanned) {
copySpansFrom(fullText as Spanned?, textLength - workingText.length,
textLength, null, dest, 0)
}
return dest
}
}
/**
* An [EllipsizingTextView.EllipsizeStrategy] that
* ellipsizes text in the middle.
*/
private inner class EllipsizeMiddleStrategy : EllipsizeStrategy() {
override fun createEllipsizedText(fullText: CharSequence?): CharSequence? {
val layout = createWorkingLayout(fullText)
val cutOffIndex = layout.getLineEnd(maxLines - 1)
val textLength = fullText!!.length
var cutOffLength = textLength - cutOffIndex
if (cutOffLength < ELLIPSIS.length) cutOffLength = ELLIPSIS.length
cutOffLength += cutOffIndex % 2 // Make it even.
var firstPart = substring(
fullText, 0, textLength / 2 - cutOffLength / 2).trim()
var secondPart = substring(
fullText, textLength / 2 + cutOffLength / 2, textLength).trim()
while (!isInLayout(concat(firstPart, ELLIPSIS, secondPart))) {
val lastSpaceFirstPart = firstPart.lastIndexOf(' ')
val firstSpaceSecondPart = secondPart.indexOf(' ')
if (lastSpaceFirstPart == -1 || firstSpaceSecondPart == -1) break
firstPart = firstPart.substring(0, lastSpaceFirstPart).trim()
secondPart = secondPart.substring(firstSpaceSecondPart, secondPart.length).trim()
}
val firstDest = SpannableStringBuilder(firstPart)
val secondDest = SpannableStringBuilder(secondPart)
if (fullText is Spanned) {
copySpansFrom(fullText as Spanned?, 0, firstPart.length,
null, firstDest, 0)
copySpansFrom(fullText as Spanned?, textLength - secondPart.length,
textLength, null, secondDest, 0)
}
return concat(firstDest, ELLIPSIS, secondDest)
}
}
companion object {
const val ELLIPSIZE_ALPHA = 0x88
private val DEFAULT_END_PUNCTUATION = Pattern.compile("[.!?,;:\u2026]*$", Pattern.DOTALL)
}
init {
val a = context.obtainStyledAttributes(attrs, intArrayOf(R.attr.maxLines, R.attr.ellipsize), defStyle, 0)
maxLines = a.getInt(0, Int.MAX_VALUE)
a.recycle()
setEndPunctuationPattern(DEFAULT_END_PUNCTUATION)
val currentTextColor = currentTextColor
val ellipsizeColor = Color.argb(ELLIPSIZE_ALPHA, Color.red(currentTextColor), Color.green(currentTextColor), Color.blue(currentTextColor))
ELLIPSIS.setSpan(ForegroundColorSpan(ellipsizeColor), 0, ELLIPSIS.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}

View File

@ -33,7 +33,6 @@ import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import butterknife.ButterKnife import butterknife.ButterKnife
import butterknife.Unbinder import butterknife.Unbinder
import com.airbnb.mvrx.BaseMvRxFragment import com.airbnb.mvrx.BaseMvRxFragment