From d6ae071a096e65162369915a4a8d132f4e8a4057 Mon Sep 17 00:00:00 2001 From: kyori19 Date: Tue, 3 Dec 2019 00:53:24 +0900 Subject: [PATCH] List tabs (#1570) * List tabs * Add comment * Move decoder part to TabData.createTabDataFromId * Fix drawable tint * Use dedicated view for list picker * Error log * Fix logging Co-Authored-By: Konrad Pozniak * Fix tint color Co-Authored-By: Konrad Pozniak * Fix missing import * Move encoding part too * Fix comment * Fix decoder * Revert "Fix decoder" This reverts commit fdc45aac9c113348f8740e6692d2d8e9ace14f7f. * Revert "Fix comment" This reverts commit 704b4e6d2e5545d5f2d20c9bc6bc276d6257d119. * Revert "Move encoding part too" This reverts commit 32e77346ff98ae1133e76ab6dfd880b26171005e. * Revert "Move decoder part to TabData.createTabDataFromId" This reverts commit d1cd2070ab564d2e33874225272a71f0904d681e. --- .../com/keylesspalace/tusky/MainActivity.java | 8 +++- .../java/com/keylesspalace/tusky/TabData.kt | 2 + .../tusky/TabPreferenceActivity.kt | 39 +++++++++++++++ .../tusky/adapter/ListSelectionAdapter.kt | 48 +++++++++++++++++++ .../keylesspalace/tusky/adapter/TabAdapter.kt | 7 ++- .../com/keylesspalace/tusky/db/Converters.kt | 7 ++- app/src/main/res/drawable-anydpi/ic_list.xml | 9 ++++ app/src/main/res/layout/item_picker_list.xml | 8 ++++ app/src/main/res/values/strings.xml | 2 + 9 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt create mode 100644 app/src/main/res/drawable-anydpi/ic_list.xml create mode 100644 app/src/main/res/layout/item_picker_list.xml diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index 1c874c65..78d80d04 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -478,8 +478,12 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut tabLayout.removeAllTabs(); for (int i = 0; i < tabs.size(); i++) { TabLayout.Tab tab = tabLayout.newTab() - .setIcon(tabs.get(i).getIcon()) - .setContentDescription(tabs.get(i).getText()); + .setIcon(tabs.get(i).getIcon()); + if (tabs.get(i).getId().equals(TabDataKt.LIST)) { + tab.setContentDescription(tabs.get(i).getArguments().get(1)); + } else { + tab.setContentDescription(tabs.get(i).getText()); + } tabLayout.addTab(tab); if (tabs.get(i).getId().equals(TabDataKt.NOTIFICATIONS)) { notificationTabPosition = i; diff --git a/app/src/main/java/com/keylesspalace/tusky/TabData.kt b/app/src/main/java/com/keylesspalace/tusky/TabData.kt index 5e6b0d39..5901d6e5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabData.kt @@ -30,6 +30,7 @@ const val LOCAL = "Local" const val FEDERATED = "Federated" const val DIRECT = "Direct" const val HASHTAG = "Hashtag" +const val LIST = "List" data class TabData(val id: String, @StringRes val text: Int, @@ -45,6 +46,7 @@ fun createTabDataFromId(id: String, arguments: List = emptyList()): TabD FEDERATED -> TabData(FEDERATED, R.string.title_public_federated, R.drawable.ic_public_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED) }) DIRECT -> TabData(DIRECT, R.string.title_direct_messages, R.drawable.reblog_direct_dark, { ConversationsFragment.newInstance() }) HASHTAG -> TabData(HASHTAG, R.string.hashtag, R.drawable.ic_hashtag, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.TAG, args.getOrNull(0).orEmpty()) }, arguments) + LIST -> TabData(LIST, R.string.list, R.drawable.ic_list, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty()) }, arguments) else -> throw IllegalArgumentException("unknown tab type") } } diff --git a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt index f2876bbe..fe8783ac 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt @@ -16,6 +16,7 @@ package com.keylesspalace.tusky import android.os.Bundle +import android.util.Log import android.view.MenuItem import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.AppCompatEditText @@ -25,15 +26,18 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.adapter.ItemInteractionListener +import com.keylesspalace.tusky.adapter.ListSelectionAdapter import com.keylesspalace.tusky.adapter.TabAdapter import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.MainTabsChangedEvent import com.keylesspalace.tusky.di.Injectable +import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.onTextChanged import com.keylesspalace.tusky.util.visible import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from import com.uber.autodispose.autoDispose import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import kotlinx.android.synthetic.main.activity_tab_preference.* import kotlinx.android.synthetic.main.toolbar_basic.* @@ -42,6 +46,8 @@ import javax.inject.Inject class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListener { + @Inject + lateinit var mastodonApi: MastodonApi @Inject lateinit var eventHub: EventHub @@ -151,6 +157,11 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene return } + if (tab.id == LIST) { + showSelectListDialog() + return + } + currentTabs.add(tab) currentTabsAdapter.notifyItemInserted(currentTabs.size - 1) updateAvailableTabs() @@ -200,6 +211,33 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene editText.requestFocus() } + private fun showSelectListDialog() { + val adapter = ListSelectionAdapter(this) + mastodonApi.getLists() + .observeOn(AndroidSchedulers.mainThread()) + .autoDispose(from(this, Lifecycle.Event.ON_DESTROY)) + .subscribe ( + { lists -> + adapter.addAll(lists) + }, + { throwable -> + Log.e("TabPreferenceActivity", "failed to load lists", throwable) + } + ) + + AlertDialog.Builder(this) + .setTitle(R.string.select_list_title) + .setAdapter(adapter) { _, position -> + val list = adapter.getItem(position) + val newTab = createTabDataFromId(LIST, listOf(list!!.id, list.title)) + currentTabs.add(newTab) + currentTabsAdapter.notifyItemInserted(currentTabs.size - 1) + updateAvailableTabs() + saveTabs() + } + .show() + } + private fun validateHashtag(input: CharSequence?): Boolean { val trimmedInput = input?.trim() ?: "" return trimmedInput.isNotEmpty() && hashtagRegex.matcher(trimmedInput).matches() @@ -230,6 +268,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene } addableTabs.add(createTabDataFromId(HASHTAG)) + addableTabs.add(createTabDataFromId(LIST)) addTabAdapter.updateData(addableTabs) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt new file mode 100644 index 00000000..de7604f1 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt @@ -0,0 +1,48 @@ +/* Copyright 2019 kyori19 + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky 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 Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.entity.MastoList +import com.keylesspalace.tusky.util.ThemeUtils +import kotlinx.android.synthetic.main.item_picker_list.view.* + +class ListSelectionAdapter(context: Context) : ArrayAdapter(context, R.layout.item_autocomplete_hashtag) { + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var view = convertView + + if (convertView == null) { + val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + view = layoutInflater.inflate(R.layout.item_picker_list, parent, false) + } + view!! + + val list = getItem(position) + if (list != null) { + val title = view.title + title.text = list.title + val icon = ThemeUtils.getTintedDrawable(context, R.drawable.ic_list, android.R.attr.textColorTertiary) + title.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null) + } + + return view + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt index dc08af5b..a1689819 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt @@ -21,6 +21,7 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.HASHTAG +import com.keylesspalace.tusky.LIST import com.keylesspalace.tusky.R import com.keylesspalace.tusky.TabData import com.keylesspalace.tusky.util.ThemeUtils @@ -57,7 +58,11 @@ class TabAdapter(private var data: List, override fun onBindViewHolder(holder: ViewHolder, position: Int) { val context = holder.itemView.context - holder.itemView.textView.setText(data[position].text) + if (!small && data[position].id == LIST) { + holder.itemView.textView.text = data[position].arguments.getOrNull(1).orEmpty() + } else { + holder.itemView.textView.setText(data[position].text) + } val iconDrawable = ThemeUtils.getTintedDrawable(context, data[position].icon, android.R.attr.textColorSecondary) holder.itemView.textView.setCompoundDrawablesRelativeWithIntrinsicBounds(iconDrawable, null, null, null) if (small) { diff --git a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt index 4dad5a08..0074b8d9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt @@ -28,6 +28,8 @@ import com.keylesspalace.tusky.entity.Poll import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.json.SpannedTypeAdapter import com.keylesspalace.tusky.util.HtmlUtils +import java.net.URLDecoder +import java.net.URLEncoder import java.util.* class Converters { @@ -61,13 +63,14 @@ class Converters { return str?.split(";") ?.map { val data = it.split(":") - createTabDataFromId(data[0], data.drop(1)) + createTabDataFromId(data[0], data.drop(1).map { s -> URLDecoder.decode(s, "UTF-8") }) } } @TypeConverter fun tabDataToString(tabData: List?): String? { - return tabData?.joinToString(";") { it.id + ":" + it.arguments.joinToString(":") } + // List name may include ":" + return tabData?.joinToString(";") { it.id + ":" + it.arguments.joinToString(":") { s -> URLEncoder.encode(s, "UTF-8") } } } @TypeConverter diff --git a/app/src/main/res/drawable-anydpi/ic_list.xml b/app/src/main/res/drawable-anydpi/ic_list.xml new file mode 100644 index 00000000..4c2fb883 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_list.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/item_picker_list.xml b/app/src/main/res/layout/item_picker_list.xml new file mode 100644 index 00000000..65154afb --- /dev/null +++ b/app/src/main/res/layout/item_picker_list.xml @@ -0,0 +1,8 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 357695da..f06e34df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -470,6 +470,8 @@ Edit hashtag Hashtag without # Hashtag + Select list + List Clear Filter Apply