Add back search filters from v3

This commit is contained in:
Rahul Kumar Patel
2021-04-05 04:28:09 +05:30
parent a76638af0d
commit ee8cde3f26
14 changed files with 575 additions and 35 deletions

View File

@@ -0,0 +1,29 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.data
class Filter {
var appsWithAds = true
var appsWithIAP = true
var paidApps = true
var gsfDependentApps = true
var rating = 0.0f
var downloads = 0
}

View File

@@ -0,0 +1,50 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.data.providers
import android.content.Context
import com.aurora.store.data.Filter
import com.aurora.store.data.SingletonHolder
import com.aurora.store.util.Preferences
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.lang.reflect.Modifier
class FilterProvider private constructor(var context: Context) {
companion object : SingletonHolder<FilterProvider, Context>(::FilterProvider) {
const val PREFERENCE_FILTER = "PREFERENCE_FILTER"
}
private var gson: Gson = GsonBuilder()
.excludeFieldsWithModifiers(Modifier.TRANSIENT)
.create()
fun getSavedFilter(): Filter {
var rawFilter = Preferences.getString(context, PREFERENCE_FILTER)
if (rawFilter.isEmpty())
rawFilter = "{}"
return gson.fromJson(rawFilter, Filter::class.java)
}
fun saveFilter(filter: Filter) {
Preferences.putString(context, PREFERENCE_FILTER, gson.toJson(filter))
}
}

View File

@@ -40,6 +40,7 @@ object Preferences {
const val PREFERENCE_FILTER_GOOGLE = "PREFERENCE_FILTER_GOOGLE"
const val PREFERENCE_FILTER_FDROID = "PREFERENCE_FILTER_FDROID"
const val PREFERENCE_FILTER_SEARCH = "PREFERENCE_FILTER_SEARCH"
const val PREFERENCE_AUTO_INSTALL = "PREFERENCE_AUTO_INSTALL"
const val PREFERENCE_AUTO_DELETE = "PREFERENCE_AUTO_DELETE"
@@ -55,7 +56,7 @@ object Preferences {
const val PREFERENCE_INSECURE_ANONYMOUS = "PREFERENCE_INSECURE_ANONYMOUS"
private fun getPrefs(context: Context): SharedPreferences {
fun getPrefs(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}

View File

@@ -37,6 +37,7 @@ import com.aurora.store.util.Preferences.PREFERENCE_DOWNLOAD_ACTIVE
import com.aurora.store.util.Preferences.PREFERENCE_DOWNLOAD_EXTERNAL
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_FDROID
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_GOOGLE
import com.aurora.store.util.Preferences.PREFERENCE_FILTER_SEARCH
import com.aurora.store.util.Preferences.PREFERENCE_FOR_YOU
import com.aurora.store.util.Preferences.PREFERENCE_INSECURE_ANONYMOUS
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
@@ -155,6 +156,7 @@ class OnboardingActivity : BaseActivity() {
/*Filters*/
save(PREFERENCE_FILTER_FDROID, true)
save(PREFERENCE_FILTER_GOOGLE, false)
save(PREFERENCE_FILTER_SEARCH, true)
/*Downloader*/
save(PREFERENCE_DOWNLOAD_ACTIVE, 3)

View File

@@ -19,6 +19,8 @@
package com.aurora.store.view.ui.search
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@@ -28,20 +30,27 @@ import android.view.inputmethod.EditorInfo
import android.widget.TextView
import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.store.databinding.ActivitySearchResultBinding
import com.aurora.extensions.close
import com.aurora.extensions.open
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.store.R
import com.aurora.store.data.Filter
import com.aurora.store.data.providers.FilterProvider
import com.aurora.store.databinding.ActivitySearchResultBinding
import com.aurora.store.util.Preferences
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.view.ui.downloads.DownloadActivity
import com.aurora.store.view.ui.sheets.FilterSheet
import com.aurora.store.viewmodel.search.SearchResultViewModel
import com.google.android.material.textfield.TextInputEditText
class SearchResultsActivity : BaseActivity() {
class SearchResultsActivity : BaseActivity(), OnSharedPreferenceChangeListener {
lateinit var B: ActivitySearchResultBinding
lateinit var VM: SearchResultViewModel
@@ -49,6 +58,9 @@ class SearchResultsActivity : BaseActivity() {
lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
lateinit var searchView: TextInputEditText
lateinit var sharedPreferences: SharedPreferences
lateinit var filter: Filter
var query: String? = null
var searchBundle: SearchBundle = SearchBundle()
@@ -69,6 +81,10 @@ class SearchResultsActivity : BaseActivity() {
B = ActivitySearchResultBinding.inflate(layoutInflater)
VM = ViewModelProvider(this).get(SearchResultViewModel::class.java)
sharedPreferences = Preferences.getPrefs(this)
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
filter = FilterProvider.with(this).getSavedFilter()
setContentView(B.root)
VM.liveData.observe(this, {
@@ -79,6 +95,7 @@ class SearchResultsActivity : BaseActivity() {
attachToolbar()
attachSearch()
attachRecycler()
attachFilter()
query = intent.getStringExtra(Constants.STRING_EXTRA)
query?.let {
@@ -86,6 +103,13 @@ class SearchResultsActivity : BaseActivity() {
}
}
override fun onDestroy() {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
if (Preferences.getBoolean(this, Preferences.PREFERENCE_FILTER_SEARCH))
FilterProvider.with(this).saveFilter(Filter())
super.onDestroy()
}
private fun attachToolbar() {
searchView = B.layoutViewToolbar.inputSearch
@@ -97,6 +121,12 @@ class SearchResultsActivity : BaseActivity() {
}
}
private fun attachFilter() {
B.filterFab.setOnClickListener {
FilterSheet.newInstance().show(supportFragmentManager, "")
}
}
private fun attachRecycler() {
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
@@ -107,27 +137,63 @@ class SearchResultsActivity : BaseActivity() {
}
private fun updateController(searchBundle: SearchBundle) {
B.recycler
.withModels {
setFilterDuplicates(true)
searchBundle.appList.forEach { app ->
add(
AppListViewModel_()
.id(app.id)
.app(app)
.click(View.OnClickListener {
openDetailsActivity(app)
})
)
}
val filteredAppList = filter(searchBundle.appList)
if (searchBundle.subBundles.isNotEmpty()) {
if (filteredAppList.isEmpty()) {
if (searchBundle.subBundles.isNotEmpty()) {
VM.next(searchBundle.subBundles)
B.recycler.withModels {
setFilterDuplicates(true)
add(
AppProgressViewModel_()
.id("progress")
)
}
} else {
B.recycler.adapter?.let {
/*Show empty search list if nothing found or no app matches filter criterion*/
if (it.itemCount == 1 && searchBundle.subBundles.isEmpty()) {
B.recycler.withModels {
add(
NoAppViewModel_()
.id("no_app")
.message(getString(R.string.details_no_app_match))
.icon(R.drawable.ic_round_search)
)
}
}
}
}
} else {
B.recycler
.withModels {
setFilterDuplicates(true)
filteredAppList.forEach { app ->
add(
AppListViewModel_()
.id(app.id)
.app(app)
.click(View.OnClickListener {
openDetailsActivity(app)
})
)
}
if (searchBundle.subBundles.isNotEmpty()) {
add(
AppProgressViewModel_()
.id("progress")
)
}
}
B.recycler.adapter?.let {
if (it.itemCount < 10) {
VM.next(searchBundle.subBundles)
}
}
}
}
private fun attachSearch() {
@@ -137,12 +203,7 @@ class SearchResultsActivity : BaseActivity() {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.isNotEmpty()) {
/*val query = s.toString()
if (query.isNotEmpty()) {
VM.observeSearchResults(query)
}*/
}
}
override fun afterTextChanged(s: Editable) {}
@@ -150,9 +211,9 @@ class SearchResultsActivity : BaseActivity() {
searchView.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
val query = searchView.text.toString()
if (query.isNotEmpty()) {
queryViewModel(query)
query = searchView.text.toString()
query?.let {
queryViewModel(it)
return@setOnEditorActionListener true
}
}
@@ -167,10 +228,29 @@ class SearchResultsActivity : BaseActivity() {
}
private fun queryViewModel(query: String) {
VM.observeSearchResults(query)
endlessRecyclerOnScrollListener.resetPageCount()
B.recycler.clear()
searchBundle.subBundles.clear()
searchBundle.appList.clear()
VM.observeSearchResults(query)
}
private fun filter(appList: MutableList<App>): List<App> {
filter = FilterProvider.with(this).getSavedFilter()
return appList
.asSequence()
.filter { if (!filter.paidApps) it.isFree else true }
.filter { if (!filter.appsWithAds) !it.containsAds else true }
.filter { if (!filter.gsfDependentApps) it.dependencies.dependentPackages.isEmpty() else true }
.filter { if (filter.rating > 0) it.rating.average >= filter.rating else true }
.filter { if (filter.downloads > 0) it.installs >= filter.downloads else true }
.toList()
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == FilterProvider.PREFERENCE_FILTER) {
filter = FilterProvider.with(this).getSavedFilter()
query?.let {
queryViewModel(it)
}
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.sheets
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.aurora.store.R
import com.aurora.store.data.Filter
import com.aurora.store.data.providers.FilterProvider
import com.aurora.store.databinding.SheetFilterBinding
import com.google.android.material.chip.Chip
class FilterSheet : BaseBottomSheet() {
private lateinit var B: SheetFilterBinding
private lateinit var filter: Filter
companion object {
const val TAG = "TOSSheet"
@JvmStatic
fun newInstance(): FilterSheet {
return FilterSheet().apply {
arguments = Bundle().apply {
}
}
}
}
override fun onCreateContentView(
inflater: LayoutInflater,
container: ViewGroup,
savedInstanceState: Bundle?
): View {
B = SheetFilterBinding.inflate(inflater, container, false)
filter = FilterProvider.with(requireContext()).getSavedFilter()
return B.root
}
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
attachSingleChips()
attachMultipleChips()
attachActions()
}
private fun attachActions() {
B.btnPositive.setOnClickListener {
FilterProvider.with(requireContext()).saveFilter(filter)
dismissAllowingStateLoss()
}
B.btnNegative.setOnClickListener {
dismissAllowingStateLoss()
}
}
private fun attachSingleChips() {
B.filterGfs.apply {
isChecked = filter.gsfDependentApps
setOnCheckedChangeListener { _, checked ->
isChecked = checked
filter.gsfDependentApps = checked
}
}
B.filterPaid.apply {
isChecked = filter.paidApps
setOnCheckedChangeListener { _, checked ->
isChecked = checked
filter.paidApps = checked
}
}
B.filterAds.apply {
isChecked = filter.appsWithAds
setOnCheckedChangeListener { _, checked ->
isChecked = checked
filter.appsWithAds = checked
}
}
}
private fun attachMultipleChips() {
val downloadLabels = resources.getStringArray(R.array.filterDownloadsLabels)
val downloadValues = resources.getStringArray(R.array.filterDownloadsValues)
val ratingLabels = resources.getStringArray(R.array.filterRatingLabels)
val ratingValues = resources.getStringArray(R.array.filterRatingValues)
var i = 0
for (downloadLabel in downloadLabels) {
val chip = Chip(requireContext())
chip.id = i
chip.text = downloadLabel
chip.isChecked = filter.downloads == downloadValues[i].toInt()
B.downloadChips.addView(chip)
i++
}
B.downloadChips.setOnCheckedChangeListener { _, id: Int ->
filter.downloads = downloadValues[id].toInt()
}
i = 0
for (ratingLabel in ratingLabels) {
val chip = Chip(requireContext())
chip.id = i
chip.text = ratingLabel
chip.isChecked = filter.rating == ratingValues[i].toFloat()
B.ratingChips.addView(chip)
i++
}
B.ratingChips.setOnCheckedChangeListener { _, id: Int ->
filter.rating = ratingValues[id].toFloat()
}
}
}