diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt index 19cf99251b367a8b243ab8b6f67d449d61bd8ca2..6a437ebc1cfb7f831fb874f1827783241cad565a 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferListAdapter.kt @@ -29,6 +29,7 @@ class BufferListAdapter( lifecycleOwner: LifecycleOwner, liveData: LiveData<List<BufferProps>?>, private val selectedBuffer: MutableLiveData<BufferId>, + private val collapsedNetworks: MutableLiveData<Set<NetworkId>>, runInBackground: (() -> Unit) -> Any, runOnUiThread: (Runnable) -> Any, private val clickListener: ((BufferId) -> Unit)? = null, @@ -36,8 +37,6 @@ class BufferListAdapter( ) : RecyclerView.Adapter<BufferListAdapter.BufferViewHolder>() { var data = mutableListOf<BufferListItem>() - private val collapsedNetworks = MutableLiveData<Set<NetworkId>>() - fun expandListener(networkId: NetworkId) { if (collapsedNetworks.value.orEmpty().contains(networkId)) collapsedNetworks.postValue(collapsedNetworks.value.orEmpty() - networkId) @@ -58,9 +57,6 @@ class BufferListAdapter( } init { - collapsedNetworks.value = emptySet() - selectedBuffer.value = -1 - liveData.zip(collapsedNetworks, selectedBuffer).observe( lifecycleOwner, Observer { it: Triple<List<BufferProps>?, Set<NetworkId>, BufferId>? -> runInBackground { @@ -163,9 +159,16 @@ class BufferListAdapter( val description: CharSequence, val activity: Message_Types, val highlights: Int = 0, - val bufferActivity: Buffer_Activities = Buffer_Activity.of(Buffer_Activity.NoActivity) + val bufferActivity: Buffer_Activities = Buffer_Activity.of(Buffer_Activity.NoActivity), + val hiddenState: HiddenState ) + enum class HiddenState { + VISIBLE, + HIDDEN_TEMPORARY, + HIDDEN_PERMANENT + } + data class BufferState( val networkExpanded: Boolean, val selected: Boolean diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt index a4a5ab9a5453f5b6daa5ce42a42a6a2bf6b7f65f..a3c15efe2bbb72f284ade1a32f452d73323637c5 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/chat/buffers/BufferViewConfigFragment.kt @@ -53,7 +53,9 @@ class BufferViewConfigFragment : ServiceBoundFragment() { val selected = viewModel.selectedBuffer.value val info = selected?.info val session = viewModel.session.value + val bufferSyncer = session?.bufferSyncer val network = session?.networks?.get(selected?.info?.networkId) + val bufferViewConfig = viewModel.getBufferViewConfig().value return if (info != null && session != null) { when (item?.itemId) { @@ -93,6 +95,20 @@ class BufferViewConfigFragment : ServiceBoundFragment() { actionMode?.finish() true } + R.id.action_unhide -> { + bufferSyncer?.let { + bufferViewConfig?.requestAddBuffer(info, bufferSyncer) + } + true + } + R.id.action_hide_temp -> { + bufferViewConfig?.requestRemoveBuffer(info.bufferId) + true + } + R.id.action_hide_perm -> { + bufferViewConfig?.requestRemoveBufferPermanently(info.bufferId) + true + } else -> false } } else { @@ -180,6 +196,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { } }, viewModel.selectedBufferId, + viewModel.collapsedNetworks, handlerThread::post, activity!!::runOnUiThread, clickListener, @@ -196,9 +213,27 @@ class BufferViewConfigFragment : ServiceBoundFragment() { R.id.action_disconnect, R.id.action_join, R.id.action_part, - R.id.action_delete + R.id.action_delete, + R.id.action_unhide, + R.id.action_hide_temp, + R.id.action_hide_perm ) + val visibilityActions = when (buffer.hiddenState) { + BufferListAdapter.HiddenState.VISIBLE -> setOf( + R.id.action_hide_temp, + R.id.action_hide_perm + ) + BufferListAdapter.HiddenState.HIDDEN_TEMPORARY -> setOf( + R.id.action_unhide, + R.id.action_hide_perm + ) + BufferListAdapter.HiddenState.HIDDEN_PERMANENT -> setOf( + R.id.action_unhide, + R.id.action_hide_temp + ) + } + val availableActions = when (buffer.info?.type?.enabledValues()?.firstOrNull()) { Buffer_Type.StatusBuffer -> { when (buffer.connectionState) { @@ -214,12 +249,12 @@ class BufferViewConfigFragment : ServiceBoundFragment() { setOf(R.id.action_part) } else { setOf(R.id.action_join, R.id.action_delete) - } + } + visibilityActions } Buffer_Type.QueryBuffer -> { - setOf(R.id.action_delete) + setOf(R.id.action_delete) + visibilityActions } - else -> emptySet() + else -> visibilityActions } val unavailableActions = allActions - availableActions @@ -236,7 +271,17 @@ class BufferViewConfigFragment : ServiceBoundFragment() { } }) - chatListToolbar.startActionMode(actionModeCallback) + chatListToolbar.inflateMenu(R.menu.context_bufferlist) + chatListToolbar.setOnMenuItemClickListener { item -> + when (item.itemId) { + R.id.action_show_hidden -> { + item.isChecked = !item.isChecked + viewModel.showHidden.value = item.isChecked + true + } + else -> false + } + } chatList.layoutManager = LinearLayoutManager(context) chatList.itemAnimator = DefaultItemAnimator() chatList.setItemViewCacheSize(10) @@ -266,6 +311,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() { data class SelectedItem( val info: BufferInfo? = null, val connectionState: INetwork.ConnectionState = INetwork.ConnectionState.Disconnected, - val joined: Boolean = false + val joined: Boolean = false, + val hiddenState: BufferListAdapter.HiddenState = BufferListAdapter.HiddenState.VISIBLE ) } diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt index eff40a23f4d300c6919b0de5c481211f712ee627..d9b90d73b843d64ab41a9da69fb23b73ffcf18ab 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt @@ -5,10 +5,12 @@ import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.ViewModel 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.BufferViewConfig import de.kuschku.libquassel.quassel.syncables.IrcChannel import de.kuschku.libquassel.quassel.syncables.IrcUser +import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.libquassel.session.Backend import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.session.SessionManager @@ -192,10 +194,23 @@ class QuasselViewModel : ViewModel() { } } + val showHidden = MutableLiveData<Boolean>() + val collapsedNetworks = MutableLiveData<Set<NetworkId>>() val selectedBufferId = MutableLiveData<BufferId>() - val selectedBuffer = session.zip(selectedBufferId).switchMapRx { (session, buffer) -> + val selectedBuffer = session.zip( + selectedBufferId, bufferViewConfig + ).switchMapRx { (session, buffer, bufferViewConfig) -> val bufferSyncer = session?.bufferSyncer - if (bufferSyncer != null) { + if (bufferSyncer != null && bufferViewConfig != null) { + val hiddenState = when { + bufferViewConfig.removedBuffers().contains(buffer) -> + BufferListAdapter.HiddenState.HIDDEN_PERMANENT + bufferViewConfig.temporarilyRemovedBuffers().contains(buffer) -> + BufferListAdapter.HiddenState.HIDDEN_TEMPORARY + else -> + BufferListAdapter.HiddenState.VISIBLE + } + val info = bufferSyncer.bufferInfo(buffer) if (info != null) { val network = session.networks[info.networkId] @@ -204,7 +219,8 @@ class QuasselViewModel : ViewModel() { network?.liveConnectionState?.map { BufferViewConfigFragment.SelectedItem( info, - connectionState = it + connectionState = it, + hiddenState = hiddenState ) } } @@ -212,14 +228,16 @@ class QuasselViewModel : ViewModel() { network?.liveIrcChannel(info.bufferName)?.map { BufferViewConfigFragment.SelectedItem( info, - joined = it != IrcChannel.NULL + joined = it != IrcChannel.NULL, + hiddenState = hiddenState ) } } - else -> Observable.just(BufferViewConfigFragment.SelectedItem(info)) + else -> + Observable.just(BufferViewConfigFragment.SelectedItem(info, hiddenState = hiddenState)) } } else { - Observable.just(BufferViewConfigFragment.SelectedItem(info)) + Observable.just(BufferViewConfigFragment.SelectedItem(info, hiddenState = hiddenState)) } } else { Observable.just(BufferViewConfigFragment.SelectedItem()) @@ -227,103 +245,131 @@ class QuasselViewModel : ViewModel() { } val bufferList: LiveData<Pair<BufferViewConfig?, List<BufferListAdapter.BufferProps>>?> = session.zip( - bufferViewConfig - ).switchMapRx { (session, config) -> + bufferViewConfig, showHidden + ).switchMapRx { (session, config, showHiddenRaw) -> val bufferSyncer = session?.bufferSyncer + val showHidden = showHiddenRaw ?: false if (bufferSyncer != null && config != null) { config.live_config.debounce(16, TimeUnit.MILLISECONDS).switchMap { currentConfig -> - config.live_buffers.switchMap { ids -> - bufferSyncer.liveBufferInfos().switchMap { - Observable.combineLatest( - ids.mapNotNull { id -> - bufferSyncer.bufferInfo(id) + Observable.combineLatest( + listOf( + config.live_buffers, + config.live_temporarilyRemovedBuffers, + config.live_removedBuffers + ), + object : Function<Array<Any>, List<Collection<BufferId>>> { + override fun apply(objects: Array<Any>): List<Collection<BufferId>> { + return objects.toList() as List<Collection<BufferId>> + } + } + ).switchMap { (ids, temp, perm) -> + fun transformIds(ids: Collection<BufferId>, state: BufferListAdapter.HiddenState) = + ids.mapNotNull { id -> + bufferSyncer.bufferInfo(id) + }.filter { + currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId }.filter { - currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId - }.filter { - (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() || - it.type.hasFlag(Buffer_Type.StatusBuffer) - }.mapNotNull { - val network = session.networks[it.networkId] - if (network == null) { - null - } else { - it to network + (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() || + it.type.hasFlag(Buffer_Type.StatusBuffer) + }.mapNotNull { + val network = session.networks[it.networkId] + if (network == null) { + null + } else { + it to network + } + }.map<Pair<BufferInfo, Network>, Observable<BufferListAdapter.BufferProps>?> { (info, network) -> + bufferSyncer.liveActivity(info.bufferId).switchMap { activity -> + bufferSyncer.liveHighlightCount(info.bufferId).map { highlights -> + activity to highlights } - }.map { (info, network) -> - bufferSyncer.liveActivity(info.bufferId).switchMap { activity -> - bufferSyncer.liveHighlightCount(info.bufferId).map { highlights -> - activity to highlights - } - }.switchMap { (activity, highlights) -> - when (info.type.toInt()) { - BufferInfo.Type.QueryBuffer.toInt() -> { - network.liveIrcUser(info.bufferName).switchMap { user -> - user.live_away.switchMap { away -> - user.live_realName.map { realName -> - BufferListAdapter.BufferProps( - info = info, - network = network.networkInfo(), - bufferStatus = when { - user == IrcUser.NULL -> BufferListAdapter.BufferStatus.OFFLINE - away -> BufferListAdapter.BufferStatus.AWAY - else -> BufferListAdapter.BufferStatus.ONLINE - }, - description = realName, - activity = activity, - highlights = highlights - ) - } - } - } - } - BufferInfo.Type.ChannelBuffer.toInt() -> { - network.liveIrcChannel( - info.bufferName - ).switchMap { channel -> - channel.live_topic.map { topic -> + }.switchMap { (activity, highlights) -> + when (info.type.toInt()) { + BufferInfo.Type.QueryBuffer.toInt() -> { + network.liveIrcUser(info.bufferName).switchMap { user -> + user.live_away.switchMap { away -> + user.live_realName.map { realName -> BufferListAdapter.BufferProps( info = info, network = network.networkInfo(), - bufferStatus = when (channel) { - IrcChannel.NULL -> BufferListAdapter.BufferStatus.OFFLINE - else -> BufferListAdapter.BufferStatus.ONLINE + bufferStatus = when { + user == IrcUser.NULL -> BufferListAdapter.BufferStatus.OFFLINE + away -> BufferListAdapter.BufferStatus.AWAY + else -> BufferListAdapter.BufferStatus.ONLINE }, - description = topic, + description = realName, activity = activity, - highlights = highlights + highlights = highlights, + hiddenState = state ) } } } - BufferInfo.Type.StatusBuffer.toInt() -> { - network.liveConnectionState.map { + } + BufferInfo.Type.ChannelBuffer.toInt() -> { + network.liveIrcChannel( + info.bufferName + ).switchMap { channel -> + channel.live_topic.map { topic -> BufferListAdapter.BufferProps( info = info, network = network.networkInfo(), - bufferStatus = BufferListAdapter.BufferStatus.OFFLINE, - description = "", + bufferStatus = when (channel) { + IrcChannel.NULL -> BufferListAdapter.BufferStatus.OFFLINE + else -> BufferListAdapter.BufferStatus.ONLINE + }, + description = topic, activity = activity, - highlights = highlights + highlights = highlights, + hiddenState = state ) } } - else -> Observable.just( + } + BufferInfo.Type.StatusBuffer.toInt() -> { + network.liveConnectionState.map { BufferListAdapter.BufferProps( info = info, network = network.networkInfo(), bufferStatus = BufferListAdapter.BufferStatus.OFFLINE, description = "", activity = activity, - highlights = highlights + highlights = highlights, + hiddenState = state ) - ) + } } + else -> Observable.just( + BufferListAdapter.BufferProps( + info = info, + network = network.networkInfo(), + bufferStatus = BufferListAdapter.BufferStatus.OFFLINE, + description = "", + activity = activity, + highlights = highlights, + hiddenState = state + ) + ) } - }, object : Function<Array<Any>, List<BufferListAdapter.BufferProps>> { - override fun apply(objects: Array<Any>): List<BufferListAdapter.BufferProps> { - return objects.toList() as List<BufferListAdapter.BufferProps> + } } + + bufferSyncer.liveBufferInfos().switchMap { + val buffers = if (showHidden) { + transformIds(ids, BufferListAdapter.HiddenState.VISIBLE) + + transformIds(temp, BufferListAdapter.HiddenState.HIDDEN_TEMPORARY) + + transformIds(perm, BufferListAdapter.HiddenState.HIDDEN_PERMANENT) + } else { + transformIds(ids, BufferListAdapter.HiddenState.VISIBLE) } + + Observable.combineLatest( + buffers, + object : Function<Array<Any>, List<BufferListAdapter.BufferProps>> { + override fun apply(objects: Array<Any>): List<BufferListAdapter.BufferProps> { + return objects.toList() as List<BufferListAdapter.BufferProps> + } + } ).map { list -> Pair<BufferViewConfig?, List<BufferListAdapter.BufferProps>>( config, @@ -342,4 +388,10 @@ class QuasselViewModel : ViewModel() { ) } } + + init { + showHidden.postValue(false) + selectedBufferId.postValue(-1) + collapsedNetworks.value = emptySet() + } } \ No newline at end of file diff --git a/app/src/main/res/menu/context_buffer.xml b/app/src/main/res/menu/context_buffer.xml index 08152e25cae16452dc58001bed6d8411fbfe2b79..9754406733c30da0117b61e6e71c436cd4c0e712 100644 --- a/app/src/main/res/menu/context_buffer.xml +++ b/app/src/main/res/menu/context_buffer.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_connect" android:title="@string/label_connect" /> @@ -15,5 +16,16 @@ <item android:id="@+id/action_delete" android:title="@string/label_delete" /> - <!--<item android:title="Merge" />--> + <item + android:id="@+id/action_unhide" + android:title="@string/label_unhide" + app:showAsAction="never" /> + <item + android:id="@+id/action_hide_temp" + android:title="@string/label_hide_temp" + app:showAsAction="never" /> + <item + android:id="@+id/action_hide_perm" + android:title="@string/label_hide_perm" + app:showAsAction="never" /> </menu> \ No newline at end of file diff --git a/app/src/main/res/menu/context_bufferlist.xml b/app/src/main/res/menu/context_bufferlist.xml new file mode 100644 index 0000000000000000000000000000000000000000..2c85564867ec2735ae80c3ef28478c5a60d99b3f --- /dev/null +++ b/app/src/main/res/menu/context_bufferlist.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/action_show_hidden" + android:checkable="true" + android:title="@string/label_show_hidden" /> +</menu> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd5bb3719004db9b2e10d4ff236574a732c84058..fe8fac721ac408771ae41cf424ced212c7850ad7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -11,6 +11,8 @@ <string name="label_delete">Delete</string> <string name="label_disconnect">Disconnect</string> <string name="label_filter_messages">Filter Messages</string> + <string name="label_hide_perm">Hide Permanently</string> + <string name="label_hide_temp">Hide Temporarily</string> <string name="label_input_history">Input History</string> <string name="label_join">Join</string> <string name="label_open">Open</string> @@ -19,6 +21,8 @@ <string name="label_save">Save</string> <string name="label_select_multiple">Select</string> <string name="label_settings">Settings</string> + <string name="label_show_hidden">Show Hidden</string> + <string name="label_unhide">Make Visible</string> <string name="label_status_disconnected">Disconnected</string> <string name="label_status_connecting">Connecting</string>