diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt new file mode 100644 index 0000000000000000000000000000000000000000..f78640484357bb09d9d19bccbad3dc2573c7676c --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselBacklogStorage.kt @@ -0,0 +1,35 @@ +package de.kuschku.quasseldroid_ng.persistence + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.Message +import de.kuschku.libquassel.session.BacklogStorage + +class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage { + override fun storeMessages(vararg messages: Message) { + for (message in messages) { + db.message().save(QuasselDatabase.DatabaseMessage( + messageId = message.messageId, + time = message.time, + type = message.type.value, + flag = message.flag.value, + bufferId = message.bufferInfo.bufferId, + sender = message.sender, + senderPrefixes = message.senderPrefixes, + content = message.content + )) + } + } + + override fun clearMessages(bufferId: BufferId, idRange: IntRange) { + db.message().clearMessages(bufferId, idRange) + } + + override fun clearMessages(bufferId: BufferId) { + db.message().clearMessages(bufferId) + } + + override fun clearMessages() { + db.message().clearMessages() + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt index 1001be61f5ed0dc39ae5351da7a1abd5d701f789..fa26a136ddaafa059818177b42cf50ce3a62c755 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/persistence/QuasselDatabase.kt @@ -49,19 +49,24 @@ abstract class QuasselDatabase : RoomDatabase() { @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1") fun findLastByBufferId(bufferId: Int): DatabaseMessage - @Update(onConflict = OnConflictStrategy.REPLACE) - fun save(entity: DatabaseMessage) + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun save(vararg entities: DatabaseMessage) @Query("UPDATE message SET bufferId = :bufferId1 WHERE bufferId = :bufferId2") fun merge(@IntRange(from = 0) bufferId1: Int, @IntRange(from = 0) bufferId2: Int) + @Query("DELETE FROM message") + fun clearMessages() + @Query("DELETE FROM message WHERE bufferId = :bufferId") - fun clearBuffer(@IntRange(from = 0) bufferId: Int) + fun clearMessages(@IntRange(from = 0) bufferId: Int) + + @Query("DELETE FROM message WHERE bufferId = :bufferId AND messageId >= :first AND messageId <= :last") + fun clearMessages(@IntRange(from = 0) bufferId: Int, first: Int, last: Int) } object Creator { private var database: QuasselDatabase? = null - private set // For Singleton instantiation private val LOCK = Any() @@ -71,7 +76,7 @@ abstract class QuasselDatabase : RoomDatabase() { synchronized(LOCK) { if (database == null) { database = Room.databaseBuilder(context.applicationContext, - QuasselDatabase::class.java, DATABASE_NAME) + QuasselDatabase::class.java, DATABASE_NAME) .build() } } @@ -84,3 +89,7 @@ abstract class QuasselDatabase : RoomDatabase() { const val DATABASE_NAME = "persistence-clientData" } } + +fun QuasselDatabase.MessageDao.clearMessages(@IntRange(from = 0) bufferId: Int, idRange: kotlin.ranges.IntRange) { + this.clearMessages(bufferId, idRange.first, idRange.last) +} \ No newline at end of file diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt index 50a88bf38a394f1d3fc8d61e39919a494980245b..76a57ee88a62d0d3d2025bc502fe0ba12529fc7f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt @@ -8,6 +8,7 @@ import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.session.* import de.kuschku.quasseldroid_ng.BuildConfig import de.kuschku.quasseldroid_ng.R +import de.kuschku.quasseldroid_ng.persistence.QuasselBacklogStorage import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase import de.kuschku.quasseldroid_ng.util.AndroidHandlerThread import de.kuschku.quasseldroid_ng.util.compatibility.AndroidHandlerService @@ -99,7 +100,7 @@ class QuasselService : LifecycleService() { handler.onCreate() super.onCreate() database = QuasselDatabase.Creator.init(application) - sessionManager = SessionManager(ISession.NULL) + sessionManager = SessionManager(ISession.NULL, QuasselBacklogStorage(database)) clientData = ClientData( identifier = "${resources.getString(R.string.app_name)} ${BuildConfig.VERSION_NAME}", buildDate = Instant.ofEpochSecond(BuildConfig.GIT_COMMIT_DATE), diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt index 066856f2c6eb5f7432b49e5a00015ba2cbd8fc80..f11f3f84cb818f32288f09c33678b43fb4593024 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/BufferListAdapter.kt @@ -12,6 +12,7 @@ import android.widget.TextView import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.quassel.BufferInfo +import de.kuschku.libquassel.util.hasFlag class BufferListAdapter( lifecycleOwner: LifecycleOwner, @@ -25,7 +26,7 @@ class BufferListAdapter( liveData.observe(lifecycleOwner, Observer { list: List<BufferInfo>? -> runInBackground { val old = data - val new = list ?: emptyList() + val new = list?.sortedBy(BufferInfo::networkId) ?: emptyList() val result = DiffUtil.calculateDiff( object : DiffUtil.Callback() { override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) @@ -65,7 +66,10 @@ class BufferListAdapter( } fun bind(info: BufferInfo) { - text.text = "${info.networkId}/${info.bufferName}" + text.text = when { + info.type.hasFlag(BufferInfo.Type.StatusBuffer) -> "Network ${info.networkId}" + else -> "${info.networkId}/${info.bufferName}" + } } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt index 89ec4a464b62c82cf79ca1ea5e1785116610c54d..8767ac897ba495ab1082e640b8dd83f48434030e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/ChatActivity.kt @@ -20,7 +20,6 @@ import de.kuschku.libquassel.session.SessionManager import de.kuschku.libquassel.session.SocketAddress import de.kuschku.libquassel.util.compatibility.LoggingHandler import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO -import de.kuschku.malheur.CrashHandler import de.kuschku.quasseldroid_ng.Keys import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.AccountDatabase @@ -112,17 +111,6 @@ class ChatActivity : ServiceBoundActivity() { true ) } - CrashHandler.handle( - IllegalArgumentException( - "WRONG!", - RuntimeException( - "WRONG!", - NullPointerException( - "Super wrong!" - ) - ) - ) - ) } } }) @@ -131,7 +119,7 @@ class ChatActivity : ServiceBoundActivity() { errorList.text = "" } - state.observeSticky(this, Observer { + state.observe(this, Observer { val status = it ?: ConnectionState.DISCONNECTED snackbar?.dismiss() diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b4444cb3fb513ea4c725386d2ef27e0f97c2c85a..1ae31fe938355206800ae6f14a6dd374848f20f1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://repo.gradle.org/gradle/dist-snapshots/gradle-kotlin-dsl-4.3-20171010191714+0000-all.zip +distributionUrl=https://services.gradle.org/distributions/gradle-4.3.1-all.zip diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt index 67c4138a210adbe77e15dd419a540bf496264b3b..cb8be217a079dc5015b2eee85d8de0469d099c46 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/Message.kt @@ -60,6 +60,4 @@ class Message( override fun toString(): String { return "Message(messageId=$messageId, time=$time, type=$type, flag=$flag, bufferInfo=$bufferInfo, sender='$sender', senderPrefixes='$senderPrefixes', content='$content')" } - - } diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt index db7dbdca90bc59f5da70fcccb13bb3911db99a6d..933c3d2c025c572ba2e78fe7cdd64488b999dfca 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt @@ -5,41 +5,35 @@ class QVariant<T>(val data: T?, val type: MetaType<T>) { constructor(data: T?, type: QType) : this(data, type.typeName) constructor(data: T?, type: String) : this(data, MetaType.Companion.get(type)) - private fun <U> coerce(): QVariant<U> { - return this as QVariant<U> - } - fun or(defValue: T): T { return data ?: defValue } - fun <U> _value(defValue: U): U { - return this.coerce<U>().data ?: defValue - } - - fun <U> _valueOr(f: () -> U): U { - return this.coerce<U>().data ?: f() - } - - fun <U> _valueOrThrow(): U = this._valueOrThrow(NullPointerException()) - - fun <U> _valueOrThrow(e: Throwable): U { - return this.coerce<U>().data ?: throw e - } - override fun toString(): String { return "QVariant(${type.name}, $data)" } } -fun <U> QVariant_?.value(): U? - = this?._value<U?>(null) +@PublishedApi +internal inline fun <reified U> QVariant_?.coerce(): QVariant<U>? { + return if (this?.data is U) { + this as QVariant<U> + } else { + null + } +} + +inline fun <reified U> QVariant_?.value(): U? + = this?.value<U?>(null) + +inline fun <reified U> QVariant_?.value(defValue: U): U + = this.coerce<U>()?.data ?: defValue -fun <U> QVariant_?.value(defValue: U): U - = this?._value(defValue) ?: defValue +inline fun <reified U> QVariant_?.valueOr(f: () -> U): U + = this.coerce<U>()?.data ?: f() -fun <U> QVariant_?.valueOr(f: () -> U): U - = this?._valueOr(f) ?: f() +inline fun <reified U> QVariant_?.valueOrThrow(e: Throwable = NullPointerException()): U + = this.coerce<U>()?.data ?: throw e -fun <U> QVariant_?.valueOrThrow(e: Throwable = NullPointerException()): U - = this?._valueOrThrow<U>(e) ?: throw e +inline fun <reified U> QVariant_?.valueOrThrow(e: () -> Throwable): U + = this.coerce<U>()?.data ?: throw e() diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt index e970e085642917f449cc44d8b284b7d942210498..598f70e393f3cfc9d7125840d8631d2cd357fd91 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt @@ -1,19 +1,31 @@ package de.kuschku.libquassel.quassel.syncables -import de.kuschku.libquassel.protocol.BufferId -import de.kuschku.libquassel.protocol.MsgId -import de.kuschku.libquassel.protocol.QVariantList +import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.quassel.syncables.interfaces.IBacklogManager +import de.kuschku.libquassel.session.BacklogStorage import de.kuschku.libquassel.session.SignalProxy +import de.kuschku.libquassel.util.compatibility.LoggingHandler +import de.kuschku.libquassel.util.compatibility.log -class BacklogManager constructor( - proxy: SignalProxy +class BacklogManager( + proxy: SignalProxy, + private val backlogStorage: BacklogStorage ) : SyncableObject(proxy, "BacklogManager"), IBacklogManager { override fun receiveBacklog(bufferId: BufferId, first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList) { + for (message: Message in messages.mapNotNull<QVariant_, Message>(QVariant_::value)) { + if (message.bufferInfo.bufferId != bufferId) { + // Check if it works here + log(LoggingHandler.LogLevel.ERROR, "message has inconsistent bufferid: $bufferId != ${message.bufferInfo.bufferId}") + } + backlogStorage.storeMessages(message) + } } override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList) { + for (message: Message in messages.mapNotNull<QVariant_, Message>(QVariant_::value)) { + backlogStorage.storeMessages(message) + } } } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt index fa9001ca0411a4f132e2f25b535ea31dbccc14f1..2cf9c21908e101794b7a88224dc1542cbf4bbadc 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt @@ -5,12 +5,15 @@ import de.kuschku.libquassel.protocol.primitive.serializer.StringSerializer import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.quassel.syncables.interfaces.IRpcHandler +import de.kuschku.libquassel.session.BacklogStorage import de.kuschku.libquassel.session.Session -import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.helpers.deserializeString import java.nio.ByteBuffer -class RpcHandler(override val session: Session) : IRpcHandler { +class RpcHandler( + override val session: Session, + private val backlogStorage: BacklogStorage +) : IRpcHandler { override fun displayStatusMsg(net: String, msg: String) { } @@ -42,7 +45,7 @@ class RpcHandler(override val session: Session) : IRpcHandler { } override fun displayMsg(message: Message) { - println(message) + backlogStorage.storeMessages(message) } override fun requestCreateIdentity(identity: QVariantMap, additional: QVariantMap) { diff --git a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt new file mode 100644 index 0000000000000000000000000000000000000000..52aaa577a89c2c703434ff200c0690d626a77d61 --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt @@ -0,0 +1,14 @@ +package de.kuschku.libquassel.session + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.Message + +interface BacklogStorage { + fun storeMessages(vararg messages: Message) + + fun clearMessages(bufferId: BufferId, idRange: IntRange) + + fun clearMessages(bufferId: BufferId) + + fun clearMessages() +} \ No newline at end of file diff --git a/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt index 968896ea4d34282cb56aeb688116a025ba955c22..f5776b8c9e241f6dd517ebe892c14994947340f3 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/CoreConnection.kt @@ -114,7 +114,6 @@ class CoreConnection( override fun close() { try { - channel?.close() interrupt() handlerService.quit() setState(ConnectionState.DISCONNECTED) diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt index 3546516f75c12447e42b50082ba23871708b223e..14edd30e558c93eabaa2b883f24f036161a7cd92 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -5,7 +5,6 @@ import de.kuschku.libquassel.protocol.message.HandshakeMessage import de.kuschku.libquassel.protocol.message.SignalProxyMessage import de.kuschku.libquassel.quassel.QuasselFeature import de.kuschku.libquassel.quassel.syncables.* -import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers import de.kuschku.libquassel.util.compatibility.HandlerService import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO @@ -19,6 +18,7 @@ class Session( val trustManager: X509TrustManager, address: SocketAddress, handlerService: HandlerService, + backlogStorage: BacklogStorage, private val userData: Pair<String, String> ) : ProtocolHandler(), ISession { var coreFeatures: Quassel_Features = Quassel_Feature.NONE @@ -27,7 +27,7 @@ class Session( override val state = coreConnection.state override val aliasManager = AliasManager(this) - override val backlogManager = BacklogManager(this) + override val backlogManager = BacklogManager(this, backlogStorage) override val bufferSyncer = BufferSyncer(this) override val bufferViewManager = BufferViewManager(this) override val certManagers = mutableMapOf<IdentityId, CertManager>() @@ -39,7 +39,7 @@ class Session( override val networks = mutableMapOf<NetworkId, Network>() override val networkConfig = NetworkConfig(this) - override var rpcHandler: RpcHandler? = RpcHandler(this) + override var rpcHandler: RpcHandler? = RpcHandler(this, backlogStorage) init { coreConnection.start() diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index cb4006c0aed9a73d34f03c14e9d5e82db34d8876..0385cf9c6f86b2f04a15729f31f51718589deb08 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -13,7 +13,7 @@ import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import javax.net.ssl.X509TrustManager -class SessionManager(offlineSession: ISession) : ISession { +class SessionManager(offlineSession: ISession, private val backlogStorage: BacklogStorage) : ISession { override val aliasManager: AliasManager? get() = session.or(lastSession).aliasManager override val backlogManager: BacklogManager? @@ -69,7 +69,7 @@ class SessionManager(offlineSession: ISession) : ISession { lastHandlerService = handlerService lastUserData = userData lastShouldReconnect = shouldReconnect - inProgressSession.onNext(Session(clientData, trustManager, address, handlerService(), userData)) + inProgressSession.onNext(Session(clientData, trustManager, address, handlerService(), backlogStorage, userData)) } private var lastClientData: ClientData? = null