Merge pull request #3373 from mauriciocolli/feed-add-search-sub-list
Add search for subscription picker in the feed group dialog
This commit is contained in:
commit
734680b9f0
|
@ -20,6 +20,13 @@ abstract class SubscriptionDAO : BasicDAO<SubscriptionEntity> {
|
||||||
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
|
@Query("SELECT * FROM subscriptions ORDER BY name COLLATE NOCASE ASC")
|
||||||
abstract override fun getAll(): Flowable<List<SubscriptionEntity>>
|
abstract override fun getAll(): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT * FROM subscriptions
|
||||||
|
WHERE name LIKE '%' || :filter || '%'
|
||||||
|
ORDER BY name COLLATE NOCASE ASC
|
||||||
|
""")
|
||||||
|
abstract fun filterByName(filter: String): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
|
@Query("SELECT * FROM subscriptions WHERE url LIKE :url AND service_id = :serviceId")
|
||||||
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>>
|
abstract fun getSubscriptionFlowable(serviceId: Int, url: String): Flowable<List<SubscriptionEntity>>
|
||||||
|
|
||||||
|
|
|
@ -130,4 +130,55 @@ public class SubscriptionEntity {
|
||||||
item.setDescription(getDescription());
|
item.setDescription(getDescription());
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Remove these generated methods by migrating this class to a data class from Kotlin.
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("EqualsReplaceableByObjectsCall")
|
||||||
|
public boolean equals(final Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final SubscriptionEntity that = (SubscriptionEntity) o;
|
||||||
|
|
||||||
|
if (uid != that.uid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serviceId != that.serviceId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!url.equals(that.url)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (name != null ? !name.equals(that.name) : that.name != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (avatarUrl != null ? !avatarUrl.equals(that.avatarUrl) : that.avatarUrl != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (subscriberCount != null
|
||||||
|
? !subscriberCount.equals(that.subscriberCount)
|
||||||
|
: that.subscriberCount != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return description != null
|
||||||
|
? description.equals(that.description)
|
||||||
|
: that.description == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = (int) (uid ^ (uid >>> 32));
|
||||||
|
result = 31 * result + serviceId;
|
||||||
|
result = 31 * result + url.hashCode();
|
||||||
|
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||||
|
result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0);
|
||||||
|
result = 31 * result + (subscriberCount != null ? subscriberCount.hashCode() : 0);
|
||||||
|
result = 31 * result + (description != null ? description.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ class SubscriptionManager(context: Context) {
|
||||||
fun subscriptionTable(): SubscriptionDAO = subscriptionTable
|
fun subscriptionTable(): SubscriptionDAO = subscriptionTable
|
||||||
fun subscriptions() = subscriptionTable.all
|
fun subscriptions() = subscriptionTable.all
|
||||||
|
|
||||||
|
fun filterByName(filter: String) = subscriptionTable.filterByName(filter)
|
||||||
|
|
||||||
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
|
fun upsertAll(infoList: List<ChannelInfo>): List<SubscriptionEntity> {
|
||||||
val listEntities = subscriptionTable.upsertAll(
|
val listEntities = subscriptionTable.upsertAll(
|
||||||
infoList.map { SubscriptionEntity.from(it) })
|
infoList.map { SubscriptionEntity.from(it) })
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
|
import android.text.TextUtils
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -13,34 +14,22 @@ import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.xwray.groupie.GroupAdapter
|
import com.xwray.groupie.GroupAdapter
|
||||||
|
import com.xwray.groupie.OnItemClickListener
|
||||||
import com.xwray.groupie.Section
|
import com.xwray.groupie.Section
|
||||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||||
import icepick.Icepick
|
import icepick.Icepick
|
||||||
import icepick.State
|
import icepick.State
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.cancel_button
|
import kotlin.collections.contains
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.confirm_button
|
import kotlinx.android.synthetic.main.dialog_feed_group_create.*
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.delete_button
|
import kotlinx.android.synthetic.main.toolbar_search_layout.*
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.delete_screen_message
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.group_name_input
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.group_name_input_container
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.icon_preview
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.icon_selector
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.options_root
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.select_channel_button
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.selected_subscription_count_view
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.separator
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.subscriptions_selector
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.subscriptions_selector_header_info
|
|
||||||
import kotlinx.android.synthetic.main.dialog_feed_group_create.subscriptions_selector_list
|
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
import org.schabi.newpipe.fragments.BackPressable
|
||||||
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
||||||
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.DeleteScreen
|
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.DeleteScreen
|
||||||
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.IconPickerScreen
|
import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialog.ScreenState.IconPickerScreen
|
||||||
|
@ -51,9 +40,10 @@ import org.schabi.newpipe.local.subscription.dialog.FeedGroupDialogViewModel.Dia
|
||||||
import org.schabi.newpipe.local.subscription.item.EmptyPlaceholderItem
|
import org.schabi.newpipe.local.subscription.item.EmptyPlaceholderItem
|
||||||
import org.schabi.newpipe.local.subscription.item.PickerIconItem
|
import org.schabi.newpipe.local.subscription.item.PickerIconItem
|
||||||
import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
|
import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
|
||||||
|
import org.schabi.newpipe.util.AndroidTvUtils
|
||||||
import org.schabi.newpipe.util.ThemeHelper
|
import org.schabi.newpipe.util.ThemeHelper
|
||||||
|
|
||||||
class FeedGroupDialog : DialogFragment() {
|
class FeedGroupDialog : DialogFragment(), BackPressable {
|
||||||
private lateinit var viewModel: FeedGroupDialogViewModel
|
private lateinit var viewModel: FeedGroupDialogViewModel
|
||||||
private var groupId: Long = NO_GROUP_SELECTED
|
private var groupId: Long = NO_GROUP_SELECTED
|
||||||
private var groupIcon: FeedGroupIcon? = null
|
private var groupIcon: FeedGroupIcon? = null
|
||||||
|
@ -66,22 +56,19 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
object DeleteScreen : ScreenState()
|
object DeleteScreen : ScreenState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@State
|
@State @JvmField var selectedIcon: FeedGroupIcon? = null
|
||||||
@JvmField
|
@State @JvmField var selectedSubscriptions: HashSet<Long> = HashSet()
|
||||||
var selectedIcon: FeedGroupIcon? = null
|
@State @JvmField var wasSubscriptionSelectionChanged: Boolean = false
|
||||||
@State
|
@State @JvmField var currentScreen: ScreenState = InitialScreen
|
||||||
@JvmField
|
|
||||||
var selectedSubscriptions: HashSet<Long> = HashSet()
|
|
||||||
@State
|
|
||||||
@JvmField
|
|
||||||
var currentScreen: ScreenState = InitialScreen
|
|
||||||
|
|
||||||
@State
|
@State @JvmField var subscriptionsListState: Parcelable? = null
|
||||||
@JvmField
|
@State @JvmField var iconsListState: Parcelable? = null
|
||||||
var subscriptionsListState: Parcelable? = null
|
@State @JvmField var wasSearchSubscriptionsVisible = false
|
||||||
@State
|
@State @JvmField var subscriptionsCurrentSearchQuery = ""
|
||||||
@JvmField
|
|
||||||
var iconsListState: Parcelable? = null
|
private val subscriptionMainSection = Section()
|
||||||
|
private val subscriptionEmptyFooter = Section()
|
||||||
|
private lateinit var subscriptionGroupAdapter: GroupAdapter<GroupieViewHolder>
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -91,22 +78,30 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
groupId = arguments?.getLong(KEY_GROUP_ID, NO_GROUP_SELECTED) ?: NO_GROUP_SELECTED
|
groupId = arguments?.getLong(KEY_GROUP_ID, NO_GROUP_SELECTED) ?: NO_GROUP_SELECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
return inflater.inflate(R.layout.dialog_feed_group_create, container)
|
return inflater.inflate(R.layout.dialog_feed_group_create, container)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return object : Dialog(requireActivity(), theme) {
|
return object : Dialog(requireActivity(), theme) {
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (currentScreen !is InitialScreen) {
|
if (!this@FeedGroupDialog.onBackPressed()) {
|
||||||
showScreen(InitialScreen)
|
|
||||||
} else {
|
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
|
||||||
|
wasSearchSubscriptionsVisible = isSearchVisible()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
|
|
||||||
|
@ -119,11 +114,15 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(this, FeedGroupDialogViewModel.Factory(requireContext(), groupId))
|
viewModel = ViewModelProvider(this,
|
||||||
.get(FeedGroupDialogViewModel::class.java)
|
FeedGroupDialogViewModel.Factory(requireContext(),
|
||||||
|
groupId, subscriptionsCurrentSearchQuery)
|
||||||
|
).get(FeedGroupDialogViewModel::class.java)
|
||||||
|
|
||||||
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
|
viewModel.groupLiveData.observe(viewLifecycleOwner, Observer(::handleGroup))
|
||||||
viewModel.subscriptionsLiveData.observe(viewLifecycleOwner, Observer { setupSubscriptionPicker(it.first, it.second) })
|
viewModel.subscriptionsLiveData.observe(viewLifecycleOwner, Observer {
|
||||||
|
setupSubscriptionPicker(it.first, it.second)
|
||||||
|
})
|
||||||
viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
|
viewModel.dialogEventLiveData.observe(viewLifecycleOwner, Observer {
|
||||||
when (it) {
|
when (it) {
|
||||||
ProcessingEvent -> disableInput()
|
ProcessingEvent -> disableInput()
|
||||||
|
@ -131,15 +130,54 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
subscriptionGroupAdapter = GroupAdapter<GroupieViewHolder>().apply {
|
||||||
|
add(subscriptionMainSection)
|
||||||
|
add(subscriptionEmptyFooter)
|
||||||
|
spanCount = 4
|
||||||
|
}
|
||||||
|
subscriptions_selector_list.apply {
|
||||||
|
// Disable animations, too distracting.
|
||||||
|
itemAnimator = null
|
||||||
|
adapter = subscriptionGroupAdapter
|
||||||
|
layoutManager = GridLayoutManager(requireContext(), subscriptionGroupAdapter.spanCount,
|
||||||
|
RecyclerView.VERTICAL, false).apply {
|
||||||
|
spanSizeLookup = subscriptionGroupAdapter.spanSizeLookup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setupIconPicker()
|
setupIconPicker()
|
||||||
setupListeners()
|
setupListeners()
|
||||||
|
|
||||||
showScreen(currentScreen)
|
showScreen(currentScreen)
|
||||||
|
|
||||||
|
if (currentScreen == SubscriptionsPickerScreen && wasSearchSubscriptionsVisible) {
|
||||||
|
showSearch()
|
||||||
|
} else if (currentScreen == InitialScreen && groupId == NO_GROUP_SELECTED) {
|
||||||
|
showKeyboard()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
subscriptions_selector_list?.adapter = null
|
||||||
|
icon_selector?.adapter = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/*///////////////////////////////////////////////////////////////////////////
|
||||||
// Setup
|
// Setup
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////// */
|
||||||
|
|
||||||
|
override fun onBackPressed(): Boolean {
|
||||||
|
if (currentScreen is SubscriptionsPickerScreen && isSearchVisible()) {
|
||||||
|
hideSearch()
|
||||||
|
return true
|
||||||
|
} else if (currentScreen !is InitialScreen) {
|
||||||
|
showScreen(InitialScreen)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
delete_button.setOnClickListener { showScreen(DeleteScreen) }
|
delete_button.setOnClickListener { showScreen(DeleteScreen) }
|
||||||
|
@ -163,14 +201,55 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
confirm_button.setOnClickListener {
|
confirm_button.setOnClickListener { handlePositiveButton() }
|
||||||
when (currentScreen) {
|
|
||||||
InitialScreen -> handlePositiveButtonInitialScreen()
|
select_channel_button.setOnClickListener {
|
||||||
DeleteScreen -> viewModel.deleteGroup()
|
subscriptions_selector_list.scrollToPosition(0)
|
||||||
|
showScreen(SubscriptionsPickerScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
val headerMenu = subscriptions_header_toolbar.menu
|
||||||
|
requireActivity().menuInflater.inflate(R.menu.menu_feed_group_dialog, headerMenu)
|
||||||
|
|
||||||
|
headerMenu.findItem(R.id.action_search).setOnMenuItemClickListener {
|
||||||
|
showSearch()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar_search_clear.setOnClickListener {
|
||||||
|
if (TextUtils.isEmpty(toolbar_search_edit_text.text)) {
|
||||||
|
hideSearch()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
resetSearch()
|
||||||
|
showKeyboardSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar_search_edit_text.setOnClickListener {
|
||||||
|
if (AndroidTvUtils.isTv(context)) {
|
||||||
|
showKeyboardSearch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar_search_edit_text.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
|
||||||
|
override fun afterTextChanged(s: Editable) = Unit
|
||||||
|
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||||
|
val newQuery: String = toolbar_search_edit_text.text.toString()
|
||||||
|
subscriptionsCurrentSearchQuery = newQuery
|
||||||
|
viewModel.filterSubscriptionsBy(newQuery)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
subscriptionGroupAdapter?.setOnItemClickListener(subscriptionPickerItemListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handlePositiveButton() = when {
|
||||||
|
currentScreen is InitialScreen -> handlePositiveButtonInitialScreen()
|
||||||
|
currentScreen is DeleteScreen -> viewModel.deleteGroup()
|
||||||
|
currentScreen is SubscriptionsPickerScreen && isSearchVisible() -> hideSearch()
|
||||||
else -> showScreen(InitialScreen)
|
else -> showScreen(InitialScreen)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handlePositiveButtonInitialScreen() {
|
private fun handlePositiveButtonInitialScreen() {
|
||||||
val name = group_name_input.text.toString().trim()
|
val name = group_name_input.text.toString().trim()
|
||||||
|
@ -202,54 +281,18 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
groupIcon = feedGroupEntity?.icon
|
groupIcon = feedGroupEntity?.icon
|
||||||
groupSortOrder = feedGroupEntity?.sortOrder ?: -1
|
groupSortOrder = feedGroupEntity?.sortOrder ?: -1
|
||||||
|
|
||||||
icon_preview.setImageResource((if (selectedIcon == null) icon else selectedIcon!!).getDrawableRes(requireContext()))
|
val feedGroupIcon = if (selectedIcon == null) icon else selectedIcon!!
|
||||||
|
icon_preview.setImageResource(feedGroupIcon.getDrawableRes(requireContext()))
|
||||||
|
|
||||||
if (group_name_input.text.isNullOrBlank()) {
|
if (group_name_input.text.isNullOrBlank()) {
|
||||||
group_name_input.setText(name)
|
group_name_input.setText(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSubscriptionPicker(subscriptions: List<SubscriptionEntity>, selectedSubscriptions: Set<Long>) {
|
private val subscriptionPickerItemListener = OnItemClickListener { item, view ->
|
||||||
this.selectedSubscriptions.addAll(selectedSubscriptions)
|
if (item is PickerSubscriptionItem) {
|
||||||
val useGridLayout = subscriptions.isNotEmpty()
|
|
||||||
|
|
||||||
val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
|
||||||
groupAdapter.spanCount = if (useGridLayout) 4 else 1
|
|
||||||
|
|
||||||
val subscriptionsCount = this.selectedSubscriptions.size
|
|
||||||
val selectedCountText = resources.getQuantityString(R.plurals.feed_group_dialog_selection_count, subscriptionsCount, subscriptionsCount)
|
|
||||||
selected_subscription_count_view.text = selectedCountText
|
|
||||||
subscriptions_selector_header_info.text = selectedCountText
|
|
||||||
|
|
||||||
Section().apply {
|
|
||||||
addAll(subscriptions.map {
|
|
||||||
val isSelected = this@FeedGroupDialog.selectedSubscriptions.contains(it.uid)
|
|
||||||
PickerSubscriptionItem(it, isSelected)
|
|
||||||
})
|
|
||||||
setPlaceholder(EmptyPlaceholderItem())
|
|
||||||
|
|
||||||
groupAdapter.add(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptions_selector_list.apply {
|
|
||||||
layoutManager = if (useGridLayout) {
|
|
||||||
GridLayoutManager(requireContext(), groupAdapter.spanCount, RecyclerView.VERTICAL, false)
|
|
||||||
} else {
|
|
||||||
LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter = groupAdapter
|
|
||||||
|
|
||||||
if (subscriptionsListState != null) {
|
|
||||||
layoutManager?.onRestoreInstanceState(subscriptionsListState)
|
|
||||||
subscriptionsListState = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
groupAdapter.setOnItemClickListener { item, _ ->
|
|
||||||
when (item) {
|
|
||||||
is PickerSubscriptionItem -> {
|
|
||||||
val subscriptionId = item.subscriptionEntity.uid
|
val subscriptionId = item.subscriptionEntity.uid
|
||||||
|
wasSubscriptionSelectionChanged = true
|
||||||
|
|
||||||
val isSelected = if (this.selectedSubscriptions.contains(subscriptionId)) {
|
val isSelected = if (this.selectedSubscriptions.contains(subscriptionId)) {
|
||||||
this.selectedSubscriptions.remove(subscriptionId)
|
this.selectedSubscriptions.remove(subscriptionId)
|
||||||
|
@ -259,23 +302,52 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
item.isSelected = isSelected
|
item.updateSelected(view, isSelected)
|
||||||
item.notifyChanged(PickerSubscriptionItem.UPDATE_SELECTED)
|
updateSubscriptionSelectedCount()
|
||||||
|
|
||||||
val subscriptionsCount = this.selectedSubscriptions.size
|
|
||||||
val updateSelectedCountText = resources.getQuantityString(R.plurals.feed_group_dialog_selection_count, subscriptionsCount, subscriptionsCount)
|
|
||||||
selected_subscription_count_view.text = updateSelectedCountText
|
|
||||||
subscriptions_selector_header_info.text = updateSelectedCountText
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select_channel_button.setOnClickListener {
|
private fun setupSubscriptionPicker(
|
||||||
|
subscriptions: List<PickerSubscriptionItem>,
|
||||||
|
selectedSubscriptions: Set<Long>
|
||||||
|
) {
|
||||||
|
if (!wasSubscriptionSelectionChanged) {
|
||||||
|
this.selectedSubscriptions.addAll(selectedSubscriptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSubscriptionSelectedCount()
|
||||||
|
|
||||||
|
if (subscriptions.isEmpty()) {
|
||||||
|
subscriptionEmptyFooter.clear()
|
||||||
|
subscriptionEmptyFooter.add(EmptyPlaceholderItem())
|
||||||
|
} else {
|
||||||
|
subscriptionEmptyFooter.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions.forEach {
|
||||||
|
it.isSelected = this@FeedGroupDialog.selectedSubscriptions
|
||||||
|
.contains(it.subscriptionEntity.uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptionMainSection.update(subscriptions, false)
|
||||||
|
|
||||||
|
if (subscriptionsListState != null) {
|
||||||
|
subscriptions_selector_list.layoutManager?.onRestoreInstanceState(subscriptionsListState)
|
||||||
|
subscriptionsListState = null
|
||||||
|
} else {
|
||||||
subscriptions_selector_list.scrollToPosition(0)
|
subscriptions_selector_list.scrollToPosition(0)
|
||||||
showScreen(SubscriptionsPickerScreen)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateSubscriptionSelectedCount() {
|
||||||
|
val selectedCount = this.selectedSubscriptions.size
|
||||||
|
val selectedCountText = resources.getQuantityString(
|
||||||
|
R.plurals.feed_group_dialog_selection_count,
|
||||||
|
selectedCount, selectedCount)
|
||||||
|
selected_subscription_count_view.text = selectedCountText
|
||||||
|
subscriptions_header_info.text = selectedCountText
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupIconPicker() {
|
private fun setupIconPicker() {
|
||||||
val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
val groupAdapter = GroupAdapter<GroupieViewHolder>()
|
||||||
groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(requireContext(), it) })
|
groupAdapter.addAll(FeedGroupIcon.values().map { PickerIconItem(requireContext(), it) })
|
||||||
|
@ -311,9 +383,9 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
/*///////////////////////////////////////////////////////////////////////////
|
||||||
// Screen Selector
|
// Screen Selector
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////// */
|
||||||
|
|
||||||
private fun showScreen(screen: ScreenState) {
|
private fun showScreen(screen: ScreenState) {
|
||||||
currentScreen = screen
|
currentScreen = screen
|
||||||
|
@ -337,7 +409,8 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
else -> View.VISIBLE
|
else -> View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentScreen != InitialScreen) hideKeyboard()
|
hideKeyboard()
|
||||||
|
hideSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun View.onlyVisibleIn(vararg screens: ScreenState) {
|
private fun View.onlyVisibleIn(vararg screens: ScreenState) {
|
||||||
|
@ -347,13 +420,58 @@ class FeedGroupDialog : DialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
/*///////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////// */
|
||||||
|
|
||||||
|
private fun isSearchVisible() = subscriptions_header_search_container?.visibility == View.VISIBLE
|
||||||
|
|
||||||
|
private fun resetSearch() {
|
||||||
|
toolbar_search_edit_text.setText("")
|
||||||
|
subscriptionsCurrentSearchQuery = ""
|
||||||
|
viewModel.clearSubscriptionsFilter()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideSearch() {
|
||||||
|
resetSearch()
|
||||||
|
subscriptions_header_search_container.visibility = View.GONE
|
||||||
|
subscriptions_header_info_container.visibility = View.VISIBLE
|
||||||
|
subscriptions_header_toolbar.menu.findItem(R.id.action_search).isVisible = true
|
||||||
|
hideKeyboardSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSearch() {
|
||||||
|
subscriptions_header_search_container.visibility = View.VISIBLE
|
||||||
|
subscriptions_header_info_container.visibility = View.GONE
|
||||||
|
subscriptions_header_toolbar.menu.findItem(R.id.action_search).isVisible = false
|
||||||
|
showKeyboardSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val inputMethodManager by lazy {
|
||||||
|
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showKeyboardSearch() {
|
||||||
|
if (toolbar_search_edit_text.requestFocus()) {
|
||||||
|
inputMethodManager.showSoftInput(toolbar_search_edit_text, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideKeyboardSearch() {
|
||||||
|
inputMethodManager.hideSoftInputFromWindow(toolbar_search_edit_text.windowToken,
|
||||||
|
InputMethodManager.RESULT_UNCHANGED_SHOWN)
|
||||||
|
toolbar_search_edit_text.clearFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showKeyboard() {
|
||||||
|
if (group_name_input.requestFocus()) {
|
||||||
|
inputMethodManager.showSoftInput(group_name_input, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun hideKeyboard() {
|
private fun hideKeyboard() {
|
||||||
val inputMethodManager = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
inputMethodManager.hideSoftInputFromWindow(group_name_input.windowToken,
|
||||||
inputMethodManager.hideSoftInputFromWindow(group_name_input.windowToken, InputMethodManager.RESULT_UNCHANGED_SHOWN)
|
InputMethodManager.RESULT_UNCHANGED_SHOWN)
|
||||||
group_name_input.clearFocus()
|
group_name_input.clearFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,29 +9,42 @@ import io.reactivex.Completable
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.BiFunction
|
||||||
|
import io.reactivex.processors.BehaviorProcessor
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
|
||||||
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
import org.schabi.newpipe.local.feed.FeedDatabaseManager
|
||||||
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
import org.schabi.newpipe.local.subscription.FeedGroupIcon
|
||||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||||
|
import org.schabi.newpipe.local.subscription.item.PickerSubscriptionItem
|
||||||
|
|
||||||
class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModel() {
|
class FeedGroupDialogViewModel(
|
||||||
class Factory(val context: Context, val groupId: Long = FeedGroupEntity.GROUP_ALL_ID) : ViewModelProvider.Factory {
|
applicationContext: Context,
|
||||||
@Suppress("UNCHECKED_CAST")
|
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
initialQuery: String = ""
|
||||||
return FeedGroupDialogViewModel(context.applicationContext, groupId) as T
|
) : ViewModel() {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
|
private var feedDatabaseManager: FeedDatabaseManager = FeedDatabaseManager(applicationContext)
|
||||||
private var subscriptionManager = SubscriptionManager(applicationContext)
|
private var subscriptionManager = SubscriptionManager(applicationContext)
|
||||||
|
|
||||||
|
private var filterSubscriptions = BehaviorProcessor.create<String>()
|
||||||
|
private var allSubscriptions = subscriptionManager.subscriptions()
|
||||||
|
|
||||||
|
private var subscriptionsFlowable = filterSubscriptions
|
||||||
|
.startWith(initialQuery)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.switchMap { query ->
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
allSubscriptions
|
||||||
|
} else {
|
||||||
|
subscriptionManager.filterByName(query)
|
||||||
|
}
|
||||||
|
}.map { list -> list.map { PickerSubscriptionItem(it) } }
|
||||||
|
|
||||||
private val mutableGroupLiveData = MutableLiveData<FeedGroupEntity>()
|
private val mutableGroupLiveData = MutableLiveData<FeedGroupEntity>()
|
||||||
private val mutableSubscriptionsLiveData = MutableLiveData<Pair<List<SubscriptionEntity>, Set<Long>>>()
|
private val mutableSubscriptionsLiveData = MutableLiveData<Pair<List<PickerSubscriptionItem>, Set<Long>>>()
|
||||||
private val mutableDialogEventLiveData = MutableLiveData<DialogEvent>()
|
private val mutableDialogEventLiveData = MutableLiveData<DialogEvent>()
|
||||||
val groupLiveData: LiveData<FeedGroupEntity> = mutableGroupLiveData
|
val groupLiveData: LiveData<FeedGroupEntity> = mutableGroupLiveData
|
||||||
val subscriptionsLiveData: LiveData<Pair<List<SubscriptionEntity>, Set<Long>>> = mutableSubscriptionsLiveData
|
val subscriptionsLiveData: LiveData<Pair<List<PickerSubscriptionItem>, Set<Long>>> = mutableSubscriptionsLiveData
|
||||||
val dialogEventLiveData: LiveData<DialogEvent> = mutableDialogEventLiveData
|
val dialogEventLiveData: LiveData<DialogEvent> = mutableDialogEventLiveData
|
||||||
|
|
||||||
private var actionProcessingDisposable: Disposable? = null
|
private var actionProcessingDisposable: Disposable? = null
|
||||||
|
@ -41,8 +54,8 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long =
|
||||||
.subscribe(mutableGroupLiveData::postValue)
|
.subscribe(mutableGroupLiveData::postValue)
|
||||||
|
|
||||||
private var subscriptionsDisposable = Flowable
|
private var subscriptionsDisposable = Flowable
|
||||||
.combineLatest(subscriptionManager.subscriptions(), feedDatabaseManager.subscriptionIdsForGroup(groupId),
|
.combineLatest(subscriptionsFlowable, feedDatabaseManager.subscriptionIdsForGroup(groupId),
|
||||||
BiFunction { t1: List<SubscriptionEntity>, t2: List<Long> -> t1 to t2.toSet() })
|
BiFunction { t1: List<PickerSubscriptionItem>, t2: List<Long> -> t1 to t2.toSet() })
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.subscribe(mutableSubscriptionsLiveData::postValue)
|
.subscribe(mutableSubscriptionsLiveData::postValue)
|
||||||
|
|
||||||
|
@ -79,8 +92,28 @@ class FeedGroupDialogViewModel(applicationContext: Context, val groupId: Long =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun filterSubscriptionsBy(query: String) {
|
||||||
|
filterSubscriptions.onNext(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearSubscriptionsFilter() {
|
||||||
|
filterSubscriptions.onNext("")
|
||||||
|
}
|
||||||
|
|
||||||
sealed class DialogEvent {
|
sealed class DialogEvent {
|
||||||
object ProcessingEvent : DialogEvent()
|
object ProcessingEvent : DialogEvent()
|
||||||
object SuccessEvent : DialogEvent()
|
object SuccessEvent : DialogEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Factory(
|
||||||
|
private val context: Context,
|
||||||
|
private val groupId: Long = FeedGroupEntity.GROUP_ALL_ID,
|
||||||
|
private val initialQuery: String = ""
|
||||||
|
) : ViewModelProvider.Factory {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
|
return FeedGroupDialogViewModel(context.applicationContext,
|
||||||
|
groupId, initialQuery) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,4 +7,5 @@ import org.schabi.newpipe.R
|
||||||
class EmptyPlaceholderItem : Item() {
|
class EmptyPlaceholderItem : Item() {
|
||||||
override fun getLayout(): Int = R.layout.list_empty_view
|
override fun getLayout(): Int = R.layout.list_empty_view
|
||||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {}
|
override fun bind(viewHolder: GroupieViewHolder, position: Int) {}
|
||||||
|
override fun getSpanSize(spanCount: Int, position: Int): Int = spanCount
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,28 @@
|
||||||
package org.schabi.newpipe.local.subscription.item
|
package org.schabi.newpipe.local.subscription.item
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.nostra13.universalimageloader.core.DisplayImageOptions
|
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader
|
import com.nostra13.universalimageloader.core.ImageLoader
|
||||||
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
|
||||||
import com.xwray.groupie.kotlinandroidextensions.Item
|
import com.xwray.groupie.kotlinandroidextensions.Item
|
||||||
import kotlinx.android.synthetic.main.picker_subscription_item.selected_highlight
|
import kotlinx.android.synthetic.main.picker_subscription_item.*
|
||||||
import kotlinx.android.synthetic.main.picker_subscription_item.thumbnail_view
|
import kotlinx.android.synthetic.main.picker_subscription_item.view.*
|
||||||
import kotlinx.android.synthetic.main.picker_subscription_item.title_view
|
|
||||||
import org.schabi.newpipe.R
|
import org.schabi.newpipe.R
|
||||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||||
import org.schabi.newpipe.util.AnimationUtils
|
import org.schabi.newpipe.util.AnimationUtils
|
||||||
import org.schabi.newpipe.util.AnimationUtils.animateView
|
import org.schabi.newpipe.util.AnimationUtils.animateView
|
||||||
import org.schabi.newpipe.util.ImageDisplayConstants
|
import org.schabi.newpipe.util.ImageDisplayConstants
|
||||||
|
|
||||||
data class PickerSubscriptionItem(val subscriptionEntity: SubscriptionEntity, var isSelected: Boolean = false) : Item() {
|
data class PickerSubscriptionItem(
|
||||||
companion object {
|
val subscriptionEntity: SubscriptionEntity,
|
||||||
const val UPDATE_SELECTED = 123
|
var isSelected: Boolean = false
|
||||||
|
) : Item() {
|
||||||
val IMAGE_LOADING_OPTIONS: DisplayImageOptions = ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS
|
override fun getId(): Long = subscriptionEntity.uid
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLayout(): Int = R.layout.picker_subscription_item
|
override fun getLayout(): Int = R.layout.picker_subscription_item
|
||||||
|
override fun getSpanSize(spanCount: Int, position: Int): Int = 1
|
||||||
override fun bind(viewHolder: GroupieViewHolder, position: Int, payloads: MutableList<Any>) {
|
|
||||||
if (payloads.contains(UPDATE_SELECTED)) {
|
|
||||||
animateView(viewHolder.selected_highlight, AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
super.bind(viewHolder, position, payloads)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
|
||||||
ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl, viewHolder.thumbnail_view, IMAGE_LOADING_OPTIONS)
|
ImageLoader.getInstance().displayImage(subscriptionEntity.avatarUrl,
|
||||||
|
viewHolder.thumbnail_view, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS)
|
||||||
|
|
||||||
viewHolder.title_view.text = subscriptionEntity.name
|
viewHolder.title_view.text = subscriptionEntity.name
|
||||||
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
|
viewHolder.selected_highlight.visibility = if (isSelected) View.VISIBLE else View.GONE
|
||||||
|
@ -47,7 +36,9 @@ data class PickerSubscriptionItem(val subscriptionEntity: SubscriptionEntity, va
|
||||||
viewHolder.selected_highlight.alpha = 1F
|
viewHolder.selected_highlight.alpha = 1F
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getId(): Long {
|
fun updateSelected(containerView: View, isSelected: Boolean) {
|
||||||
return subscriptionEntity.uid
|
this.isSelected = isSelected
|
||||||
|
animateView(containerView.selected_highlight,
|
||||||
|
AnimationUtils.Type.LIGHT_SCALE_AND_ALPHA, isSelected, 150)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,22 +102,27 @@
|
||||||
android:id="@+id/subscriptions_selector"
|
android:id="@+id/subscriptions_selector"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
android:visibility="gone">
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/subscriptions_header_toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
|
||||||
|
app:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar"
|
||||||
|
app:titleTextAppearance="@style/Toolbar.Title">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/subscriptions_header_info_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:paddingLeft="16dp"
|
android:gravity="center_vertical">
|
||||||
android:paddingTop="12dp"
|
|
||||||
android:paddingRight="16dp"
|
|
||||||
android:paddingBottom="12dp">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
android:text="@string/tab_subscriptions"
|
android:text="@string/tab_subscriptions"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
@ -125,7 +130,7 @@
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/subscriptions_selector_header_info"
|
android:id="@+id/subscriptions_header_info"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
|
@ -134,10 +139,19 @@
|
||||||
tools:text="1 selected" />
|
tools:text="1 selected" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/subscriptions_header_search_container"
|
||||||
|
layout="@layout/toolbar_search_layout"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/subscriptions_selector_list"
|
android:id="@+id/subscriptions_selector_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
tools:itemCount="200"
|
tools:itemCount="200"
|
||||||
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
tools:listitem="@layout/picker_subscription_item"
|
tools:listitem="@layout/picker_subscription_item"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
tools:background="?attr/colorPrimary">
|
||||||
android:background="?attr/colorPrimary">
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/toolbar_search_edit_text"
|
android:id="@+id/toolbar_search_edit_text"
|
||||||
|
@ -14,7 +15,7 @@
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
android:layout_marginRight="48dp"
|
android:layout_marginRight="48dp"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="@null"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:nextFocusDown="@+id/suggestions_list"
|
android:nextFocusDown="@+id/suggestions_list"
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:layout_gravity="right|center_vertical"
|
android:layout_gravity="right|center_vertical"
|
||||||
|
android:contentDescription="@string/clear"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
tools:ignore="RtlHardcoded">
|
tools:ignore="RtlHardcoded">
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_search"
|
||||||
|
android:icon="?attr/ic_search"
|
||||||
|
android:title="@string/search"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
Loading…
Reference in New Issue