mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-06-07 16:29:55 +00:00
- Screenshots are Expanded by Default
- Reformatted Code - Added background to Show More Button - Shortened Tab Indicator...
This commit is contained in:
parent
8c0c48236e
commit
baf9944dc9
File diff suppressed because it is too large
Load Diff
@ -18,26 +18,21 @@ import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import com.looker.droidify.R
|
||||
import com.looker.droidify.content.ProductPreferences
|
||||
import com.looker.droidify.database.Database
|
||||
import com.looker.droidify.entity.InstalledItem
|
||||
import com.looker.droidify.entity.Product
|
||||
import com.looker.droidify.entity.ProductPreference
|
||||
import com.looker.droidify.entity.Release
|
||||
import com.looker.droidify.entity.Repository
|
||||
import com.looker.droidify.entity.*
|
||||
import com.looker.droidify.service.Connection
|
||||
import com.looker.droidify.service.DownloadService
|
||||
import com.looker.droidify.utility.RxUtils
|
||||
import com.looker.droidify.utility.Utils
|
||||
import com.looker.droidify.utility.extension.android.*
|
||||
import com.looker.droidify.widget.DividerItemDecoration
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
||||
class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
class ProductFragment() : ScreenFragment(), ProductAdapter.Callbacks {
|
||||
companion object {
|
||||
private const val EXTRA_PACKAGE_NAME = "packageName"
|
||||
|
||||
@ -45,7 +40,7 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
private const val STATE_ADAPTER = "adapter"
|
||||
}
|
||||
|
||||
constructor(packageName: String): this() {
|
||||
constructor(packageName: String) : this() {
|
||||
arguments = Bundle().apply {
|
||||
putString(EXTRA_PACKAGE_NAME, packageName)
|
||||
}
|
||||
@ -53,7 +48,11 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
|
||||
private class Nullable<T>(val value: T?)
|
||||
|
||||
private enum class Action(val id: Int, val adapterAction: ProductAdapter.Action, val iconResId: Int) {
|
||||
private enum class Action(
|
||||
val id: Int,
|
||||
val adapterAction: ProductAdapter.Action,
|
||||
val iconResId: Int
|
||||
) {
|
||||
INSTALL(1, ProductAdapter.Action.INSTALL, R.drawable.ic_archive),
|
||||
UPDATE(2, ProductAdapter.Action.UPDATE, R.drawable.ic_archive),
|
||||
LAUNCH(3, ProductAdapter.Action.LAUNCH, R.drawable.ic_launch),
|
||||
@ -61,8 +60,10 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
UNINSTALL(5, ProductAdapter.Action.UNINSTALL, R.drawable.ic_delete)
|
||||
}
|
||||
|
||||
private class Installed(val installedItem: InstalledItem, val isSystem: Boolean,
|
||||
val launcherActivities: List<Pair<String, String>>)
|
||||
private class Installed(
|
||||
val installedItem: InstalledItem, val isSystem: Boolean,
|
||||
val launcherActivities: List<Pair<String, String>>
|
||||
)
|
||||
|
||||
val packageName: String
|
||||
get() = requireArguments().getString(EXTRA_PACKAGE_NAME)!!
|
||||
@ -87,7 +88,11 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
downloadDisposable = null
|
||||
})
|
||||
|
||||
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.fragment, container, false)
|
||||
}
|
||||
|
||||
@ -122,15 +127,16 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
isVerticalScrollBarEnabled = false
|
||||
val adapter = ProductAdapter(this@ProductFragment, columns)
|
||||
this.adapter = adapter
|
||||
layoutManager.spanSizeLookup = object: GridLayoutManager.SpanSizeLookup() {
|
||||
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (adapter.requiresGrid(position)) 1 else layoutManager.spanCount
|
||||
}
|
||||
}
|
||||
addOnScrollListener(scrollListener)
|
||||
addItemDecoration(adapter.gridItemDecoration)
|
||||
addItemDecoration(DividerItemDecoration(context, adapter::configureDivider))
|
||||
savedInstanceState?.getParcelable<ProductAdapter.SavedState>(STATE_ADAPTER)?.let(adapter::restoreState)
|
||||
// addItemDecoration(DividerItemDecoration(context, adapter::configureDivider))
|
||||
savedInstanceState?.getParcelable<ProductAdapter.SavedState>(STATE_ADAPTER)
|
||||
?.let(adapter::restoreState)
|
||||
layoutManagerState = savedInstanceState?.getParcelable(STATE_LAYOUT_MANAGER)
|
||||
recyclerView = this
|
||||
}, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
|
||||
@ -140,22 +146,41 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
.concatWith(Database.observable(Database.Subject.Products))
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMapSingle { RxUtils.querySingle { Database.ProductAdapter.get(packageName, it) } }
|
||||
.flatMapSingle { products -> RxUtils
|
||||
.flatMapSingle { products ->
|
||||
RxUtils
|
||||
.querySingle { Database.RepositoryAdapter.getAll(it) }
|
||||
.map { it.asSequence().map { Pair(it.id, it) }.toMap()
|
||||
.let { products.mapNotNull { product -> it[product.repositoryId]?.let { Pair(product, it) } } } } }
|
||||
.flatMapSingle { products -> RxUtils
|
||||
.map { it ->
|
||||
it.asSequence().map { Pair(it.id, it) }.toMap()
|
||||
.let {
|
||||
products.mapNotNull { product ->
|
||||
it[product.repositoryId]?.let {
|
||||
Pair(
|
||||
product,
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.flatMapSingle { products ->
|
||||
RxUtils
|
||||
.querySingle { Nullable(Database.InstalledAdapter.get(packageName, it)) }
|
||||
.map { Pair(products, it) } }
|
||||
.map { Pair(products, it) }
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
.subscribe { it ->
|
||||
val (products, installedItem) = it
|
||||
val firstChanged = first
|
||||
first = false
|
||||
val productChanged = this.products != products
|
||||
val installedItemChanged = this.installed?.installedItem != installedItem.value
|
||||
if (firstChanged || productChanged || installedItemChanged) {
|
||||
layoutManagerState?.let { recyclerView?.layoutManager!!.onRestoreInstanceState(it) }
|
||||
layoutManagerState?.let {
|
||||
recyclerView?.layoutManager!!.onRestoreInstanceState(
|
||||
it
|
||||
)
|
||||
}
|
||||
layoutManagerState = null
|
||||
if (firstChanged || productChanged) {
|
||||
this.products = products
|
||||
@ -163,19 +188,28 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
if (firstChanged || installedItemChanged) {
|
||||
installed = installedItem.value?.let {
|
||||
val isSystem = try {
|
||||
((requireContext().packageManager.getApplicationInfo(packageName, 0).flags)
|
||||
((requireContext().packageManager.getApplicationInfo(
|
||||
packageName,
|
||||
0
|
||||
).flags)
|
||||
and ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
val launcherActivities = if (packageName == requireContext().packageName) {
|
||||
val launcherActivities =
|
||||
if (packageName == requireContext().packageName) {
|
||||
// Don't allow to launch self
|
||||
emptyList()
|
||||
} else {
|
||||
val packageManager = requireContext().packageManager
|
||||
packageManager
|
||||
.queryIntentActivities(Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER), 0)
|
||||
.asSequence().mapNotNull { it.activityInfo }.filter { it.packageName == packageName }
|
||||
.queryIntentActivities(
|
||||
Intent(Intent.ACTION_MAIN).addCategory(
|
||||
Intent.CATEGORY_LAUNCHER
|
||||
), 0
|
||||
)
|
||||
.asSequence().mapNotNull { it.activityInfo }
|
||||
.filter { it.packageName == packageName }
|
||||
.mapNotNull { activityInfo ->
|
||||
val label = try {
|
||||
activityInfo.loadLabel(packageManager).toString()
|
||||
@ -193,7 +227,12 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
val recyclerView = recyclerView!!
|
||||
val adapter = recyclerView.adapter as ProductAdapter
|
||||
if (firstChanged || productChanged || installedItemChanged) {
|
||||
adapter.setProducts(recyclerView.context, packageName, products, installedItem.value)
|
||||
adapter.setProducts(
|
||||
recyclerView.context,
|
||||
packageName,
|
||||
products,
|
||||
installedItem.value
|
||||
)
|
||||
}
|
||||
updateButtons()
|
||||
}
|
||||
@ -218,7 +257,8 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
val layoutManagerState = layoutManagerState ?: recyclerView?.layoutManager?.onSaveInstanceState()
|
||||
val layoutManagerState =
|
||||
layoutManagerState ?: recyclerView?.layoutManager?.onSaveInstanceState()
|
||||
layoutManagerState?.let { outState.putParcelable(STATE_LAYOUT_MANAGER, it) }
|
||||
val adapterState = (recyclerView?.adapter as? ProductAdapter)?.saveState()
|
||||
adapterState?.let { outState.putParcelable(STATE_ADAPTER, it) }
|
||||
@ -234,10 +274,12 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
val compatible = product != null && product.selectedReleases.firstOrNull()
|
||||
.let { it != null && it.incompatibilities.isEmpty() }
|
||||
val canInstall = product != null && installed == null && compatible
|
||||
val canUpdate = product != null && compatible && product.canUpdate(installed?.installedItem) &&
|
||||
val canUpdate =
|
||||
product != null && compatible && product.canUpdate(installed?.installedItem) &&
|
||||
!preference.shouldIgnoreUpdate(product.versionCode)
|
||||
val canUninstall = product != null && installed != null && !installed.isSystem
|
||||
val canLaunch = product != null && installed != null && installed.launcherActivities.isNotEmpty()
|
||||
val canLaunch =
|
||||
product != null && installed != null && installed.launcherActivities.isNotEmpty()
|
||||
|
||||
val actions = mutableSetOf<Action>()
|
||||
if (canInstall) {
|
||||
@ -263,7 +305,8 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
else -> null
|
||||
}
|
||||
|
||||
val adapterAction = if (downloading) ProductAdapter.Action.CANCEL else primaryAction?.adapterAction
|
||||
val adapterAction =
|
||||
if (downloading) ProductAdapter.Action.CANCEL else primaryAction?.adapterAction
|
||||
(recyclerView?.adapter as? ProductAdapter)?.setAction(adapterAction)
|
||||
|
||||
val toolbar = toolbar
|
||||
@ -299,7 +342,10 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
val status = when (state) {
|
||||
is DownloadService.State.Pending -> ProductAdapter.Status.Pending
|
||||
is DownloadService.State.Connecting -> ProductAdapter.Status.Connecting
|
||||
is DownloadService.State.Downloading -> ProductAdapter.Status.Downloading(state.read, state.total)
|
||||
is DownloadService.State.Downloading -> ProductAdapter.Status.Downloading(
|
||||
state.read,
|
||||
state.total
|
||||
)
|
||||
is DownloadService.State.Success, is DownloadService.State.Error, is DownloadService.State.Cancel, null -> null
|
||||
}
|
||||
val downloading = status != null
|
||||
@ -314,11 +360,12 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
}
|
||||
}
|
||||
|
||||
private val scrollListener = object: RecyclerView.OnScrollListener() {
|
||||
private val scrollListener = object : RecyclerView.OnScrollListener() {
|
||||
private var lastPosition = -1
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
val position = (recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
val position =
|
||||
(recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
val lastPosition = lastPosition
|
||||
this.lastPosition = position
|
||||
if ((lastPosition == 0) != (position == 0)) {
|
||||
@ -338,36 +385,48 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
val release = if (compatibleReleases.size >= 2) {
|
||||
compatibleReleases
|
||||
.filter { it.platforms.contains(Android.primaryPlatform) }
|
||||
.minBy { it.platforms.size }
|
||||
?: compatibleReleases.minBy { it.platforms.size }
|
||||
.minByOrNull { it.platforms.size }
|
||||
?: compatibleReleases.minByOrNull { it.platforms.size }
|
||||
?: compatibleReleases.firstOrNull()
|
||||
} else {
|
||||
compatibleReleases.firstOrNull()
|
||||
}
|
||||
val binder = downloadConnection.binder
|
||||
if (productRepository != null && release != null && binder != null) {
|
||||
binder.enqueue(packageName, productRepository.first.name, productRepository.second, release)
|
||||
binder.enqueue(
|
||||
packageName,
|
||||
productRepository.first.name,
|
||||
productRepository.second,
|
||||
release
|
||||
)
|
||||
}
|
||||
Unit
|
||||
}
|
||||
ProductAdapter.Action.LAUNCH -> {
|
||||
val launcherActivities = installed?.launcherActivities.orEmpty()
|
||||
if (launcherActivities.size >= 2) {
|
||||
LaunchDialog(launcherActivities).show(childFragmentManager, LaunchDialog::class.java.name)
|
||||
LaunchDialog(launcherActivities).show(
|
||||
childFragmentManager,
|
||||
LaunchDialog::class.java.name
|
||||
)
|
||||
} else {
|
||||
launcherActivities.firstOrNull()?.let { startLauncherActivity(it.first) }
|
||||
}
|
||||
Unit
|
||||
}
|
||||
ProductAdapter.Action.DETAILS -> {
|
||||
startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.setData(Uri.parse("package:$packageName")))
|
||||
startActivity(
|
||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.setData(Uri.parse("package:$packageName"))
|
||||
)
|
||||
}
|
||||
ProductAdapter.Action.UNINSTALL -> {
|
||||
// TODO Handle deprecation
|
||||
@Suppress("DEPRECATION")
|
||||
startActivity(Intent(Intent.ACTION_UNINSTALL_PACKAGE)
|
||||
.setData(Uri.parse("package:$packageName")))
|
||||
startActivity(
|
||||
Intent(Intent.ACTION_UNINSTALL_PACKAGE)
|
||||
.setData(Uri.parse("package:$packageName"))
|
||||
)
|
||||
}
|
||||
ProductAdapter.Action.CANCEL -> {
|
||||
val binder = downloadConnection.binder
|
||||
@ -381,10 +440,12 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
|
||||
private fun startLauncherActivity(name: String) {
|
||||
try {
|
||||
startActivity(Intent(Intent.ACTION_MAIN)
|
||||
startActivity(
|
||||
Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
.setComponent(ComponentName(packageName, name))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
@ -395,17 +456,26 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
}
|
||||
|
||||
override fun onPermissionsClick(group: String?, permissions: List<String>) {
|
||||
MessageDialog(MessageDialog.Message.Permissions(group, permissions)).show(childFragmentManager)
|
||||
MessageDialog(MessageDialog.Message.Permissions(group, permissions)).show(
|
||||
childFragmentManager
|
||||
)
|
||||
}
|
||||
|
||||
override fun onScreenshotClick(screenshot: Product.Screenshot) {
|
||||
val pair = products.asSequence()
|
||||
.map { Pair(it.second, it.first.screenshots.find { it === screenshot }?.identifier) }
|
||||
.map { it ->
|
||||
Pair(
|
||||
it.second,
|
||||
it.first.screenshots.find { it === screenshot }?.identifier
|
||||
)
|
||||
}
|
||||
.filter { it.second != null }.firstOrNull()
|
||||
if (pair != null) {
|
||||
val (repository, identifier) = pair
|
||||
if (identifier != null) {
|
||||
ScreenshotsFragment(packageName, repository.id, identifier).show(childFragmentManager)
|
||||
ScreenshotsFragment(packageName, repository.id, identifier).show(
|
||||
childFragmentManager
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -414,20 +484,30 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
val installedItem = installed?.installedItem
|
||||
when {
|
||||
release.incompatibilities.isNotEmpty() -> {
|
||||
MessageDialog(MessageDialog.Message.ReleaseIncompatible(release.incompatibilities,
|
||||
release.platforms, release.minSdkVersion, release.maxSdkVersion)).show(childFragmentManager)
|
||||
MessageDialog(
|
||||
MessageDialog.Message.ReleaseIncompatible(
|
||||
release.incompatibilities,
|
||||
release.platforms, release.minSdkVersion, release.maxSdkVersion
|
||||
)
|
||||
).show(childFragmentManager)
|
||||
}
|
||||
installedItem != null && installedItem.versionCode > release.versionCode -> {
|
||||
MessageDialog(MessageDialog.Message.ReleaseOlder).show(childFragmentManager)
|
||||
}
|
||||
installedItem != null && installedItem.signature != release.signature -> {
|
||||
MessageDialog(MessageDialog.Message.ReleaseSignatureMismatch).show(childFragmentManager)
|
||||
MessageDialog(MessageDialog.Message.ReleaseSignatureMismatch).show(
|
||||
childFragmentManager
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
val productRepository = products.asSequence().filter { it.first.releases.any { it === release } }.firstOrNull()
|
||||
val productRepository =
|
||||
products.asSequence().filter { it -> it.first.releases.any { it === release } }
|
||||
.firstOrNull()
|
||||
if (productRepository != null) {
|
||||
downloadConnection.binder?.enqueue(packageName, productRepository.first.name,
|
||||
productRepository.second, release)
|
||||
downloadConnection.binder?.enqueue(
|
||||
packageName, productRepository.first.name,
|
||||
productRepository.second, release
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -448,13 +528,13 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
}
|
||||
}
|
||||
|
||||
class LaunchDialog(): DialogFragment() {
|
||||
class LaunchDialog() : DialogFragment() {
|
||||
companion object {
|
||||
private const val EXTRA_NAMES = "names"
|
||||
private const val EXTRA_LABELS = "labels"
|
||||
}
|
||||
|
||||
constructor(launcherActivities: List<Pair<String, String>>): this() {
|
||||
constructor(launcherActivities: List<Pair<String, String>>) : this() {
|
||||
arguments = Bundle().apply {
|
||||
putStringArrayList(EXTRA_NAMES, ArrayList(launcherActivities.map { it.first }))
|
||||
putStringArrayList(EXTRA_LABELS, ArrayList(launcherActivities.map { it.second }))
|
||||
@ -466,8 +546,10 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
|
||||
val labels = requireArguments().getStringArrayList(EXTRA_LABELS)!!
|
||||
return AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.launch)
|
||||
.setItems(labels.toTypedArray()) { _, position -> (parentFragment as ProductFragment)
|
||||
.startLauncherActivity(names[position]) }
|
||||
.setItems(labels.toTypedArray()) { _, position ->
|
||||
(parentFragment as ProductFragment)
|
||||
.startLauncherActivity(names[position])
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
@ -18,15 +18,14 @@ import com.looker.droidify.entity.Repository
|
||||
import com.looker.droidify.network.PicassoDownloader
|
||||
import com.looker.droidify.utility.Utils
|
||||
import com.looker.droidify.utility.extension.resources.*
|
||||
import com.looker.droidify.utility.extension.text.*
|
||||
import com.looker.droidify.utility.extension.text.nullIfEmpty
|
||||
import com.looker.droidify.widget.CursorRecyclerAdapter
|
||||
import com.looker.droidify.widget.DividerItemDecoration
|
||||
|
||||
class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
class ProductsAdapter(private val onClick: (ProductItem) -> Unit) :
|
||||
CursorRecyclerAdapter<ProductsAdapter.ViewType, RecyclerView.ViewHolder>() {
|
||||
enum class ViewType { PRODUCT, LOADING, EMPTY }
|
||||
|
||||
private class ProductViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||
private class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val name = itemView.findViewById<TextView>(R.id.name)!!
|
||||
val status = itemView.findViewById<TextView>(R.id.status)!!
|
||||
val summary = itemView.findViewById<TextView>(R.id.summary)!!
|
||||
@ -42,18 +41,23 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadingViewHolder(context: Context): RecyclerView.ViewHolder(FrameLayout(context)) {
|
||||
private class LoadingViewHolder(context: Context) :
|
||||
RecyclerView.ViewHolder(FrameLayout(context)) {
|
||||
init {
|
||||
itemView as FrameLayout
|
||||
val progressBar = ProgressBar(itemView.context)
|
||||
itemView.addView(progressBar, FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.CENTER })
|
||||
itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
|
||||
RecyclerView.LayoutParams.MATCH_PARENT)
|
||||
itemView.addView(progressBar, FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply { gravity = Gravity.CENTER })
|
||||
itemView.layoutParams = RecyclerView.LayoutParams(
|
||||
RecyclerView.LayoutParams.MATCH_PARENT,
|
||||
RecyclerView.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
|
||||
private class EmptyViewHolder(context: Context) : RecyclerView.ViewHolder(TextView(context)) {
|
||||
val text: TextView
|
||||
get() = itemView as TextView
|
||||
|
||||
@ -64,22 +68,10 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
itemView.typeface = TypefaceExtra.light
|
||||
itemView.setTextColor(context.getColorFromAttr(android.R.attr.textColorPrimary))
|
||||
itemView.setTextSizeScaled(20)
|
||||
itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
|
||||
RecyclerView.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
}
|
||||
|
||||
fun configureDivider(context: Context, position: Int, configuration: DividerItemDecoration.Configuration) {
|
||||
val currentItem = if (getItemEnumViewType(position) == ViewType.PRODUCT) getProductItem(position) else null
|
||||
val nextItem = if (position + 1 < itemCount && getItemEnumViewType(position + 1) == ViewType.PRODUCT)
|
||||
getProductItem(position + 1) else null
|
||||
when {
|
||||
currentItem != null && nextItem != null && currentItem.matchRank != nextItem.matchRank -> {
|
||||
configuration.set(true, false, 0, 0)
|
||||
}
|
||||
else -> {
|
||||
configuration.set(true, false, context.resources.sizeScaled(72), 0)
|
||||
}
|
||||
itemView.layoutParams = RecyclerView.LayoutParams(
|
||||
RecyclerView.LayoutParams.MATCH_PARENT,
|
||||
RecyclerView.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +112,10 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
return Database.ProductAdapter.transformItem(moveTo(position))
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: ViewType): RecyclerView.ViewHolder {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: ViewType
|
||||
): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ViewType.PRODUCT -> ProductViewHolder(parent.inflate(R.layout.product_item)).apply {
|
||||
itemView.setOnClickListener { onClick(getProductItem(adapterPosition)) }
|
||||
@ -136,12 +131,18 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
holder as ProductViewHolder
|
||||
val productItem = getProductItem(position)
|
||||
holder.name.text = productItem.name
|
||||
holder.summary.text = if (productItem.name == productItem.summary) "" else productItem.summary
|
||||
holder.summary.visibility = if (holder.summary.text.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
holder.summary.text =
|
||||
if (productItem.name == productItem.summary) "" else productItem.summary
|
||||
holder.summary.visibility =
|
||||
if (holder.summary.text.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
val repository: Repository? = repositories[productItem.repositoryId]
|
||||
if ((productItem.icon.isNotEmpty() || productItem.metadataIcon.isNotEmpty()) && repository != null) {
|
||||
holder.icon.load(PicassoDownloader.createIconUri(holder.icon, productItem.packageName,
|
||||
productItem.icon, productItem.metadataIcon, repository)) {
|
||||
holder.icon.load(
|
||||
PicassoDownloader.createIconUri(
|
||||
holder.icon, productItem.packageName,
|
||||
productItem.icon, productItem.metadataIcon, repository
|
||||
)
|
||||
) {
|
||||
placeholder(holder.progressIcon)
|
||||
error(holder.defaultIcon)
|
||||
}
|
||||
@ -155,8 +156,12 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
if (background == null) {
|
||||
resources.sizeScaled(4).let { setPadding(it, 0, it, 0) }
|
||||
setTextColor(holder.status.context.getColorFromAttr(android.R.attr.colorBackground))
|
||||
background = GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, null).apply {
|
||||
color = holder.status.context.getColorFromAttr(android.R.attr.colorAccent)
|
||||
background = GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
null
|
||||
).apply {
|
||||
color =
|
||||
holder.status.context.getColorFromAttr(android.R.attr.colorAccent)
|
||||
cornerRadius = holder.status.resources.sizeScaled(2).toFloat()
|
||||
}
|
||||
}
|
||||
@ -170,7 +175,9 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
|
||||
}
|
||||
}
|
||||
val enabled = productItem.compatible || productItem.installedVersion.isNotEmpty()
|
||||
sequenceOf(holder.name, holder.status, holder.summary).forEach { it.isEnabled = enabled }
|
||||
sequenceOf(holder.name, holder.status, holder.summary).forEach {
|
||||
it.isEnabled = enabled
|
||||
}
|
||||
}
|
||||
ViewType.LOADING -> {
|
||||
// Do nothing
|
||||
|
@ -17,7 +17,6 @@ import com.looker.droidify.database.CursorOwner
|
||||
import com.looker.droidify.database.Database
|
||||
import com.looker.droidify.entity.ProductItem
|
||||
import com.looker.droidify.utility.RxUtils
|
||||
import com.looker.droidify.widget.DividerItemDecoration
|
||||
import com.looker.droidify.widget.RecyclerFastScroller
|
||||
|
||||
class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
||||
@ -80,7 +79,6 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
||||
recycledViewPool.setMaxRecycledViews(ProductsAdapter.ViewType.PRODUCT.ordinal, 30)
|
||||
val adapter = ProductsAdapter { screenActivity.navigateProduct(it.packageName) }
|
||||
this.adapter = adapter
|
||||
addItemDecoration(DividerItemDecoration(context, adapter::configureDivider))
|
||||
RecyclerFastScroller(this)
|
||||
recyclerView = this
|
||||
}
|
||||
|
@ -9,28 +9,15 @@ import android.graphics.Paint
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.*
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.SearchView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toolbar
|
||||
import android.widget.*
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import com.looker.droidify.R
|
||||
import com.looker.droidify.content.Preferences
|
||||
import com.looker.droidify.database.Database
|
||||
@ -44,9 +31,13 @@ import com.looker.droidify.utility.extension.resources.*
|
||||
import com.looker.droidify.widget.DividerItemDecoration
|
||||
import com.looker.droidify.widget.FocusSearchView
|
||||
import com.looker.droidify.widget.StableRecyclerAdapter
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlin.math.*
|
||||
|
||||
class TabsFragment: ScreenFragment() {
|
||||
class TabsFragment : ScreenFragment() {
|
||||
companion object {
|
||||
private const val STATE_SEARCH_FOCUSED = "searchFocused"
|
||||
private const val STATE_SEARCH_QUERY = "searchQuery"
|
||||
@ -75,8 +66,10 @@ class TabsFragment: ScreenFragment() {
|
||||
if (field != value) {
|
||||
field = value
|
||||
val layout = layout
|
||||
layout?.tabs?.let { (0 until it.childCount)
|
||||
.forEach { index -> it.getChildAt(index)!!.isEnabled = !value } }
|
||||
layout?.tabs?.let {
|
||||
(0 until it.childCount)
|
||||
.forEach { index -> it.getChildAt(index)!!.isEnabled = !value }
|
||||
}
|
||||
layout?.sectionIcon?.scaleY = if (value) -1f else 1f
|
||||
if ((sectionsList?.parent as? View)?.height ?: 0 > 0) {
|
||||
animateSectionsList()
|
||||
@ -106,7 +99,11 @@ class TabsFragment: ScreenFragment() {
|
||||
get() = if (host == null) emptySequence() else
|
||||
childFragmentManager.fragments.asSequence().mapNotNull { it as? ProductsFragment }
|
||||
|
||||
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.fragment, container, false)
|
||||
}
|
||||
|
||||
@ -125,7 +122,7 @@ class TabsFragment: ScreenFragment() {
|
||||
searchView.allowFocus = savedInstanceState?.getBoolean(STATE_SEARCH_FOCUSED) == true
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
searchView.queryHint = getString(R.string.search)
|
||||
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener {
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
searchView.clearFocus()
|
||||
return true
|
||||
@ -155,12 +152,14 @@ class TabsFragment: ScreenFragment() {
|
||||
.let { menu ->
|
||||
menu.item.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||
val items = Preferences.Key.SortOrder.default.value.values
|
||||
.map { sortOrder -> menu
|
||||
.map { sortOrder ->
|
||||
menu
|
||||
.add(sortOrder.order.titleResId)
|
||||
.setOnMenuItemClickListener {
|
||||
Preferences[Preferences.Key.SortOrder] = sortOrder
|
||||
true
|
||||
} }
|
||||
}
|
||||
}
|
||||
menu.setGroupCheckable(0, true, true)
|
||||
Pair(menu.item, items)
|
||||
}
|
||||
@ -193,20 +192,29 @@ class TabsFragment: ScreenFragment() {
|
||||
val layout = Layout(view)
|
||||
this.layout = layout
|
||||
|
||||
layout.tabs.background = TabsBackgroundDrawable(layout.tabs.context,
|
||||
layout.tabs.layoutDirection == View.LAYOUT_DIRECTION_RTL)
|
||||
layout.tabs.background = TabsBackgroundDrawable(
|
||||
layout.tabs.context,
|
||||
layout.tabs.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
)
|
||||
ProductsFragment.Source.values().forEach {
|
||||
val tab = TextView(layout.tabs.context)
|
||||
val selectedColor = tab.context.getColorFromAttr(android.R.attr.textColorPrimary).defaultColor
|
||||
val normalColor = tab.context.getColorFromAttr(android.R.attr.textColorSecondary).defaultColor
|
||||
val selectedColor =
|
||||
tab.context.getColorFromAttr(android.R.attr.textColorPrimary).defaultColor
|
||||
val normalColor =
|
||||
tab.context.getColorFromAttr(android.R.attr.textColorSecondary).defaultColor
|
||||
tab.gravity = Gravity.CENTER
|
||||
tab.typeface = TypefaceExtra.medium
|
||||
tab.setTextColor(ColorStateList(arrayOf(intArrayOf(android.R.attr.state_selected), intArrayOf()),
|
||||
intArrayOf(selectedColor, normalColor)))
|
||||
tab.setTextColor(
|
||||
ColorStateList(
|
||||
arrayOf(intArrayOf(android.R.attr.state_selected), intArrayOf()),
|
||||
intArrayOf(selectedColor, normalColor)
|
||||
)
|
||||
)
|
||||
tab.setTextSizeScaled(14)
|
||||
tab.isAllCaps = true
|
||||
tab.text = getString(it.titleResId)
|
||||
tab.background = tab.context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
|
||||
tab.background =
|
||||
tab.context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
|
||||
tab.setOnClickListener { _ ->
|
||||
setSelectedTab(it)
|
||||
viewPager!!.setCurrentItem(it.ordinal, Utils.areAnimationsEnabled(tab.context))
|
||||
@ -216,10 +224,13 @@ class TabsFragment: ScreenFragment() {
|
||||
}
|
||||
|
||||
showSections = savedInstanceState?.getByte(STATE_SHOW_SECTIONS)?.toInt() ?: 0 != 0
|
||||
sections = savedInstanceState?.getParcelableArrayList<ProductItem.Section>(STATE_SECTIONS).orEmpty()
|
||||
sections = savedInstanceState?.getParcelableArrayList<ProductItem.Section>(STATE_SECTIONS)
|
||||
.orEmpty()
|
||||
section = savedInstanceState?.getParcelable(STATE_SECTION) ?: ProductItem.Section.All
|
||||
layout.sectionChange.setOnClickListener { showSections = sections
|
||||
.any { it !is ProductItem.Section.All } && !showSections }
|
||||
layout.sectionChange.setOnClickListener {
|
||||
showSections = sections
|
||||
.any { it !is ProductItem.Section.All } && !showSections
|
||||
}
|
||||
|
||||
updateOrder()
|
||||
sortOrderDisposable = Preferences.observable.subscribe {
|
||||
@ -232,12 +243,18 @@ class TabsFragment: ScreenFragment() {
|
||||
|
||||
viewPager = ViewPager2(content.context).apply {
|
||||
id = R.id.fragment_pager
|
||||
adapter = object: FragmentStateAdapter(this@TabsFragment) {
|
||||
adapter = object : FragmentStateAdapter(this@TabsFragment) {
|
||||
override fun getItemCount(): Int = ProductsFragment.Source.values().size
|
||||
override fun createFragment(position: Int): Fragment = ProductsFragment(ProductsFragment
|
||||
.Source.values()[position])
|
||||
override fun createFragment(position: Int): Fragment = ProductsFragment(
|
||||
ProductsFragment
|
||||
.Source.values()[position]
|
||||
)
|
||||
}
|
||||
content.addView(this, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
|
||||
content.addView(
|
||||
this,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
registerOnPageChangeCallback(pageChangeCallback)
|
||||
offscreenPageLimit = 1
|
||||
}
|
||||
@ -247,15 +264,21 @@ class TabsFragment: ScreenFragment() {
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMapSingle { RxUtils.querySingle { Database.CategoryAdapter.getAll(it) } }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { setSectionsAndUpdate(it.asSequence().sorted()
|
||||
.map(ProductItem.Section::Category).toList(), null) }
|
||||
.subscribe {
|
||||
setSectionsAndUpdate(
|
||||
it.asSequence().sorted()
|
||||
.map(ProductItem.Section::Category).toList(), null
|
||||
)
|
||||
}
|
||||
repositoriesDisposable = Observable.just(Unit)
|
||||
.concatWith(Database.observable(Database.Subject.Repositories))
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMapSingle { RxUtils.querySingle { Database.RepositoryAdapter.getAll(it) } }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { setSectionsAndUpdate(null, it.asSequence().filter { it.enabled }
|
||||
.map { ProductItem.Section.Repository(it.id, it.name) }.toList()) }
|
||||
.subscribe { it ->
|
||||
setSectionsAndUpdate(null, it.asSequence().filter { it.enabled }
|
||||
.map { ProductItem.Section.Repository(it.id, it.name) }.toList())
|
||||
}
|
||||
updateSection()
|
||||
|
||||
val sectionsList = RecyclerView(toolbar.context).apply {
|
||||
@ -273,7 +296,7 @@ class TabsFragment: ScreenFragment() {
|
||||
}
|
||||
this.adapter = adapter
|
||||
addItemDecoration(DividerItemDecoration(context, adapter::configureDivider))
|
||||
setBackgroundColor(context.getColorFromAttr(android.R.attr.colorPrimaryDark).defaultColor)
|
||||
setBackgroundResource(R.drawable.background_border)
|
||||
elevation = resources.sizeScaled(4).toFloat()
|
||||
content.addView(this, FrameLayout.LayoutParams.MATCH_PARENT, 0)
|
||||
visibility = View.GONE
|
||||
@ -368,7 +391,9 @@ class TabsFragment: ScreenFragment() {
|
||||
|
||||
private fun setSelectedTab(source: ProductsFragment.Source) {
|
||||
val layout = layout!!
|
||||
(0 until layout.tabs.childCount).forEach { layout.tabs.getChildAt(it).isSelected = it == source.ordinal }
|
||||
(0 until layout.tabs.childCount).forEach {
|
||||
layout.tabs.getChildAt(it).isSelected = it == source.ordinal
|
||||
}
|
||||
}
|
||||
|
||||
internal fun selectUpdates() = selectUpdatesInternal(true)
|
||||
@ -376,7 +401,10 @@ class TabsFragment: ScreenFragment() {
|
||||
private fun selectUpdatesInternal(allowSmooth: Boolean) {
|
||||
if (view != null) {
|
||||
val viewPager = viewPager
|
||||
viewPager?.setCurrentItem(ProductsFragment.Source.UPDATES.ordinal, allowSmooth && viewPager.isLaidOut)
|
||||
viewPager?.setCurrentItem(
|
||||
ProductsFragment.Source.UPDATES.ordinal,
|
||||
allowSmooth && viewPager.isLaidOut
|
||||
)
|
||||
} else {
|
||||
needSelectUpdates = true
|
||||
}
|
||||
@ -397,13 +425,15 @@ class TabsFragment: ScreenFragment() {
|
||||
productFragments.forEach { it.setOrder(order) }
|
||||
}
|
||||
|
||||
private inline fun <reified T: ProductItem.Section> collectOldSections(list: List<T>?): List<T>? {
|
||||
private inline fun <reified T : ProductItem.Section> collectOldSections(list: List<T>?): List<T>? {
|
||||
val oldList = sections.mapNotNull { it as? T }
|
||||
return if (list == null || oldList == list) oldList else null
|
||||
}
|
||||
|
||||
private fun setSectionsAndUpdate(categories: List<ProductItem.Section.Category>?,
|
||||
repositories: List<ProductItem.Section.Repository>?) {
|
||||
private fun setSectionsAndUpdate(
|
||||
categories: List<ProductItem.Section.Category>?,
|
||||
repositories: List<ProductItem.Section.Repository>?
|
||||
) {
|
||||
val oldCategories = collectOldSections(categories)
|
||||
val oldRepositories = collectOldSections(repositories)
|
||||
if (oldCategories == null || oldRepositories == null) {
|
||||
@ -423,7 +453,8 @@ class TabsFragment: ScreenFragment() {
|
||||
is ProductItem.Section.Category -> section.name
|
||||
is ProductItem.Section.Repository -> section.name
|
||||
}
|
||||
layout?.sectionIcon?.visibility = if (sections.any { it !is ProductItem.Section.All }) View.VISIBLE else View.GONE
|
||||
layout?.sectionIcon?.visibility =
|
||||
if (sections.any { it !is ProductItem.Section.All }) View.VISIBLE else View.GONE
|
||||
productFragments.forEach { it.setSection(section) }
|
||||
sectionsList?.adapter?.notifyDataSetChanged()
|
||||
}
|
||||
@ -439,7 +470,8 @@ class TabsFragment: ScreenFragment() {
|
||||
if (value != target) {
|
||||
sectionsAnimator = ValueAnimator.ofFloat(value, target).apply {
|
||||
duration = (250 * abs(target - value)).toLong()
|
||||
interpolator = if (target >= 1f) AccelerateInterpolator(2f) else DecelerateInterpolator(2f)
|
||||
interpolator =
|
||||
if (target >= 1f) AccelerateInterpolator(2f) else DecelerateInterpolator(2f)
|
||||
addUpdateListener {
|
||||
val newValue = animatedValue as Float
|
||||
sectionsList.apply {
|
||||
@ -462,8 +494,12 @@ class TabsFragment: ScreenFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private val pageChangeCallback = object: ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
private val pageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageScrolled(
|
||||
position: Int,
|
||||
positionOffset: Float,
|
||||
positionOffsetPixels: Int
|
||||
) {
|
||||
val layout = layout!!
|
||||
val fromSections = ProductsFragment.Source.values()[position].sections
|
||||
val toSections = if (positionOffset <= 0f) fromSections else
|
||||
@ -490,8 +526,11 @@ class TabsFragment: ScreenFragment() {
|
||||
val source = ProductsFragment.Source.values()[position]
|
||||
updateUpdateNotificationBlocker(source)
|
||||
sortOrderMenu!!.first.isVisible = source.order
|
||||
syncRepositoriesMenuItem!!.setShowAsActionFlags(if (!source.order ||
|
||||
resources.configuration.screenWidthDp >= 400) MenuItem.SHOW_AS_ACTION_ALWAYS else 0)
|
||||
syncRepositoriesMenuItem!!.setShowAsActionFlags(
|
||||
if (!source.order ||
|
||||
resources.configuration.screenWidthDp >= 400
|
||||
) MenuItem.SHOW_AS_ACTION_ALWAYS else 0
|
||||
)
|
||||
setSelectedTab(source)
|
||||
if (showSections && !source.sections) {
|
||||
showSections = false
|
||||
@ -500,7 +539,8 @@ class TabsFragment: ScreenFragment() {
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
val source = ProductsFragment.Source.values()[viewPager!!.currentItem]
|
||||
layout!!.sectionChange.isEnabled = state != ViewPager2.SCROLL_STATE_DRAGGING && source.sections
|
||||
layout!!.sectionChange.isEnabled =
|
||||
state != ViewPager2.SCROLL_STATE_DRAGGING && source.sections
|
||||
if (state == ViewPager2.SCROLL_STATE_IDLE) {
|
||||
// onPageSelected can be called earlier than fragments created
|
||||
updateUpdateNotificationBlocker(source)
|
||||
@ -508,10 +548,10 @@ class TabsFragment: ScreenFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private class TabsBackgroundDrawable(context: Context, private val rtl: Boolean): Drawable() {
|
||||
private class TabsBackgroundDrawable(context: Context, private val rtl: Boolean) : Drawable() {
|
||||
private val height = context.resources.sizeScaled(2)
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = context.getColorFromAttr(android.R.attr.colorPrimary).defaultColor
|
||||
color = context.getColorFromAttr(android.R.attr.textColor).defaultColor
|
||||
}
|
||||
|
||||
private var position = 0f
|
||||
@ -529,11 +569,18 @@ class TabsFragment: ScreenFragment() {
|
||||
val width = bounds.width() / total.toFloat()
|
||||
val x = width * position
|
||||
if (rtl) {
|
||||
canvas.drawRect(bounds.right - width - x, (bounds.bottom - height).toFloat(),
|
||||
bounds.right - x, bounds.bottom.toFloat(), paint)
|
||||
canvas.drawRect(
|
||||
bounds.right - width - x, (bounds.bottom - height).toFloat(),
|
||||
bounds.right - x, bounds.bottom.toFloat(), paint
|
||||
)
|
||||
} else {
|
||||
canvas.drawRect(bounds.left + x, (bounds.bottom - height).toFloat(),
|
||||
bounds.left + x + width, bounds.bottom.toFloat(), paint)
|
||||
canvas.drawRect(
|
||||
bounds.left + x + width / 4, // + width/4 from start
|
||||
(bounds.bottom - height).toFloat(),
|
||||
bounds.left + x + width - width / 4, // - width/4 from end
|
||||
bounds.bottom.toFloat(),
|
||||
paint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -543,12 +590,15 @@ class TabsFragment: ScreenFragment() {
|
||||
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
|
||||
}
|
||||
|
||||
private class SectionsAdapter(private val sections: () -> List<ProductItem.Section>,
|
||||
private val onClick: (ProductItem.Section) -> Unit): StableRecyclerAdapter<SectionsAdapter.ViewType,
|
||||
private class SectionsAdapter(
|
||||
private val sections: () -> List<ProductItem.Section>,
|
||||
private val onClick: (ProductItem.Section) -> Unit
|
||||
) : StableRecyclerAdapter<SectionsAdapter.ViewType,
|
||||
RecyclerView.ViewHolder>() {
|
||||
enum class ViewType { SECTION }
|
||||
|
||||
private class SectionViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
|
||||
private class SectionViewHolder(context: Context) :
|
||||
RecyclerView.ViewHolder(TextView(context)) {
|
||||
val title: TextView
|
||||
get() = itemView as TextView
|
||||
|
||||
@ -556,24 +606,41 @@ class TabsFragment: ScreenFragment() {
|
||||
itemView as TextView
|
||||
itemView.gravity = Gravity.CENTER_VERTICAL
|
||||
itemView.resources.sizeScaled(16).let { itemView.setPadding(it, 0, it, 0) }
|
||||
itemView.setTextColor(context.getColorFromAttr(android.R.attr.textColorPrimary))
|
||||
itemView.setTextColor(context.getColorFromAttr(android.R.attr.textColor))
|
||||
itemView.setTextSizeScaled(16)
|
||||
itemView.background = context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
|
||||
itemView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT,
|
||||
itemView.resources.sizeScaled(48))
|
||||
itemView.background =
|
||||
context.getDrawableFromAttr(android.R.attr.selectableItemBackground)
|
||||
itemView.layoutParams = RecyclerView.LayoutParams(
|
||||
RecyclerView.LayoutParams.MATCH_PARENT,
|
||||
itemView.resources.sizeScaled(48)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun configureDivider(context: Context, position: Int, configuration: DividerItemDecoration.Configuration) {
|
||||
fun configureDivider(
|
||||
context: Context,
|
||||
position: Int,
|
||||
configuration: DividerItemDecoration.Configuration
|
||||
) {
|
||||
val currentSection = sections()[position]
|
||||
val nextSection = sections().getOrNull(position + 1)
|
||||
when {
|
||||
nextSection != null && currentSection.javaClass != nextSection.javaClass -> {
|
||||
val padding = context.resources.sizeScaled(16)
|
||||
configuration.set(true, false, padding, padding)
|
||||
configuration.set(
|
||||
needDivider = true,
|
||||
toTop = false,
|
||||
paddingStart = padding,
|
||||
paddingEnd = padding
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
configuration.set(false, false, 0, 0)
|
||||
configuration.set(
|
||||
needDivider = false,
|
||||
toTop = false,
|
||||
paddingStart = 0,
|
||||
paddingEnd = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -585,7 +652,10 @@ class TabsFragment: ScreenFragment() {
|
||||
override fun getItemDescriptor(position: Int): String = sections()[position].toString()
|
||||
override fun getItemEnumViewType(position: Int): ViewType = ViewType.SECTION
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: ViewType): RecyclerView.ViewHolder {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: ViewType
|
||||
): RecyclerView.ViewHolder {
|
||||
return SectionViewHolder(parent.context).apply {
|
||||
itemView.setOnClickListener { onClick(sections()[adapterPosition]) }
|
||||
}
|
||||
@ -599,9 +669,11 @@ class TabsFragment: ScreenFragment() {
|
||||
val margin = holder.itemView.resources.sizeScaled(8)
|
||||
val layoutParams = holder.itemView.layoutParams as RecyclerView.LayoutParams
|
||||
layoutParams.topMargin = if (previousSection == null ||
|
||||
section.javaClass != previousSection.javaClass) margin else 0
|
||||
section.javaClass != previousSection.javaClass
|
||||
) margin else 0
|
||||
layoutParams.bottomMargin = if (nextSection == null ||
|
||||
section.javaClass != nextSection.javaClass) margin else 0
|
||||
section.javaClass != nextSection.javaClass
|
||||
) margin else 0
|
||||
holder.title.text = when (section) {
|
||||
is ProductItem.Section.All -> holder.itemView.resources.getString(R.string.all_applications)
|
||||
is ProductItem.Section.Category -> section.name
|
||||
|
Loading…
x
Reference in New Issue
Block a user