Blacklist: Add support for import & export
This commit is contained in:
@@ -20,14 +20,23 @@
|
||||
package com.aurora.store.view.ui.commons
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.event.BusEvent
|
||||
import com.aurora.store.databinding.FragmentGenericWithSearchBinding
|
||||
import com.aurora.store.view.epoxy.views.BlackListViewModel_
|
||||
@@ -35,12 +44,23 @@ import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
||||
import com.aurora.store.viewmodel.all.BlacklistViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Calendar
|
||||
|
||||
@AndroidEntryPoint
|
||||
class BlacklistFragment : BaseFragment<FragmentGenericWithSearchBinding>() {
|
||||
|
||||
private val viewModel: BlacklistViewModel by viewModels()
|
||||
|
||||
private val mimeType = "application/json"
|
||||
private val startForDocumentImport =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||
if (it != null) importBlacklist(it) else toast(R.string.toast_black_import_failed)
|
||||
}
|
||||
private val startForDocumentExport =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument(mimeType)) {
|
||||
if (it != null) exportBlacklist(it) else toast(R.string.toast_black_export_failed)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
@@ -53,7 +73,15 @@ class BlacklistFragment : BaseFragment<FragmentGenericWithSearchBinding>() {
|
||||
// Toolbar
|
||||
binding.layoutToolbarNative.apply {
|
||||
imgActionPrimary.visibility = View.VISIBLE
|
||||
imgActionSecondary.visibility = View.GONE
|
||||
imgActionSecondary.apply {
|
||||
visibility = View.VISIBLE
|
||||
setImageDrawable(
|
||||
AppCompatResources.getDrawable(requireContext(), R.drawable.ic_menu)
|
||||
)
|
||||
setOnClickListener {
|
||||
showMenu(it)
|
||||
}
|
||||
}
|
||||
|
||||
imgActionPrimary.setOnClickListener {
|
||||
viewModel.blacklistProvider.blacklist = viewModel.selected
|
||||
@@ -79,7 +107,8 @@ class BlacklistFragment : BaseFragment<FragmentGenericWithSearchBinding>() {
|
||||
start: Int,
|
||||
count: Int,
|
||||
after: Int
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -89,6 +118,37 @@ class BlacklistFragment : BaseFragment<FragmentGenericWithSearchBinding>() {
|
||||
viewModel.blacklistProvider.blacklist = viewModel.selected
|
||||
}
|
||||
|
||||
private fun showMenu(anchor: View) {
|
||||
val popupMenu = PopupMenu(
|
||||
ContextThemeWrapper(
|
||||
requireContext(),
|
||||
R.style.AppTheme_PopupMenu
|
||||
), anchor
|
||||
)
|
||||
|
||||
val inflater: MenuInflater = popupMenu.menuInflater
|
||||
inflater.inflate(R.menu.menu_import_export, popupMenu.menu)
|
||||
|
||||
popupMenu.setOnMenuItemClickListener { menuItem: MenuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_import -> {
|
||||
startForDocumentImport.launch(arrayOf(mimeType))
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_export -> {
|
||||
startForDocumentExport.launch(
|
||||
"aurora_store_blacklist_${Calendar.getInstance().time.time}.json"
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
popupMenu.show()
|
||||
}
|
||||
|
||||
private fun updateController(packages: List<PackageInfo>?) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
@@ -125,4 +185,15 @@ class BlacklistFragment : BaseFragment<FragmentGenericWithSearchBinding>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importBlacklist(uri: Uri) {
|
||||
viewModel.importBlacklist(requireContext(), uri)
|
||||
binding.recycler.requestModelBuild()
|
||||
toast(R.string.toast_black_import_success)
|
||||
}
|
||||
|
||||
private fun exportBlacklist(uri: Uri) {
|
||||
viewModel.exportBlacklist(requireContext(), uri)
|
||||
toast(R.string.toast_black_export_success)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +45,11 @@ class FavouriteFragment : BaseFragment<FragmentFavouriteBinding>() {
|
||||
private val mimeType = "application/json"
|
||||
private val startForDocumentImport =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||
if (it != null) importDeviceConfig(it) else toast(R.string.toast_fav_import_failed)
|
||||
if (it != null) importFavourites(it) else toast(R.string.toast_fav_import_failed)
|
||||
}
|
||||
private val startForDocumentExport =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument(mimeType)) {
|
||||
if (it != null) exportDeviceConfig(it) else toast(R.string.toast_fav_export_failed)
|
||||
if (it != null) exportFavourites(it) else toast(R.string.toast_fav_export_failed)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@@ -111,13 +111,13 @@ class FavouriteFragment : BaseFragment<FragmentFavouriteBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun importDeviceConfig(uri: Uri) {
|
||||
private fun importFavourites(uri: Uri) {
|
||||
viewModel.importFavourites(requireContext(), uri)
|
||||
binding.recycler.requestModelBuild()
|
||||
toast(R.string.toast_fav_import_success)
|
||||
}
|
||||
|
||||
private fun exportDeviceConfig(uri: Uri) {
|
||||
private fun exportFavourites(uri: Uri) {
|
||||
viewModel.exportFavourites(requireContext(), uri)
|
||||
toast(R.string.toast_fav_export_success)
|
||||
}
|
||||
|
||||
@@ -21,11 +21,14 @@ package com.aurora.store.viewmodel.all
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aurora.store.data.providers.BlacklistProvider
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -37,6 +40,7 @@ import javax.inject.Inject
|
||||
@HiltViewModel
|
||||
class BlacklistViewModel @Inject constructor(
|
||||
val blacklistProvider: BlacklistProvider,
|
||||
val gson: Gson,
|
||||
@ApplicationContext private val context: Context
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -60,4 +64,36 @@ class BlacklistViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun importBlacklist(context: Context, uri: Uri) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
context.contentResolver.openInputStream(uri)?.use {
|
||||
val importedSet: MutableSet<String> = gson.fromJson(
|
||||
it.bufferedReader().readText(),
|
||||
object : TypeToken<MutableSet<String?>?>() {}.type
|
||||
)
|
||||
|
||||
val knownSet = blacklistProvider.blacklist
|
||||
knownSet.addAll(importedSet)
|
||||
|
||||
selected = knownSet
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to import blacklist", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun exportBlacklist(context: Context, uri: Uri) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
context.contentResolver.openOutputStream(uri)?.use {
|
||||
it.write(gson.toJson(blacklistProvider.blacklist).encodeToByteArray())
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to export blacklist", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
app/src/main/res/drawable/ic_menu.xml
Normal file
9
app/src/main/res/drawable/ic_menu.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M480,800q-33,0 -56.5,-23.5T400,720q0,-33 23.5,-56.5T480,640q33,0 56.5,23.5T560,720q0,33 -23.5,56.5T480,800ZM480,560q-33,0 -56.5,-23.5T400,480q0,-33 23.5,-56.5T480,400q33,0 56.5,23.5T560,480q0,33 -23.5,56.5T480,560ZM480,320q-33,0 -56.5,-23.5T400,240q0,-33 23.5,-56.5T480,160q33,0 56.5,23.5T560,240q0,33 -23.5,56.5T480,320Z"
|
||||
android:fillColor="#e8eaed"/>
|
||||
</vector>
|
||||
@@ -5,5 +5,6 @@
|
||||
<item name="chipStyle">@style/Chip.Filter</item>
|
||||
<item name="preferenceTheme">@style/AppTheme.PreferenceThemeOverlay</item>
|
||||
<item name="android:statusBarColor">@color/colorTransparent</item>
|
||||
<item name="popupMenuStyle">@style/AppTheme.PopupMenu</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -437,6 +437,12 @@
|
||||
<string name="toast_fav_import_success">Favourites imported!</string>
|
||||
<string name="toast_fav_export_success">Favourites exported!</string>
|
||||
|
||||
<!-- BlacklistFragment-->
|
||||
<string name="toast_black_import_failed">Failed to import blacklist!</string>
|
||||
<string name="toast_black_export_failed">Failed to export blacklist!</string>
|
||||
<string name="toast_black_import_success">Blacklist imported!</string>
|
||||
<string name="toast_black_export_success">Blacklist exported!</string>
|
||||
|
||||
<!-- ExportWorker -->
|
||||
<string name="export_app_title">File Exporter</string>
|
||||
<string name="export_app_summary">Hold on, exporting your file</string>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<item name="preferenceTheme">@style/AppTheme.PreferenceThemeOverlay</item>
|
||||
<item name="bottomSheetStyle">@style/AppTheme.BottomSheetStyle</item>
|
||||
<item name="android:statusBarColor">@color/colorTransparent</item>
|
||||
<item name="popupMenuStyle">@style/AppTheme.PopupMenu</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Translucent" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
@@ -47,17 +48,23 @@
|
||||
<item name="widgetLayout">@layout/preference_material_switch</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.PopupMenu" parent="Widget.Material3.PopupMenu">
|
||||
<item name="android:popupBackground">?attr/colorSurfaceContainer</item>
|
||||
<item name="android:textColor">?attr/colorOnSurface</item>
|
||||
<item name="android:elevation">4dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Chip.Filter" parent="@style/Widget.Material3.Chip.Filter" />
|
||||
|
||||
<style name="Chip.TopChart" parent="@style/Widget.Material3.Chip.Filter">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Aurora.Line1</item>
|
||||
<item name="chipStrokeColor">?colorControlHighlight</item>
|
||||
<item name="chipStrokeColor">?attr/colorControlHighlight</item>
|
||||
</style>
|
||||
|
||||
<style name="Chip.Tag" parent="@style/Widget.Material3.Chip.Assist.Elevated">
|
||||
<item name="android:clickable">false</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Aurora.Line2</item>
|
||||
<item name="chipBackgroundColor">?colorControlHighlight</item>
|
||||
<item name="chipBackgroundColor">?attr/colorControlHighlight</item>
|
||||
<item name="chipStrokeWidth">0dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user