diff --git a/app/src/main/java/org/wikipedia/dataclient/Service.kt b/app/src/main/java/org/wikipedia/dataclient/Service.kt index b14b0c4b9e4..6f1efdee53b 100644 --- a/app/src/main/java/org/wikipedia/dataclient/Service.kt +++ b/app/src/main/java/org/wikipedia/dataclient/Service.kt @@ -172,6 +172,9 @@ interface Service { @GET(MW_API_PREFIX + "action=query&prop=info&generator=categories&inprop=varianttitles|displaytitle&gclshow=!hidden&gcllimit=500") suspend fun getCategories(@Query("titles") titles: String): MwQueryResponse + @GET(MW_API_PREFIX + "action=query&prop=categories&clshow=!hidden&cllimit=100") + suspend fun getCategoriesProps(@Query("titles") titles: String): MwQueryResponse + @GET(MW_API_PREFIX + "action=query&prop=description|pageimages|info&pilicense=any&generator=categorymembers&inprop=varianttitles|displaytitle&gcmprop=ids|title") suspend fun getCategoryMembers( @Query("gcmtitle") title: String, diff --git a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt index aba6fbbd8ac..40dd00fa90f 100644 --- a/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt +++ b/app/src/main/java/org/wikipedia/dataclient/mwapi/MwQueryPage.kt @@ -24,6 +24,7 @@ class MwQueryPage { @SerialName("pageid") val pageId = 0 @SerialName("pageprops") val pageProps: PageProps? = null @SerialName("entityterms") val entityTerms: EntityTerms? = null + @SerialName("categories") val categoriesProps: List? = null val ns = 0 val coordinates: List? = null @@ -147,4 +148,10 @@ class MwQueryPage { val label: List = emptyList() val description: List = emptyList() } + + @Serializable + class Category { + val ns = 0 + val title: String = "" + } } diff --git a/app/src/main/java/org/wikipedia/history/HistoryFragment.kt b/app/src/main/java/org/wikipedia/history/HistoryFragment.kt index b3036f0c567..99a1d0a72fd 100644 --- a/app/src/main/java/org/wikipedia/history/HistoryFragment.kt +++ b/app/src/main/java/org/wikipedia/history/HistoryFragment.kt @@ -9,11 +9,13 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout +import android.widget.ScrollView import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.core.view.isVisible +import androidx.core.view.setPadding import androidx.core.view.updateLayoutParams import androidx.core.view.updateMarginsRelative import androidx.fragment.app.Fragment @@ -38,6 +40,7 @@ import org.wikipedia.util.DimenUtil import org.wikipedia.util.FeedbackUtil import org.wikipedia.util.Resource import org.wikipedia.util.ResourceUtil +import org.wikipedia.util.StringUtil import org.wikipedia.util.log.L import org.wikipedia.views.DefaultViewHolder import org.wikipedia.views.PageItemView @@ -91,7 +94,6 @@ class HistoryFragment : Fragment(), BackPressedHandler { } } } - private fun setUpScrollListener() { binding.historyList.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -260,6 +262,7 @@ class HistoryFragment : Fragment(), BackPressedHandler { init { val searchCardView = itemView.findViewById(R.id.search_card) val voiceSearchButton = itemView.findViewById(R.id.voice_search_button) + val categoriesDialogButton = itemView.findViewById(R.id.categories_dialog) historyFilterButton = itemView.findViewById(R.id.history_filter) clearHistoryButton = itemView.findViewById(R.id.history_delete) searchCardView.setOnClickListener { (requireParentFragment() as MainFragment).openSearchActivity(Constants.InvokeSource.NAV_MENU, null, it) } @@ -282,6 +285,34 @@ class HistoryFragment : Fragment(), BackPressedHandler { deleteSelectedPages() } } + categoriesDialogButton.setOnClickListener { + val groupData = viewModel.groupedTitles + + var htmlContent = "" + groupData.forEach { + htmlContent += "${it.first} (${it.second})
" + } + + val textView = TextView(requireContext()).apply { + text = StringUtil.fromHtml(htmlContent) + textSize = 14f + } + + val scrollView = ScrollView(requireContext()).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setPadding(DimenUtil.roundedDpToPx(12f)) + addView(textView) + } + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.action_item_categories) + .setView(scrollView) + .setPositiveButton(android.R.string.ok, null) + .show() + } FeedbackUtil.setButtonTooltip(historyFilterButton, clearHistoryButton) adjustSearchCardView(searchCardView) } diff --git a/app/src/main/java/org/wikipedia/history/HistoryViewModel.kt b/app/src/main/java/org/wikipedia/history/HistoryViewModel.kt index d2df6169045..dbd5e0c9d48 100644 --- a/app/src/main/java/org/wikipedia/history/HistoryViewModel.kt +++ b/app/src/main/java/org/wikipedia/history/HistoryViewModel.kt @@ -6,12 +6,18 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.wikipedia.WikipediaApp import org.wikipedia.database.AppDatabase +import org.wikipedia.dataclient.ServiceFactory +import org.wikipedia.dataclient.mwapi.MwQueryPage.Category import org.wikipedia.util.Resource import org.wikipedia.util.SingleLiveData +import org.wikipedia.util.StringUtil class HistoryViewModel : ViewModel() { @@ -33,6 +39,7 @@ class HistoryViewModel : ViewModel() { val historyItems = MutableLiveData(Resource>()) val deleteHistoryItemsAction = SingleLiveData>() + var groupedTitles = emptyList>() init { reloadHistoryItems() @@ -47,6 +54,26 @@ class HistoryViewModel : ViewModel() { private suspend fun loadHistoryItems() { withContext(Dispatchers.IO) { val items = AppDatabase.instance.historyEntryWithImageDao().filterHistoryItems(searchQuery.orEmpty()) + val historyEntryItems = items.filterIsInstance() + + val categories = mutableListOf() + val deferredCategories = historyEntryItems.map { item -> + async { + try { + val response = ServiceFactory.get(WikipediaApp.instance.wikiSite).getCategoriesProps(item.apiTitle) + response.query?.firstPage()?.categoriesProps ?: emptyList() + } catch (e: Exception) { + // Handle exceptions appropriately, e.g., log, return emptyList, etc. + println("Error fetching categories for ${item.apiTitle}: ${e.message}") + emptyList() // or throw e; depending on your error handling. + } + } + } + + deferredCategories.awaitAll().forEach { categories.addAll(it) } + + // Grouping the titles by setting in a Pair<> with its name and count + groupedTitles = categories.groupBy { it.title }.map { Pair(StringUtil.removeNamespace(it.key), it.value.size) }.sortedByDescending { it.second } historyItems.postValue(Resource.Success(items)) } } diff --git a/app/src/main/res/layout/view_history_header_with_search.xml b/app/src/main/res/layout/view_history_header_with_search.xml index 011d90c11fe..e00dd0a6254 100644 --- a/app/src/main/res/layout/view_history_header_with_search.xml +++ b/app/src/main/res/layout/view_history_header_with_search.xml @@ -35,6 +35,18 @@ android:textSize="24sp" android:textStyle="bold" /> + +