...
 
Commits (6)
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"
......
......@@ -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()
)
......
......@@ -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()
......
......@@ -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
......@@ -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,
......@@ -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()
......@@ -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)
......@@ -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
}
......@@ -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)
......
......@@ -191,7 +191,7 @@ object BufferContextPresenter {
true
}
R.id.action_unhide -> {
bufferViewConfig?.insertBufferSorted(info, bufferSyncer)
bufferViewConfig?.insertBufferSorted(info, bufferSyncer, session.networks)
actionMode.finish()
true
}
......
......@@ -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")
}
}
......
......@@ -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)
}
}
}
......@@ -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
......
......@@ -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
......@@ -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)
}
}
......@@ -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))
}
}
......@@ -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)
}
}
......
......@@ -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
......@@ -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)
}
......@@ -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)
}
......@@ -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) }
......@@ -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)
......@@ -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)
}
......
......@@ -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),
......
......@@ -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>())
......@@ -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"
......
......@@ -60,5 +60,5 @@ open class ArchiveViewModelHelper @Inject constructor(
showHandle
)
val selectedBuffer = processSelectedBuffer(archive.selectedBufferId)
val selectedBuffer = processSelectedBuffer(bufferViewConfig, archive.selectedBufferId)
}
......@@ -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>>
......
......@@ -456,50 +456,76 @@ 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) {
session.liveNetworks().safeSwitchMap { networks ->
val info = if (!buffer.isValidId()) networks[NetworkId(-buffer.id)]?.let {
BufferInfo(
bufferId = buffer,
networkId = it.networkId(),
groupId = 0,
bufferName = it.networkName(),
type = Buffer_Type.of(Buffer_Type.StatusBuffer)
)
} else bufferSyncer.bufferInfo(buffer)
if (info != null) {
val network = networks[info.networkId]
when (info.type.enabledValues().firstOrNull()) {
Buffer_Type.StatusBuffer -> {
network?.liveConnectionState()?.map {
SelectedBufferItem(
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(
bufferId = buffer,
networkId = it.networkId(),
groupId = 0,
bufferName = it.networkName(),
type = Buffer_Type.of(Buffer_Type.StatusBuffer)
)
} else bufferSyncer.bufferInfo(buffer)
if (info != null) {
val network = networks[info.networkId]
when (info.type.enabledValues().firstOrNull()) {
Buffer_Type.StatusBuffer -> {
network?.liveConnectionState()?.map {
SelectedBufferItem(
info,
connectionState = it,
hiddenState = bufferHiddenState
)
} ?: Observable.just(SelectedBufferItem(info))
}
Buffer_Type.ChannelBuffer -> {
network?.liveIrcChannel(info.bufferName)?.mapNullable(IrcChannel.NULL) {
SelectedBufferItem(
info,
joined = it != null,
hiddenState = bufferHiddenState
)
} ?: Observable.just(SelectedBufferItem(
info,
connectionState = it
)
} ?: Observable.just(SelectedBufferItem(info))
}
Buffer_Type.ChannelBuffer -> {
network?.liveIrcChannel(info.bufferName)?.mapNullable(IrcChannel.NULL) {
SelectedBufferItem(
hiddenState = bufferHiddenState
))
}
else ->
Observable.just(SelectedBufferItem(
info,
joined = it != null
)
} ?: Observable.just(SelectedBufferItem(info))
hiddenState = bufferHiddenState
))
}
else ->
Observable.just(SelectedBufferItem(info))
} else {
Observable.just(SelectedBufferItem())
}
} 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
}
}
}