15 files + 218 − 81 Inline Compare changes Side-by-side Inline Show whitespace changes Files 15 .gitlab-ci.yml +1 −1 Original line number Diff line number Diff line image: "k8r.eu/justjanne/android-sdk:28-28.0.3-26.1.1" image: "k8r.eu/justjanne/android-sdk:28-28.0.3" variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" Loading app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +1 −1 Original line number Diff line number Diff line Loading @@ -497,7 +497,7 @@ class QuasselService : DaggerLifecycleService(), session.bufferViewManager.requestCreateBufferView( Defaults.bufferViewConfigInitial(translatedLocale).apply { for (info in session.bufferSyncer.bufferInfos()) { handleBuffer(info, session.bufferSyncer) handleBuffer(info, session.bufferSyncer, session.networks) } }.toVariantMap() ) Loading app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt +6 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,12 @@ class ChatlineFragment : ServiceBoundFragment() { } editorHelper.setOnEnterListener(::send) editorHelper.setOnDownListener { chatline.setText(modelHelper.chat.recentMessagesIndexDown(chatline.safeText)) } editorHelper.setOnUpListener { chatline.setText(modelHelper.chat.recentMessagesIndexUp()) } send.setOnClickListener { send() } send.setTooltip() Loading app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/EditorHelper.kt +42 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.text.Editable import android.text.TextWatcher import android.view.KeyEvent import android.view.inputmethod.EditorInfo import android.widget.Toast import androidx.annotation.ColorInt import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity Loading @@ -44,6 +45,8 @@ class EditorHelper( appearanceSettings: AppearanceSettings ) { private var enterListener: (() -> Unit)? = null private var upListener: (() -> Unit)? = null private var downListener: (() -> Unit)? = null private val mircColors = listOf( R.color.mircColor00, R.color.mircColor01, R.color.mircColor02, R.color.mircColor03, Loading Loading @@ -124,8 +127,28 @@ class EditorHelper( ) } editText.addTextChangedListener(textWatcher) editText.setOnKeyListener { _, keyCode, event: KeyEvent? -> if (event?.action == KeyEvent.ACTION_DOWN) { editText.setOnKeyListener { _, keyCode, event: KeyEvent -> val action = when (event.action) { KeyEvent.ACTION_UP -> "up" KeyEvent.ACTION_DOWN -> "down" KeyEvent.ACTION_MULTIPLE -> "multiple" else -> "unknown" } val key = when (keyCode) { KeyEvent.KEYCODE_ENTER -> "enter" KeyEvent.KEYCODE_NUMPAD_ENTER -> "numpad_enter" KeyEvent.KEYCODE_DPAD_DOWN -> "down" KeyEvent.KEYCODE_DPAD_UP -> "up" else -> "#$keyCode" } val modifiers = listOfNotNull( if (event.isCtrlPressed) "ctrl" else null, if (event.isAltPressed) "alt" else null, if (event.isShiftPressed) "shift" else null ).joinToString(", ") Toast.makeText(editText.context, "$key $action $modifiers", Toast.LENGTH_SHORT).show() if (event.action == KeyEvent.ACTION_DOWN) { if (event.isCtrlPressed && !event.isAltPressed) when (keyCode) { KeyEvent.KEYCODE_B -> { editText.toggleBold() Loading @@ -148,6 +171,14 @@ class EditorHelper( enterListener?.invoke() true } KeyEvent.KEYCODE_DPAD_DOWN -> { downListener?.invoke() true } KeyEvent.KEYCODE_DPAD_UP -> { upListener?.invoke() true } KeyEvent.KEYCODE_TAB -> { if (!event.isAltPressed && !event.isCtrlPressed) { autoCompleteHelper.autoComplete(event.isShiftPressed) Loading @@ -159,7 +190,7 @@ class EditorHelper( else -> false } } else if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) { !(event?.isShiftPressed ?: false) !event.isShiftPressed } else { false } Loading Loading @@ -191,6 +222,14 @@ class EditorHelper( this.enterListener = listener } fun setOnUpListener(listener: (() -> Unit)?) { this.upListener = listener } fun setOnDownListener(listener: (() -> Unit)?) { this.downListener = listener } fun setMultiLine(enabled: Boolean) = editText.setMultiLine(enabled) fun replaceText(text: CharSequence?) = editText.replaceText(text) Loading app/src/main/java/de/kuschku/quasseldroid/util/ui/presenter/BufferContextPresenter.kt +1 −1 Original line number Diff line number Diff line Loading @@ -191,7 +191,7 @@ object BufferContextPresenter { true } R.id.action_unhide -> { bufferViewConfig?.insertBufferSorted(info, bufferSyncer) bufferViewConfig?.insertBufferSorted(info, bufferSyncer, session.networks) actionMode.finish() true } Loading build.gradle.kts +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ buildscript { jcenter() } dependencies { classpath("com.android.tools.build:gradle:3.4.1") classpath("com.android.tools.build:gradle:3.4.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.41") } } Loading lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt +2 −2 Original line number Diff line number Diff line Loading @@ -245,7 +245,7 @@ class BufferSyncer constructor( live_bufferInfos.onNext(Unit) if (oldInfo == null) { session.bufferViewManager.handleBuffer(info, this) session.bufferViewManager.handleBuffer(info, this, session.networks) } } } Loading Loading @@ -282,7 +282,7 @@ class BufferSyncer constructor( activity hasFlag Message_Type.Notice || activity hasFlag Message_Type.Action) { bufferInfo(buffer)?.let { session.bufferViewManager.handleBuffer(it, this, true) session.bufferViewManager.handleBuffer(it, this, session.networks, true) } } _bufferActivities[buffer] = activity Loading lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt +30 −12 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.IBufferViewConfig import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.flag.hasFlag import de.kuschku.libquassel.util.helper.clampOf import de.kuschku.libquassel.util.irc.IrcCaseMappers import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject Loading Loading @@ -326,28 +325,33 @@ class BufferViewConfig constructor( (a?.bufferViewName() ?: "").compareTo((b?.bufferViewName() ?: ""), true) } fun insertBufferSorted(info: BufferInfo, bufferSyncer: BufferSyncer) { fun insertBufferSorted(info: BufferInfo, bufferSyncer: BufferSyncer, networks: Map<NetworkId, Network>) { if (!_buffers.contains(info.bufferId)) { val element = IrcCaseMappers.unicode.toLowerCaseNullable(info.bufferName) val position = if (_sortAlphabetically) -_buffers.mapNotNull { IrcCaseMappers.unicode.toLowerCaseNullable(bufferSyncer.bufferInfo(it)?.bufferName) }.binarySearch(element) - 1 else _buffers.size requestAddBuffer(info.bufferId, position) requestAddBuffer( info.bufferId, sortBuffers( _buffers.mapNotNull { bufferSyncer.bufferInfo(it) } + info, sortAlphabetically(), { it.bufferName ?: "" }, { networks[it.networkId]?.networkName() ?: "" }, { it.type } ).indexOf(info) ) } } fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, unhide: Boolean = false) { fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, networks: Map<NetworkId, Network>, unhide: Boolean = false) { if (_addNewBuffersAutomatically && !_buffers.contains(info.bufferId) && !_temporarilyRemovedBuffers.contains(info.bufferId) && !_removedBuffers.contains(info.bufferId) && !info.type.hasFlag(Buffer_Type.StatusBuffer)) { insertBufferSorted(info, bufferSyncer) insertBufferSorted(info, bufferSyncer, networks) } else if (unhide && !_buffers.contains(info.bufferId) && _temporarilyRemovedBuffers.contains(info.bufferId)) { insertBufferSorted(info, bufferSyncer) insertBufferSorted(info, bufferSyncer, networks) } } Loading @@ -365,4 +369,18 @@ class BufferViewConfig constructor( override fun toString(): String { return "BufferViewConfig(_bufferViewId=$_bufferViewId, _bufferViewName='$_bufferViewName', _networkId=$_networkId, _addNewBuffersAutomatically=$_addNewBuffersAutomatically, _sortAlphabetically=$_sortAlphabetically, _hideInactiveBuffers=$_hideInactiveBuffers, _hideInactiveNetworks=$_hideInactiveNetworks, _disableDecoration=$_disableDecoration, _allowedBufferTypes=$_allowedBufferTypes, _minimumActivity=$_minimumActivity, _showSearch=$_showSearch)" } companion object { inline fun <T> sortBuffers(list: List<T>, sortAlphabetically: Boolean, crossinline bufferName: (T) -> String, crossinline networkName: (T) -> String, crossinline type: (T) -> Buffer_Types) = list.let { if (sortAlphabetically) list.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, bufferName)) else list }.sortedBy { !type(it).hasFlag(Buffer_Type.StatusBuffer) }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, networkName)) } } lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt +4 −3 Original line number Diff line number Diff line Loading @@ -90,12 +90,13 @@ class BufferViewManager constructor( private val _bufferViewConfigs: MutableMap<Int, BufferViewConfig> = mutableMapOf() private val live_bufferViewConfigs: BehaviorSubject<Set<Int>> = BehaviorSubject.createDefault<Set<Int>>( private val live_bufferViewConfigs: BehaviorSubject<Set<Int>> = BehaviorSubject.createDefault( emptySet()) fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, unhide: Boolean = false) { fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, networks: Map<NetworkId, Network>, unhide: Boolean = false) { for (bufferViewConfig in bufferViewConfigs()) { bufferViewConfig.handleBuffer(info, bufferSyncer, unhide) bufferViewConfig.handleBuffer(info, bufferSyncer, networks, unhide) } } Loading lib/src/main/java/de/kuschku/libquassel/session/Session.kt +32 −21 Original line number Diff line number Diff line Loading @@ -85,21 +85,24 @@ class Session( override val backlogManager = BacklogManager(this, backlogStorage) override val bufferViewManager = BufferViewManager(this) override val bufferSyncer = BufferSyncer(this, notificationManager) override val certManagers = mutableMapOf<IdentityId, CertManager>() override var certManagers = emptyMap<IdentityId, CertManager>() private set override val coreInfo = CoreInfo(this) override val dccConfig = DccConfig(this) override val identities = mutableMapOf<IdentityId, Identity>() override var identities = emptyMap<IdentityId, Identity>() private set private val live_identities = BehaviorSubject.createDefault(Unit) override fun liveIdentities(): Observable<Map<IdentityId, Identity>> = live_identities.map { identities.toMap() } override fun liveIdentities(): Observable<Map<IdentityId, Identity>> = live_identities.map { identities } override val ignoreListManager = IgnoreListManager(this) override val highlightRuleManager = HighlightRuleManager(this) override val ircListHelper = IrcListHelper(this) override val networks = mutableMapOf<NetworkId, Network>() override var networks = emptyMap<NetworkId, Network>() private set private val live_networks = BehaviorSubject.createDefault(Unit) override fun liveNetworks(): Observable<Map<NetworkId, Network>> = live_networks.map { networks.toMap() } override fun liveNetworks(): Observable<Map<NetworkId, Network>> = live_networks.map { networks } private val network_added = PublishSubject.create<NetworkId>() override fun liveNetworkAdded(): Observable<NetworkId> = network_added Loading Loading @@ -183,14 +186,15 @@ class Session( override fun addNetwork(networkId: NetworkId) { val network = Network(networkId, this) networks[networkId] = network networks = networks + Pair(networkId, network) synchronize(network) live_networks.onNext(Unit) network_added.onNext(networkId) } override fun removeNetwork(networkId: NetworkId) { val network = networks.remove(networkId) val network = networks[networkId] networks = networks - networkId stopSynchronize(network) live_networks.onNext(Unit) } Loading @@ -198,13 +202,14 @@ class Session( override fun addIdentity(initData: QVariantMap) { val identity = Identity(this) identity.fromVariantMap(initData) identities[identity.id()] = identity identities = identities + Pair(identity.id(), identity) synchronize(identity) live_identities.onNext(Unit) } override fun removeIdentity(identityId: IdentityId) { val identity = identities.remove(identityId) val identity = identities[identityId] identities = identities - identityId stopSynchronize(identity) live_identities.onNext(Unit) } Loading @@ -219,23 +224,29 @@ class Session( handlerService.backend { bufferSyncer.initSetBufferInfos(f.bufferInfos) f.networkIds?.forEach { val network = Network(it.value(NetworkId(-1)), this) networks[network.networkId()] = network } networks = f.networkIds?.map { Pair(it.value(NetworkId(-1)), Network(it.value(NetworkId(-1)), this)) }?.toMap().orEmpty() live_networks.onNext(Unit) f.identities?.forEach { val identityCertmanagerPairs = f.identities?.map { val identity = Identity(this) identity.fromVariantMap(it.valueOr(::emptyMap)) identity.initialized = true identity.init() identities[identity.id()] = identity synchronize(identity) val certManager = CertManager(identity.id(), this) certManagers[identity.id()] = certManager } Pair(identity, certManager) }?.toMap().orEmpty() identities = identityCertmanagerPairs.map { (identity, _) -> Pair(identity.id(), identity) }.toMap() certManagers = identityCertmanagerPairs.map { (identity, certManager) -> Pair(identity.id(), certManager) }.toMap() isInitializing = true networks.values.forEach { syncableObject -> this.synchronize(syncableObject, true) } Loading Loading @@ -269,7 +280,7 @@ class Session( initCallback?.invoke(this) for (config in bufferViewManager.bufferViewConfigs()) { for (info in bufferSyncer.bufferInfos()) { config.handleBuffer(info, bufferSyncer) config.handleBuffer(info, bufferSyncer, networks) } } notificationManager?.init(this) Loading Loading @@ -318,10 +329,10 @@ class Session( backlogManager.deinit() rpcHandler.deinit() certManagers.clear() identities.clear() certManagers = emptyMap() identities = emptyMap() live_identities.onNext(Unit) networks.clear() networks = emptyMap() live_networks.onNext(Unit) } Loading lib/src/test/java/de/kuschku/libquassel/integration/BufferViewConfigTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ class BufferViewConfigTest { val bufferViewConfig = bufferViewManager.bufferViewConfig(0)!! ensure { bufferViewConfig.insertBufferSorted(bufferSyncer.bufferInfo(BufferId(4))!!, bufferSyncer) bufferViewConfig.insertBufferSorted(bufferSyncer.bufferInfo(BufferId(4))!!, bufferSyncer, networks) }.does { callSync(bufferViewConfig, "requestAddBuffer", listOf( QVariant_.of(BufferId(4), QType.BufferId), Loading viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt +36 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ open class ChatViewModel : QuasselViewModel() { val bufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE) val bufferViewConfigId = BehaviorSubject.createDefault(-1) val recentlySentMessages = BehaviorSubject.createDefault(emptyList<CharSequence>()) val recentlySentMessageIndex = BehaviorSubject.createDefault(-1) val inputCache = BehaviorSubject.createDefault<CharSequence>("") val showHidden = BehaviorSubject.createDefault(false) val bufferSearchTemporarilyVisible = BehaviorSubject.createDefault(false) val expandedNetworks = BehaviorSubject.createDefault(emptyMap<NetworkId, Boolean>()) Loading Loading @@ -145,6 +147,40 @@ open class ChatViewModel : QuasselViewModel() { ) } private fun recentMessagesChange(value: Int) { val current = recentlySentMessageIndex.safeValue val size = recentlySentMessages.safeValue.size val nextValue = current + value recentlySentMessageIndex.onNext( if (nextValue < 0) -1 else (size + current + value) % size ) } fun recentMessagesValue() = if (recentlySentMessageIndex.safeValue == -1) inputCache.safeValue else recentlySentMessages.safeValue[recentlySentMessageIndex.safeValue] fun recentMessagesIndexDown(content: CharSequence): CharSequence { if (recentlySentMessageIndex.safeValue == -1) { inputCache.onNext(content) } recentMessagesChange(+1) return recentMessagesValue() } fun recentMessagesIndexUp(): CharSequence? { if (recentlySentMessageIndex.safeValue > -1) { recentMessagesChange(-1) } return recentMessagesValue() } fun recentMessagesIndexReset(): CharSequence? { recentlySentMessageIndex.onNext(-1) return recentMessagesValue() } companion object { const val KEY_SELECTED_MESSAGES = "model_chat_selectedMessages" const val KEY_BUFFER_SEARCH = "model_chat_bufferSearch" Loading viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ArchiveViewModelHelper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -60,5 +60,5 @@ open class ArchiveViewModelHelper @Inject constructor( showHandle ) val selectedBuffer = processSelectedBuffer(archive.selectedBufferId) val selectedBuffer = processSelectedBuffer(bufferViewConfig, archive.selectedBufferId) } viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,7 @@ open class ChatViewModelHelper @Inject constructor( val nickDataThrottled = nickData.distinctUntilChanged().throttleLast(100, TimeUnit.MILLISECONDS) val selectedBuffer = processSelectedBuffer(chat.selectedBufferId) val selectedBuffer = processSelectedBuffer(bufferViewConfig, chat.selectedBufferId) fun processChatBufferList( filtered: Observable<Pair<Map<BufferId, Int>, Int>> Loading viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QuasselViewModelHelper.kt +59 −33 Original line number Diff line number Diff line Loading @@ -456,12 +456,15 @@ open class QuasselViewModelHelper @Inject constructor( } fun processSelectedBuffer( bufferViewConfigObservable: Observable<Optional<BufferViewConfig>>, selectedBufferId: Observable<BufferId> ) = combineLatest(connectedSession, selectedBufferId) .safeSwitchMap { (sessionOptional, buffer) -> ) = combineLatest(connectedSession, selectedBufferId, bufferViewConfigObservable) .safeSwitchMap { (sessionOptional, buffer, bufferViewConfigOptional) -> val session = sessionOptional.orNull() val bufferViewConfig = bufferViewConfigOptional.orNull() val bufferSyncer = session?.bufferSyncer if (bufferSyncer != null) { if (bufferSyncer != null && bufferViewConfig != null) { bufferHiddenState(bufferViewConfig, buffer).safeSwitchMap { bufferHiddenState -> session.liveNetworks().safeSwitchMap { networks -> val info = if (!buffer.isValidId()) networks[NetworkId(-buffer.id)]?.let { BufferInfo( Loading @@ -479,7 +482,8 @@ open class QuasselViewModelHelper @Inject constructor( network?.liveConnectionState()?.map { SelectedBufferItem( info, connectionState = it connectionState = it, hiddenState = bufferHiddenState ) } ?: Observable.just(SelectedBufferItem(info)) } Loading @@ -487,19 +491,41 @@ open class QuasselViewModelHelper @Inject constructor( network?.liveIrcChannel(info.bufferName)?.mapNullable(IrcChannel.NULL) { SelectedBufferItem( info, joined = it != null joined = it != null, hiddenState = bufferHiddenState ) } ?: Observable.just(SelectedBufferItem(info)) } ?: Observable.just(SelectedBufferItem( info, hiddenState = bufferHiddenState )) } else -> Observable.just(SelectedBufferItem(info)) Observable.just(SelectedBufferItem( info, hiddenState = bufferHiddenState )) } } else { Observable.just(SelectedBufferItem()) } } } } else { Observable.just(SelectedBufferItem()) } } fun bufferHiddenState(bufferViewConfig: BufferViewConfig, bufferId: BufferId): Observable<BufferHiddenState> = combineLatest(bufferViewConfig.liveBuffers(), bufferViewConfig.liveTemporarilyRemovedBuffers(), bufferViewConfig.liveRemovedBuffers()) .map { (visible, temp, perm) -> when (bufferId) { in visible -> BufferHiddenState.VISIBLE in temp -> BufferHiddenState.HIDDEN_TEMPORARY in perm -> BufferHiddenState.HIDDEN_PERMANENT else -> BufferHiddenState.HIDDEN_PERMANENT } } }
.gitlab-ci.yml +1 −1 Original line number Diff line number Diff line image: "k8r.eu/justjanne/android-sdk:28-28.0.3-26.1.1" image: "k8r.eu/justjanne/android-sdk:28-28.0.3" variables: GRADLE_OPTS: "-Dorg.gradle.daemon=false" Loading
app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +1 −1 Original line number Diff line number Diff line Loading @@ -497,7 +497,7 @@ class QuasselService : DaggerLifecycleService(), session.bufferViewManager.requestCreateBufferView( Defaults.bufferViewConfigInitial(translatedLocale).apply { for (info in session.bufferSyncer.bufferInfos()) { handleBuffer(info, session.bufferSyncer) handleBuffer(info, session.bufferSyncer, session.networks) } }.toVariantMap() ) Loading
app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/ChatlineFragment.kt +6 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,12 @@ class ChatlineFragment : ServiceBoundFragment() { } editorHelper.setOnEnterListener(::send) editorHelper.setOnDownListener { chatline.setText(modelHelper.chat.recentMessagesIndexDown(chatline.safeText)) } editorHelper.setOnUpListener { chatline.setText(modelHelper.chat.recentMessagesIndexUp()) } send.setOnClickListener { send() } send.setTooltip() Loading
app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/EditorHelper.kt +42 −3 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.text.Editable import android.text.TextWatcher import android.view.KeyEvent import android.view.inputmethod.EditorInfo import android.widget.Toast import androidx.annotation.ColorInt import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity Loading @@ -44,6 +45,8 @@ class EditorHelper( appearanceSettings: AppearanceSettings ) { private var enterListener: (() -> Unit)? = null private var upListener: (() -> Unit)? = null private var downListener: (() -> Unit)? = null private val mircColors = listOf( R.color.mircColor00, R.color.mircColor01, R.color.mircColor02, R.color.mircColor03, Loading Loading @@ -124,8 +127,28 @@ class EditorHelper( ) } editText.addTextChangedListener(textWatcher) editText.setOnKeyListener { _, keyCode, event: KeyEvent? -> if (event?.action == KeyEvent.ACTION_DOWN) { editText.setOnKeyListener { _, keyCode, event: KeyEvent -> val action = when (event.action) { KeyEvent.ACTION_UP -> "up" KeyEvent.ACTION_DOWN -> "down" KeyEvent.ACTION_MULTIPLE -> "multiple" else -> "unknown" } val key = when (keyCode) { KeyEvent.KEYCODE_ENTER -> "enter" KeyEvent.KEYCODE_NUMPAD_ENTER -> "numpad_enter" KeyEvent.KEYCODE_DPAD_DOWN -> "down" KeyEvent.KEYCODE_DPAD_UP -> "up" else -> "#$keyCode" } val modifiers = listOfNotNull( if (event.isCtrlPressed) "ctrl" else null, if (event.isAltPressed) "alt" else null, if (event.isShiftPressed) "shift" else null ).joinToString(", ") Toast.makeText(editText.context, "$key $action $modifiers", Toast.LENGTH_SHORT).show() if (event.action == KeyEvent.ACTION_DOWN) { if (event.isCtrlPressed && !event.isAltPressed) when (keyCode) { KeyEvent.KEYCODE_B -> { editText.toggleBold() Loading @@ -148,6 +171,14 @@ class EditorHelper( enterListener?.invoke() true } KeyEvent.KEYCODE_DPAD_DOWN -> { downListener?.invoke() true } KeyEvent.KEYCODE_DPAD_UP -> { upListener?.invoke() true } KeyEvent.KEYCODE_TAB -> { if (!event.isAltPressed && !event.isCtrlPressed) { autoCompleteHelper.autoComplete(event.isShiftPressed) Loading @@ -159,7 +190,7 @@ class EditorHelper( else -> false } } else if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) { !(event?.isShiftPressed ?: false) !event.isShiftPressed } else { false } Loading Loading @@ -191,6 +222,14 @@ class EditorHelper( this.enterListener = listener } fun setOnUpListener(listener: (() -> Unit)?) { this.upListener = listener } fun setOnDownListener(listener: (() -> Unit)?) { this.downListener = listener } fun setMultiLine(enabled: Boolean) = editText.setMultiLine(enabled) fun replaceText(text: CharSequence?) = editText.replaceText(text) Loading
app/src/main/java/de/kuschku/quasseldroid/util/ui/presenter/BufferContextPresenter.kt +1 −1 Original line number Diff line number Diff line Loading @@ -191,7 +191,7 @@ object BufferContextPresenter { true } R.id.action_unhide -> { bufferViewConfig?.insertBufferSorted(info, bufferSyncer) bufferViewConfig?.insertBufferSorted(info, bufferSyncer, session.networks) actionMode.finish() true } Loading
build.gradle.kts +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ buildscript { jcenter() } dependencies { classpath("com.android.tools.build:gradle:3.4.1") classpath("com.android.tools.build:gradle:3.4.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.41") } } Loading
lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt +2 −2 Original line number Diff line number Diff line Loading @@ -245,7 +245,7 @@ class BufferSyncer constructor( live_bufferInfos.onNext(Unit) if (oldInfo == null) { session.bufferViewManager.handleBuffer(info, this) session.bufferViewManager.handleBuffer(info, this, session.networks) } } } Loading Loading @@ -282,7 +282,7 @@ class BufferSyncer constructor( activity hasFlag Message_Type.Notice || activity hasFlag Message_Type.Action) { bufferInfo(buffer)?.let { session.bufferViewManager.handleBuffer(it, this, true) session.bufferViewManager.handleBuffer(it, this, session.networks, true) } } _bufferActivities[buffer] = activity Loading
lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewConfig.kt +30 −12 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.IBufferViewConfig import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.flag.hasFlag import de.kuschku.libquassel.util.helper.clampOf import de.kuschku.libquassel.util.irc.IrcCaseMappers import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject Loading Loading @@ -326,28 +325,33 @@ class BufferViewConfig constructor( (a?.bufferViewName() ?: "").compareTo((b?.bufferViewName() ?: ""), true) } fun insertBufferSorted(info: BufferInfo, bufferSyncer: BufferSyncer) { fun insertBufferSorted(info: BufferInfo, bufferSyncer: BufferSyncer, networks: Map<NetworkId, Network>) { if (!_buffers.contains(info.bufferId)) { val element = IrcCaseMappers.unicode.toLowerCaseNullable(info.bufferName) val position = if (_sortAlphabetically) -_buffers.mapNotNull { IrcCaseMappers.unicode.toLowerCaseNullable(bufferSyncer.bufferInfo(it)?.bufferName) }.binarySearch(element) - 1 else _buffers.size requestAddBuffer(info.bufferId, position) requestAddBuffer( info.bufferId, sortBuffers( _buffers.mapNotNull { bufferSyncer.bufferInfo(it) } + info, sortAlphabetically(), { it.bufferName ?: "" }, { networks[it.networkId]?.networkName() ?: "" }, { it.type } ).indexOf(info) ) } } fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, unhide: Boolean = false) { fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, networks: Map<NetworkId, Network>, unhide: Boolean = false) { if (_addNewBuffersAutomatically && !_buffers.contains(info.bufferId) && !_temporarilyRemovedBuffers.contains(info.bufferId) && !_removedBuffers.contains(info.bufferId) && !info.type.hasFlag(Buffer_Type.StatusBuffer)) { insertBufferSorted(info, bufferSyncer) insertBufferSorted(info, bufferSyncer, networks) } else if (unhide && !_buffers.contains(info.bufferId) && _temporarilyRemovedBuffers.contains(info.bufferId)) { insertBufferSorted(info, bufferSyncer) insertBufferSorted(info, bufferSyncer, networks) } } Loading @@ -365,4 +369,18 @@ class BufferViewConfig constructor( override fun toString(): String { return "BufferViewConfig(_bufferViewId=$_bufferViewId, _bufferViewName='$_bufferViewName', _networkId=$_networkId, _addNewBuffersAutomatically=$_addNewBuffersAutomatically, _sortAlphabetically=$_sortAlphabetically, _hideInactiveBuffers=$_hideInactiveBuffers, _hideInactiveNetworks=$_hideInactiveNetworks, _disableDecoration=$_disableDecoration, _allowedBufferTypes=$_allowedBufferTypes, _minimumActivity=$_minimumActivity, _showSearch=$_showSearch)" } companion object { inline fun <T> sortBuffers(list: List<T>, sortAlphabetically: Boolean, crossinline bufferName: (T) -> String, crossinline networkName: (T) -> String, crossinline type: (T) -> Buffer_Types) = list.let { if (sortAlphabetically) list.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, bufferName)) else list }.sortedBy { !type(it).hasFlag(Buffer_Type.StatusBuffer) }.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, networkName)) } }
lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt +4 −3 Original line number Diff line number Diff line Loading @@ -90,12 +90,13 @@ class BufferViewManager constructor( private val _bufferViewConfigs: MutableMap<Int, BufferViewConfig> = mutableMapOf() private val live_bufferViewConfigs: BehaviorSubject<Set<Int>> = BehaviorSubject.createDefault<Set<Int>>( private val live_bufferViewConfigs: BehaviorSubject<Set<Int>> = BehaviorSubject.createDefault( emptySet()) fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, unhide: Boolean = false) { fun handleBuffer(info: BufferInfo, bufferSyncer: BufferSyncer, networks: Map<NetworkId, Network>, unhide: Boolean = false) { for (bufferViewConfig in bufferViewConfigs()) { bufferViewConfig.handleBuffer(info, bufferSyncer, unhide) bufferViewConfig.handleBuffer(info, bufferSyncer, networks, unhide) } } Loading
lib/src/main/java/de/kuschku/libquassel/session/Session.kt +32 −21 Original line number Diff line number Diff line Loading @@ -85,21 +85,24 @@ class Session( override val backlogManager = BacklogManager(this, backlogStorage) override val bufferViewManager = BufferViewManager(this) override val bufferSyncer = BufferSyncer(this, notificationManager) override val certManagers = mutableMapOf<IdentityId, CertManager>() override var certManagers = emptyMap<IdentityId, CertManager>() private set override val coreInfo = CoreInfo(this) override val dccConfig = DccConfig(this) override val identities = mutableMapOf<IdentityId, Identity>() override var identities = emptyMap<IdentityId, Identity>() private set private val live_identities = BehaviorSubject.createDefault(Unit) override fun liveIdentities(): Observable<Map<IdentityId, Identity>> = live_identities.map { identities.toMap() } override fun liveIdentities(): Observable<Map<IdentityId, Identity>> = live_identities.map { identities } override val ignoreListManager = IgnoreListManager(this) override val highlightRuleManager = HighlightRuleManager(this) override val ircListHelper = IrcListHelper(this) override val networks = mutableMapOf<NetworkId, Network>() override var networks = emptyMap<NetworkId, Network>() private set private val live_networks = BehaviorSubject.createDefault(Unit) override fun liveNetworks(): Observable<Map<NetworkId, Network>> = live_networks.map { networks.toMap() } override fun liveNetworks(): Observable<Map<NetworkId, Network>> = live_networks.map { networks } private val network_added = PublishSubject.create<NetworkId>() override fun liveNetworkAdded(): Observable<NetworkId> = network_added Loading Loading @@ -183,14 +186,15 @@ class Session( override fun addNetwork(networkId: NetworkId) { val network = Network(networkId, this) networks[networkId] = network networks = networks + Pair(networkId, network) synchronize(network) live_networks.onNext(Unit) network_added.onNext(networkId) } override fun removeNetwork(networkId: NetworkId) { val network = networks.remove(networkId) val network = networks[networkId] networks = networks - networkId stopSynchronize(network) live_networks.onNext(Unit) } Loading @@ -198,13 +202,14 @@ class Session( override fun addIdentity(initData: QVariantMap) { val identity = Identity(this) identity.fromVariantMap(initData) identities[identity.id()] = identity identities = identities + Pair(identity.id(), identity) synchronize(identity) live_identities.onNext(Unit) } override fun removeIdentity(identityId: IdentityId) { val identity = identities.remove(identityId) val identity = identities[identityId] identities = identities - identityId stopSynchronize(identity) live_identities.onNext(Unit) } Loading @@ -219,23 +224,29 @@ class Session( handlerService.backend { bufferSyncer.initSetBufferInfos(f.bufferInfos) f.networkIds?.forEach { val network = Network(it.value(NetworkId(-1)), this) networks[network.networkId()] = network } networks = f.networkIds?.map { Pair(it.value(NetworkId(-1)), Network(it.value(NetworkId(-1)), this)) }?.toMap().orEmpty() live_networks.onNext(Unit) f.identities?.forEach { val identityCertmanagerPairs = f.identities?.map { val identity = Identity(this) identity.fromVariantMap(it.valueOr(::emptyMap)) identity.initialized = true identity.init() identities[identity.id()] = identity synchronize(identity) val certManager = CertManager(identity.id(), this) certManagers[identity.id()] = certManager } Pair(identity, certManager) }?.toMap().orEmpty() identities = identityCertmanagerPairs.map { (identity, _) -> Pair(identity.id(), identity) }.toMap() certManagers = identityCertmanagerPairs.map { (identity, certManager) -> Pair(identity.id(), certManager) }.toMap() isInitializing = true networks.values.forEach { syncableObject -> this.synchronize(syncableObject, true) } Loading Loading @@ -269,7 +280,7 @@ class Session( initCallback?.invoke(this) for (config in bufferViewManager.bufferViewConfigs()) { for (info in bufferSyncer.bufferInfos()) { config.handleBuffer(info, bufferSyncer) config.handleBuffer(info, bufferSyncer, networks) } } notificationManager?.init(this) Loading Loading @@ -318,10 +329,10 @@ class Session( backlogManager.deinit() rpcHandler.deinit() certManagers.clear() identities.clear() certManagers = emptyMap() identities = emptyMap() live_identities.onNext(Unit) networks.clear() networks = emptyMap() live_networks.onNext(Unit) } Loading
lib/src/test/java/de/kuschku/libquassel/integration/BufferViewConfigTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,7 @@ class BufferViewConfigTest { val bufferViewConfig = bufferViewManager.bufferViewConfig(0)!! ensure { bufferViewConfig.insertBufferSorted(bufferSyncer.bufferInfo(BufferId(4))!!, bufferSyncer) bufferViewConfig.insertBufferSorted(bufferSyncer.bufferInfo(BufferId(4))!!, bufferSyncer, networks) }.does { callSync(bufferViewConfig, "requestAddBuffer", listOf( QVariant_.of(BufferId(4), QType.BufferId), Loading
viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/ChatViewModel.kt +36 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ open class ChatViewModel : QuasselViewModel() { val bufferId = BehaviorSubject.createDefault(BufferId.MAX_VALUE) val bufferViewConfigId = BehaviorSubject.createDefault(-1) val recentlySentMessages = BehaviorSubject.createDefault(emptyList<CharSequence>()) val recentlySentMessageIndex = BehaviorSubject.createDefault(-1) val inputCache = BehaviorSubject.createDefault<CharSequence>("") val showHidden = BehaviorSubject.createDefault(false) val bufferSearchTemporarilyVisible = BehaviorSubject.createDefault(false) val expandedNetworks = BehaviorSubject.createDefault(emptyMap<NetworkId, Boolean>()) Loading Loading @@ -145,6 +147,40 @@ open class ChatViewModel : QuasselViewModel() { ) } private fun recentMessagesChange(value: Int) { val current = recentlySentMessageIndex.safeValue val size = recentlySentMessages.safeValue.size val nextValue = current + value recentlySentMessageIndex.onNext( if (nextValue < 0) -1 else (size + current + value) % size ) } fun recentMessagesValue() = if (recentlySentMessageIndex.safeValue == -1) inputCache.safeValue else recentlySentMessages.safeValue[recentlySentMessageIndex.safeValue] fun recentMessagesIndexDown(content: CharSequence): CharSequence { if (recentlySentMessageIndex.safeValue == -1) { inputCache.onNext(content) } recentMessagesChange(+1) return recentMessagesValue() } fun recentMessagesIndexUp(): CharSequence? { if (recentlySentMessageIndex.safeValue > -1) { recentMessagesChange(-1) } return recentMessagesValue() } fun recentMessagesIndexReset(): CharSequence? { recentlySentMessageIndex.onNext(-1) return recentMessagesValue() } companion object { const val KEY_SELECTED_MESSAGES = "model_chat_selectedMessages" const val KEY_BUFFER_SEARCH = "model_chat_bufferSearch" Loading
viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ArchiveViewModelHelper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -60,5 +60,5 @@ open class ArchiveViewModelHelper @Inject constructor( showHandle ) val selectedBuffer = processSelectedBuffer(archive.selectedBufferId) val selectedBuffer = processSelectedBuffer(bufferViewConfig, archive.selectedBufferId) }
viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt +1 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,7 @@ open class ChatViewModelHelper @Inject constructor( val nickDataThrottled = nickData.distinctUntilChanged().throttleLast(100, TimeUnit.MILLISECONDS) val selectedBuffer = processSelectedBuffer(chat.selectedBufferId) val selectedBuffer = processSelectedBuffer(bufferViewConfig, chat.selectedBufferId) fun processChatBufferList( filtered: Observable<Pair<Map<BufferId, Int>, Int>> Loading
viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QuasselViewModelHelper.kt +59 −33 Original line number Diff line number Diff line Loading @@ -456,12 +456,15 @@ open class QuasselViewModelHelper @Inject constructor( } fun processSelectedBuffer( bufferViewConfigObservable: Observable<Optional<BufferViewConfig>>, selectedBufferId: Observable<BufferId> ) = combineLatest(connectedSession, selectedBufferId) .safeSwitchMap { (sessionOptional, buffer) -> ) = combineLatest(connectedSession, selectedBufferId, bufferViewConfigObservable) .safeSwitchMap { (sessionOptional, buffer, bufferViewConfigOptional) -> val session = sessionOptional.orNull() val bufferViewConfig = bufferViewConfigOptional.orNull() val bufferSyncer = session?.bufferSyncer if (bufferSyncer != null) { if (bufferSyncer != null && bufferViewConfig != null) { bufferHiddenState(bufferViewConfig, buffer).safeSwitchMap { bufferHiddenState -> session.liveNetworks().safeSwitchMap { networks -> val info = if (!buffer.isValidId()) networks[NetworkId(-buffer.id)]?.let { BufferInfo( Loading @@ -479,7 +482,8 @@ open class QuasselViewModelHelper @Inject constructor( network?.liveConnectionState()?.map { SelectedBufferItem( info, connectionState = it connectionState = it, hiddenState = bufferHiddenState ) } ?: Observable.just(SelectedBufferItem(info)) } Loading @@ -487,19 +491,41 @@ open class QuasselViewModelHelper @Inject constructor( network?.liveIrcChannel(info.bufferName)?.mapNullable(IrcChannel.NULL) { SelectedBufferItem( info, joined = it != null joined = it != null, hiddenState = bufferHiddenState ) } ?: Observable.just(SelectedBufferItem(info)) } ?: Observable.just(SelectedBufferItem( info, hiddenState = bufferHiddenState )) } else -> Observable.just(SelectedBufferItem(info)) Observable.just(SelectedBufferItem( info, hiddenState = bufferHiddenState )) } } else { Observable.just(SelectedBufferItem()) } } } } else { Observable.just(SelectedBufferItem()) } } fun bufferHiddenState(bufferViewConfig: BufferViewConfig, bufferId: BufferId): Observable<BufferHiddenState> = combineLatest(bufferViewConfig.liveBuffers(), bufferViewConfig.liveTemporarilyRemovedBuffers(), bufferViewConfig.liveRemovedBuffers()) .map { (visible, temp, perm) -> when (bufferId) { in visible -> BufferHiddenState.VISIBLE in temp -> BufferHiddenState.HIDDEN_TEMPORARY in perm -> BufferHiddenState.HIDDEN_PERMANENT else -> BufferHiddenState.HIDDEN_PERMANENT } } }