From 0504d55d609d9dad2efee17309e4f135b449b965 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Wed, 30 Jan 2019 19:07:28 +0100 Subject: [PATCH] Implement typesafe SignedIds --- .../quasseldroid/dagger/ActivityModule.kt | 2 +- .../kuschku/quasseldroid/defaults/Defaults.kt | 3 +- .../quasseldroid/service/BacklogRequester.kt | 10 +- .../service/QuasselNotificationBackend.kt | 11 +- .../quasseldroid/service/QuasselService.kt | 24 +- .../QuasseldroidNotificationManager.kt | 2 +- .../quasseldroid/ui/chat/ChatActivity.kt | 37 ++- .../ui/chat/buffers/BufferListAdapter.kt | 6 +- .../chat/buffers/BufferViewConfigFragment.kt | 24 +- .../ui/chat/input/AutoCompleteHelper.kt | 3 +- .../ui/chat/messages/MessageAdapter.kt | 2 +- .../ui/chat/messages/MessageListFragment.kt | 27 +- .../ui/chat/topic/TopicFragment.kt | 3 +- .../ui/coresettings/CoreSettingsFragment.kt | 14 +- .../ui/coresettings/SettingsItem.kt | 4 +- .../ui/coresettings/SettingsItemAdapter.kt | 19 +- .../chatlist/ChatListBaseFragment.kt | 3 +- .../coresettings/chatlist/NetworkAdapter.kt | 2 +- .../identity/IdentityBaseFragment.kt | 3 +- .../identity/IdentityEditActivity.kt | 2 +- .../coresettings/network/IdentityAdapter.kt | 6 +- .../network/NetworkBaseFragment.kt | 6 +- .../network/NetworkEditActivity.kt | 2 +- .../ui/info/channel/ChannelInfoActivity.kt | 2 +- .../ui/info/channel/ChannelInfoFragment.kt | 10 +- .../info/channellist/ChannelListActivity.kt | 2 +- .../ui/info/channellist/ChannelListAdapter.kt | 6 +- .../info/channellist/ChannelListFragment.kt | 2 +- .../quasseldroid/ui/info/user/IrcUserInfo.kt | 3 +- .../ui/info/user/UserInfoActivity.kt | 4 +- .../ui/info/user/UserInfoFragment.kt | 15 +- .../ui/setup/ServiceBoundSetupActivity.kt | 2 +- .../ui/setup/ServiceBoundSlideFragment.kt | 4 +- .../ui/setup/core/QuasselSetupEntry.kt | 8 +- .../ui/setup/network/NetworkSetupActivity.kt | 11 +- .../setup/network/NetworkSetupNetworkSlide.kt | 14 +- .../util/service/ServiceBoundActivity.kt | 2 +- .../util/service/ServiceBoundFragment.kt | 4 +- .../quasseldroid/util/AvatarHelperTest.kt | 27 +- invokergenerator/build.gradle.kts | 12 +- .../kuschku/libquassel/annotations/Context.kt | 29 ++ .../kuschku/libquassel/annotations/Helpers.kt | 76 +++++ .../annotations/InvokerProcessor.java | 261 ------------------ .../annotations/InvokerProcessor.kt | 54 ++++ .../annotations/data/ParsedClass.kt | 28 ++ .../annotations/data/ParsedMethod.kt | 26 ++ .../annotations/data/ParsedParameter.kt | 27 ++ .../annotations/generator/Generator.kt | 112 ++++++++ .../libquassel/annotations/parser/Parser.kt | 255 +++++++++++++++++ .../annotations/parser/ParserEnvironment.kt | 52 ++++ .../de/kuschku/libquassel/protocol/QType.kt | 8 +- .../de/kuschku/libquassel/protocol/QTypes.kt | 8 - .../kuschku/libquassel/protocol/SignedId.kt | 87 ++++++ .../serializer/BufferIdSerializer.kt | 35 +++ .../serializer/BufferInfoSerializer.kt | 8 +- .../serializer/IdentityIdSerializer.kt | 35 +++ .../primitive/serializer/MessageSerializer.kt | 4 +- .../primitive/serializer/MsgIdSerializer.kt | 35 +++ .../serializer/NetworkIdSerializer.kt | 35 +++ .../serializer/SignedIdSerializer.kt | 35 +++ .../kuschku/libquassel/quassel/BufferInfo.kt | 6 +- .../quassel/syncables/BacklogManager.kt | 31 ++- .../quassel/syncables/BufferSyncer.kt | 18 +- .../quassel/syncables/BufferViewConfig.kt | 6 +- .../quassel/syncables/CertManager.kt | 2 +- .../libquassel/quassel/syncables/Identity.kt | 4 +- .../quassel/syncables/IrcChannel.kt | 2 +- .../quassel/syncables/IrcListHelper.kt | 4 +- .../libquassel/quassel/syncables/IrcUser.kt | 2 +- .../libquassel/quassel/syncables/Network.kt | 8 +- .../syncables/interfaces/IBacklogManager.kt | 21 +- .../quassel/syncables/interfaces/INetwork.kt | 4 +- .../syncables/interfaces/IRpcHandler.kt | 2 +- .../libquassel/session/BacklogStorage.kt | 2 +- .../de/kuschku/libquassel/session/Session.kt | 2 +- .../serializer/BufferInfoSerializerTest.kt | 10 +- .../serializer/MessageSerializerTest.kt | 14 +- .../quassel/syncables/AliasManagerTest.kt | 6 +- .../quassel/syncables/BufferViewConfigTest.kt | 5 +- .../quassel/syncables/IdentityTest.kt | 5 +- .../quassel/syncables/NetworkTest.kt | 10 +- .../syncables/interfaces/INetworkInfoTest.kt | 5 +- .../kuschku/libquassel/util/RandomHelpers.kt | 2 +- .../persistence/QuasselBacklogStorage.kt | 11 +- .../persistence/QuasselDatabase.kt | 188 ++++++++++--- .../persistence/QuasselDatabaseHelpers.kt | 91 ++++++ .../quasseldroid/viewmodel/EditorViewModel.kt | 3 +- .../viewmodel/QuasselViewModel.kt | 16 +- 88 files changed, 1462 insertions(+), 571 deletions(-) create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt delete mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt create mode 100644 invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt create mode 100644 lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt create mode 100644 persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt index eb5e6e12a..3feea1e69 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt @@ -237,7 +237,7 @@ abstract class ActivityModule { @ContributesAndroidInjector(modules = [CoreSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) abstract fun bindCoreSetupActivity(): CoreSetupActivity - // Service + // Service @ActivityScope @ContributesAndroidInjector(modules = [QuasselServiceModule::class, SettingsModule::class, DatabaseModule::class]) diff --git a/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt b/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt index 4c575575b..7931c2561 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/defaults/Defaults.kt @@ -22,6 +22,7 @@ package de.kuschku.quasseldroid.defaults import android.content.Context import de.kuschku.libquassel.protocol.Buffer_Activity import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.BufferViewConfig import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.quassel.syncables.Network @@ -72,7 +73,7 @@ object Defaults { } fun network(context: Context, proxy: SignalProxy = SignalProxy.NULL) = - Network(-1, proxy).apply { + Network(NetworkId(-1), proxy).apply { setNetworkName("") } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt index 1b0308f40..b61d63ee2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/BacklogRequester.kt @@ -26,9 +26,7 @@ import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG import de.kuschku.libquassel.util.helpers.value -import de.kuschku.quasseldroid.persistence.AccountDatabase -import de.kuschku.quasseldroid.persistence.QuasselBacklogStorage -import de.kuschku.quasseldroid.persistence.QuasselDatabase +import de.kuschku.quasseldroid.persistence.* import de.kuschku.quasseldroid.viewmodel.QuasselViewModel class BacklogRequester( @@ -52,9 +50,9 @@ class BacklogRequester( ?: 0) it.requestBacklog( bufferId = buffer, - last = lastMessageId ?: database.message().findFirstByBufferId( - buffer - )?.messageId ?: -1, + last = lastMessageId + ?: database.message().findFirstByBufferId(buffer)?.messageId + ?: MsgId(-1), limit = amount ) { if (it.isNotEmpty()) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt index 24e67bb29..e4c308a31 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselNotificationBackend.kt @@ -38,6 +38,9 @@ import de.kuschku.quasseldroid.GlideApp import de.kuschku.quasseldroid.GlideRequest import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.persistence.QuasselDatabase +import de.kuschku.quasseldroid.persistence.all +import de.kuschku.quasseldroid.persistence.buffers +import de.kuschku.quasseldroid.persistence.markRead import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.settings.NotificationSettings @@ -122,7 +125,7 @@ class QuasselNotificationBackend @Inject constructor( activity.hasFlag(Message_Type.Action) || activity.hasFlag(Message_Type.Notice)) session.backlogManager.requestBacklogFiltered( - buffer.bufferId, lastSeenId, -1, 20, 0, + buffer.bufferId, lastSeenId, MsgId(-1), 20, 0, Message_Type.of(Message_Type.Plain, Message_Type.Action, Message_Type.Notice).toInt(), @@ -136,7 +139,7 @@ class QuasselNotificationBackend @Inject constructor( val highlightCount = session.bufferSyncer.highlightCount(buffer.bufferId) if (highlightCount != 0) { session.backlogManager.requestBacklogFiltered( - buffer.bufferId, lastSeenId, -1, 20, 0, + buffer.bufferId, lastSeenId, MsgId(-1), 20, 0, Message_Type.of(Message_Type.Plain, Message_Type.Action, Message_Type.Notice).toInt(), @@ -224,7 +227,7 @@ class QuasselNotificationBackend @Inject constructor( }.map { val network = session.network(it.bufferInfo.networkId) val me = network?.me() - QuasselDatabase.NotificationData( + QuasselDatabase.NotificationData.of( messageId = it.messageId, creationTime = now, time = it.time, @@ -369,7 +372,7 @@ class QuasselNotificationBackend @Inject constructor( notificationSettings, buffer, selfInfo, notificationData, isLoud, isConnected ) notificationHandler.notify(notification) - } ?: notificationHandler.remove(buffer) + } ?: notificationHandler.remove(buffer.id) } @Synchronized diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt index 8a990c698..a2726b36f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt @@ -45,9 +45,7 @@ import de.kuschku.quasseldroid.BuildConfig import de.kuschku.quasseldroid.Keys import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.defaults.Defaults -import de.kuschku.quasseldroid.persistence.AccountDatabase -import de.kuschku.quasseldroid.persistence.QuasselBacklogStorage -import de.kuschku.quasseldroid.persistence.QuasselDatabase +import de.kuschku.quasseldroid.persistence.* import de.kuschku.quasseldroid.settings.ConnectionSettings import de.kuschku.quasseldroid.settings.NotificationSettings import de.kuschku.quasseldroid.settings.Settings @@ -172,10 +170,10 @@ class QuasselService : DaggerLifecycleService(), } } - val bufferId = intent.getIntExtra("buffer", -1) + val bufferId = BufferId(intent.getIntExtra("buffer", -1)) val inputResults = RemoteInput.getResultsFromIntent(intent)?.getCharSequence("reply_content") - if (inputResults != null && bufferId != -1) { + if (inputResults != null && bufferId.isValidId()) { if (inputResults.isNotBlank()) { val lines = inputResults.lineSequence().map { it.toString() to ircFormatSerializer.toEscapeCodes(SpannableString(it)) @@ -197,14 +195,14 @@ class QuasselService : DaggerLifecycleService(), } } } else { - val clearMessageId = intent.getLongExtra("mark_read_message", -1) - if (bufferId != -1 && clearMessageId != -1L) { + val clearMessageId = MsgId(intent.getLongExtra("mark_read_message", -1)) + if (bufferId.isValidId() && clearMessageId.isValidId()) { sessionManager.session.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId, clearMessageId) sessionManager.session.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId) } - val hideMessageId = intent.getLongExtra("hide_message", -1) - if (bufferId != -1 && hideMessageId != -1L) { + val hideMessageId = MsgId(intent.getLongExtra("hide_message", -1)) + if (bufferId.isValidId() && hideMessageId.isValidId()) { if (notificationSettings.markReadOnSwipe) { sessionManager.session.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId, hideMessageId) sessionManager.session.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId) @@ -501,7 +499,7 @@ class QuasselService : DaggerLifecycleService(), val deletedBuffersMessage = database.message().buffers().toSet() - buffers log(INFO, "QuasselService", "Buffers deleted from message storage: $deletedBuffersMessage") for (deletedBuffer in deletedBuffersMessage) { - database.message().clearMessages(deletedBuffer) + database.message().clearMessages(deletedBuffer.id) } val deletedBuffersFiltered = database.filtered().buffers(accountId).toSet() - buffers @@ -546,13 +544,13 @@ class QuasselService : DaggerLifecycleService(), putExtra("disconnect", disconnect) } if (bufferId != null) { - putExtra("buffer", bufferId) + putExtra("buffer", bufferId.id) } if (markReadMessage != null) { - putExtra("mark_read_message", markReadMessage) + putExtra("mark_read_message", markReadMessage.id) } if (hideMessage != null) { - putExtra("hide_message", hideMessage) + putExtra("hide_message", hideMessage.id) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt index 99817f5a5..c7261280d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasseldroidNotificationManager.kt @@ -237,7 +237,7 @@ class QuasseldroidNotificationManager @Inject constructor(private val context: C } } } - return Handle(buffer.id, notification) + return Handle(buffer.id.id, notification) } fun notificationBackground(): Handle { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt index 3f55b80e6..5d48d4176 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt @@ -48,10 +48,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import de.kuschku.libquassel.connection.ConnectionState import de.kuschku.libquassel.connection.ProtocolVersionException import de.kuschku.libquassel.connection.QuasselSecurityException -import de.kuschku.libquassel.protocol.Buffer_Type -import de.kuschku.libquassel.protocol.Message -import de.kuschku.libquassel.protocol.Message_Type -import de.kuschku.libquassel.protocol.NetworkId +import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.protocol.coresetup.CoreSetupData import de.kuschku.libquassel.protocol.message.HandshakeMessage import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_PLAINTEXT @@ -67,8 +64,7 @@ import de.kuschku.libquassel.util.helpers.value import de.kuschku.quasseldroid.Keys import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.defaults.DefaultNetworkServer -import de.kuschku.quasseldroid.persistence.AccountDatabase -import de.kuschku.quasseldroid.persistence.QuasselDatabase +import de.kuschku.quasseldroid.persistence.* import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.settings.Settings @@ -161,7 +157,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } intent.hasExtra(KEY_BUFFER_ID) -> { - viewModel.buffer.onNext(intent.getIntExtra(KEY_BUFFER_ID, -1)) + viewModel.buffer.onNext(BufferId(intent.getIntExtra(KEY_BUFFER_ID, -1))) viewModel.bufferOpened.onNext(Unit) if (intent.hasExtra(KEY_ACCOUNT_ID)) { val accountId = intent.getLongExtra(ChatActivity.KEY_ACCOUNT_ID, -1) @@ -183,7 +179,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc drawerLayout.closeDrawers() } intent.hasExtra(KEY_NETWORK_ID) && intent.hasExtra(KEY_CHANNEL) -> { - val networkId = intent.getIntExtra(KEY_NETWORK_ID, -1) + val networkId = NetworkId(intent.getIntExtra(KEY_NETWORK_ID, -1)) val channel = intent.getStringExtra(KEY_CHANNEL) viewModel.session.filter(Optional<ISession>::isPresent).firstElement().subscribe { @@ -310,9 +306,9 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc // If we connect to a new network without statusbuffer, the bufferid may be -networkId. // In that case, once we’re connected (and a status buffer exists), we want to switch to it. combineLatest(viewModel.allBuffers, viewModel.buffer).map { (buffers, current) -> - if (current > 0) Optional.empty() + if (current.isValidId()) Optional.empty() else Optional.ofNullable(buffers.firstOrNull { - it.networkId == -current && it.type.hasFlag(Buffer_Type.StatusBuffer) + it.networkId == NetworkId(-current.id) && it.type.hasFlag(Buffer_Type.StatusBuffer) }) }.toLiveData().observe(this, Observer { info -> info?.orNull()?.let { @@ -632,7 +628,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .observe(this, Observer { if (connectedAccount != accountId) { if (resources.getBoolean(R.bool.buffer_drawer_exists) && - viewModel.buffer.value == Int.MAX_VALUE && + viewModel.buffer.value == BufferId.MAX_VALUE && !restoredDrawerState) { drawerLayout.openDrawer(GravityCompat.START) } @@ -782,7 +778,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc override fun onSaveInstanceState(outState: Bundle?) { super.onSaveInstanceState(outState) - outState?.putInt(KEY_OPEN_BUFFER, viewModel.buffer.value ?: -1) + outState?.putInt(KEY_OPEN_BUFFER, viewModel.buffer.value.id ?: -1) outState?.putInt(KEY_OPEN_BUFFERVIEWCONFIG, viewModel.bufferViewConfigId.value ?: -1) outState?.putLong(KEY_CONNECTED_ACCOUNT, connectedAccount) outState?.putBoolean(KEY_OPEN_DRAWER_START, drawerLayout.isDrawerOpen(GravityCompat.START)) @@ -791,7 +787,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc override fun onRestoreInstanceState(savedInstanceState: Bundle?) { super.onRestoreInstanceState(savedInstanceState) - viewModel.buffer.onNext(savedInstanceState?.getInt(KEY_OPEN_BUFFER, -1) ?: -1) + viewModel.buffer.onNext(BufferId(savedInstanceState?.getInt(KEY_OPEN_BUFFER, -1) ?: -1)) viewModel.bufferViewConfigId.onNext(savedInstanceState?.getInt(KEY_OPEN_BUFFERVIEWCONFIG, -1) ?: -1) connectedAccount = savedInstanceState?.getLong(KEY_CONNECTED_ACCOUNT, -1L) ?: -1L @@ -816,7 +812,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } menuInflater.inflate(R.menu.activity_main, menu) - menu?.findItem(R.id.action_nicklist)?.isVisible = bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) ?: false + menu?.findItem(R.id.action_nicklist)?.isVisible = bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) + ?: false menu?.findItem(R.id.action_filter_messages)?.isVisible = (bufferData?.info?.type?.hasFlag(Buffer_Type.ChannelBuffer) ?: false || bufferData?.info?.type?.hasFlag(Buffer_Type.QueryBuffer) ?: false) @@ -895,7 +892,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc .fold(Message_Type.of()) { acc, i -> acc or i } database.filtered().replace( - QuasselDatabase.Filtered(accountId, buffer, newlyFiltered.value.toInt()) + QuasselDatabase.Filtered.of(accountId, buffer, newlyFiltered.value.toInt()) ) } } @@ -964,7 +961,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc startedSelection = true connectedAccount = -1L restoredDrawerState = false - ChatActivity.launch(this, bufferId = Int.MAX_VALUE) + ChatActivity.launch(this, bufferId = BufferId.MAX_VALUE) viewModel.resetAccount() } @@ -998,7 +995,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc autoCompleteSuffix: String? = null, channel: String? = null, networkId: NetworkId? = null, - bufferId: Int? = null, + bufferId: BufferId? = null, accountId: Long? = null ) = context.startActivity( intent(context, @@ -1018,7 +1015,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc autoCompleteSuffix: String? = null, channel: String? = null, networkId: NetworkId? = null, - bufferId: Int? = null, + bufferId: BufferId? = null, accountId: Long? = null ) = Intent(context, ChatActivity::class.java).apply { if (sharedText != null) { @@ -1032,13 +1029,13 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } } if (bufferId != null) { - putExtra(KEY_BUFFER_ID, bufferId) + putExtra(KEY_BUFFER_ID, bufferId.id) if (accountId != null) { putExtra(KEY_ACCOUNT_ID, accountId) } } if (networkId != null && channel != null) { - putExtra(KEY_NETWORK_ID, networkId) + putExtra(KEY_NETWORK_ID, networkId.id) putExtra(KEY_CHANNEL, channel) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt index d3fda7c5f..aa3b82bd7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferListAdapter.kt @@ -84,13 +84,13 @@ class BufferListAdapter( } fun toggleSelection(buffer: BufferId): Boolean { - val next = if (selectedBuffer.value == buffer) Int.MAX_VALUE else buffer + val next = if (selectedBuffer.value == buffer) BufferId.MAX_VALUE else buffer selectedBuffer.onNext(next) - return next != Int.MAX_VALUE + return next != BufferId.MAX_VALUE } fun unselectAll() { - selectedBuffer.onNext(Int.MAX_VALUE) + selectedBuffer.onNext(BufferId.MAX_VALUE) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BufferViewHolder { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt index 60cfaeacd..ada92099e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt @@ -120,41 +120,41 @@ class BufferViewConfigFragment : ServiceBoundFragment() { return if (info != null && session != null) { when (item?.itemId) { - R.id.action_channellist -> { + R.id.action_channellist -> { network?.let { ChannelListActivity.launch(requireContext(), network = it.networkId()) } actionMode?.finish() true } - R.id.action_configure -> { + R.id.action_configure -> { network?.let { NetworkEditActivity.launch(requireContext(), network = it.networkId()) } actionMode?.finish() true } - R.id.action_connect -> { + R.id.action_connect -> { network?.requestConnect() actionMode?.finish() true } - R.id.action_disconnect -> { + R.id.action_disconnect -> { network?.requestDisconnect() actionMode?.finish() true } - R.id.action_join -> { + R.id.action_join -> { session.rpcHandler?.sendInput(info, "/join ${info.bufferName}") actionMode?.finish() true } - R.id.action_part -> { + R.id.action_part -> { session.rpcHandler?.sendInput(info, "/part ${info.bufferName}") actionMode?.finish() true } - R.id.action_delete -> { + R.id.action_delete -> { MaterialDialog.Builder(activity!!) .content(R.string.buffer_delete_confirmation) .positiveText(R.string.label_yes) @@ -174,7 +174,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { .show() true } - R.id.action_rename -> { + R.id.action_rename -> { MaterialDialog.Builder(activity!!) .input( getString(R.string.label_buffer_name), @@ -197,24 +197,24 @@ class BufferViewConfigFragment : ServiceBoundFragment() { .show() true } - R.id.action_unhide -> { + R.id.action_unhide -> { bufferSyncer?.let { bufferViewConfig?.orNull()?.insertBufferSorted(info, bufferSyncer) } actionMode?.finish() true } - R.id.action_hide_temp -> { + R.id.action_hide_temp -> { bufferViewConfig?.orNull()?.requestRemoveBuffer(info.bufferId) actionMode?.finish() true } - R.id.action_hide_perm -> { + R.id.action_hide_perm -> { bufferViewConfig?.orNull()?.requestRemoveBufferPermanently(info.bufferId) actionMode?.finish() true } - else -> false + else -> false } } else { false diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt index 5f14a2352..1137b959d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt @@ -25,6 +25,7 @@ import android.text.style.ForegroundColorSpan import android.text.style.StyleSpan import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Observer +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.quassel.syncables.IrcChannel import de.kuschku.libquassel.quassel.syncables.IrcUser @@ -168,7 +169,7 @@ class AutoCompleteHelper( this.dataListeners += listener } - private fun fullAutoComplete(sessionOptional: Optional<ISession>, id: Int, + private fun fullAutoComplete(sessionOptional: Optional<ISession>, id: BufferId, lastWord: Pair<String, IntRange>): List<AutoCompleteItem> { val session = sessionOptional.orNull() val bufferSyncer = session?.bufferSyncer diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt index 0fec43779..95e31b796 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt @@ -120,7 +120,7 @@ class MessageAdapter @Inject constructor( } ?: 0 override fun getItemId(position: Int): Long { - return getItem(position)?.content?.messageId ?: 0L + return getItem(position)?.content?.messageId?.id ?: 0L } private fun messageType(viewType: Int): Message_Type? = diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt index 0dbfd3d77..2636c2c55 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt @@ -55,8 +55,7 @@ import de.kuschku.libquassel.util.helpers.value import de.kuschku.libquassel.util.irc.HostmaskHelper import de.kuschku.quasseldroid.GlideApp import de.kuschku.quasseldroid.R -import de.kuschku.quasseldroid.persistence.AccountDatabase -import de.kuschku.quasseldroid.persistence.QuasselDatabase +import de.kuschku.quasseldroid.persistence.* import de.kuschku.quasseldroid.service.BacklogRequester import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings @@ -290,11 +289,11 @@ class MessageListFragment : ServiceBoundFragment() { } adapter.setOnUrlLongClickListener(LinkLongClickMenuHelper()) - adapter.setOnExpansionListener { (messageId) -> + adapter.setOnExpansionListener { val value = viewModel.expandedMessages.value viewModel.expandedMessages.onNext( - if (value.contains(messageId)) value - messageId - else value + messageId + if (value.contains(it.messageId)) value - it.messageId + else value + it.messageId ) } @@ -392,7 +391,7 @@ class MessageListFragment : ServiceBoundFragment() { } viewModel.buffer.toLiveData().observe(this, Observer { bufferId -> - swipeRefreshLayout.isEnabled = (bufferId != null || bufferId != -1) + swipeRefreshLayout.isEnabled = (bufferId != null || bufferId?.isValidId() == true) }) viewModel.sessionManager.mapSwitchMap(SessionManager::state).distinctUntilChanged().toLiveData().observe( @@ -407,7 +406,7 @@ class MessageListFragment : ServiceBoundFragment() { // Try loading messages when switching to isEmpty buffer val hasVisibleMessages = database.message().hasVisibleMessages(bufferId, filtered) if (!hasVisibleMessages) { - if (bufferId > 0 && bufferId != Int.MAX_VALUE) { + if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) { loadMore(initial = true) } } @@ -484,7 +483,7 @@ class MessageListFragment : ServiceBoundFragment() { (messageList.layoutManager as RecyclerView.LayoutManager).onRestoreInstanceState(getParcelable( KEY_STATE_LIST)) previousLoadKey = getInt(KEY_STATE_PAGING).nullIf { it == -1 } - lastBuffer = getInt(KEY_STATE_BUFFER).nullIf { it == -1 } + lastBuffer = BufferId(getInt(KEY_STATE_BUFFER)).nullIf { !it.isValidId() } } data.observe(this, Observer { list -> @@ -496,7 +495,8 @@ class MessageListFragment : ServiceBoundFragment() { list?.let(adapter::submitList) } - val buffer = viewModel.buffer.value ?: -1 + val buffer = viewModel.buffer.value + ?: BufferId(-1) if (buffer != lastBuffer) { adapter.clearCache() viewModel.session.value?.orNull()?.bufferSyncer?.let { bufferSyncer -> @@ -514,7 +514,7 @@ class MessageListFragment : ServiceBoundFragment() { super.onSaveInstanceState(outState) outState.putParcelable(KEY_STATE_LIST, messageList.layoutManager?.onSaveInstanceState()) outState.putInt(KEY_STATE_PAGING, previousLoadKey ?: -1) - outState.putInt(KEY_STATE_BUFFER, lastBuffer ?: -1) + outState.putInt(KEY_STATE_BUFFER, lastBuffer?.id ?: -1) } private fun markAsRead(bufferSyncer: BufferSyncer, buffer: BufferId, lastMessageId: MsgId?) { @@ -537,7 +537,7 @@ class MessageListFragment : ServiceBoundFragment() { ?: 0) val hasVisibleMessages = database.message().hasVisibleMessages(current, filtered) if (!hasVisibleMessages) { - if (current > 0 && current != Int.MAX_VALUE) { + if (current.isValidId() && current != BufferId.MAX_VALUE) { loadMore(initial = true) } } @@ -559,7 +559,7 @@ class MessageListFragment : ServiceBoundFragment() { // This can be called *after* we’re already detached from the activity activity?.runOnUiThread { viewModel.buffer { bufferId -> - if (bufferId > 0 && bufferId != Int.MAX_VALUE) { + if (bufferId.isValidId() && bufferId != BufferId.MAX_VALUE) { if (initial) swipeRefreshLayout.isRefreshing = true runInBackground { backlogRequester.loadMore( @@ -568,7 +568,8 @@ class MessageListFragment : ServiceBoundFragment() { amount = if (initial) backlogSettings.initialAmount else backlogSettings.pageSize, pageSize = backlogSettings.pageSize, lastMessageId = lastMessageId - ?: database.message().findFirstByBufferId(bufferId)?.messageId ?: -1, + ?: database.message().findFirstByBufferId(bufferId)?.messageId + ?: MsgId(-1), untilAllVisible = initial ) { activity?.runOnUiThread { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt index c3cda191e..b8ca3f596 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/topic/TopicFragment.kt @@ -30,6 +30,7 @@ import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife import com.google.android.material.bottomsheet.BottomSheetBehavior +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings @@ -117,7 +118,7 @@ class TopicFragment : ServiceBoundSettingsFragment(), Savable { } } - val bufferId = arguments?.getInt("buffer", -1) ?: -1 + val bufferId = BufferId(arguments?.getInt("buffer", -1) ?: -1) viewModel.buffer.onNext(bufferId) viewModel.bufferData.filter { it.info != null diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt index d9a42f420..33ce70726 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt @@ -31,6 +31,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife +import de.kuschku.libquassel.protocol.IdentityId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.BufferViewConfig import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.quassel.syncables.Network @@ -102,15 +104,15 @@ class CoreSettingsFragment : ServiceBoundFragment() { val view = inflater.inflate(R.layout.settings_list, container, false) ButterKnife.bind(this, view) - val networkAdapter = SettingsItemAdapter { + val networkAdapter = SettingsItemAdapter<NetworkId> { NetworkEditActivity.launch(requireContext(), network = it) } - val identityAdapter = SettingsItemAdapter { + val identityAdapter = SettingsItemAdapter<IdentityId> { IdentityEditActivity.launch(requireContext(), identity = it) } - val chatListAdapter = SettingsItemAdapter { + val chatListAdapter = SettingsItemAdapter<Int> { ChatlistEditActivity.launch(requireContext(), chatlist = it) } @@ -136,7 +138,7 @@ class CoreSettingsFragment : ServiceBoundFragment() { combineLatest(it.values.map(Network::liveNetworkInfo)).map { it.map { SettingsItem(it.networkId, it.networkName) - }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, SettingsItem::name)) + }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, SettingsItem<NetworkId>::name)) } } }.toLiveData().observe(this, Observer { @@ -155,7 +157,7 @@ class CoreSettingsFragment : ServiceBoundFragment() { combineLatest(it.values.map(Identity::liveUpdates)).map { it.map { SettingsItem(it.id(), it.identityName() ?: "") - }.sortedBy(SettingsItem::name) + }.sortedBy(SettingsItem<IdentityId>::name) } } }.toLiveData().observe(this, Observer { @@ -171,7 +173,7 @@ class CoreSettingsFragment : ServiceBoundFragment() { combineLatest(it.values.map(BufferViewConfig::liveUpdates)).map { it.map { SettingsItem(it.bufferViewId(), it.bufferViewName()) - }.sortedBy(SettingsItem::name) + }.sortedBy(SettingsItem<Int>::name) } }.toLiveData().observe(this, Observer { chatListAdapter.submitList(it.orEmpty()) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt index 6020cdc3b..9dec46e60 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItem.kt @@ -19,7 +19,7 @@ package de.kuschku.quasseldroid.ui.coresettings -data class SettingsItem( - val id: Int, +data class SettingsItem<T>( + val id: T, val name: String ) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt index 16d7046a1..42b2f8c3d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/SettingsItemAdapter.kt @@ -28,16 +28,15 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife -import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.quasseldroid.R -class SettingsItemAdapter(private val clickListener: (Int) -> Unit) : - ListAdapter<SettingsItem, SettingsItemAdapter.SettingsItemViewHolder>( - object : DiffUtil.ItemCallback<SettingsItem>() { - override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem) = +class SettingsItemAdapter<T>(private val clickListener: (T) -> Unit) : + ListAdapter<SettingsItem<T>, SettingsItemAdapter.SettingsItemViewHolder<T>>( + object : DiffUtil.ItemCallback<SettingsItem<T>>() { + override fun areItemsTheSame(oldItem: SettingsItem<T>, newItem: SettingsItem<T>) = oldItem.id == newItem.id - override fun areContentsTheSame(oldItem: SettingsItem, newItem: SettingsItem) = + override fun areContentsTheSame(oldItem: SettingsItem<T>, newItem: SettingsItem<T>) = oldItem == newItem } ) { @@ -46,16 +45,16 @@ class SettingsItemAdapter(private val clickListener: (Int) -> Unit) : clickListener ) - override fun onBindViewHolder(holder: SettingsItemViewHolder, position: Int) { + override fun onBindViewHolder(holder: SettingsItemViewHolder<T>, position: Int) { holder.bind(getItem(position)) } - class SettingsItemViewHolder(itemView: View, clickListener: (Int) -> Unit) : + class SettingsItemViewHolder<T>(itemView: View, clickListener: (T) -> Unit) : RecyclerView.ViewHolder(itemView) { @BindView(R.id.title) lateinit var title: TextView - var id: IdentityId? = null + var id: T? = null init { ButterKnife.bind(this, itemView) @@ -64,7 +63,7 @@ class SettingsItemAdapter(private val clickListener: (Int) -> Unit) : } } - fun bind(item: SettingsItem) { + fun bind(item: SettingsItem<T>) { this.id = item.id this.title.text = item.name } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt index 9070b0af0..d624d7078 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt @@ -32,6 +32,7 @@ import butterknife.BindView import butterknife.ButterKnife import de.kuschku.libquassel.protocol.Buffer_Activity import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.BufferViewConfig import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork @@ -224,7 +225,7 @@ abstract class ChatListBaseFragment(private val initDefault: Boolean) : else allowedBufferTypes -= Buffer_Type.StatusBuffer data.setAllowedBufferTypes(allowedBufferTypes) - data.setNetworkId(networkId.selectedItemId.toInt()) + data.setNetworkId(NetworkId(networkId.selectedItemId.toInt())) data.setMinimumActivity(minimumActivity.selectedItemId.toInt()) if (old != null) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt index aedf2d7c6..3d14d42d8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/NetworkAdapter.kt @@ -71,7 +71,7 @@ class NetworkAdapter(@StringRes private val fallbackName: Int) : } override fun getItem(position: Int): INetwork.NetworkInfo? = data[position] - override fun getItemId(position: Int) = getItem(position)?.networkId?.toLong() ?: -1 + override fun getItemId(position: Int) = getItem(position)?.networkId?.id?.toLong() ?: -1 override fun hasStableIds() = true override fun getCount() = data.size class NetworkViewHolder(@StringRes private val fallbackName: Int, itemView: View) : diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt index 9b2bfb294..1b2154fe2 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityBaseFragment.kt @@ -34,6 +34,7 @@ import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife import com.afollestad.materialdialogs.MaterialDialog +import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.util.Optional @@ -94,7 +95,7 @@ abstract class IdentityBaseFragment(private val initDefault: Boolean) : val view = inflater.inflate(R.layout.settings_identity, container, false) ButterKnife.bind(this, view) - val identityId = arguments?.getInt("identity", -1) ?: -1 + val identityId = IdentityId(arguments?.getInt("identity", -1) ?: -1) adapter = IdentityNicksAdapter(::nickClick, ::startDrag) nicks.layoutManager = LinearLayoutManager(requireContext()) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt index 5e4c2c0ce..103eeacbc 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/identity/IdentityEditActivity.kt @@ -35,7 +35,7 @@ class IdentityEditActivity : ServiceBoundSettingsActivity(IdentityEditFragment() context: Context, identity: IdentityId ) = Intent(context, IdentityEditActivity::class.java).apply { - putExtra("identity", identity) + putExtra("identity", identity.id) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt index 4e0caa06c..3bbfaf988 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/IdentityAdapter.kt @@ -27,7 +27,7 @@ import androidx.appcompat.widget.ThemedSpinnerAdapter import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife -import de.kuschku.libquassel.protocol.NetworkId +import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.util.ui.ContextThemeWrapper @@ -55,7 +55,7 @@ class IdentityAdapter : RecyclerSpinnerAdapter<IdentityAdapter.NetworkViewHolder return NetworkViewHolder(inflater.inflate(R.layout.widget_spinner_item_inline, parent, false)) } - fun indexOf(id: NetworkId): Int? { + fun indexOf(id: IdentityId): Int? { for ((key, item) in data.withIndex()) { if (item.id() == id) { return key @@ -67,7 +67,7 @@ class IdentityAdapter : RecyclerSpinnerAdapter<IdentityAdapter.NetworkViewHolder override fun getItem(position: Int): Identity? = if (position in 0 until data.size) data[position] else null - override fun getItemId(position: Int) = getItem(position)?.id()?.toLong() ?: -1 + override fun getItemId(position: Int) = getItem(position)?.id()?.id?.toLong() ?: -1 override fun hasStableIds() = true diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt index 8c550485e..c5394955a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt @@ -36,6 +36,8 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.ButterKnife +import de.kuschku.libquassel.protocol.IdentityId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork @@ -137,7 +139,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) : val view = inflater.inflate(R.layout.settings_network, container, false) ButterKnife.bind(this, view) - val networkId = arguments?.getInt("network", -1) ?: -1 + val networkId = NetworkId(arguments?.getInt("network", -1) ?: -1) adapter = NetworkServerAdapter(::serverClick, ::startDrag) servers.layoutManager = LinearLayoutManager(requireContext()) @@ -286,7 +288,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) : protected fun applyChanges(data: Network) { data.setNetworkName(networkName.text.toString()) - data.setIdentity(identity.selectedItemId.toInt()) + data.setIdentity(IdentityId(identity.selectedItemId.toInt())) data.setActualServerList(adapter.list) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt index 6bd392809..42b4480fd 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkEditActivity.kt @@ -35,7 +35,7 @@ class NetworkEditActivity : ServiceBoundSettingsActivity(NetworkEditFragment()) context: Context, network: NetworkId ) = Intent(context, NetworkEditActivity::class.java).apply { - putExtra("network", network) + putExtra("network", network.id) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt index e23c27cf0..962f6161d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoActivity.kt @@ -37,7 +37,7 @@ class ChannelInfoActivity : ServiceBoundSettingsActivity(ChannelInfoFragment()) openBuffer: Boolean, bufferId: BufferId ) = Intent(context, ChannelInfoActivity::class.java).apply { - putExtra("bufferId", bufferId) + putExtra("bufferId", bufferId.id) putExtra("openBuffer", openBuffer) } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt index 3ab0bca26..4a04aae2e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt @@ -29,7 +29,9 @@ import android.widget.TextView import androidx.lifecycle.Observer import butterknife.BindView import butterknife.ButterKnife +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.syncables.IrcChannel import de.kuschku.libquassel.util.helpers.value @@ -79,21 +81,23 @@ class ChannelInfoFragment : ServiceBoundFragment() { ButterKnife.bind(this, view) val openBuffer = arguments?.getBoolean("openBuffer") + val bufferId = BufferId(arguments?.getInt("bufferId") ?: -1) + val networkId = NetworkId(arguments?.getInt("networkId") ?: -1) - var currentBufferInfo: BufferInfo? = null + var currentBufferInfo: BufferInfo? combineLatest(viewModel.session, viewModel.networks).map { (sessionOptional, networks) -> if (openBuffer == true) { val session = sessionOptional?.orNull() val bufferSyncer = session?.bufferSyncer - val bufferInfo = bufferSyncer?.bufferInfo(arguments?.getInt("bufferId") ?: -1) + val bufferInfo = bufferSyncer?.bufferInfo(bufferId) bufferInfo?.let { info -> networks[info.networkId]?.ircChannel(info.bufferName)?.let { Pair(info, it) } } } else { - networks[arguments?.getInt("networkId")]?.ircChannel(arguments?.getString("nick"))?.let { + networks[networkId]?.ircChannel(arguments?.getString("nick"))?.let { Pair(null, it) } } ?: Pair(null, IrcChannel.NULL) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt index 0258ce035..f786b33f4 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListActivity.kt @@ -35,7 +35,7 @@ class ChannelListActivity : ServiceBoundSettingsActivity(ChannelListFragment()) context: Context, network: NetworkId ) = Intent(context, ChannelListActivity::class.java).apply { - putExtra("network_id", network) + putExtra("network_id", network.id) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt index 6b9d849b5..3e96dc703 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListAdapter.kt @@ -46,10 +46,12 @@ class ChannelListAdapter @Inject constructor( ) : ListAdapter<IrcListHelper.ChannelDescription, ChannelListAdapter.ChannelViewHolder>( object : DiffUtil.ItemCallback<IrcListHelper.ChannelDescription>() { - override fun areItemsTheSame(oldItem: IrcListHelper.ChannelDescription, newItem: IrcListHelper.ChannelDescription) = + override fun areItemsTheSame(oldItem: IrcListHelper.ChannelDescription, + newItem: IrcListHelper.ChannelDescription) = oldItem.channelName == newItem.channelName - override fun areContentsTheSame(oldItem: IrcListHelper.ChannelDescription, newItem: IrcListHelper.ChannelDescription) = + override fun areContentsTheSame(oldItem: IrcListHelper.ChannelDescription, + newItem: IrcListHelper.ChannelDescription) = oldItem == newItem } ) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt index 8ce88f5ff..b8e1baf97 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channellist/ChannelListFragment.kt @@ -109,7 +109,7 @@ class ChannelListFragment : ServiceBoundSettingsFragment() { val view = inflater.inflate(R.layout.info_channellist, container, false) ButterKnife.bind(this, view) - val networkId = arguments?.getInt("network_id", -1) ?: -1 + val networkId = NetworkId(arguments?.getInt("network_id", -1) ?: -1) searchResults.adapter = adapter searchResults.layoutManager = LinearLayoutManager(view.context) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt index 7d51299a4..98adeadae 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/IrcUserInfo.kt @@ -19,12 +19,13 @@ package de.kuschku.quasseldroid.ui.info.user +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.syncables.IrcUser import de.kuschku.libquassel.quassel.syncables.Network data class IrcUserInfo( - val networkId: Int, + val networkId: NetworkId, val nick: String, val user: String? = null, val host: String? = null, diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt index ad0237d0b..c4eb983a5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoActivity.kt @@ -44,13 +44,13 @@ class UserInfoActivity : ServiceBoundSettingsActivity(UserInfoFragment()) { ) = Intent(context, UserInfoActivity::class.java).apply { putExtra("openBuffer", openBuffer) if (bufferId != null) { - putExtra("bufferId", bufferId) + putExtra("bufferId", bufferId.id) } if (nick != null) { putExtra("nick", nick) } if (networkId != null) { - putExtra("networkId", networkId) + putExtra("networkId", networkId.id) } } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt index a1def4e56..6da78c876 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt @@ -34,7 +34,9 @@ import android.widget.Toast import androidx.lifecycle.Observer import butterknife.BindView import butterknife.ButterKnife +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.syncables.IrcUser import de.kuschku.libquassel.util.Optional @@ -130,11 +132,12 @@ class UserInfoFragment : ServiceBoundFragment() { val openBuffer = arguments?.getBoolean("openBuffer") - val networkId2 = arguments?.getInt("networkId") - val nickName2 = arguments?.getString("nick") + val bufferId = BufferId(arguments?.getInt("bufferId") ?: -1) + val networkId = NetworkId(arguments?.getInt("networkId") ?: -1) + val nickName = arguments?.getString("nick") var currentBufferInfo: BufferInfo? = null - var currentIrcUser: IrcUser? = null + var currentIrcUser: IrcUser? fun updateShortcutVisibility() { actionShortcut.visibleIf(currentBufferInfo != null) @@ -172,15 +175,15 @@ class UserInfoFragment : ServiceBoundFragment() { if (openBuffer == true) { val session = sessionOptional?.orNull() val bufferSyncer = session?.bufferSyncer - val bufferInfo = bufferSyncer?.bufferInfo(arguments?.getInt("bufferId") ?: -1) + val bufferInfo = bufferSyncer?.bufferInfo(bufferId) bufferInfo?.let { networks[it.networkId]?.liveIrcUser(it.bufferName)?.switchMap(IrcUser::updates)?.map { processUser(it, bufferInfo) } } } else { - networks[networkId2] - ?.liveIrcUser(nickName2) + networks[networkId] + ?.liveIrcUser(nickName) ?.switchMap(IrcUser::updates) ?.map { user -> processUser(user) } } ?: Observable.just(IrcUser.NULL).map { user -> processUser(user) } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt index d07427e07..d813d329c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt @@ -289,7 +289,7 @@ abstract class ServiceBoundSetupActivity : private fun checkConnection() { accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 val reconnect = sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) { getBoolean(Keys.Status.reconnect, false) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt index 90e4e0269..1c13bde36 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSlideFragment.kt @@ -53,7 +53,7 @@ abstract class ServiceBoundSlideFragment : SlideFragment() { override fun onCreate(savedInstanceState: Bundle?) { accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 connection.context = context lifecycle.addObserver(connection) @@ -63,7 +63,7 @@ abstract class ServiceBoundSlideFragment : SlideFragment() { override fun onStart() { super.onStart() accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 } override fun onDestroy() { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt index 8c7f376ea..ba5c56ff8 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt @@ -69,21 +69,21 @@ class QuasselSetupEntry : FrameLayout { wrapper.isPasswordVisibilityToggleEnabled = true field.inputType = InputType.TYPE_CLASS_TEXT or - InputType.TYPE_TEXT_VARIATION_PASSWORD + InputType.TYPE_TEXT_VARIATION_PASSWORD field.setText(data.defaultValue.value("")) } data.defaultValue.type == Type.QString && data.key.contains("hostname", ignoreCase = true) -> { field.inputType = InputType.TYPE_CLASS_TEXT or - InputType.TYPE_TEXT_VARIATION_URI + InputType.TYPE_TEXT_VARIATION_URI field.setText(data.defaultValue.value("")) } data.defaultValue.type == Type.QString -> { field.inputType = InputType.TYPE_CLASS_TEXT or - InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or - InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or + InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS field.setText(data.defaultValue.value("")) } data.defaultValue.type == Type.Int -> { diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt index 1ed76e38c..48bd8417f 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupActivity.kt @@ -25,6 +25,7 @@ import android.content.Intent import android.os.Bundle import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.IdentityId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.util.helpers.value import de.kuschku.quasseldroid.ui.setup.ServiceBoundSetupActivity @@ -41,15 +42,15 @@ class NetworkSetupActivity : ServiceBoundSetupActivity() { override fun onDone(data: Bundle) { val network = data.getSerializable("network") as? LinkNetwork - val networkId = data.getInt("network_id", -1) - val identity = data.getInt("identity", -1) - if (networkId != -1 || (network != null && identity != -1)) { + val networkId = NetworkId(data.getInt("network_id", -1)) + val identity = IdentityId(data.getInt("identity", -1)) + if (networkId.isValidId() || (network != null && identity.isValidId())) { viewModel.backend?.value?.ifPresent { backend -> val session = viewModel.session.value?.orNull() session?.apply { rpcHandler?.apply { when { - networkId != -1 -> { + networkId.isValidId() -> { val buffer = bufferSyncer?.find(networkId = networkId, type = Buffer_Type.of(Buffer_Type.StatusBuffer)) if (buffer != null) { @@ -107,7 +108,7 @@ class NetworkSetupActivity : ServiceBoundSetupActivity() { bundle.putSerializable("network", network) } if (identity != null) { - bundle.putInt("identity", identity) + bundle.putInt("identity", identity.id) } if (channels != null) { bundle.putStringArray("channels", channels) diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt index 09b135725..c87f2a25a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt @@ -32,6 +32,8 @@ import androidx.lifecycle.Observer import butterknife.BindView import butterknife.ButterKnife import com.google.android.material.textfield.TextInputLayout +import de.kuschku.libquassel.protocol.IdentityId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork @@ -108,7 +110,7 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() { ) val networkId = (network.selectedItem as? INetwork.NetworkInfo)?.networkId if (networkId != null) { - data.putInt("network_id", networkId) + data.putInt("network_id", networkId.id) } else { data.putSerializable( "network", @@ -227,7 +229,7 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() { val linkNetwork = data.getSerializable("network") as? LinkNetwork val selectedNetworkId = if (data.containsKey("network_id")) { - data.getInt("network_id") + NetworkId(data.getInt("network_id")) } else { val existingNetwork = networks.firstOrNull { it.serverList.any { @@ -236,10 +238,10 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() { } existingNetwork?.networkId } - val selectedNetworkPosition = networkAdapter.indexOf(selectedNetworkId ?: -1) ?: -1 + val selectedNetworkPosition = networkAdapter.indexOf(selectedNetworkId ?: NetworkId(-1)) ?: -1 if (!hasSetNetwork) { - if (selectedNetworkPosition != -1 || selectedNetworkId == -1) { + if (selectedNetworkPosition != -1 || selectedNetworkId?.isValidId() != true) { network.setSelection(selectedNetworkPosition) hasSetNetwork = true } @@ -254,8 +256,8 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() { } if (data.containsKey("identity")) { - val identity = data.getInt("identity", -1) - if (identity != -1) { + val identity = IdentityId(data.getInt("identity", -1)) + if (identity.isValidId()) { val position = identityAdapter.indexOf(identity) if (position == -1) { this.identity.setSelection(-1) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt index 0ac7ea43d..ed67800fa 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt @@ -143,7 +143,7 @@ abstract class ServiceBoundActivity : protected fun checkConnection() { accountId = getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 val reconnect = sharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) { getBoolean(Keys.Status.reconnect, false) diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt index 019f7a7d3..d7d848a7c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundFragment.kt @@ -53,7 +53,7 @@ abstract class ServiceBoundFragment : DaggerFragment() { override fun onCreate(savedInstanceState: Bundle?) { accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 connection.context = context lifecycle.addObserver(connection) @@ -63,7 +63,7 @@ abstract class ServiceBoundFragment : DaggerFragment() { override fun onStart() { super.onStart() accountId = context?.getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE) - ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 + ?.getLong(Keys.Status.selectedAccount, -1) ?: -1 } override fun onDestroy() { diff --git a/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt b/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt index f1e4ed188..0f2f6743d 100644 --- a/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt +++ b/app/src/test/java/de/kuschku/quasseldroid/util/AvatarHelperTest.kt @@ -19,8 +19,7 @@ package de.kuschku.quasseldroid.util -import de.kuschku.libquassel.protocol.Message_Flag -import de.kuschku.libquassel.protocol.Message_Type +import de.kuschku.libquassel.protocol.* import de.kuschku.quasseldroid.persistence.QuasselDatabase import de.kuschku.quasseldroid.settings.MessageSettings import de.kuschku.quasseldroid.util.avatars.AvatarHelper @@ -31,13 +30,13 @@ import org.threeten.bp.Instant class AvatarHelperTest { @Test fun testGravatarAvatars() { - val message = QuasselDatabase.MessageData( - messageId = 1, + val message = QuasselDatabase.MessageData.of( + messageId = MsgId(1), time = Instant.now(), type = Message_Type.of(Message_Type.Plain), flag = Message_Flag.of(), - bufferId = 0, - networkId = 0, + bufferId = BufferId(0), + networkId = NetworkId(0), sender = "justJanne", senderPrefixes = "", realName = "Janne Koschinski <janne@kuschku.de>", @@ -71,13 +70,13 @@ class AvatarHelperTest { @Test fun testIrcCloudAvatars() { - val message = QuasselDatabase.MessageData( - messageId = 1, + val message = QuasselDatabase.MessageData.of( + messageId = MsgId(1), time = Instant.now(), type = Message_Type.of(Message_Type.Plain), flag = Message_Flag.of(), - bufferId = 0, - networkId = 0, + bufferId = BufferId(0), + networkId = NetworkId(0), sender = "jwheare!sid2@irccloud.com", senderPrefixes = "", realName = "James Wheare", @@ -111,13 +110,13 @@ class AvatarHelperTest { @Test fun testActualAvatars() { - val message = QuasselDatabase.MessageData( - messageId = 1, + val message = QuasselDatabase.MessageData.of( + messageId = MsgId(1), time = Instant.now(), type = Message_Type.of(Message_Type.Plain), flag = Message_Flag.of(), - bufferId = 0, - networkId = 0, + bufferId = BufferId(0), + networkId = NetworkId(0), sender = "jwheare!sid2@irccloud.com", senderPrefixes = "", realName = "James Wheare", diff --git a/invokergenerator/build.gradle.kts b/invokergenerator/build.gradle.kts index 0a03551ef..0e4ff262b 100644 --- a/invokergenerator/build.gradle.kts +++ b/invokergenerator/build.gradle.kts @@ -17,16 +17,16 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -import org.gradle.kotlin.dsl.DependencyHandlerScope -import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.project - plugins { - id("java") + kotlin("jvm") + kotlin("kapt") } dependencies { + implementation(kotlin("stdlib", "1.3.20")) implementation(project(":invokerannotations")) + implementation("org.jetbrains.kotlin", "kotlin-compiler-embeddable", "1.3.20") + implementation("com.squareup", "kotlinpoet", "1.0.1") implementation("com.google.auto.service:auto-service:1.0-rc4") - implementation("com.squareup:javapoet:1.11.1") + kapt("com.google.auto.service:auto-service:1.0-rc4") } diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt new file mode 100644 index 000000000..132adb407 --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Context.kt @@ -0,0 +1,29 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations + +import javax.annotation.processing.ProcessingEnvironment + +data class Context( + val processingEnv: ProcessingEnvironment, + val targetPath: String? = processingEnv.options["kapt.kotlin.generated"], + val sourcePath: String? = targetPath?.replace("build/generated/source/kaptKotlin/", + "src/") + "/java" +) diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt new file mode 100644 index 000000000..b5a4214cf --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/Helpers.kt @@ -0,0 +1,76 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations + +import org.jetbrains.kotlin.com.intellij.psi.PsiElement +import javax.annotation.processing.Messager +import javax.tools.Diagnostic + + +fun Any?.toIndentString(): String { + val notFancy = toString() + return buildString(notFancy.length) { + var indent = 0 + fun StringBuilder.line() { + appendln() + repeat(2 * indent) { append(' ') } + } + + for (char in notFancy) { + if (char == ' ') continue + + when (char) { + ')', ']' -> { + indent-- + line() + } + } + + if (char == '=') append(' ') + append(char) + if (char == '=') append(' ') + + when (char) { + '(', '[', ',' -> { + if (char != ',') indent++ + line() + } + } + } + } +} + +fun splitQualifiedName(qualifiedName: String): Pair<String, String> { + val index = qualifiedName.lastIndexOf('.') + return if (index >= 0 && index + 1 < qualifiedName.length) { + Pair(qualifiedName.substring(0, index), + qualifiedName.substring(index + 1)) + } else { + Pair("", qualifiedName) + } +} + +fun Messager.printAST(element: PsiElement, indent: String = "") { + printMessage(Diagnostic.Kind.NOTE, "$indent$element {") + for (child in element.children) { + printAST(child, "$indent ") + } + printMessage(Diagnostic.Kind.NOTE, "$indent}") +} diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java deleted file mode 100644 index a1fdccdba..000000000 --- a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Quasseldroid - Quassel client for Android - * - * Copyright (c) 2019 Janne Koschinski - * Copyright (c) 2019 The Quassel Project - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3 as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package de.kuschku.libquassel.annotations; - -import com.google.auto.service.AutoService; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.FieldSpec; -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.ParameterSpec; -import com.squareup.javapoet.ParameterizedTypeName; -import com.squareup.javapoet.TypeName; -import com.squareup.javapoet.TypeSpec; -import com.squareup.javapoet.WildcardTypeName; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.Filer; -import javax.annotation.processing.ProcessingEnvironment; -import javax.annotation.processing.Processor; -import javax.annotation.processing.RoundEnvironment; -import javax.annotation.processing.SupportedAnnotationTypes; -import javax.annotation.processing.SupportedSourceVersion; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.ExecutableType; -import javax.lang.model.type.TypeMirror; - -@SuppressWarnings("WeakerAccess") -@AutoService(Processor.class) -@SupportedAnnotationTypes("de.kuschku.libquassel.annotations.Syncable") -@SupportedSourceVersion(SourceVersion.RELEASE_8) -public class InvokerProcessor extends AbstractProcessor { - - private Filer filer; - - @Override - public synchronized void init(ProcessingEnvironment processingEnv) { - filer = processingEnv.getFiler(); - } - - @Override - public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { - List<SyncableElement> syncableElements = new ArrayList<>(); - for (Element element : roundEnv.getElementsAnnotatedWith(Syncable.class)) { - if (element.getKind() == ElementKind.INTERFACE) { - List<SlotElement> slotElements = new ArrayList<>(); - for (Element element1 : element.getEnclosedElements()) { - if (element1.getKind() == ElementKind.METHOD) { - ExecutableElement it = (ExecutableElement) element1; - ExecutableType methodType = (ExecutableType) it.asType(); - - Slot slotAnnotation = element1.getAnnotation(Slot.class); - if (slotAnnotation != null) { - String slotName = slotAnnotation.value().isEmpty() ? it.getSimpleName().toString() : slotAnnotation.value(); - slotElements.add(new SlotElement(it, methodType, slotName, slotAnnotation)); - } - } - } - - PackageElement packageElement = (PackageElement) element.getEnclosingElement(); - TypeElement typeElement = (TypeElement) element; - Syncable annotation = typeElement.getAnnotation(Syncable.class); - - syncableElements.add(new SyncableElement(packageElement, typeElement, annotation, slotElements)); - } - } - - try { - for (SyncableElement syncableElement : syncableElements) { - generateInvoker(syncableElement); - } - } catch (IOException e) { - e.printStackTrace(); - } - return true; - } - - private void generateInvoker(SyncableElement element) throws IOException { - String packageName = element.packageElement.getQualifiedName().toString() + ".invokers"; - String invokerName = element.annotation.name() + "Invoker"; - - ClassName type = ClassName.get(packageName, invokerName); - ClassName wrongObjectTypeException = ClassName.get("de.kuschku.libquassel.quassel.exceptions", "WrongObjectTypeException"); - ClassName unknownMethodException = ClassName.get("de.kuschku.libquassel.quassel.exceptions", "UnknownMethodException"); - ClassName nonNullAnnotation = ClassName.get("androidx.annotation", "NonNull"); - - MethodSpec methodSpecConstructor = MethodSpec - .constructorBuilder() - .addModifiers(Modifier.PRIVATE) - .build(); - - FieldSpec fieldSpecInstance = FieldSpec - .builder(type, "INSTANCE", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) - .initializer("new $T()", type) - .build(); - - MethodSpec methodSpecClassName = MethodSpec - .methodBuilder("getClassName") - .addModifiers(Modifier.PUBLIC) - .addAnnotation(nonNullAnnotation) - .addAnnotation(Override.class) - .returns(String.class) - .addStatement("return $S", element.annotation.name()) - .build(); - - ParameterSpec parameterSpecOn = ParameterSpec - .builder( - Object.class, - "on" - ) - .addAnnotation(nonNullAnnotation) - .build(); - - ParameterSpec parameterSpecMethod = ParameterSpec - .builder( - String.class, - "method" - ) - .addAnnotation(nonNullAnnotation).build(); - - ParameterSpec parameterSpecParams = ParameterSpec - .builder( - ParameterizedTypeName.get( - ClassName.get(List.class), - WildcardTypeName.subtypeOf( - ParameterizedTypeName.get( - ClassName.get("de.kuschku.libquassel.protocol", "QVariant"), - WildcardTypeName.subtypeOf(Object.class) - ) - ) - ), - "params" - ) - .addAnnotation(nonNullAnnotation) - .build(); - - MethodSpec.Builder invokeSpec = MethodSpec - .methodBuilder("invoke") - .addModifiers(Modifier.PUBLIC) - .addAnnotation(Override.class) - .addException(wrongObjectTypeException) - .addException(unknownMethodException) - .addParameter(parameterSpecOn) - .addParameter(parameterSpecMethod) - .addParameter(parameterSpecParams) - .beginControlFlow("if (on instanceof $T)", element.typeElement) - .addStatement("$T it = ($T) $N", element.typeElement, element.typeElement, parameterSpecOn) - .beginControlFlow("switch ($N)", parameterSpecMethod); - - for (SlotElement slot : element.slots) { - invokeSpec = invokeSpec.beginControlFlow("case $S:", slot.slotName); - invokeSpec = invokeSpec.addCode("it.$N(\n$>", slot.element.getSimpleName()); - for (int i = 0; i < slot.type.getParameterTypes().size(); i++) { - TypeMirror parameterType = slot.type.getParameterTypes().get(i); - boolean isLast = i + 1 == slot.type.getParameterTypes().size(); - - invokeSpec = invokeSpec.addCode("($T) ($T) $N.get($L).getData()", parameterType, Object.class, parameterSpecParams, i); - if (!isLast) - invokeSpec = invokeSpec.addCode(","); - invokeSpec = invokeSpec.addCode("\n"); - } - invokeSpec = invokeSpec.addCode("$<);\n"); - invokeSpec = invokeSpec.endControlFlow("return"); - } - - invokeSpec = invokeSpec - .beginControlFlow("default:") - .addStatement("throw new $T($N(), $N)", - unknownMethodException, - methodSpecClassName, - parameterSpecMethod - ) - .endControlFlow() - .endControlFlow() - .addCode("$<} else{\n$>") - .addStatement("throw new $T($N, $N())", - wrongObjectTypeException, - parameterSpecOn, - methodSpecClassName - ) - .endControlFlow(); - - TypeSpec typeSpec = TypeSpec - .classBuilder(type) - .addSuperinterface(ParameterizedTypeName.get( - ClassName.get(packageName, "Invoker"), - TypeName.get(element.typeElement.asType()) - )) - .addModifiers(Modifier.PUBLIC) - .addField(fieldSpecInstance) - .addMethod(methodSpecConstructor) - .addMethod(methodSpecClassName) - .addMethod(invokeSpec.build()) - .build(); - - JavaFile javaFile = JavaFile - .builder(packageName, typeSpec) - .build(); - - javaFile.writeTo(filer); - } - - private class SlotElement { - final ExecutableElement element; - final ExecutableType type; - - final String slotName; - - final Slot slot; - - public SlotElement(ExecutableElement element, ExecutableType type, String slotName, Slot slot) { - this.element = element; - this.type = type; - this.slotName = slotName; - this.slot = slot; - } - } - - private class SyncableElement { - final PackageElement packageElement; - final TypeElement typeElement; - - final Syncable annotation; - - final List<SlotElement> slots; - - public SyncableElement(PackageElement packageElement, TypeElement typeElement, Syncable annotation, List<SlotElement> slots) { - this.packageElement = packageElement; - this.typeElement = typeElement; - this.annotation = annotation; - this.slots = slots; - } - } -} diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt new file mode 100644 index 000000000..cb23c99c2 --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/InvokerProcessor.kt @@ -0,0 +1,54 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations + +import com.google.auto.service.AutoService +import de.kuschku.libquassel.annotations.generator.Generator +import de.kuschku.libquassel.annotations.parser.ParserEnvironment +import javax.annotation.processing.* +import javax.lang.model.SourceVersion +import javax.lang.model.element.TypeElement + +@AutoService(Processor::class) +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedAnnotationTypes("de.kuschku.libquassel.annotations.Syncable") +class InvokerProcessor : AbstractProcessor() { + lateinit var parserEnvironment: ParserEnvironment + lateinit var generator: Generator + + @Synchronized + override fun init(processingEnv: ProcessingEnvironment) { + val context = Context(processingEnv) + parserEnvironment = ParserEnvironment(context) + generator = Generator(context) + } + + override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { + parserEnvironment.use { parser -> + for (annotatedElement in roundEnv.getElementsAnnotatedWith(Syncable::class.java)) { + val parsedClass = parser.parse(annotatedElement) + if (parsedClass != null) { + generator.generate(parsedClass) + } + } + } + return true + } +} diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt new file mode 100644 index 000000000..31069e582 --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedClass.kt @@ -0,0 +1,28 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations.data + +import com.squareup.kotlinpoet.ClassName + +data class ParsedClass( + val name: ClassName, + val quasselName: String, + val methods: List<ParsedMethod> +) diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt new file mode 100644 index 000000000..e2fa1ab2d --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedMethod.kt @@ -0,0 +1,26 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations.data + +data class ParsedMethod( + val name: String?, + val quasselName: String, + val parameters: List<ParsedParameter> +) diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt new file mode 100644 index 000000000..a8211f535 --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/data/ParsedParameter.kt @@ -0,0 +1,27 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations.data + +import com.squareup.kotlinpoet.TypeName + +data class ParsedParameter( + val name: String, + val type: TypeName +) diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt new file mode 100644 index 000000000..fd95ac366 --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/generator/Generator.kt @@ -0,0 +1,112 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations.generator + +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import de.kuschku.libquassel.annotations.Context +import de.kuschku.libquassel.annotations.data.ParsedClass +import java.io.File + +class Generator( + private val context: Context +) { + fun generate(parsedClass: ParsedClass) { + val file = FileSpec.builder( + parsedClass.name.packageName + ".invokers", + parsedClass.quasselName + "Invoker" + ).addType( + TypeSpec.objectBuilder(parsedClass.quasselName + "Invoker") + .addSuperinterface(TYPENAME_INVOKER.parameterizedBy(parsedClass.name)) + .addProperty( + PropertySpec.builder( + "className", + String::class.asTypeName(), + KModifier.OVERRIDE + ).initializer("\"${parsedClass.quasselName}\"").build() + ) + .addFunction( + FunSpec.builder("invoke") + .addModifiers(KModifier.OVERRIDE, KModifier.OPERATOR) + .addParameter( + ParameterSpec.builder( + "on", + ANY.copy(nullable = true) + ).build() + ).addParameter( + ParameterSpec.builder( + "method", + String::class.asTypeName() + ).build() + ).addParameter( + ParameterSpec.builder( + "params", + TYPENAME_QVARIANTLIST + ).build() + ) + .addCode( + buildCodeBlock { + beginControlFlow("if (on is %T)", parsedClass.name) + beginControlFlow("when (method)") + for (method in parsedClass.methods) { + beginControlFlow("%S ->", method.quasselName) + addStatement("on.${method.name}(") + indent() + val lastIndex = method.parameters.size - 1 + for ((i, parameter) in method.parameters.withIndex()) { + if (i == lastIndex) { + addStatement("${parameter.name} = params[$i].data as %T", parameter.type) + } else { + addStatement("${parameter.name} = params[$i].data as %T,", parameter.type) + } + } + unindent() + addStatement(")") + endControlFlow() + } + endControlFlow() + nextControlFlow("else") + addStatement("throw %T(on, className)", TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION) + endControlFlow() + } + ) + .build() + ) + .build() + ).build() + + file.writeTo(File(context.targetPath)) + } + + companion object { + private val TYPENAME_INVOKER = ClassName( + "de.kuschku.libquassel.quassel.syncables.interfaces.invokers", + "Invoker" + ) + private val TYPENAME_QVARIANTLIST = ClassName( + "de.kuschku.libquassel.protocol", + "QVariantList" + ) + private val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName( + "de.kuschku.libquassel.quassel.exceptions", + "WrongObjectTypeException" + ) + } +} diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt new file mode 100644 index 000000000..e25bb6dde --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/Parser.kt @@ -0,0 +1,255 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations.parser + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.asClassName +import de.kuschku.libquassel.annotations.Context +import de.kuschku.libquassel.annotations.Slot +import de.kuschku.libquassel.annotations.Syncable +import de.kuschku.libquassel.annotations.data.ParsedClass +import de.kuschku.libquassel.annotations.data.ParsedMethod +import de.kuschku.libquassel.annotations.data.ParsedParameter +import de.kuschku.libquassel.annotations.splitQualifiedName +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.com.intellij.psi.PsiFileFactory +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType +import java.io.File +import java.net.JarURLConnection +import javax.lang.model.element.Element +import javax.lang.model.element.TypeElement + +class Parser( + private val context: Context, + private val environment: KotlinCoreEnvironment +) { + fun parse(element: Element): ParsedClass? { + val typeElement = element as TypeElement + + val sourcePath = typeElement.qualifiedName.toString().replace('.', '/') + ".kt" + val sourceFile = File(context.sourcePath, sourcePath) + + val source = sourceFile.readText(Charsets.UTF_8) + + val file = PsiFileFactory.getInstance(environment.project) + .createFileFromText(KotlinLanguage.INSTANCE, source) + + val importDirectives = file.collectDescendantsOfType<KtImportDirective>() + val imports = buildImports( + wildcard = importDirectives.filter { + it.importedName == null + }.mapNotNull { + it.importPath?.toString() + }, + named = importDirectives.mapNotNull { + val simpleName = it.alias?.toString() ?: it.importedName?.toString() + val qualifiedName = it.importPath?.toString() + if (simpleName != null && qualifiedName != null) { + Pair(simpleName, qualifiedName) + } else { + null + } + } + ) + + val clazz = file.collectDescendantsOfType<KtClass> { + it.isInterface() && it.annotationEntries.any { + it.shortName.toString() == Syncable::class.java.simpleName + } + }.first() + val body = clazz.findDescendantOfType<KtClassBody>() + + if (body != null) { + val methods = body.collectDescendantsOfType<KtFunction> { + it.annotationEntries.any { + it.shortName.toString() == Slot::class.java.simpleName + } + } + + val subclassImports = body.children.mapNotNull { + it as? KtClass + }.map { + Pair(it.name!!, typeElement.asClassName().canonicalName + "." + it.name) + }.toMap() + val importsWithSubclasses = imports + subclassImports + + return ParsedClass( + name = typeElement.asClassName(), + quasselName = clazz.parseAnnotations<Syncable>()["name"] + ?: clazz.name + ?: "", + methods = methods.map { method -> + parseMethod(method, importsWithSubclasses) + } + ) + } + return null + } + + private fun parseMethod(method: KtFunction, imports: Map<String, String>) = ParsedMethod( + name = method.name + ?: "", + quasselName = method.parseAnnotations<Slot>()["value"] + ?: method.name + ?: "", + parameters = method + .findDescendantOfType<KtParameterList>() + ?.collectDescendantsOfType<KtParameter>() + .orEmpty() + .map { + parseParameter(it, imports) + } + ) + + private fun parseTypeReference(typeReference: KtTypeReference?, + imports: Map<String, String>): TypeName { + val child = typeReference?.firstChild + return when (child) { + is KtUserType -> parseUserType(child, imports) + is KtNullableType -> parseNullableType(child, imports) + else -> throw IllegalArgumentException("Invalid Type") + } ?: throw IllegalArgumentException("Invalid Type") + } + + private fun parseUserType(type: KtUserType, imports: Map<String, String>): TypeName? { + val qualifiedName = resolveImport(imports, type.referencedName.toString()) + val typeArguments = type.children.mapNotNull { + it as? KtTypeArgumentList + }.firstOrNull()?.children.orEmpty().mapNotNull { + it as? KtTypeProjection + }.mapNotNull { + it.typeReference + }.map { + parseTypeReference(it, imports) + }.toTypedArray() + val (packageName, className) = splitQualifiedName(qualifiedName) + val typeName = ClassName(packageName, className) + return if (typeArguments.isEmpty()) { + typeName + } else { + typeName.parameterizedBy(*typeArguments) + } + } + + private fun parseNullableType(type: KtNullableType, imports: Map<String, String>) = + type.findDescendantOfType<KtUserType>()?.let { + parseUserType(it, imports) + }?.copy(nullable = true) + + private fun parseParameter(parameter: KtParameter, imports: Map<String, String>) = + ParsedParameter( + parameter.name!!, + parseTypeReference(parameter.findDescendantOfType(), imports) + ) + + private inline fun <reified T> KtAnnotated.parseAnnotations(): Map<String, String?> = + annotationEntries + .first { + it.shortName.toString() == T::class.java.simpleName + } + .findDescendantOfType<KtValueArgumentList>() + ?.collectDescendantsOfType<KtValueArgument>() + .orEmpty() + .map { + Pair( + it.findDescendantOfType<KtValueArgumentName>() + ?.findDescendantOfType<KtReferenceExpression>() + ?.text + ?: "value", + it.findDescendantOfType<KtLiteralStringTemplateEntry>()?.text + ) + } + .toMap() + + private fun resolveImport(imports: Map<String, String>, import: String): String { + val qualifiedName = imports.getOrDefault(import, import) + return JavaToKotlinClassMap.mapJavaToKotlin(FqName(qualifiedName))?.asSingleFqName()?.asString() + ?: qualifiedName + } + + private fun resolveWildcardImport(import: String): List<Pair<String, String>> { + val imports = mutableListOf<Pair<String, String>>() + + val packageName = import.removeSuffix(".*") + val packagePath = packageName.replace('.', '/') + val folder = File(context.sourcePath, packagePath) + val sourceFiles = folder.listFiles()?.filter { it.isFile } + if (sourceFiles == null) { + val jarURLConnection = Object::class.java.getResource("Object.class").openConnection() as JarURLConnection + val jdkFile = jarURLConnection.jarFile + for (classEntry in jdkFile.entries()) { + if (classEntry.name.endsWith(".class") && + classEntry.name.startsWith(packagePath) && + !classEntry.name.contains('$')) { + val qualifiedName = classEntry.name.removeSuffix(".class").replace('/', '.') + val (_, simpleName) = splitQualifiedName(qualifiedName) + imports.add(Pair(simpleName, qualifiedName)) + } + } + } else { + for (sourceFile in sourceFiles) { + val source = sourceFile.readText(Charsets.UTF_8) + val file = PsiFileFactory.getInstance(environment.project) + .createFileFromText(KotlinLanguage.INSTANCE, source) + + val foundPackageName = file.findDescendantOfType<KtPackageDirective>() + ?.qualifiedName + + val classes = file.findDescendantOfType<KtScript>()?.children?.mapNotNull { + it as? KtBlockExpression + }.orEmpty().flatMap { it.children.asIterable() }.mapNotNull { + it as? KtClass + } + for (clazz in classes) { + val className = clazz.name + if (className != null) { + imports.add( + Pair( + className, + listOfNotNull(foundPackageName, className).joinToString(".") + ) + ) + } + } + + val typeAliases = file.collectDescendantsOfType<KtTypeAlias>() + for (typeAlias in typeAliases) { + val className = typeAlias.name + if (className != null) { + imports.add(Pair( + className, + listOfNotNull(foundPackageName, className).joinToString("."))) + } + } + } + } + return imports + } + + private fun buildImports(wildcard: List<String>, named: List<Pair<String, String>>) = + (wildcard.flatMap(this::resolveWildcardImport) + named).toMap() +} diff --git a/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt new file mode 100644 index 000000000..3d83a30ec --- /dev/null +++ b/invokergenerator/src/main/java/de/kuschku/libquassel/annotations/parser/ParserEnvironment.kt @@ -0,0 +1,52 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.annotations.parser + +import de.kuschku.libquassel.annotations.Context +import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer +import org.jetbrains.kotlin.config.CompilerConfiguration + +class ParserEnvironment( + private val context: Context +) { + fun <T> use(f: (Parser) -> T): T { + val rootDisposable = Disposer.newDisposable() + try { + val environment = KotlinCoreEnvironment.createForProduction( + rootDisposable, + CompilerConfiguration().apply { + context.sourcePath?.let { + addKotlinSourceRoot(it, isCommon = false) + } + }, + EnvironmentConfigFiles.JVM_CONFIG_FILES + ) + + val parser = Parser(context, environment) + + return f(parser) + } finally { + rootDisposable.dispose() + } + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt index 977970a12..3ae8faa1b 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QType.kt @@ -23,7 +23,7 @@ import de.kuschku.libquassel.protocol.primitive.serializer.* enum class QType(val typeName: String, val serializer: Serializer<*>, val type: Type = Type.UserType) { - BufferId("BufferId", IntSerializer), + BufferId("BufferId", BufferIdSerializer), BufferInfo("BufferInfo", BufferInfoSerializer), DccConfig_IpDetectionMode("DccConfig::IpDetectionMode", DccConfig_IpDetectionModeSerializer), @@ -32,10 +32,10 @@ enum class QType(val typeName: String, val serializer: Serializer<*>, IrcUser("IrcUser", VariantMapSerializer), IrcChannel("IrcChannel", VariantMapSerializer), Identity("Identity", VariantMapSerializer), - IdentityId("IdentityId", IntSerializer), + IdentityId("IdentityId", IdentityIdSerializer), Message("Message", MessageSerializer), - MsgId("MsgId", SignedId64Serializer), - NetworkId("NetworkId", IntSerializer), + MsgId("MsgId", MsgIdSerializer), + NetworkId("NetworkId", NetworkIdSerializer), NetworkInfo("NetworkInfo", VariantMapSerializer), Network_Server("Network::Server", VariantMapSerializer), QHostAddress("QHostAddress", HostAddressSerializer), diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt index 79ac7d4c6..eb5ad4338 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QTypes.kt @@ -35,14 +35,6 @@ typealias QVariant_ = QVariant<*> typealias QVariantMap = Map<String, QVariant_> typealias QVariantList = List<QVariant_> -typealias SignedId = Int -typealias SignedId64 = Long - -typealias IdentityId = SignedId -typealias BufferId = SignedId -typealias MsgId = SignedId64 -typealias NetworkId = SignedId - typealias Message_Type = Message.MessageType typealias Message_Types = Flags<Message_Type> diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt new file mode 100644 index 000000000..1167b82dd --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/SignedId.kt @@ -0,0 +1,87 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol + +typealias SignedId = Int +typealias SignedId64 = Long + +typealias MsgId_Type = SignedId64 + +inline class MsgId(val id: MsgId_Type) : Comparable<MsgId> { + override fun compareTo(other: MsgId) = id.compareTo(other.id) + inline fun isValidId() = id > 0 + + override fun toString(): String { + return "MsgId($id)" + } + + companion object { + val MIN_VALUE = MsgId(MsgId_Type.MIN_VALUE) + val MAX_VALUE = MsgId(MsgId_Type.MAX_VALUE) + } +} + +typealias NetworkId_Type = SignedId + +inline class NetworkId(val id: NetworkId_Type) : Comparable<NetworkId> { + override fun compareTo(other: NetworkId) = id.compareTo(other.id) + inline fun isValidId() = id > 0 + + override fun toString(): String { + return "NetworkId($id)" + } + + companion object { + val MIN_VALUE = NetworkId(NetworkId_Type.MIN_VALUE) + val MAX_VALUE = NetworkId(NetworkId_Type.MAX_VALUE) + } +} + +typealias BufferId_Type = SignedId + +inline class BufferId(val id: BufferId_Type) : Comparable<BufferId> { + override fun compareTo(other: BufferId) = id.compareTo(other.id) + inline fun isValidId() = id > 0 + + override fun toString(): String { + return "BufferId($id)" + } + + companion object { + val MIN_VALUE = BufferId(BufferId_Type.MIN_VALUE) + val MAX_VALUE = BufferId(BufferId_Type.MAX_VALUE) + } +} + +typealias IdentityId_Type = SignedId + +inline class IdentityId(val id: IdentityId_Type) : Comparable<IdentityId> { + override fun compareTo(other: IdentityId) = id.compareTo(other.id) + inline fun isValidId() = id > 0 + + override fun toString(): String { + return "IdentityId($id)" + } + + companion object { + val MIN_VALUE = IdentityId(IdentityId_Type.MIN_VALUE) + val MAX_VALUE = IdentityId(IdentityId_Type.MAX_VALUE) + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt new file mode 100644 index 000000000..96754d435 --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferIdSerializer.kt @@ -0,0 +1,35 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.primitive.serializer + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.util.nio.ChainedByteBuffer +import java.nio.ByteBuffer + +object BufferIdSerializer : Serializer<BufferId> { + override fun serialize(buffer: ChainedByteBuffer, data: BufferId, features: QuasselFeatures) { + SignedIdSerializer.serialize(buffer, data.id, features) + } + + override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): BufferId { + return BufferId(SignedIdSerializer.deserialize(buffer, features)) + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt index ab9da5837..11d4758d1 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializer.kt @@ -27,16 +27,16 @@ import java.nio.ByteBuffer object BufferInfoSerializer : Serializer<BufferInfo> { override fun serialize(buffer: ChainedByteBuffer, data: BufferInfo, features: QuasselFeatures) { - IntSerializer.serialize(buffer, data.bufferId, features) - IntSerializer.serialize(buffer, data.networkId, features) + BufferIdSerializer.serialize(buffer, data.bufferId, features) + NetworkIdSerializer.serialize(buffer, data.networkId, features) ShortSerializer.serialize(buffer, data.type.toShort(), features) IntSerializer.serialize(buffer, data.groupId, features) StringSerializer.UTF8.serialize(buffer, data.bufferName, features) } override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): BufferInfo { - val bufferId = IntSerializer.deserialize(buffer, features) - val networkId = IntSerializer.deserialize(buffer, features) + val bufferId = BufferIdSerializer.deserialize(buffer, features) + val networkId = NetworkIdSerializer.deserialize(buffer, features) val type = Buffer_Type.of(ShortSerializer.deserialize(buffer, features)) val groupId = IntSerializer.deserialize(buffer, features) val bufferName = StringSerializer.UTF8.deserialize(buffer, features) diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt new file mode 100644 index 000000000..7283fe893 --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/IdentityIdSerializer.kt @@ -0,0 +1,35 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.primitive.serializer + +import de.kuschku.libquassel.protocol.IdentityId +import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.util.nio.ChainedByteBuffer +import java.nio.ByteBuffer + +object IdentityIdSerializer : Serializer<IdentityId> { + override fun serialize(buffer: ChainedByteBuffer, data: IdentityId, features: QuasselFeatures) { + SignedIdSerializer.serialize(buffer, data.id, features) + } + + override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): IdentityId { + return IdentityId(SignedIdSerializer.deserialize(buffer, features)) + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt index 4d1aa2a33..e3d038887 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializer.kt @@ -28,7 +28,7 @@ import java.nio.ByteBuffer object MessageSerializer : Serializer<Message> { override fun serialize(buffer: ChainedByteBuffer, data: Message, features: QuasselFeatures) { - SignedId64Serializer.serialize(buffer, data.messageId, features) + MsgIdSerializer.serialize(buffer, data.messageId, features) if (features.hasFeature(ExtendedFeature.LongTime)) LongSerializer.serialize(buffer, data.time.toEpochMilli(), features) else @@ -48,7 +48,7 @@ object MessageSerializer : Serializer<Message> { override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): Message { return Message( - messageId = SignedId64Serializer.deserialize(buffer, features), + messageId = MsgIdSerializer.deserialize(buffer, features), time = if (features.hasFeature(ExtendedFeature.LongTime)) Instant.ofEpochMilli(LongSerializer.deserialize(buffer, features)) else diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt new file mode 100644 index 000000000..818f93066 --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/MsgIdSerializer.kt @@ -0,0 +1,35 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.primitive.serializer + +import de.kuschku.libquassel.protocol.MsgId +import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.util.nio.ChainedByteBuffer +import java.nio.ByteBuffer + +object MsgIdSerializer : Serializer<MsgId> { + override fun serialize(buffer: ChainedByteBuffer, data: MsgId, features: QuasselFeatures) { + SignedId64Serializer.serialize(buffer, data.id, features) + } + + override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): MsgId { + return MsgId(SignedId64Serializer.deserialize(buffer, features)) + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt new file mode 100644 index 000000000..5adc15ebc --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/NetworkIdSerializer.kt @@ -0,0 +1,35 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.primitive.serializer + +import de.kuschku.libquassel.protocol.NetworkId +import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.util.nio.ChainedByteBuffer +import java.nio.ByteBuffer + +object NetworkIdSerializer : Serializer<NetworkId> { + override fun serialize(buffer: ChainedByteBuffer, data: NetworkId, features: QuasselFeatures) { + SignedIdSerializer.serialize(buffer, data.id, features) + } + + override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): NetworkId { + return NetworkId(SignedIdSerializer.deserialize(buffer, features)) + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt new file mode 100644 index 000000000..a9faab417 --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/SignedIdSerializer.kt @@ -0,0 +1,35 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.libquassel.protocol.primitive.serializer + +import de.kuschku.libquassel.protocol.SignedId +import de.kuschku.libquassel.quassel.QuasselFeatures +import de.kuschku.libquassel.util.nio.ChainedByteBuffer +import java.nio.ByteBuffer + +object SignedIdSerializer : Serializer<SignedId> { + override fun serialize(buffer: ChainedByteBuffer, data: SignedId, features: QuasselFeatures) { + buffer.putInt(data) + } + + override fun deserialize(buffer: ByteBuffer, features: QuasselFeatures): SignedId { + return buffer.int + } +} diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt index 3e7510584..e70453469 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt @@ -19,16 +19,18 @@ package de.kuschku.libquassel.quassel +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.Buffer_Types +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.util.flag.Flag import de.kuschku.libquassel.util.flag.Flags import de.kuschku.libquassel.util.flag.ShortFlag import de.kuschku.libquassel.util.flag.ShortFlags data class BufferInfo( - var bufferId: Int = -1, - var networkId: Int = -1, + var bufferId: BufferId = BufferId(-1), + var networkId: NetworkId = NetworkId(-1), var type: Buffer_Types = Buffer_Type.of(), var groupId: Int = -1, var bufferName: String? = null 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 369d28711..31188fed8 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 @@ -44,33 +44,34 @@ class BacklogManager( fun updateIgnoreRules() = backlogStorage?.updateIgnoreRules(session) - fun requestBacklog(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, limit: Int = -1, - additional: Int = 0, callback: (List<Message>) -> Boolean) { + fun requestBacklog(bufferId: BufferId, first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), + limit: Int = -1, additional: Int = 0, callback: (List<Message>) -> Boolean) { if (loading.contains(bufferId)) return loading[bufferId] = callback requestBacklog(bufferId, first, last, limit, additional) } - fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, - limit: Int = -1, additional: Int = 0, type: Int = -1, flags: Int = -1, + fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = MsgId(-1), + last: MsgId = MsgId(-1), limit: Int = -1, additional: Int = 0, + type: Int = -1, flags: Int = -1, callback: (List<Message>) -> Boolean) { if (loadingFiltered.contains(bufferId)) return loadingFiltered[bufferId] = callback requestBacklogFiltered(bufferId, first, last, limit, additional, type, flags) } - fun requestBacklogAll(first: MsgId = -1, last: MsgId = -1, limit: Int = -1, additional: Int = 0, - callback: (List<Message>) -> Boolean) { - if (loading.contains(-1)) return - loading[-1] = callback + fun requestBacklogAll(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1, + additional: Int = 0, callback: (List<Message>) -> Boolean) { + if (loading.contains(BufferId(-1))) return + loading[BufferId(-1)] = callback requestBacklogAll(first, last, limit, additional) } - fun requestBacklogAllFiltered(first: MsgId = -1, last: MsgId = -1, limit: Int = -1, - additional: Int = 0, type: Int = -1, flags: Int = -1, - callback: (List<Message>) -> Boolean) { - if (loading.contains(-1)) return - loadingFiltered[-1] = callback + fun requestBacklogAllFiltered(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), + limit: Int = -1, additional: Int = 0, type: Int = -1, + flags: Int = -1, callback: (List<Message>) -> Boolean) { + if (loadingFiltered.contains(BufferId(-1))) return + loadingFiltered[BufferId(-1)] = callback requestBacklogAllFiltered(first, last, limit, additional, type, flags) } @@ -86,7 +87,7 @@ class BacklogManager( override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList) { val list = messages.mapNotNull<QVariant_, Message>(QVariant_::value) - if (loading.remove(-1)?.invoke(list) != false) { + if (loading.remove(BufferId(-1))?.invoke(list) != false) { log(DEBUG, "BacklogManager", "storeMessages(${list.size})") backlogStorage?.storeMessages(session, list) } @@ -105,7 +106,7 @@ class BacklogManager( override fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int, flags: Int, messages: QVariantList) { val list = messages.mapNotNull<QVariant_, Message>(QVariant_::value) - if (loadingFiltered.remove(-1)?.invoke(list) != false) { + if (loadingFiltered.remove(BufferId(-1))?.invoke(list) != false) { log(DEBUG, "BacklogManager", "storeMessages(${list.size})") backlogStorage?.storeMessages(session, list) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt index fbc7df8cb..6a1c9e48f 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt @@ -39,7 +39,7 @@ class BufferSyncer constructor( session = ISession.NULL } - fun lastSeenMsg(buffer: BufferId): MsgId = _lastSeenMsg[buffer] ?: 0 + fun lastSeenMsg(buffer: BufferId): MsgId = _lastSeenMsg[buffer] ?: MsgId(0) fun liveLastSeenMsg(buffer: BufferId): Observable<MsgId> = live_lastSeenMsg.map { markerLine(buffer) }.distinctUntilChanged() @@ -47,7 +47,7 @@ class BufferSyncer constructor( fun liveLastSeenMsgs(): Observable<Map<BufferId, MsgId>> = live_lastSeenMsg.map { _lastSeenMsg.toMap() } - fun markerLine(buffer: BufferId): MsgId = _markerLines[buffer] ?: 0 + fun markerLine(buffer: BufferId): MsgId = _markerLines[buffer] ?: MsgId(0) fun liveMarkerLine(buffer: BufferId): Observable<MsgId> = live_markerLines.map { markerLine(buffer) }.distinctUntilChanged() @@ -129,7 +129,7 @@ class BufferSyncer constructor( override fun initSetActivities(data: QVariantList) { (0 until data.size step 2).map { - data[it].value(0) to data[it + 1].value(0) + data[it].value(BufferId(0)) to data[it + 1].value(0) }.forEach { (buffer, activity) -> setBufferActivity(buffer, activity) } @@ -138,7 +138,7 @@ class BufferSyncer constructor( override fun initSetHighlightCounts(data: QVariantList) { (0 until data.size step 2).map { - data[it].value(0) to data[it + 1].value(0) + data[it].value(BufferId(0)) to data[it + 1].value(0) }.forEach { (buffer, count) -> setHighlightCount(buffer, count) } @@ -147,7 +147,7 @@ class BufferSyncer constructor( override fun initSetLastSeenMsg(data: QVariantList) { (0 until data.size step 2).map { - data[it].value(0) to data[it + 1].value(0L) + data[it].value(BufferId(0)) to data[it + 1].value(MsgId(0L)) }.forEach { (buffer, msgId) -> setLastSeenMsg(buffer, msgId) } @@ -156,7 +156,7 @@ class BufferSyncer constructor( override fun initSetMarkerLines(data: QVariantList) { (0 until data.size step 2).map { - data[it].value(0) to data[it + 1].value(0L) + data[it].value(BufferId(0)) to data[it + 1].value(MsgId(0L)) }.forEach { (buffer, msgId) -> setMarkerLine(buffer, msgId) } @@ -204,7 +204,7 @@ class BufferSyncer constructor( } override fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) { - if (msgId < 0) + if (msgId < MsgId(0)) return val oldLastSeenMsg = lastSeenMsg(buffer) @@ -217,7 +217,7 @@ class BufferSyncer constructor( } override fun setMarkerLine(buffer: BufferId, msgId: MsgId) { - if (msgId < 0 || markerLine(buffer) == msgId) + if (msgId < MsgId(0) || markerLine(buffer) == msgId) return _markerLines[buffer] = msgId @@ -260,7 +260,7 @@ class BufferSyncer constructor( }.filter { groupId == null || it.groupId == groupId }.filter { - val caseMapper = IrcCaseMappers[session.networks[it.bufferId]?.support("CASEMAPPING")] + val caseMapper = IrcCaseMappers[session.networks[it.networkId]?.support("CASEMAPPING")] bufferName == null || caseMapper.equalsIgnoreCaseNullable(it.bufferName, bufferName) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt index 59a3669d7..22ebb65ef 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt @@ -130,12 +130,12 @@ class BufferViewConfig constructor( if (currentPos > targetPos) { _buffers.removeAt(currentPos) - _buffers.add(bufferId, targetPos) + _buffers.add(targetPos, bufferId) } if (currentPos < targetPos) { _buffers.removeAt(currentPos) - _buffers.add(bufferId, targetPos - 1) + _buffers.add(targetPos - 1, bufferId) } live_buffers.onNext(Unit) @@ -267,7 +267,7 @@ class BufferViewConfig constructor( field = value live_config.onNext(Unit) } - private var _networkId: NetworkId = 0 + private var _networkId: NetworkId = NetworkId(0) set(value) { field = value live_config.onNext(Unit) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt index fdf6bc6d7..948e28b47 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/CertManager.kt @@ -29,7 +29,7 @@ class CertManager constructor( proxy: SignalProxy ) : SyncableObject(proxy, "CertManager"), ICertManager { override fun init() { - renameObject("$_identityId") + renameObject("${_identityId.id}") } override fun toVariantMap() = initProperties() diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt index e036e1d77..0b50a9040 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Identity.kt @@ -34,7 +34,7 @@ class Identity constructor( } override fun init() { - renameObject("${id()}") + renameObject("${id().id}") } override fun initProperties(): QVariantMap = mapOf( @@ -210,7 +210,7 @@ class Identity constructor( private val _change = BehaviorSubject.createDefault(Unit) - private var _identityId: IdentityId = -1 + private var _identityId: IdentityId = IdentityId(-1) set(value) { field = value _change.onNext(Unit) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt index b97056c4e..6819fff92 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt @@ -40,7 +40,7 @@ class IrcChannel( if (name().isEmpty()) { log(ERROR, "IrcChannel", "Error: channelName is empty") } - renameObject("${network().networkId()}/${name()}") + renameObject("${network().networkId().id}/${name()}") } override fun toVariantMap(): QVariantMap = mapOf( diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt index e067c96bb..0ffd69501 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcListHelper.kt @@ -30,7 +30,7 @@ import de.kuschku.libquassel.util.rxjava.ReusableUnicastSubject class IrcListHelper constructor( proxy: SignalProxy ) : SyncableObject(proxy, "IrcListHelper"), IIrcListHelper { - private var waitingNetwork: NetworkId = 0 + private var waitingNetwork: NetworkId = NetworkId(0) data class ChannelDescription( val netId: NetworkId, @@ -75,7 +75,7 @@ class IrcListHelper constructor( override fun reportFinishedList(netId: NetworkId) { if (waitingNetwork == netId) { - waitingNetwork = 0 + waitingNetwork = NetworkId(0) requestChannelList(netId, emptyList()) subject.onNext(Event.Finished(netId)) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt index 2ca56f3ac..cf21fd1fc 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt @@ -283,7 +283,7 @@ class IrcUser( } fun updateObjectName() { - val identifier = "${network().networkId()}/${nick()}" + val identifier = "${network().networkId().id}/${nick()}" renameObject(identifier) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt index d0ef17881..77e28b4b0 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt @@ -39,7 +39,7 @@ class Network constructor( proxy: SignalProxy ) : SyncableObject(proxy, "Network"), INetwork { override fun init() { - renameObject("$_networkId") + renameObject("${_networkId.id}") } override fun fromVariantMap(properties: QVariantMap) { @@ -190,7 +190,7 @@ class Network constructor( // we don't set our ID! if (!info.networkName.isNullOrEmpty() && info.networkName != networkName()) setNetworkName(info.networkName) - if (info.identity > 0 && info.identity != identity()) + if (info.identity.isValidId() && info.identity != identity()) setIdentity(info.identity) if (info.codecForServer != codecForServer()) setCodecForServer(info.codecForServer) @@ -858,7 +858,7 @@ class Network constructor( field = value live_networkInfo.onNext(Unit) } - private var _identity: IdentityId = -1 + private var _identity: IdentityId = IdentityId(-1) set(value) { field = value live_networkInfo.onNext(Unit) @@ -1063,6 +1063,6 @@ class Network constructor( } companion object { - val NULL = Network(-1, SignalProxy.NULL) + val NULL = Network(NetworkId(-1), SignalProxy.NULL) } } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt index f7b83e55c..585ca94b5 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IBacklogManager.kt @@ -27,8 +27,8 @@ import de.kuschku.libquassel.protocol.Type @Syncable(name = "BacklogManager") interface IBacklogManager : ISyncableObject { @Slot - fun requestBacklog(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, limit: Int = -1, - additional: Int = 0) { + fun requestBacklog(bufferId: BufferId, first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), + limit: Int = -1, additional: Int = 0) { REQUEST( "requestBacklog", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId), ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int) @@ -36,9 +36,9 @@ interface IBacklogManager : ISyncableObject { } @Slot - fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = -1, last: MsgId = -1, - limit: Int = -1, additional: Int = 0, type: Int = -1, - flags: Int = -1) { + fun requestBacklogFiltered(bufferId: BufferId, first: MsgId = MsgId(-1), + last: MsgId = MsgId(-1), limit: Int = -1, additional: Int = 0, + type: Int = -1, flags: Int = -1) { REQUEST( "requestBacklogFiltered", ARG(bufferId, QType.BufferId), ARG(first, QType.MsgId), ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int), ARG(type, Type.Int), @@ -47,7 +47,7 @@ interface IBacklogManager : ISyncableObject { } @Slot - fun requestBacklogAll(first: MsgId = -1, last: MsgId = -1, limit: Int = -1, + fun requestBacklogAll(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1, additional: Int = 0) { REQUEST( "requestBacklogAll", ARG(first, QType.MsgId), ARG(last, QType.MsgId), @@ -56,8 +56,9 @@ interface IBacklogManager : ISyncableObject { } @Slot - fun requestBacklogAllFiltered(first: MsgId = -1, last: MsgId = -1, limit: Int = -1, - additional: Int = 0, type: Int = -1, flags: Int = -1) { + fun requestBacklogAllFiltered(first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), + limit: Int = -1, additional: Int = 0, type: Int = -1, + flags: Int = -1) { REQUEST( "requestBacklogAllFiltered", ARG(first, QType.MsgId), ARG(last, QType.MsgId), ARG(limit, Type.Int), ARG(additional, Type.Int), ARG(type, Type.Int), ARG(flags, Type.Int) @@ -77,8 +78,8 @@ interface IBacklogManager : ISyncableObject { messages: QVariantList) @Slot - fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, - type: Int, flags: Int, messages: QVariantList) + fun receiveBacklogAllFiltered(first: MsgId, last: MsgId, limit: Int, additional: Int, type: Int, + flags: Int, messages: QVariantList) @Slot override fun update(properties: QVariantMap) { diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt index 701989b6b..42e8e8af6 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt @@ -295,9 +295,9 @@ interface INetwork : ISyncableObject { } data class NetworkInfo( - var networkId: NetworkId = -1, + var networkId: NetworkId = NetworkId(-1), var networkName: String = "", - var identity: IdentityId = -1, + var identity: IdentityId = IdentityId(-1), // unused var useCustomEncodings: Boolean = false, var codecForServer: String = "UTF_8", diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt index 7ae0ce7a4..b6f9ea207 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/IRpcHandler.kt @@ -34,7 +34,7 @@ import java.nio.ByteBuffer interface IRpcHandler { val session: Session - @Slot("__objectRenamed__") + @Slot(value = "__objectRenamed__") fun objectRenamed(classname: ByteBuffer, newname: String?, oldname: String?) @Slot("2displayMsg(Message)") diff --git a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt index 74c703b36..e5f4e366d 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt @@ -28,7 +28,7 @@ interface BacklogStorage { fun storeMessages(session: ISession, vararg messages: Message) fun storeMessages(session: ISession, messages: Iterable<Message>) - fun clearMessages(bufferId: BufferId, idRange: IntRange) + fun clearMessages(bufferId: BufferId, idRange: LongRange) fun clearMessages(bufferId: BufferId) 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 6d9cbaae6..5fc7fee8a 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -215,7 +215,7 @@ class Session( bufferSyncer.initSetBufferInfos(f.bufferInfos) f.networkIds?.forEach { - val network = Network(it.value(-1), this) + val network = Network(it.value(NetworkId(-1)), this) networks[network.networkId()] = network } live_networks.onNext(Unit) diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt index bf1928f4d..c7989949c 100644 --- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/BufferInfoSerializerTest.kt @@ -19,7 +19,9 @@ package de.kuschku.libquassel.protocol.primitive.serializer +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.util.deserialize import de.kuschku.libquassel.util.roundTrip @@ -30,8 +32,8 @@ class BufferInfoSerializerTest { @Test fun testBaseCase() { val value = BufferInfo( - -1, - -1, + BufferId(-1), + NetworkId(-1), Buffer_Type.of(), -1, "" @@ -45,8 +47,8 @@ class BufferInfoSerializerTest { @Test fun testNormal() { val value = BufferInfo( - Int.MAX_VALUE, - Int.MAX_VALUE, + BufferId.MAX_VALUE, + NetworkId.MAX_VALUE, Buffer_Type.of(*Buffer_Type.validValues), Int.MAX_VALUE, "äẞ\u0000\uFFFF" diff --git a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt index 19be62a40..e4fe91853 100644 --- a/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/protocol/primitive/serializer/MessageSerializerTest.kt @@ -32,13 +32,13 @@ class MessageSerializerTest { @Test fun testBaseCaseNoFeatures() { val value = Message( - -1, + MsgId(-1), Instant.EPOCH, Message_Type.of(), Message_Flag.of(), BufferInfo( - -1, - -1, + BufferId(-1), + NetworkId(-1), Buffer_Type.of(), -1, "" @@ -59,7 +59,7 @@ class MessageSerializerTest { @Test fun testNormalNoFeatures() { val value = Message( - Int.MAX_VALUE.toLong(), + MsgId(Int.MAX_VALUE.toLong()), Instant.ofEpochMilli(1524601750000), Message_Type.of(*Message_Type.values()), Message_Flag.of(*Message_Flag.values()), @@ -86,13 +86,13 @@ class MessageSerializerTest { @Test fun testBaseCaseAllFeatures() { val value = Message( - -1, + MsgId(-1), Instant.EPOCH, Message_Type.of(), Message_Flag.of(), BufferInfo( - -1, - -1, + BufferId(-1), + NetworkId(-1), Buffer_Type.of(), -1, "" diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt index f98c1fa7d..1be52a9e5 100644 --- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/AliasManagerTest.kt @@ -19,8 +19,10 @@ package de.kuschku.libquassel.quassel.syncables +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.Buffer_Types +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer import de.kuschku.libquassel.quassel.BufferInfo import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager @@ -64,8 +66,8 @@ class AliasManagerTest { manager.setAliasList(manager.defaults() + aliases) val bufferInfo = BufferInfo( - bufferId = -1, - networkId = -1, + bufferId = BufferId(-1), + networkId = NetworkId(-1), type = Buffer_Types.of(Buffer_Type.StatusBuffer), bufferName = "#quassel-test", groupId = -1 diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt index fe468b734..b79f480d6 100644 --- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfigTest.kt @@ -21,6 +21,7 @@ package de.kuschku.libquassel.quassel.syncables import de.kuschku.libquassel.protocol.Buffer_Activity import de.kuschku.libquassel.protocol.Buffer_Type +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.* @@ -31,7 +32,7 @@ class BufferViewConfigTest { fun testSerialization() { val original = BufferViewConfig(randomInt(), SignalProxy.NULL) original.setBufferViewName(randomString()) - original.setNetworkId(randomInt()) + original.setNetworkId(NetworkId(randomInt())) original.setAddNewBuffersAutomatically(randomBoolean()) original.setSortAlphabetically(randomBoolean()) original.setHideInactiveNetworks(randomBoolean()) @@ -54,7 +55,7 @@ class BufferViewConfigTest { fun testCopy() { val original = BufferViewConfig(randomInt(), SignalProxy.NULL) original.setBufferViewName(randomString()) - original.setNetworkId(randomInt()) + original.setNetworkId(NetworkId(randomInt())) original.setAddNewBuffersAutomatically(randomBoolean()) original.setSortAlphabetically(randomBoolean()) original.setHideInactiveNetworks(randomBoolean()) diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt index d23fdb3c7..7efacedbc 100644 --- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/IdentityTest.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.quassel.syncables +import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.randomInt @@ -30,7 +31,7 @@ class IdentityTest { @Test fun testSerialization() { val original = Identity(SignalProxy.NULL) - original.setId(randomInt()) + original.setId(IdentityId(randomInt())) original.setIdentityName(randomString()) original.setRealName(randomString()) original.setNicks(listOf( @@ -50,7 +51,7 @@ class IdentityTest { @Test fun testCopy() { val original = Identity(SignalProxy.NULL) - original.setId(randomInt()) + original.setId(IdentityId(randomInt())) original.setIdentityName(randomString()) original.setRealName(randomString()) original.setNicks(listOf( diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt index ffb0309bf..4d0088107 100644 --- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/NetworkTest.kt @@ -19,6 +19,8 @@ package de.kuschku.libquassel.quassel.syncables +import de.kuschku.libquassel.protocol.IdentityId +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.session.SignalProxy @@ -28,9 +30,9 @@ import org.junit.Test class NetworkTest { @Test fun testSerialization() { - val original = Network(randomInt(), SignalProxy.NULL) + val original = Network(NetworkId(randomInt()), SignalProxy.NULL) original.setNetworkName(randomString()) - original.setIdentity(randomInt()) + original.setIdentity(IdentityId(randomInt())) original.setActualServerList(listOf( INetwork.Server( host = randomString(), @@ -155,9 +157,9 @@ class NetworkTest { @Test fun testCopy() { - val original = Network(randomInt(), SignalProxy.NULL) + val original = Network(NetworkId(randomInt()), SignalProxy.NULL) original.setNetworkName(randomString()) - original.setIdentity(randomInt()) + original.setIdentity(IdentityId(randomInt())) original.setActualServerList(listOf( INetwork.Server( host = randomString(), diff --git a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt index b3be2f176..83e7ce99c 100644 --- a/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt +++ b/lib/src/test/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetworkInfoTest.kt @@ -19,6 +19,7 @@ package de.kuschku.libquassel.quassel.syncables.interfaces +import de.kuschku.libquassel.protocol.IdentityId import de.kuschku.libquassel.protocol.primitive.serializer.VariantMapSerializer import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_PLAINTEXT import de.kuschku.libquassel.util.roundTrip @@ -30,7 +31,7 @@ class INetworkInfoTest { fun testSerialization() { val original = INetwork.NetworkInfo( networkName = "QuakeNet", - identity = 5, + identity = IdentityId(5), serverList = listOf( INetwork.Server( host = "irc.quakenet.org", @@ -47,7 +48,7 @@ class INetworkInfoTest { fun testCopy() { val original = INetwork.NetworkInfo( networkName = "QuakeNet", - identity = 5, + identity = IdentityId(5), serverList = listOf( INetwork.Server( host = "irc.quakenet.org", diff --git a/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt b/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt index c3319d935..5eee69af8 100644 --- a/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt +++ b/lib/src/test/java/de/kuschku/libquassel/util/RandomHelpers.kt @@ -23,7 +23,7 @@ import org.threeten.bp.Instant import java.nio.charset.Charset import java.util.* -val random = Random() +private val random = Random() fun Any?.randomBoolean(): Boolean = random.nextBoolean() diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt index 1416124c1..1e5702931 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt @@ -21,6 +21,7 @@ package de.kuschku.quasseldroid.persistence import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Message +import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.quassel.syncables.IgnoreListManager import de.kuschku.libquassel.session.BacklogStorage import de.kuschku.libquassel.session.ISession @@ -39,7 +40,7 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage { override fun storeMessages(session: ISession, messages: Iterable<Message>) { db.message().save(*messages.map { - QuasselDatabase.MessageData( + QuasselDatabase.MessageData.of( messageId = it.messageId, time = it.time, type = it.type, @@ -56,12 +57,12 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage { }.toTypedArray()) } - override fun clearMessages(bufferId: BufferId, idRange: IntRange) { - db.message().clearMessages(bufferId, idRange.first, idRange.last) + override fun clearMessages(bufferId: BufferId, idRange: LongRange) { + db.message().clearMessages(bufferId.id, idRange.first, idRange.last) } override fun clearMessages(bufferId: BufferId) { - db.message().clearMessages(bufferId) + db.message().clearMessages(bufferId.id) } override fun clearMessages() { @@ -82,7 +83,7 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage { fun isIgnored(session: ISession, message: QuasselDatabase.MessageData): Boolean { val bufferInfo = session.bufferSyncer?.bufferInfo(message.bufferId) val bufferName = bufferInfo?.bufferName ?: "" - val networkId = bufferInfo?.networkId ?: -1 + val networkId = bufferInfo?.networkId ?: NetworkId(-1) val networkName = session.network(networkId)?.networkName() ?: "" return session.ignoreListManager?.match( diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt index 5ac9d4e44..8c56bff6c 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt @@ -20,7 +20,6 @@ package de.kuschku.quasseldroid.persistence import android.content.Context -import androidx.annotation.IntRange import androidx.lifecycle.LiveData import androidx.paging.DataSource import androidx.room.* @@ -43,19 +42,60 @@ abstract class QuasselDatabase : RoomDatabase() { @Entity(tableName = "message", indices = [Index("bufferId"), Index("ignored")]) data class MessageData( - @PrimaryKey var messageId: MsgId, + @PrimaryKey + @ColumnInfo(name = "messageId") + var rawMessageId: MsgId_Type, var time: Instant, var type: Message_Types, var flag: Message_Flags, - var bufferId: BufferId, - var networkId: NetworkId, + @ColumnInfo(name = "bufferId") + var rawBufferId: BufferId_Type, + @ColumnInfo(name = "networkId") + var rawNetworkId: NetworkId_Type, var sender: String, var senderPrefixes: String, var realName: String, var avatarUrl: String, var content: String, var ignored: Boolean - ) + ) { + inline val messageId + get() = MsgId(rawMessageId) + inline val bufferId + get() = BufferId(rawBufferId) + inline val networkId + get() = NetworkId(rawNetworkId) + + companion object { + inline fun of( + messageId: MsgId, + time: Instant, + type: Message_Types, + flag: Message_Flags, + bufferId: BufferId, + networkId: NetworkId, + sender: String, + senderPrefixes: String, + realName: String, + avatarUrl: String, + content: String, + ignored: Boolean + ) = MessageData( + messageId.id, + time, + type, + flag, + bufferId.id, + networkId.id, + sender, + senderPrefixes, + realName, + avatarUrl, + content, + ignored + ) + } + } @Dao interface MessageDao { @@ -63,79 +103,96 @@ abstract class QuasselDatabase : RoomDatabase() { fun all(): List<MessageData> @Query("SELECT DISTINCT bufferId FROM message") - fun buffers(): List<BufferId> + fun _buffers(): List<BufferId_Type> @Query("SELECT * FROM message WHERE messageId = :messageId") - fun find(messageId: Int): MessageData? + fun find(messageId: MsgId_Type): MessageData? @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC") - fun findByBufferId(bufferId: Int): List<MessageData> + fun _findByBufferId(bufferId: BufferId_Type): List<MessageData> @Query("SELECT * FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0 ORDER BY messageId DESC") - fun findByBufferIdPaged(bufferId: Int, type: Int): DataSource.Factory<Int, MessageData> + fun _findByBufferIdPaged(bufferId: BufferId_Type, + type: Int): DataSource.Factory<Int, MessageData> @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1") - fun findLastByBufferId(bufferId: Int): MessageData? + fun _findLastByBufferId(bufferId: BufferId_Type): MessageData? @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1") - fun lastMsgId(bufferId: Int): LiveData<MessageData> + fun _lastMsgId(bufferId: BufferId_Type): LiveData<MessageData> @Query("SELECT messageId FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC LIMIT 1") - fun firstMsgId(bufferId: Int): Flowable<MsgId> + fun _firstMsgId(bufferId: BufferId_Type): Flowable<MsgId_Type> @Query("SELECT messageId FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0 ORDER BY messageId ASC LIMIT 1") - fun firstVisibleMsgId(bufferId: Int, type: Int): MsgId? + fun _firstVisibleMsgId(bufferId: BufferId_Type, type: Int): MsgId_Type? @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId ASC LIMIT 1") - fun findFirstByBufferId(bufferId: Int): MessageData? + fun _findFirstByBufferId(bufferId: BufferId_Type): MessageData? @Query("SELECT EXISTS(SELECT 1 FROM message WHERE bufferId = :bufferId AND type & ~ :type > 0 AND ignored = 0)") - fun hasVisibleMessages(bufferId: Int, type: Int): Boolean + fun _hasVisibleMessages(bufferId: BufferId_Type, type: Int): Boolean @Insert(onConflict = OnConflictStrategy.REPLACE) fun save(vararg entities: MessageData) @Query("UPDATE message SET bufferId = :bufferId1 WHERE bufferId = :bufferId2") - fun merge(@IntRange(from = 0) bufferId1: Int, @IntRange(from = 0) bufferId2: Int) + fun _merge(bufferId1: BufferId_Type, bufferId2: BufferId_Type) @Query("SELECT count(*) FROM message WHERE bufferId = :bufferId") - fun bufferSize(@IntRange(from = 0) bufferId: Int): Int + fun _bufferSize(bufferId: BufferId_Type): Int @Query("DELETE FROM message") fun clearMessages() @Query("DELETE FROM message WHERE bufferId = :bufferId") - fun clearMessages(@IntRange(from = 0) bufferId: Int) + fun clearMessages(bufferId: BufferId_Type) @Query( "DELETE FROM message WHERE bufferId = :bufferId AND messageId >= :first AND messageId <= :last" ) - fun clearMessages(@IntRange(from = 0) bufferId: Int, first: Int, last: Int) + fun clearMessages(bufferId: BufferId_Type, first: MsgId_Type, last: MsgId_Type) } @Entity(tableName = "filtered", primaryKeys = ["accountId", "bufferId"]) data class Filtered( var accountId: Long, - var bufferId: BufferId, + @ColumnInfo(name = "bufferId") + var rawBufferId: BufferId_Type, var filtered: Int - ) + ) { + inline val bufferId + get() = BufferId(rawBufferId) + + companion object { + inline fun of( + accountId: Long, + bufferId: BufferId, + filtered: Int + ) = Filtered( + accountId, + bufferId.id, + filtered + ) + } + } @Dao interface FilteredDao { @Query("SELECT DISTINCT bufferId FROM filtered WHERE accountId = :accountId") - fun buffers(accountId: Long): List<BufferId> + fun _buffers(accountId: Long): List<BufferId_Type> @Insert(onConflict = OnConflictStrategy.REPLACE) fun replace(vararg entities: Filtered) @Query("UPDATE filtered SET filtered = :filtered WHERE accountId = :accountId AND bufferId = :bufferId") - fun setFiltered(accountId: Long, bufferId: Int, filtered: Int) + fun _setFiltered(accountId: Long, bufferId: BufferId_Type, filtered: Int) @Query("SELECT IFNULL(t.filtered, :defaultValue) FROM (SELECT filtered FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId UNION SELECT NULL ORDER BY filtered DESC LIMIT 1) t") - fun get(accountId: Long, bufferId: Int, defaultValue: Int): Int + fun _get(accountId: Long, bufferId: BufferId_Type, defaultValue: Int): Int @Query("SELECT IFNULL(t.filtered, :defaultValue) FROM (SELECT filtered FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId UNION SELECT NULL ORDER BY filtered DESC LIMIT 1) t") - fun listen(accountId: Long, bufferId: Int, defaultValue: Int): LiveData<Int> + fun _listen(accountId: Long, bufferId: BufferId_Type, defaultValue: Int): LiveData<Int> @Query("SELECT * FROM filtered WHERE accountId = :accountId") fun listen(accountId: Long): LiveData<List<Filtered>> @@ -147,7 +204,7 @@ abstract class QuasselDatabase : RoomDatabase() { fun clear(accountId: Long) @Query("DELETE FROM filtered WHERE bufferId = :bufferId AND accountId = :accountId") - fun clear(accountId: Long, bufferId: Int) + fun _clear(accountId: Long, bufferId: BufferId_Type) } @Entity(tableName = "ssl_validity_whitelist") @@ -201,15 +258,19 @@ abstract class QuasselDatabase : RoomDatabase() { @Entity(tableName = "notification", indices = [Index("bufferId")]) data class NotificationData( - @PrimaryKey var messageId: MsgId, + @PrimaryKey + @ColumnInfo(name = "messageId") + var rawMessageId: MsgId_Type, var creationTime: Instant, var time: Instant, var type: Message_Types, var flag: Message_Flags, - var bufferId: BufferId, + @ColumnInfo(name = "bufferId") + var rawBufferId: BufferId_Type, var bufferName: String, var bufferType: Buffer_Types, - var networkId: NetworkId, + @ColumnInfo(name = "networkId") + var rawNetworkId: NetworkId_Type, var networkName: String, var sender: String, var senderPrefixes: String, @@ -221,7 +282,62 @@ abstract class QuasselDatabase : RoomDatabase() { var ownRealName: String, var ownAvatarUrl: String, var hidden: Boolean - ) + ) { + inline val messageId + get() = MsgId(rawMessageId) + + inline val bufferId + get() = BufferId(rawBufferId) + + inline val networkId + get() = NetworkId(rawNetworkId) + + companion object { + inline fun of( + messageId: MsgId, + creationTime: Instant, + time: Instant, + type: Message_Types, + flag: Message_Flags, + bufferId: BufferId, + bufferName: String, + bufferType: Buffer_Types, + networkId: NetworkId, + networkName: String, + sender: String, + senderPrefixes: String, + realName: String, + avatarUrl: String, + content: String, + ownNick: String, + ownIdent: String, + ownRealName: String, + ownAvatarUrl: String, + hidden: Boolean + ) = NotificationData( + messageId.id, + creationTime, + time, + type, + flag, + bufferId.id, + bufferName, + bufferType, + networkId.id, + networkName, + sender, + senderPrefixes, + realName, + avatarUrl, + content, + ownNick, + ownIdent, + ownRealName, + ownAvatarUrl, + hidden + ) + } + } @Dao interface NotificationDao { @@ -229,25 +345,25 @@ abstract class QuasselDatabase : RoomDatabase() { fun save(vararg entities: NotificationData) @Query("SELECT DISTINCT bufferId FROM notification") - fun buffers(): List<BufferId> + fun _buffers(): List<BufferId_Type> @Query("SELECT * FROM notification WHERE hidden = 0 ORDER BY time ASC") fun all(): List<NotificationData> @Query("SELECT * FROM notification WHERE bufferId = :bufferId AND hidden = 0 ORDER BY time ASC") - fun all(bufferId: BufferId): List<NotificationData> + fun _all(bufferId: BufferId_Type): List<NotificationData> @Query("UPDATE notification SET hidden = 1 WHERE bufferId = :bufferId AND messageId <= :messageId") - fun markHidden(bufferId: BufferId, messageId: MsgId) + fun _markHidden(bufferId: BufferId_Type, messageId: MsgId_Type) @Query("UPDATE notification SET hidden = 1 WHERE bufferId = :bufferId AND flag & 2 = 0") - fun markHiddenNormal(bufferId: BufferId) + fun _markHiddenNormal(bufferId: BufferId_Type) @Query("DELETE FROM notification WHERE bufferId = :bufferId AND messageId <= :messageId") - fun markRead(bufferId: BufferId, messageId: MsgId) + fun _markRead(bufferId: BufferId_Type, messageId: MsgId_Type) @Query("DELETE FROM notification WHERE bufferId = :bufferId AND flag & 2 = 0") - fun markReadNormal(bufferId: BufferId) + fun _markReadNormal(bufferId: BufferId_Type) @Query("DELETE FROM notification") fun clear() diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt new file mode 100644 index 000000000..2e3434c0f --- /dev/null +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabaseHelpers.kt @@ -0,0 +1,91 @@ +/* + * Quasseldroid - Quassel client for Android + * + * Copyright (c) 2019 Janne Koschinski + * Copyright (c) 2019 The Quassel Project + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package de.kuschku.quasseldroid.persistence + +import de.kuschku.libquassel.protocol.BufferId +import de.kuschku.libquassel.protocol.MsgId + +inline fun QuasselDatabase.MessageDao.buffers() = + _buffers().map { BufferId(it) } + +inline fun QuasselDatabase.MessageDao.findByBufferId(bufferId: BufferId) = + _findByBufferId(bufferId.id) + +inline fun QuasselDatabase.MessageDao.findByBufferIdPaged(bufferId: BufferId, type: Int) = + _findByBufferIdPaged(bufferId.id, type) + +inline fun QuasselDatabase.MessageDao.findLastByBufferId(bufferId: BufferId) = + _findLastByBufferId(bufferId.id) + +inline fun QuasselDatabase.MessageDao.lastMsgId(bufferId: BufferId) = + _lastMsgId(bufferId.id) + +inline fun QuasselDatabase.MessageDao.firstMsgId(bufferId: BufferId) = + _firstMsgId(bufferId.id).map(::MsgId) + +inline fun QuasselDatabase.MessageDao.firstVisibleMsgId(bufferId: BufferId, type: Int) = + _firstVisibleMsgId(bufferId.id, type)?.let(::MsgId) + +inline fun QuasselDatabase.MessageDao.findFirstByBufferId(bufferId: BufferId) = + _findFirstByBufferId(bufferId.id) + +inline fun QuasselDatabase.MessageDao.hasVisibleMessages(bufferId: BufferId, type: Int) = + _hasVisibleMessages(bufferId.id, type) + +inline fun QuasselDatabase.MessageDao.merge(bufferId1: BufferId, bufferId2: BufferId) = + _merge(bufferId1.id, bufferId2.id) + +inline fun QuasselDatabase.MessageDao.bufferSize(bufferId: BufferId) = + _bufferSize(bufferId.id) + +inline fun QuasselDatabase.NotificationDao.buffers() = + _buffers().map(::BufferId) + +inline fun QuasselDatabase.NotificationDao.all(bufferId: BufferId) = + _all(bufferId.id) + +inline fun QuasselDatabase.NotificationDao.markHidden(bufferId: BufferId, messageId: MsgId) = + _markHidden(bufferId.id, messageId.id) + +inline fun QuasselDatabase.NotificationDao.markHiddenNormal(bufferId: BufferId) = + _markHiddenNormal(bufferId.id) + +inline fun QuasselDatabase.NotificationDao.markRead(bufferId: BufferId, messageId: MsgId) = + _markRead(bufferId.id, messageId.id) + +inline fun QuasselDatabase.NotificationDao.markReadNormal(bufferId: BufferId) = + _markReadNormal(bufferId.id) + +inline fun QuasselDatabase.FilteredDao.buffers(accountId: Long) = + _buffers(accountId).map(::BufferId) + +inline fun QuasselDatabase.FilteredDao.setFiltered(accountId: Long, bufferId: BufferId, + filtered: Int) = + _setFiltered(accountId, bufferId.id, filtered) + +inline fun QuasselDatabase.FilteredDao.get(accountId: Long, bufferId: BufferId, defaultValue: Int) = + _get(accountId, bufferId.id, defaultValue) + +inline fun QuasselDatabase.FilteredDao.listen(accountId: Long, bufferId: BufferId, + defaultValue: Int) = + _listen(accountId, bufferId.id, defaultValue) + +inline fun QuasselDatabase.FilteredDao.clear(accountId: Long, bufferId: BufferId) = + _clear(accountId, bufferId.id) diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt index bb2e110d5..52df1deb6 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt @@ -20,6 +20,7 @@ package de.kuschku.quasseldroid.viewmodel import androidx.lifecycle.ViewModel +import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.quassel.syncables.AliasManager import de.kuschku.libquassel.quassel.syncables.IrcChannel @@ -45,7 +46,7 @@ class EditorViewModel : ViewModel() { val lastWord = BehaviorSubject.create<Observable<Pair<String, IntRange>>>() - val rawAutoCompleteData: Observable<Triple<Optional<ISession>, Int, Pair<String, IntRange>>> = + val rawAutoCompleteData: Observable<Triple<Optional<ISession>, BufferId, Pair<String, IntRange>>> = combineLatest(session, buffer, lastWord).switchMap { (sessionOptional, id, lastWordWrapper) -> lastWordWrapper .distinctUntilChanged() diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt index 619f58cc8..d185a84da 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt @@ -68,7 +68,7 @@ class QuasselViewModel : ViewModel() { val expandedMessages = BehaviorSubject.createDefault(emptySet<MsgId>()) - val buffer = BehaviorSubject.createDefault(Int.MAX_VALUE) + val buffer = BehaviorSubject.createDefault(BufferId.MAX_VALUE) val bufferOpened = PublishSubject.create<Unit>() val bufferViewConfigId = BehaviorSubject.createDefault(-1) @@ -301,7 +301,7 @@ class QuasselViewModel : ViewModel() { val showHidden = BehaviorSubject.createDefault(false) val expandedNetworks = BehaviorSubject.createDefault(emptyMap<NetworkId, Boolean>()) - val selectedBufferId = BehaviorSubject.createDefault(Int.MAX_VALUE) + val selectedBufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE) val selectedBuffer = combineLatest(session, selectedBufferId, bufferViewConfig) .switchMap { (sessionOptional, buffer, bufferViewConfigOptional) -> val session = sessionOptional.orNull() @@ -318,7 +318,7 @@ class QuasselViewModel : ViewModel() { BufferHiddenState.VISIBLE } - val info = if (buffer < 0) networks[-buffer]?.let { + val info = if (!buffer.isValidId()) networks[NetworkId(-buffer.id)]?.let { BufferInfo( bufferId = buffer, networkId = it.networkId(), @@ -386,10 +386,10 @@ class QuasselViewModel : ViewModel() { it.type.hasFlag(Buffer_Type.StatusBuffer) || it.bufferName?.contains(bufferSearch, ignoreCase = true) == true }.filter { - currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId + !currentConfig.networkId().isValidId() || currentConfig.networkId() == it.networkId }.filter { (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() || - (it.type.hasFlag(Buffer_Type.StatusBuffer) && currentConfig.networkId() < 0) + (it.type.hasFlag(Buffer_Type.StatusBuffer) && !currentConfig.networkId().isValidId()) }.mapNotNull { val network = networks[it.networkId] if (network == null) { @@ -489,7 +489,7 @@ class QuasselViewModel : ViewModel() { fun missingStatusBuffers( list: Collection<BufferId>): Sequence<Observable<BufferProps>?> { val totalNetworks = networks.keys - val wantedNetworks = if (currentConfig.networkId() <= 0) totalNetworks + val wantedNetworks = if (!currentConfig.networkId().isValidId()) totalNetworks else listOf(currentConfig.networkId()) val availableNetworks = list.asSequence().mapNotNull { id -> @@ -503,7 +503,7 @@ class QuasselViewModel : ViewModel() { val missingNetworks = wantedNetworks - availableNetworks return missingNetworks.asSequence().filter { - currentConfig.networkId() <= 0 || currentConfig.networkId() == it + !currentConfig.networkId().isValidId() || currentConfig.networkId() == it }.filter { currentConfig.allowedBufferTypes().hasFlag(Buffer_Type.StatusBuffer) }.mapNotNull { @@ -515,7 +515,7 @@ class QuasselViewModel : ViewModel() { network.liveConnectionState().map { connectionState -> BufferProps( info = BufferInfo( - bufferId = -networkInfo.networkId, + bufferId = BufferId(-networkInfo.networkId.id), networkId = networkInfo.networkId, groupId = 0, bufferName = networkInfo.networkName, -- GitLab