diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 867455674cd..f53d85eac76 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -313,8 +313,8 @@ android:theme="@style/AppTheme" /> >(PrefsIoUtil.getString(R.string.preference_key_user_contrib_filter_ns, null)) + var userContribFilterExcludedNs + get() = JsonUtil.decodeFromString>(PrefsIoUtil.getString(R.string.preference_key_user_contrib_filter_excluded_ns, null)) ?: emptySet() - set(value) = PrefsIoUtil.setString(R.string.preference_key_user_contrib_filter_ns, JsonUtil.encodeToString(value)) + set(value) = PrefsIoUtil.setString(R.string.preference_key_user_contrib_filter_excluded_ns, JsonUtil.encodeToString(value)) + + var userContribFilterLangCode + get() = PrefsIoUtil.getString(R.string.preference_key_user_contrib_filter_lang_code, WikipediaApp.instance.appOrSystemLanguageCode)!! + set(value) = PrefsIoUtil.setString(R.string.preference_key_user_contrib_filter_lang_code, value) var editSyntaxHighlightEnabled get() = PrefsIoUtil.getBoolean(R.string.preference_key_edit_syntax_highlight, true) diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterActivity.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterActivity.kt new file mode 100644 index 00000000000..400f4b9b45a --- /dev/null +++ b/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterActivity.kt @@ -0,0 +1,187 @@ +package org.wikipedia.usercontrib + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.activity.result.contract.ActivityResultContracts +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import org.wikipedia.Constants +import org.wikipedia.R +import org.wikipedia.WikipediaApp +import org.wikipedia.activity.BaseActivity +import org.wikipedia.databinding.ActivityUserContribWikiSelectBinding +import org.wikipedia.page.Namespace +import org.wikipedia.settings.Prefs +import org.wikipedia.settings.languages.WikipediaLanguagesActivity +import org.wikipedia.staticdata.TalkAliasData +import org.wikipedia.staticdata.UserAliasData +import org.wikipedia.staticdata.UserTalkAliasData +import org.wikipedia.views.DefaultViewHolder + +class UserContribFilterActivity : BaseActivity() { + + private lateinit var binding: ActivityUserContribWikiSelectBinding + + private val langUpdateLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + binding.recyclerView.adapter = ItemAdapter(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityUserContribWikiSelectBinding.inflate(layoutInflater) + + binding.recyclerView.layoutManager = LinearLayoutManager(this) + binding.recyclerView.adapter = ItemAdapter(this) + binding.recyclerView.itemAnimator = null + setContentView(binding.root) + setResult(RESULT_OK) + } + + inner class ItemViewHolder constructor(itemView: UserContribFilterItemView) : + DefaultViewHolder(itemView) { + fun bindItem(item: Item) { + view.setContents(item) + } + } + + private inner class FilterHeaderViewHolder constructor(itemView: View) : + DefaultViewHolder(itemView) { + val headerText = itemView.findViewById(R.id.filter_header_title)!! + + fun bindItem(filterHeader: String) { + headerText.text = filterHeader + } + } + + private inner class AddLanguageViewHolder constructor(itemView: UserContribFilterItemView) : + DefaultViewHolder(itemView), UserContribFilterItemView.Callback { + fun bindItem(text: String) { + (itemView as UserContribFilterItemView).callback = this + itemView.setSingleLabel(text) + } + + override fun onSelected(item: Item?) { + langUpdateLauncher.launch(WikipediaLanguagesActivity.newIntent(this@UserContribFilterActivity, Constants.InvokeSource.USER_CONTRIB_ACTIVITY)) + } + } + + private inner class ItemAdapter(val context: Context) : RecyclerView.Adapter>(), UserContribFilterItemView.Callback { + private val itemList = mutableListOf() + + init { + itemList.add(getString(R.string.notifications_wiki_filter_header)) + WikipediaApp.instance.languageState.appLanguageCodes.forEach { itemList.add(Item(FILTER_TYPE_WIKI, it, null)) } + itemList.add(Item(FILTER_TYPE_WIKI, Constants.WIKI_CODE_COMMONS, R.drawable.ic_commons_logo)) + itemList.add(Item(FILTER_TYPE_WIKI, Constants.WIKI_CODE_WIKIDATA, R.drawable.ic_wikidata_logo)) + itemList.add(getString(R.string.notifications_filter_update_app_languages)) + itemList.add(getString(R.string.user_contrib_filter_ns_header)) + itemList.add(Item(FILTER_TYPE_NAMESPACE, getString(R.string.user_contrib_filter_all), null)) + itemList.add(Item(FILTER_TYPE_NAMESPACE, getString(R.string.namespace_article), R.drawable.ic_article_ltr_ooui)) + itemList.add(Item(FILTER_TYPE_NAMESPACE, TalkAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode), R.drawable.ic_notification_article_talk)) + itemList.add(Item(FILTER_TYPE_NAMESPACE, UserAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode), R.drawable.ic_user_avatar)) + itemList.add(Item(FILTER_TYPE_NAMESPACE, UserTalkAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode), R.drawable.ic_notification_user_talk)) + } + + override fun onCreateViewHolder(parent: ViewGroup, type: Int): DefaultViewHolder<*> { + return when (type) { + VIEW_TYPE_HEADER -> { + FilterHeaderViewHolder(layoutInflater.inflate(R.layout.view_notification_filter_header, parent, false)) + } + VIEW_TYPE_ADD_LANGUAGE -> { + AddLanguageViewHolder(UserContribFilterItemView(context)) + } + else -> { + val view = UserContribFilterItemView(context) + view.callback = this + ItemViewHolder(view) + } + } + } + + override fun getItemCount(): Int { + return itemList.size + } + + override fun getItemViewType(position: Int): Int { + return if (itemList[position] is String && itemList[position] == getString(R.string.notifications_filter_update_app_languages)) VIEW_TYPE_ADD_LANGUAGE + else if (itemList[position] is String) VIEW_TYPE_HEADER + else VIEW_TYPE_ITEM + } + + override fun onBindViewHolder(holder: DefaultViewHolder<*>, position: Int) { + when (holder) { + is FilterHeaderViewHolder -> holder.bindItem(itemList[position] as String) + is AddLanguageViewHolder -> holder.bindItem(itemList[position] as String) + else -> (holder as ItemViewHolder).bindItem(itemList[position] as Item) + } + } + + override fun onSelected(item: Item?) { + item?.let { + if (it.type == FILTER_TYPE_WIKI) { + Prefs.userContribFilterLangCode = item.filterCode + } else if (it.type == FILTER_TYPE_NAMESPACE) { + var excludedNsFilter = Prefs.userContribFilterExcludedNs + when (val namespaceCode = getNamespaceCode(item.filterCode)) { + -1 -> { // Select "all" + excludedNsFilter = if (excludedNsFilter.isEmpty() || excludedNsFilter.size < NAMESPACE_LIST.size) { + NAMESPACE_LIST.toSet() + } else { + emptySet() + } + } + else -> { + excludedNsFilter = if (excludedNsFilter.contains(namespaceCode)) { + excludedNsFilter.minus(namespaceCode) + } else { + excludedNsFilter.plus(namespaceCode) + } + } + } + Prefs.userContribFilterExcludedNs = excludedNsFilter + } + } + notifyItemRangeChanged(0, itemCount) + } + } + + private fun getNamespaceCode(text: String): Int { + return when (text) { + getString(R.string.namespace_article) -> Namespace.MAIN.code() + TalkAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode) -> Namespace.TALK.code() + UserAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode) -> Namespace.USER.code() + UserTalkAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode) -> Namespace.USER_TALK.code() + else -> -1 + } + } + + inner class Item constructor(val type: Int, val filterCode: String, val imageRes: Int? = null) { + fun isEnabled(): Boolean { + if (type == FILTER_TYPE_WIKI) { + return Prefs.userContribFilterLangCode == filterCode + } + val excludedNsFilter = Prefs.userContribFilterExcludedNs + if (filterCode == getString(R.string.user_contrib_filter_all)) { + return NAMESPACE_LIST.find { excludedNsFilter.contains(it) } == null + } + return !excludedNsFilter.contains(getNamespaceCode(filterCode)) + } + } + + companion object { + private const val VIEW_TYPE_HEADER = 0 + private const val VIEW_TYPE_ITEM = 1 + private const val VIEW_TYPE_ADD_LANGUAGE = 2 + const val FILTER_TYPE_WIKI = 0 + const val FILTER_TYPE_NAMESPACE = 1 + val NAMESPACE_LIST = listOf(Namespace.MAIN.code(), Namespace.TALK.code(), Namespace.USER.code(), Namespace.USER_TALK.code()) + + fun newIntent(context: Context): Intent { + return Intent(context, UserContribFilterActivity::class.java) + } + } +} diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterItemView.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterItemView.kt new file mode 100644 index 00000000000..db7bbfd3852 --- /dev/null +++ b/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterItemView.kt @@ -0,0 +1,84 @@ +package org.wikipedia.usercontrib + +import android.content.Context +import android.graphics.Typeface +import android.util.AttributeSet +import android.util.TypedValue +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.core.view.isVisible +import androidx.core.widget.ImageViewCompat +import org.wikipedia.Constants +import org.wikipedia.R +import org.wikipedia.WikipediaApp +import org.wikipedia.databinding.ItemUserContribFilterBinding +import org.wikipedia.search.SearchFragment +import org.wikipedia.util.DimenUtil +import org.wikipedia.util.ResourceUtil +import org.wikipedia.views.ViewUtil + +class UserContribFilterItemView constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) { + + interface Callback { + fun onSelected(item: UserContribFilterActivity.Item?) + } + + private var item: UserContribFilterActivity.Item? = null + private var binding = ItemUserContribFilterBinding.inflate(LayoutInflater.from(context), this) + private val labelTypeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) + var callback: Callback? = null + + init { + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DimenUtil.roundedDpToPx(48f)) + setBackgroundResource(ResourceUtil.getThemedAttributeId(context, R.attr.selectableItemBackground)) + setOnClickListener { + callback?.onSelected(item) + } + } + + fun setContents(item: UserContribFilterActivity.Item) { + this.item = item + binding.itemTitle.text = WikipediaApp.instance.languageState.getWikiLanguageName(item.filterCode) + binding.itemCheck.isVisible = item.isEnabled() + + if (item.type == UserContribFilterActivity.FILTER_TYPE_WIKI) { + getTitleCodeFor(item.filterCode)?.let { + binding.languageCode.text = it + binding.languageCode.visibility = View.VISIBLE + ViewUtil.formatLangButton(binding.languageCode, it, SearchFragment.LANG_BUTTON_TEXT_SIZE_SMALLER, SearchFragment.LANG_BUTTON_TEXT_SIZE_LARGER) + } ?: run { + binding.languageCode.visibility = View.GONE + } + binding.itemCheck.setImageResource(R.drawable.ic_baseline_radio_button_checked_24) + } else { + binding.languageCode.visibility = View.GONE + binding.itemCheck.setImageResource(R.drawable.ic_check_borderless) + } + + item.imageRes?.let { + binding.itemLogo.setImageResource(it) + binding.itemLogo.visibility = View.VISIBLE + } ?: run { + binding.itemLogo.visibility = if (binding.languageCode.isVisible) View.GONE else View.INVISIBLE + } + } + + fun setSingleLabel(text: String) { + binding.languageCode.visibility = View.GONE + binding.itemLogo.visibility = View.VISIBLE + binding.itemLogo.setImageResource(R.drawable.ic_mode_edit_themed_24dp) + binding.itemCheck.visibility = View.GONE + binding.itemTitle.setTextColor(ResourceUtil.getThemedColorStateList(context, R.attr.colorAccent)) + binding.itemTitle.text = text.uppercase() + binding.itemTitle.typeface = labelTypeface + binding.itemTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f) + ImageViewCompat.setImageTintList(binding.itemLogo, ResourceUtil.getThemedColorStateList(context, R.attr.colorAccent)) + } + + private fun getTitleCodeFor(itemCode: String): String? { + return if (itemCode == Constants.WIKI_CODE_COMMONS || itemCode == Constants.WIKI_CODE_WIKIDATA) null + else itemCode + } +} diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterOverflowView.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterOverflowView.kt deleted file mode 100644 index 3c5cc5b4105..00000000000 --- a/app/src/main/java/org/wikipedia/usercontrib/UserContribFilterOverflowView.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.wikipedia.usercontrib - -import android.content.Context -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.view.Gravity -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.PopupWindow -import androidx.core.widget.PopupWindowCompat -import org.wikipedia.WikipediaApp -import org.wikipedia.databinding.ViewUserContribFilterOverflowBinding -import org.wikipedia.page.Namespace -import org.wikipedia.settings.Prefs -import org.wikipedia.staticdata.TalkAliasData -import org.wikipedia.staticdata.UserAliasData -import org.wikipedia.staticdata.UserTalkAliasData - -class UserContribFilterOverflowView(context: Context) : FrameLayout(context) { - - fun interface Callback { - fun onItemClicked() - } - - private var binding = ViewUserContribFilterOverflowBinding.inflate(LayoutInflater.from(context), this, true) - private var callback: Callback? = null - private var popupWindowHost: PopupWindow? = null - - init { - setButtonsListener() - } - - fun show(anchorView: View, callback: Callback?) { - this.callback = callback - - binding.nsUserText.text = UserAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode) - binding.nsTalkText.text = TalkAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode) - binding.nsUserTalkText.text = UserTalkAliasData.valueFor(WikipediaApp.instance.appOrSystemLanguageCode) - updateSelectedItem() - - popupWindowHost = PopupWindow(this, ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT, true) - popupWindowHost?.let { - it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - PopupWindowCompat.setOverlapAnchor(it, true) - it.showAsDropDown(anchorView, 0, 0, Gravity.END) - } - popupWindowHost?.setOnDismissListener { - popupWindowHost = null - } - } - - private fun updateSelectedItem() { - binding.nsNoneCheckIcon.visibility = View.INVISIBLE - binding.nsArticleCheckIcon.visibility = View.INVISIBLE - binding.nsTalkCheckIcon.visibility = View.INVISIBLE - binding.nsUserCheckIcon.visibility = View.INVISIBLE - binding.nsUserTalkCheckIcon.visibility = View.INVISIBLE - - if (Prefs.userContribFilterNs.isEmpty()) { - binding.nsNoneCheckIcon.visibility = View.VISIBLE - } else { - Prefs.userContribFilterNs.forEach { - when (it) { - Namespace.MAIN.code() -> { binding.nsArticleCheckIcon.visibility = View.VISIBLE } - Namespace.TALK.code() -> { binding.nsTalkCheckIcon.visibility = View.VISIBLE } - Namespace.USER.code() -> { binding.nsUserCheckIcon.visibility = View.VISIBLE } - Namespace.USER_TALK.code() -> { binding.nsUserTalkCheckIcon.visibility = View.VISIBLE } - } - } - } - } - - private fun setButtonsListener() { - binding.nsNoneButton.setOnClickListener { - Prefs.userContribFilterNs = emptySet() - onSelected() - } - binding.nsArticleButton.setOnClickListener { - Prefs.userContribFilterNs = Prefs.userContribFilterNs.plus(Namespace.MAIN.code()) - onSelected() - } - binding.nsTalkButton.setOnClickListener { - Prefs.userContribFilterNs = Prefs.userContribFilterNs.plus(Namespace.TALK.code()) - onSelected() - } - binding.nsUserButton.setOnClickListener { - Prefs.userContribFilterNs = Prefs.userContribFilterNs.plus(Namespace.USER.code()) - onSelected() - } - binding.nsUserTalkButton.setOnClickListener { - Prefs.userContribFilterNs = Prefs.userContribFilterNs.plus(Namespace.USER_TALK.code()) - onSelected() - } - } - - private fun onSelected() { - callback?.onItemClicked() - popupWindowHost?.dismiss() - } -} diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribListActivity.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribListActivity.kt index 6bd2c931aff..e1f29855149 100644 --- a/app/src/main/java/org/wikipedia/usercontrib/UserContribListActivity.kt +++ b/app/src/main/java/org/wikipedia/usercontrib/UserContribListActivity.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import org.wikipedia.Constants import org.wikipedia.R -import org.wikipedia.WikipediaApp import org.wikipedia.activity.BaseActivity import org.wikipedia.databinding.ActivityUserContribBinding import org.wikipedia.databinding.ViewEditHistoryEmptyMessagesBinding @@ -45,14 +44,11 @@ import org.wikipedia.page.LinkHandler import org.wikipedia.page.LinkMovementMethodExt import org.wikipedia.page.PageTitle import org.wikipedia.richtext.RichTextUtil -import org.wikipedia.search.SearchFragment import org.wikipedia.settings.Prefs import org.wikipedia.talk.UserTalkPopupHelper import org.wikipedia.util.* import org.wikipedia.views.SearchAndFilterActionProvider -import org.wikipedia.views.ViewUtil import org.wikipedia.views.WikiErrorView -import java.util.* class UserContribListActivity : BaseActivity() { @@ -73,16 +69,14 @@ class UserContribListActivity : BaseActivity() { linkHandler.onUrlClick(url, title, linkText, x, y) } - private val requestLanguageChange = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + private val launchFilterActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { - it.data?.let { intent -> - viewModel.langCode = intent.getStringExtra(UserContribWikiSelectActivity.INTENT_EXTRA_SELECT_LANG_CODE).orEmpty() - .ifEmpty { WikipediaApp.instance.appOrSystemLanguageCode } - updateLangButton() - viewModel.clearCache() - viewModel.loadStats() - userContribListAdapter.reload() - } + viewModel.langCode = Prefs.userContribFilterLangCode + viewModel.loadStats() + setupAdapters() + viewModel.clearCache() + userContribListAdapter.reload() + userContribSearchBarAdapter.notifyItemChanged(0) } } @@ -112,11 +106,6 @@ class UserContribListActivity : BaseActivity() { } }) - binding.langButtonContainer.setOnClickListener { - requestLanguageChange.launch(UserContribWikiSelectActivity.newIntent(this, viewModel.langCode)) - } - updateLangButton() - lifecycleScope.launchWhenCreated { userContribListAdapter.loadStateFlow.distinctUntilChangedBy { it.refresh } .filter { it.refresh is LoadState.NotLoading } @@ -158,30 +147,6 @@ class UserContribListActivity : BaseActivity() { } } - private fun updateLangButton() { - linkHandler.wikiSite = viewModel.wikiSite - when (viewModel.langCode) { - Constants.WIKI_CODE_WIKIDATA -> { - binding.langButtonText.isVisible = false - binding.langButtonIcon.setImageResource(R.drawable.ic_wikidata_logo) - binding.langButtonIcon.isVisible = true - } - Constants.WIKI_CODE_COMMONS -> { - binding.langButtonText.isVisible = false - binding.langButtonIcon.setImageResource(R.drawable.ic_commons_logo) - binding.langButtonIcon.isVisible = true - } - else -> { - binding.langButtonText.isVisible = true - binding.langButtonIcon.isVisible = false - binding.langButtonText.text = viewModel.langCode.uppercase(Locale.ENGLISH) - ViewUtil.formatLangButton(binding.langButtonText, binding.langButtonText.text.toString(), - SearchFragment.LANG_BUTTON_TEXT_SIZE_SMALLER, SearchFragment.LANG_BUTTON_TEXT_SIZE_LARGER) - } - } - FeedbackUtil.setButtonLongPressToast(binding.langButtonContainer) - } - private fun setupAdapters() { if (actionMode != null) { binding.userContribRecycler.adapter = userContribListAdapter.withLoadStateFooter(loadFooter) @@ -198,24 +163,6 @@ class UserContribListActivity : BaseActivity() { actionMode = startSupportActionMode(searchActionModeCallback) } - fun showFilterOverflowMenu() { - val editCountsValue = viewModel.userContribStatsData.value - if (editCountsValue is Resource.Success) { - val anchorView = if (actionMode != null && searchActionModeCallback.searchAndFilterActionProvider != null) - searchActionModeCallback.searchBarFilterIcon!! else if (userContribSearchBarAdapter.viewHolder != null) - userContribSearchBarAdapter.viewHolder!!.binding.filterByButton else binding.root - UserContribFilterOverflowView(this@UserContribListActivity).show(anchorView) { - setupAdapters() - viewModel.clearCache() - userContribListAdapter.reload() - userContribSearchBarAdapter.notifyItemChanged(0) - actionMode?.let { - searchActionModeCallback.updateFilterIconAndText() - } - } - } - } - private inner class SearchBarAdapter : RecyclerView.Adapter() { var viewHolder: SearchBarViewHolder? = null override fun onBindViewHolder(holder: SearchBarViewHolder, position: Int) { @@ -361,7 +308,7 @@ class UserContribListActivity : BaseActivity() { } binding.filterByButton.setOnClickListener { - showFilterOverflowMenu() + launchFilterActivity.launch(UserContribFilterActivity.newIntent(this@UserContribListActivity)) } FeedbackUtil.setButtonLongPressToast(binding.filterByButton) @@ -370,13 +317,14 @@ class UserContribListActivity : BaseActivity() { } private fun updateFilterCount() { - if (Prefs.userContribFilterNs.isEmpty()) { + val excludedFilters = viewModel.excludedFiltersCount() + if (excludedFilters == 0) { binding.filterCount.visibility = View.GONE ImageViewCompat.setImageTintList(binding.filterByButton, ResourceUtil.getThemedColorStateList(this@UserContribListActivity, R.attr.color_group_9)) } else { binding.filterCount.visibility = View.VISIBLE - binding.filterCount.text = Prefs.userContribFilterNs.size.toString() + binding.filterCount.text = excludedFilters.toString() ImageViewCompat.setImageTintList(binding.filterByButton, ResourceUtil.getThemedColorStateList(this@UserContribListActivity, R.attr.colorAccent)) } @@ -386,14 +334,13 @@ class UserContribListActivity : BaseActivity() { private inner class EmptyMessagesViewHolder constructor(val binding: ViewEditHistoryEmptyMessagesBinding) : RecyclerView.ViewHolder(binding.root) { init { binding.emptySearchMessage.movementMethod = LinkMovementMethodExt { _ -> - showFilterOverflowMenu() + launchFilterActivity.launch(UserContribFilterActivity.newIntent(this@UserContribListActivity)) } } fun bindItem() { binding.emptySearchMessage.text = StringUtil.fromHtml(getString(R.string.page_edit_history_empty_search_message)) RichTextUtil.removeUnderlinesFromLinks(binding.emptySearchMessage) - binding.searchEmptyContainer.isVisible = Prefs.userContribFilterNs.isNotEmpty() } } @@ -415,7 +362,6 @@ class UserContribListActivity : BaseActivity() { private inner class SearchCallback : SearchActionModeCallback() { var searchAndFilterActionProvider: SearchAndFilterActionProvider? = null - val searchBarFilterIcon get() = searchAndFilterActionProvider?.filterIcon override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { searchAndFilterActionProvider = SearchAndFilterActionProvider(this@UserContribListActivity, searchHintString, @@ -428,11 +374,11 @@ class UserContribListActivity : BaseActivity() { } override fun onFilterIconClick() { - showFilterOverflowMenu() + launchFilterActivity.launch(UserContribFilterActivity.newIntent(this@UserContribListActivity)) } override fun getExcludedFilterCount(): Int { - return Prefs.userContribFilterNs.size + return Prefs.userContribFilterExcludedNs.size } override fun getFilterIconContentDescription(): Int { @@ -473,10 +419,6 @@ class UserContribListActivity : BaseActivity() { override fun getParentContext(): Context { return this@UserContribListActivity } - - fun updateFilterIconAndText() { - searchAndFilterActionProvider?.updateFilterIconAndText() - } } internal inner class UserContribLinkHandler internal constructor(context: Context) : LinkHandler(context) { diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribListViewModel.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribListViewModel.kt index 76fd525242d..e253e009dcb 100644 --- a/app/src/main/java/org/wikipedia/usercontrib/UserContribListViewModel.kt +++ b/app/src/main/java/org/wikipedia/usercontrib/UserContribListViewModel.kt @@ -6,8 +6,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.paging.* -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.wikipedia.Constants import org.wikipedia.WikipediaApp import org.wikipedia.dataclient.Service @@ -70,6 +73,11 @@ class UserContribListViewModel(bundle: Bundle) : ViewModel() { loadStats() } + fun excludedFiltersCount(): Int { + val excludedNsFilter = Prefs.userContribFilterExcludedNs + return UserContribFilterActivity.NAMESPACE_LIST.count { excludedNsFilter.contains(it) } + } + fun loadStats() { viewModelScope.launch(CoroutineExceptionHandler { _, throwable -> L.e(throwable) @@ -95,7 +103,11 @@ class UserContribListViewModel(bundle: Bundle) : ViewModel() { return LoadResult.Page(cachedContribs, null, cachedContinueKey) } - val nsFilter = Prefs.userContribFilterNs.joinToString("|") + if (excludedFiltersCount() == UserContribFilterActivity.NAMESPACE_LIST.size) { + return LoadResult.Page(emptyList(), null, null) + } + + val nsFilter = UserContribFilterActivity.NAMESPACE_LIST.filter { !Prefs.userContribFilterExcludedNs.contains(it) }.joinToString("|") val response = ServiceFactory.get(wikiSite).getUserContrib(userName, 500, nsFilter.ifEmpty { null }, null, params.key) val contribs = response.query?.userContributions!! diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribWikiSelectActivity.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribWikiSelectActivity.kt deleted file mode 100644 index d422a77ffe7..00000000000 --- a/app/src/main/java/org/wikipedia/usercontrib/UserContribWikiSelectActivity.kt +++ /dev/null @@ -1,115 +0,0 @@ -package org.wikipedia.usercontrib - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.ViewGroup -import androidx.activity.result.contract.ActivityResultContracts -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import org.wikipedia.Constants -import org.wikipedia.R -import org.wikipedia.WikipediaApp -import org.wikipedia.activity.BaseActivity -import org.wikipedia.databinding.ActivityUserContribWikiSelectBinding -import org.wikipedia.settings.languages.WikipediaLanguagesActivity -import org.wikipedia.views.DefaultViewHolder - -class UserContribWikiSelectActivity : BaseActivity() { - - private lateinit var binding: ActivityUserContribWikiSelectBinding - private lateinit var selectLangCode: String - - private val langUpdateLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - binding.recyclerView.adapter = WikiSelectAdapter(this) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityUserContribWikiSelectBinding.inflate(layoutInflater) - selectLangCode = intent.getStringExtra(INTENT_EXTRA_SELECT_LANG_CODE).orEmpty().ifEmpty { WikipediaApp.instance.appOrSystemLanguageCode } - - binding.recyclerView.layoutManager = LinearLayoutManager(this) - binding.recyclerView.adapter = WikiSelectAdapter(this) - setContentView(binding.root) - } - - inner class WikiSelectItemViewHolder constructor(itemView: UserContribWikiSelectItemView) : - DefaultViewHolder(itemView) { - fun bindItem(item: Item) { - view.setContents(item, item.itemCode == selectLangCode) - } - } - - private inner class WikiSelectAddLanguageViewHolder constructor(itemView: UserContribWikiSelectItemView) : - DefaultViewHolder(itemView), UserContribWikiSelectItemView.Callback { - fun bindItem(text: String) { - (itemView as UserContribWikiSelectItemView).callback = this - itemView.setSingleLabel(text) - } - - override fun onSelected(item: Item?) { - langUpdateLauncher.launch(WikipediaLanguagesActivity.newIntent(this@UserContribWikiSelectActivity, Constants.InvokeSource.USER_CONTRIB_ACTIVITY)) - } - } - - private inner class WikiSelectAdapter(val context: Context) : RecyclerView.Adapter>(), UserContribWikiSelectItemView.Callback { - private val itemList = mutableListOf() - - init { - WikipediaApp.instance.languageState.appLanguageCodes.forEach { itemList.add(Item(it, null)) } - itemList.add(Item(Constants.WIKI_CODE_COMMONS, R.drawable.ic_commons_logo)) - itemList.add(Item(Constants.WIKI_CODE_WIKIDATA, R.drawable.ic_wikidata_logo)) - itemList.add(getString(R.string.notifications_filter_update_app_languages)) - } - - override fun onCreateViewHolder(parent: ViewGroup, type: Int): DefaultViewHolder<*> { - return when (type) { - VIEW_TYPE_ADD_LANGUAGE -> { - WikiSelectAddLanguageViewHolder(UserContribWikiSelectItemView(context)) - } - else -> { - val view = UserContribWikiSelectItemView(context) - view.callback = this - WikiSelectItemViewHolder(view) - } - } - } - - override fun getItemCount(): Int { - return itemList.size - } - - override fun getItemViewType(position: Int): Int { - return if (itemList[position] is String && itemList[position] == getString(R.string.notifications_filter_update_app_languages)) VIEW_TYPE_ADD_LANGUAGE - else VIEW_TYPE_ITEM - } - - override fun onBindViewHolder(holder: DefaultViewHolder<*>, position: Int) { - when (holder) { - is WikiSelectAddLanguageViewHolder -> holder.bindItem(itemList[position] as String) - else -> (holder as WikiSelectItemViewHolder).bindItem(itemList[position] as Item) - } - } - - override fun onSelected(item: Item?) { - setResult(RESULT_OK, intent.putExtra(INTENT_EXTRA_SELECT_LANG_CODE, item?.itemCode)) - finish() - } - } - - inner class Item constructor(val itemCode: String, val imageRes: Int? = null) - - companion object { - private const val VIEW_TYPE_ITEM = 1 - private const val VIEW_TYPE_ADD_LANGUAGE = 2 - - const val INTENT_EXTRA_SELECT_LANG_CODE = "selectLangCode" - const val ACTIVITY_RESULT_LANGUAGES_CHANGED = 2 - - fun newIntent(context: Context, selectLangCode: String): Intent { - return Intent(context, UserContribWikiSelectActivity::class.java) - .putExtra(INTENT_EXTRA_SELECT_LANG_CODE, selectLangCode) - } - } -} diff --git a/app/src/main/java/org/wikipedia/usercontrib/UserContribWikiSelectItemView.kt b/app/src/main/java/org/wikipedia/usercontrib/UserContribWikiSelectItemView.kt deleted file mode 100644 index abe71b571b4..00000000000 --- a/app/src/main/java/org/wikipedia/usercontrib/UserContribWikiSelectItemView.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.wikipedia.usercontrib - -import android.content.Context -import android.graphics.Typeface -import android.util.AttributeSet -import android.util.TypedValue -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import androidx.core.view.isVisible -import androidx.core.widget.ImageViewCompat -import org.wikipedia.Constants -import org.wikipedia.R -import org.wikipedia.WikipediaApp -import org.wikipedia.databinding.ItemUserContribWikiSelectBinding -import org.wikipedia.search.SearchFragment -import org.wikipedia.util.DimenUtil -import org.wikipedia.util.ResourceUtil -import org.wikipedia.views.ViewUtil - -class UserContribWikiSelectItemView constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) { - - interface Callback { - fun onSelected(item: UserContribWikiSelectActivity.Item?) - } - - private var item: UserContribWikiSelectActivity.Item? = null - private var binding = ItemUserContribWikiSelectBinding.inflate(LayoutInflater.from(context), this) - private val labelTypeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) - var callback: Callback? = null - - init { - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DimenUtil.roundedDpToPx(48f)) - setBackgroundResource(ResourceUtil.getThemedAttributeId(context, R.attr.selectableItemBackground)) - setOnClickListener { - callback?.onSelected(item) - } - } - - fun setContents(item: UserContribWikiSelectActivity.Item, checked: Boolean) { - this.item = item - binding.wikiTitle.text = WikipediaApp.instance.languageState.getWikiLanguageName(item.itemCode) - binding.itemCheck.isVisible = checked - getTitleCodeFor(item.itemCode)?.let { - binding.languageCode.text = it - binding.languageCode.visibility = View.VISIBLE - ViewUtil.formatLangButton(binding.languageCode, it, SearchFragment.LANG_BUTTON_TEXT_SIZE_SMALLER, SearchFragment.LANG_BUTTON_TEXT_SIZE_LARGER) - } ?: run { - binding.languageCode.visibility = View.GONE - } - item.imageRes?.let { - ImageViewCompat.setImageTintList(binding.wikiLogo, ResourceUtil.getThemedColorStateList(context, R.attr.secondary_text_color)) - binding.wikiLogo.setImageResource(it) - binding.wikiLogo.visibility = View.VISIBLE - } ?: run { - binding.wikiLogo.visibility = View.GONE - } - } - - fun setSingleLabel(text: String) { - val accentColor = ResourceUtil.getThemedColorStateList(context, R.attr.colorAccent) - binding.languageCode.visibility = View.GONE - binding.wikiLogo.visibility = View.VISIBLE - ImageViewCompat.setImageTintList(binding.wikiLogo, accentColor) - binding.wikiLogo.setImageResource(R.drawable.ic_mode_edit_themed_24dp) - binding.itemCheck.visibility = View.GONE - binding.wikiTitle.setTextColor(accentColor) - binding.wikiTitle.text = text.uppercase() - binding.wikiTitle.typeface = labelTypeface - binding.wikiTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f) - } - - private fun getTitleCodeFor(itemCode: String): String? { - return if (itemCode == Constants.WIKI_CODE_COMMONS || itemCode == Constants.WIKI_CODE_WIKIDATA) null - else itemCode - } -} diff --git a/app/src/main/res/drawable/ic_baseline_radio_button_checked_24.xml b/app/src/main/res/drawable/ic_baseline_radio_button_checked_24.xml new file mode 100644 index 00000000000..619d4028bc1 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_radio_button_checked_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_user_contrib.xml b/app/src/main/res/layout/activity_user_contrib.xml index c4458da660a..89bcc2f7f06 100644 --- a/app/src/main/res/layout/activity_user_contrib.xml +++ b/app/src/main/res/layout/activity_user_contrib.xml @@ -32,43 +32,6 @@ android:textSize="20sp" android:textStyle="bold"/> - - - - - - - - diff --git a/app/src/main/res/layout/item_user_contrib_wiki_select.xml b/app/src/main/res/layout/item_user_contrib_filter.xml similarity index 92% rename from app/src/main/res/layout/item_user_contrib_wiki_select.xml rename to app/src/main/res/layout/item_user_contrib_filter.xml index a056084623a..50bf191be3d 100644 --- a/app/src/main/res/layout/item_user_contrib_wiki_select.xml +++ b/app/src/main/res/layout/item_user_contrib_filter.xml @@ -19,17 +19,17 @@ android:textColor="?attr/secondary_text_color" /> + app:tint="?attr/secondary_text_color" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index a7cc9ba21c5..f093ee50f18 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -1015,5 +1015,5 @@ বর্তমান মিডিয়া চালনায় ত্রুটি৷ অবদান - এতে অবদান: + এতে অবদান: diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 5c350cde307..14c825c2008 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1277,5 +1277,5 @@ actual Error de reproducció multimèdia Contribucions - Contribucions a: + Contribucions a: diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0af417da023..e4d43fae280 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1291,5 +1291,5 @@ aktuell Fehler beim Abspielen von Medien. Beiträge - Beiträge zu: + Beiträge zu: diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 795a5ed1aee..b6964e26ec0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1301,5 +1301,5 @@ actuel Erreur lors de la lecture du média. Contributions - Contributions à : + Contributions à : diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 03677ee1b36..8ff913c7863 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1247,5 +1247,5 @@ terkini Terjadi kesalahan saat memutar media. Kontribusi - Kontribusi untuk: + Kontribusi untuk: diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 64796220b2b..c58b32bb4b1 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1344,5 +1344,5 @@ נוכחי שגיאה בנגינת מדיה. תרומות - תרומות ל־: + תרומות ל־: diff --git a/app/src/main/res/values-ks/strings.xml b/app/src/main/res/values-ks/strings.xml index 1473a016687..0ae255ce0fe 100644 --- a/app/src/main/res/values-ks/strings.xml +++ b/app/src/main/res/values-ks/strings.xml @@ -722,5 +722,5 @@ موجوٗدٕ گَردان مِیٖڈیا چلاوٕنس منٛز چھےٚ خرٲبی. شَرکٔژ - کُن شَرکٲژٕ: + کُن شَرکٲژٕ: diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 3c45f2bb8cc..3b1ddbdaf61 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -1258,5 +1258,5 @@ тековна Грешка при пуштањето на снимката. Придонеси - Придонеси на: + Придонеси на: diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 8cad2f17d9d..49585df19b9 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1261,5 +1261,5 @@ huidige versie Fout bij het afspelen van media. Bijdragen - Bijdragen aan: + Bijdragen aan: diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index fabb3e8878b..b988f432dcf 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1289,5 +1289,5 @@ atual Erro na reprodução de multimédia. Contribuições - Contribuições em: + Contribuições em: diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index a83cea839ba..f17a0980ff3 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1274,5 +1274,5 @@ atual Erro na reprodução de multimédia. Contribuições - Contribuições em: + Contribuições em: diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index 28fb16e5f6f..40276985fb3 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -1282,5 +1282,6 @@ Label indicating that this contribution is the current revision of the article.\n{{identical|Current}} Error shown when the selected media could not be played. Menu label for accessing the contributions of the current user. - Screen title for viewing the contributions of a user from a selected wiki. + Screen title for applying the filters for the contributions of a user. + Label for filter list that allows to filter contributions by namespace. diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 0708bcb1386..193201062db 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -1327,5 +1327,5 @@ trenutno Napaka pri predvajanju predstavnosti. Prispevki - Prispevki k: + Prispevki k: diff --git a/app/src/main/res/values-uz/strings.xml b/app/src/main/res/values-uz/strings.xml index 8d12b03b2f0..4ea43175192 100644 --- a/app/src/main/res/values-uz/strings.xml +++ b/app/src/main/res/values-uz/strings.xml @@ -1256,5 +1256,5 @@ joriy Mediani ijro etishda xatolik yuz berdi. Hissalar - Qoʻshilgan hissa: + Qoʻshilgan hissa: diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 5a8721b55c1..db46797b4ff 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -138,7 +138,8 @@ readingFocusMode editHistoryFilterType talkTopicExpandAll - userContribFilterNamespaces + userContribFilterNamespaces + userContribFilterLangCode editSyntaxHighlight editMonospaceFont editLineNumbers diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b6cbe078388..1b0edf2eb73 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1340,11 +1340,12 @@ Contributions of %s No archived pages found. Filter by namespace - All edits + All \"namespaces\" + Namespace filter Article current Error playing media. Contributions - Contributions to: + Filter contributions