mirror of
https://github.com/Michatec/michas-droid.git
synced 2026-05-30 18:02:43 +02:00
Allow to view packages per repository
This commit is contained in:
@@ -11,19 +11,19 @@ class CursorOwner: Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
|
|||||||
sealed class Request {
|
sealed class Request {
|
||||||
internal abstract val id: Int
|
internal abstract val id: Int
|
||||||
|
|
||||||
data class ProductsAvailable(val searchQuery: String, val category: String,
|
data class ProductsAvailable(val searchQuery: String, val section: ProductItem.Section,
|
||||||
val order: ProductItem.Order): Request() {
|
val order: ProductItem.Order): Request() {
|
||||||
override val id: Int
|
override val id: Int
|
||||||
get() = 1
|
get() = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ProductsInstalled(val searchQuery: String, val category: String,
|
data class ProductsInstalled(val searchQuery: String, val section: ProductItem.Section,
|
||||||
val order: ProductItem.Order): Request() {
|
val order: ProductItem.Order): Request() {
|
||||||
override val id: Int
|
override val id: Int
|
||||||
get() = 2
|
get() = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ProductsUpdates(val searchQuery: String, val category: String,
|
data class ProductsUpdates(val searchQuery: String, val section: ProductItem.Section,
|
||||||
val order: ProductItem.Order): Request() {
|
val order: ProductItem.Order): Request() {
|
||||||
override val id: Int
|
override val id: Int
|
||||||
get() = 3
|
get() = 3
|
||||||
@@ -79,11 +79,11 @@ class CursorOwner: Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
|
|||||||
return QueryLoader(requireContext()) {
|
return QueryLoader(requireContext()) {
|
||||||
when (request) {
|
when (request) {
|
||||||
is Request.ProductsAvailable -> Database.ProductAdapter
|
is Request.ProductsAvailable -> Database.ProductAdapter
|
||||||
.query(false, false, request.searchQuery, request.category, request.order, it)
|
.query(false, false, request.searchQuery, request.section, request.order, it)
|
||||||
is Request.ProductsInstalled -> Database.ProductAdapter
|
is Request.ProductsInstalled -> Database.ProductAdapter
|
||||||
.query(true, false, request.searchQuery, request.category, request.order, it)
|
.query(true, false, request.searchQuery, request.section, request.order, it)
|
||||||
is Request.ProductsUpdates -> Database.ProductAdapter
|
is Request.ProductsUpdates -> Database.ProductAdapter
|
||||||
.query(true, true, request.searchQuery, request.category, request.order, it)
|
.query(true, true, request.searchQuery, request.section, request.order, it)
|
||||||
is Request.Repositories -> Database.RepositoryAdapter.query(it)
|
is Request.Repositories -> Database.RepositoryAdapter.query(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ object Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun query(installed: Boolean, updates: Boolean, searchQuery: String,
|
fun query(installed: Boolean, updates: Boolean, searchQuery: String,
|
||||||
category: String, order: ProductItem.Order, signal: CancellationSignal?): Cursor {
|
section: ProductItem.Section, order: ProductItem.Order, signal: CancellationSignal?): Cursor {
|
||||||
val builder = QueryBuilder()
|
val builder = QueryBuilder()
|
||||||
|
|
||||||
val signatureMatches = """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND
|
val signatureMatches = """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND
|
||||||
@@ -434,16 +434,19 @@ object Database {
|
|||||||
}
|
}
|
||||||
builder += """JOIN ${Schema.Installed.name} AS installed
|
builder += """JOIN ${Schema.Installed.name} AS installed
|
||||||
ON product.${Schema.Product.ROW_PACKAGE_NAME} = installed.${Schema.Installed.ROW_PACKAGE_NAME}"""
|
ON product.${Schema.Product.ROW_PACKAGE_NAME} = installed.${Schema.Installed.ROW_PACKAGE_NAME}"""
|
||||||
if (category.isNotEmpty()) {
|
if (section is ProductItem.Section.Category) {
|
||||||
builder += """JOIN ${Schema.Category.name} AS category
|
builder += """JOIN ${Schema.Category.name} AS category
|
||||||
ON product.${Schema.Product.ROW_PACKAGE_NAME} = category.${Schema.Product.ROW_PACKAGE_NAME}"""
|
ON product.${Schema.Product.ROW_PACKAGE_NAME} = category.${Schema.Product.ROW_PACKAGE_NAME}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
builder += """WHERE repository.${Schema.Repository.ROW_ENABLED} != 0 AND
|
builder += """WHERE repository.${Schema.Repository.ROW_ENABLED} != 0 AND
|
||||||
repository.${Schema.Repository.ROW_DELETED} == 0"""
|
repository.${Schema.Repository.ROW_DELETED} == 0"""
|
||||||
if (category.isNotEmpty()) {
|
if (section is ProductItem.Section.Category) {
|
||||||
builder += "AND category.${Schema.Category.ROW_NAME} = ?"
|
builder += "AND category.${Schema.Category.ROW_NAME} = ?"
|
||||||
builder %= category
|
builder %= section.name
|
||||||
|
} else if (section is ProductItem.Section.Repository) {
|
||||||
|
builder += "AND product.${Schema.Product.ROW_REPOSITORY_ID} = ?"
|
||||||
|
builder %= section.id.toString()
|
||||||
}
|
}
|
||||||
if (searchQuery.isNotEmpty()) {
|
if (searchQuery.isNotEmpty()) {
|
||||||
builder += """AND ${Schema.Synthetic.ROW_MATCH_RANK} > 0"""
|
builder += """AND ${Schema.Synthetic.ROW_MATCH_RANK} > 0"""
|
||||||
|
|||||||
@@ -1,13 +1,49 @@
|
|||||||
package nya.kitsunyan.foxydroid.entity
|
package nya.kitsunyan.foxydroid.entity
|
||||||
|
|
||||||
|
import android.os.Parcel
|
||||||
import com.fasterxml.jackson.core.JsonGenerator
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
import com.fasterxml.jackson.core.JsonParser
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
import nya.kitsunyan.foxydroid.R
|
import nya.kitsunyan.foxydroid.R
|
||||||
|
import nya.kitsunyan.foxydroid.utility.KParcelable
|
||||||
import nya.kitsunyan.foxydroid.utility.extension.json.*
|
import nya.kitsunyan.foxydroid.utility.extension.json.*
|
||||||
|
|
||||||
data class ProductItem(val repositoryId: Long, val packageName: String,
|
data class ProductItem(val repositoryId: Long, val packageName: String,
|
||||||
val name: String, val summary: String, val icon: String, val version: String, val installedVersion: String,
|
val name: String, val summary: String, val icon: String, val version: String, val installedVersion: String,
|
||||||
val compatible: Boolean, val canUpdate: Boolean, val matchRank: Int) {
|
val compatible: Boolean, val canUpdate: Boolean, val matchRank: Int) {
|
||||||
|
sealed class Section: KParcelable {
|
||||||
|
object All: Section() {
|
||||||
|
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator { All }
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Category(val name: String): Section() {
|
||||||
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
|
dest.writeString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator {
|
||||||
|
val name = it.readString()!!
|
||||||
|
Category(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Repository(val id: Long, val name: String): Section() {
|
||||||
|
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||||
|
dest.writeLong(id)
|
||||||
|
dest.writeString(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator {
|
||||||
|
val id = it.readLong()
|
||||||
|
val name = it.readString()!!
|
||||||
|
Repository(id, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class Order(val titleResId: Int) {
|
enum class Order(val titleResId: Int) {
|
||||||
NAME(R.string.name),
|
NAME(R.string.name),
|
||||||
DATE_ADDED(R.string.date_added),
|
DATE_ADDED(R.string.date_added),
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
private const val EXTRA_SOURCE = "source"
|
private const val EXTRA_SOURCE = "source"
|
||||||
|
|
||||||
private const val STATE_CURRENT_SEARCH_QUERY = "currentSearchQuery"
|
private const val STATE_CURRENT_SEARCH_QUERY = "currentSearchQuery"
|
||||||
private const val STATE_CURRENT_CATEGORY = "currentCategory"
|
private const val STATE_CURRENT_SECTION = "currentSection"
|
||||||
private const val STATE_CURRENT_ORDER = "currentOrder"
|
private const val STATE_CURRENT_ORDER = "currentOrder"
|
||||||
private const val STATE_LAYOUT_MANAGER = "layoutManager"
|
private const val STATE_LAYOUT_MANAGER = "layoutManager"
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Source(val titleResId: Int, val categories: Boolean, val order: Boolean) {
|
enum class Source(val titleResId: Int, val sections: Boolean, val order: Boolean) {
|
||||||
AVAILABLE(R.string.available, true, true),
|
AVAILABLE(R.string.available, true, true),
|
||||||
INSTALLED(R.string.installed, false, false),
|
INSTALLED(R.string.installed, false, false),
|
||||||
UPDATES(R.string.updates, false, false)
|
UPDATES(R.string.updates, false, false)
|
||||||
@@ -46,11 +46,11 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
get() = requireArguments().getString(EXTRA_SOURCE)!!.let(Source::valueOf)
|
get() = requireArguments().getString(EXTRA_SOURCE)!!.let(Source::valueOf)
|
||||||
|
|
||||||
private var searchQuery = ""
|
private var searchQuery = ""
|
||||||
private var category = ""
|
private var section: ProductItem.Section = ProductItem.Section.All
|
||||||
private var order = ProductItem.Order.NAME
|
private var order = ProductItem.Order.NAME
|
||||||
|
|
||||||
private var currentSearchQuery = ""
|
private var currentSearchQuery = ""
|
||||||
private var currentCategory = ""
|
private var currentSection: ProductItem.Section = ProductItem.Section.All
|
||||||
private var currentOrder = ProductItem.Order.NAME
|
private var currentOrder = ProductItem.Order.NAME
|
||||||
private var layoutManagerState: Parcelable? = null
|
private var layoutManagerState: Parcelable? = null
|
||||||
|
|
||||||
@@ -61,12 +61,12 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
private val request: CursorOwner.Request
|
private val request: CursorOwner.Request
|
||||||
get() {
|
get() {
|
||||||
val searchQuery = searchQuery
|
val searchQuery = searchQuery
|
||||||
val category = if (source.categories) category else ""
|
val section = if (source.sections) section else ProductItem.Section.All
|
||||||
val order = if (source.order) order else ProductItem.Order.NAME
|
val order = if (source.order) order else ProductItem.Order.NAME
|
||||||
return when (source) {
|
return when (source) {
|
||||||
Source.AVAILABLE -> CursorOwner.Request.ProductsAvailable(searchQuery, category, order)
|
Source.AVAILABLE -> CursorOwner.Request.ProductsAvailable(searchQuery, section, order)
|
||||||
Source.INSTALLED -> CursorOwner.Request.ProductsInstalled(searchQuery, category, order)
|
Source.INSTALLED -> CursorOwner.Request.ProductsInstalled(searchQuery, section, order)
|
||||||
Source.UPDATES -> CursorOwner.Request.ProductsUpdates(searchQuery, category, order)
|
Source.UPDATES -> CursorOwner.Request.ProductsUpdates(searchQuery, section, order)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
currentSearchQuery = savedInstanceState?.getString(STATE_CURRENT_SEARCH_QUERY).orEmpty()
|
currentSearchQuery = savedInstanceState?.getString(STATE_CURRENT_SEARCH_QUERY).orEmpty()
|
||||||
currentCategory = savedInstanceState?.getString(STATE_CURRENT_CATEGORY).orEmpty()
|
currentSection = savedInstanceState?.getParcelable(STATE_CURRENT_SECTION) ?: ProductItem.Section.All
|
||||||
currentOrder = savedInstanceState?.getString(STATE_CURRENT_ORDER)
|
currentOrder = savedInstanceState?.getString(STATE_CURRENT_ORDER)
|
||||||
?.let(ProductItem.Order::valueOf) ?: ProductItem.Order.NAME
|
?.let(ProductItem.Order::valueOf) ?: ProductItem.Order.NAME
|
||||||
layoutManagerState = savedInstanceState?.getParcelable(STATE_LAYOUT_MANAGER)
|
layoutManagerState = savedInstanceState?.getParcelable(STATE_LAYOUT_MANAGER)
|
||||||
@@ -119,7 +119,7 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
|
|
||||||
outState.putString(STATE_CURRENT_SEARCH_QUERY, currentSearchQuery)
|
outState.putString(STATE_CURRENT_SEARCH_QUERY, currentSearchQuery)
|
||||||
outState.putString(STATE_CURRENT_CATEGORY, currentCategory)
|
outState.putParcelable(STATE_CURRENT_SECTION, currentSection)
|
||||||
outState.putString(STATE_CURRENT_ORDER, currentOrder.name)
|
outState.putString(STATE_CURRENT_ORDER, currentOrder.name)
|
||||||
(layoutManagerState ?: recyclerView?.layoutManager?.onSaveInstanceState())
|
(layoutManagerState ?: recyclerView?.layoutManager?.onSaveInstanceState())
|
||||||
?.let { outState.putParcelable(STATE_LAYOUT_MANAGER, it) }
|
?.let { outState.putParcelable(STATE_LAYOUT_MANAGER, it) }
|
||||||
@@ -144,9 +144,9 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
recyclerView?.layoutManager?.onRestoreInstanceState(it)
|
recyclerView?.layoutManager?.onRestoreInstanceState(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentSearchQuery != searchQuery || currentCategory != category || currentOrder != order) {
|
if (currentSearchQuery != searchQuery || currentSection != section || currentOrder != order) {
|
||||||
currentSearchQuery = searchQuery
|
currentSearchQuery = searchQuery
|
||||||
currentCategory = category
|
currentSection = section
|
||||||
currentOrder = order
|
currentOrder = order
|
||||||
recyclerView?.scrollToPosition(0)
|
recyclerView?.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
@@ -161,9 +161,9 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun setCategory(category: String) {
|
internal fun setSection(section: ProductItem.Section) {
|
||||||
if (this.category != category) {
|
if (this.section != section) {
|
||||||
this.category = category
|
this.section = section
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
screenActivity.cursorOwner.attach(this, request)
|
screenActivity.cursorOwner.attach(this, request)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,13 +34,14 @@ import io.reactivex.rxjava3.schedulers.Schedulers
|
|||||||
import nya.kitsunyan.foxydroid.R
|
import nya.kitsunyan.foxydroid.R
|
||||||
import nya.kitsunyan.foxydroid.content.Preferences
|
import nya.kitsunyan.foxydroid.content.Preferences
|
||||||
import nya.kitsunyan.foxydroid.database.Database
|
import nya.kitsunyan.foxydroid.database.Database
|
||||||
|
import nya.kitsunyan.foxydroid.entity.ProductItem
|
||||||
import nya.kitsunyan.foxydroid.service.Connection
|
import nya.kitsunyan.foxydroid.service.Connection
|
||||||
import nya.kitsunyan.foxydroid.service.SyncService
|
import nya.kitsunyan.foxydroid.service.SyncService
|
||||||
import nya.kitsunyan.foxydroid.utility.RxUtils
|
import nya.kitsunyan.foxydroid.utility.RxUtils
|
||||||
import nya.kitsunyan.foxydroid.utility.Utils
|
import nya.kitsunyan.foxydroid.utility.Utils
|
||||||
import nya.kitsunyan.foxydroid.utility.extension.android.*
|
import nya.kitsunyan.foxydroid.utility.extension.android.*
|
||||||
import nya.kitsunyan.foxydroid.utility.extension.resources.*
|
import nya.kitsunyan.foxydroid.utility.extension.resources.*
|
||||||
import nya.kitsunyan.foxydroid.utility.extension.text.*
|
import nya.kitsunyan.foxydroid.widget.DividerItemDecoration
|
||||||
import nya.kitsunyan.foxydroid.widget.EnumRecyclerAdapter
|
import nya.kitsunyan.foxydroid.widget.EnumRecyclerAdapter
|
||||||
import nya.kitsunyan.foxydroid.widget.FocusSearchView
|
import nya.kitsunyan.foxydroid.widget.FocusSearchView
|
||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
@@ -49,43 +50,43 @@ class TabsFragment: ScreenFragment() {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val STATE_SEARCH_FOCUSED = "searchFocused"
|
private const val STATE_SEARCH_FOCUSED = "searchFocused"
|
||||||
private const val STATE_SEARCH_QUERY = "searchQuery"
|
private const val STATE_SEARCH_QUERY = "searchQuery"
|
||||||
private const val STATE_SHOW_CATEGORIES = "showCategories"
|
private const val STATE_SHOW_SECTIONS = "showSections"
|
||||||
private const val STATE_CATEGORIES = "categories"
|
private const val STATE_SECTIONS = "sections"
|
||||||
private const val STATE_CATEGORY = "category"
|
private const val STATE_SECTION = "section"
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Layout(view: View) {
|
private class Layout(view: View) {
|
||||||
val tabs = view.findViewById<LinearLayout>(R.id.tabs)!!
|
val tabs = view.findViewById<LinearLayout>(R.id.tabs)!!
|
||||||
val categoryLayout = view.findViewById<ViewGroup>(R.id.category_layout)!!
|
val sectionLayout = view.findViewById<ViewGroup>(R.id.section_layout)!!
|
||||||
val categoryChange = view.findViewById<View>(R.id.category_change)!!
|
val sectionChange = view.findViewById<View>(R.id.section_change)!!
|
||||||
val categoryName = view.findViewById<TextView>(R.id.category_name)!!
|
val sectionName = view.findViewById<TextView>(R.id.section_name)!!
|
||||||
val categoryIcon = view.findViewById<ImageView>(R.id.category_icon)!!
|
val sectionIcon = view.findViewById<ImageView>(R.id.section_icon)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private var searchMenuItem: MenuItem? = null
|
private var searchMenuItem: MenuItem? = null
|
||||||
private var sortOrderMenu: Pair<MenuItem, List<MenuItem>>? = null
|
private var sortOrderMenu: Pair<MenuItem, List<MenuItem>>? = null
|
||||||
private var syncRepositoriesMenuItem: MenuItem? = null
|
private var syncRepositoriesMenuItem: MenuItem? = null
|
||||||
private var layout: Layout? = null
|
private var layout: Layout? = null
|
||||||
private var categoriesList: RecyclerView? = null
|
private var sectionsList: RecyclerView? = null
|
||||||
private var viewPager: ViewPager2? = null
|
private var viewPager: ViewPager2? = null
|
||||||
|
|
||||||
private var showCategories = false
|
private var showSections = false
|
||||||
set(value) {
|
set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
field = value
|
field = value
|
||||||
val layout = layout
|
val layout = layout
|
||||||
layout?.tabs?.let { (0 until it.childCount)
|
layout?.tabs?.let { (0 until it.childCount)
|
||||||
.forEach { index -> it.getChildAt(index)!!.isEnabled = !value } }
|
.forEach { index -> it.getChildAt(index)!!.isEnabled = !value } }
|
||||||
layout?.categoryIcon?.scaleY = if (value) -1f else 1f
|
layout?.sectionIcon?.scaleY = if (value) -1f else 1f
|
||||||
if ((categoriesList?.parent as? View)?.height ?: 0 > 0) {
|
if ((sectionsList?.parent as? View)?.height ?: 0 > 0) {
|
||||||
animateCategoriesList()
|
animateSectionsList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var searchQuery = ""
|
private var searchQuery = ""
|
||||||
private var categories = emptyList<String>()
|
private var sections = listOf<ProductItem.Section>(ProductItem.Section.All)
|
||||||
private var category = ""
|
private var section: ProductItem.Section = ProductItem.Section.All
|
||||||
|
|
||||||
private val syncConnection = Connection(SyncService::class.java, onBind = { _, _ ->
|
private val syncConnection = Connection(SyncService::class.java, onBind = { _, _ ->
|
||||||
viewPager?.let {
|
viewPager?.let {
|
||||||
@@ -96,7 +97,8 @@ class TabsFragment: ScreenFragment() {
|
|||||||
|
|
||||||
private var sortOrderDisposable: Disposable? = null
|
private var sortOrderDisposable: Disposable? = null
|
||||||
private var categoriesDisposable: Disposable? = null
|
private var categoriesDisposable: Disposable? = null
|
||||||
private var categoriesAnimator: ValueAnimator? = null
|
private var repositoriesDisposable: Disposable? = null
|
||||||
|
private var sectionsAnimator: ValueAnimator? = null
|
||||||
|
|
||||||
private var needSelectUpdates = false
|
private var needSelectUpdates = false
|
||||||
|
|
||||||
@@ -212,10 +214,11 @@ class TabsFragment: ScreenFragment() {
|
|||||||
(tab.layoutParams as LinearLayout.LayoutParams).weight = 1f
|
(tab.layoutParams as LinearLayout.LayoutParams).weight = 1f
|
||||||
}
|
}
|
||||||
|
|
||||||
showCategories = savedInstanceState?.getByte(STATE_SHOW_CATEGORIES)?.toInt() ?: 0 != 0
|
showSections = savedInstanceState?.getByte(STATE_SHOW_SECTIONS)?.toInt() ?: 0 != 0
|
||||||
categories = savedInstanceState?.getStringArrayList(STATE_CATEGORIES).orEmpty()
|
sections = savedInstanceState?.getParcelableArrayList<ProductItem.Section>(STATE_SECTIONS).orEmpty()
|
||||||
category = savedInstanceState?.getString(STATE_CATEGORY).orEmpty()
|
section = savedInstanceState?.getParcelable(STATE_SECTION) ?: ProductItem.Section.All
|
||||||
layout.categoryChange.setOnClickListener { showCategories = categories.isNotEmpty() && !showCategories }
|
layout.sectionChange.setOnClickListener { showSections = sections
|
||||||
|
.any { it !is ProductItem.Section.All } && !showSections }
|
||||||
|
|
||||||
updateOrder()
|
updateOrder()
|
||||||
sortOrderDisposable = Preferences.observable.subscribe {
|
sortOrderDisposable = Preferences.observable.subscribe {
|
||||||
@@ -243,34 +246,38 @@ class TabsFragment: ScreenFragment() {
|
|||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.flatMapSingle { RxUtils.querySingle { Database.CategoryAdapter.getAll(it) } }
|
.flatMapSingle { RxUtils.querySingle { Database.CategoryAdapter.getAll(it) } }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe {
|
.subscribe { setSectionsAndUpdate(it.asSequence().sorted()
|
||||||
val categories = it.sorted()
|
.map(ProductItem.Section::Category).toList(), null) }
|
||||||
if (this.categories != categories) {
|
repositoriesDisposable = Observable.just(Unit)
|
||||||
this.categories = categories
|
.concatWith(Database.observable(Database.Subject.Repositories))
|
||||||
updateCategory()
|
.observeOn(Schedulers.io())
|
||||||
}
|
.flatMapSingle { RxUtils.querySingle { Database.RepositoryAdapter.getAll(it) } }
|
||||||
}
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
updateCategory()
|
.subscribe { setSectionsAndUpdate(null, it.asSequence().filter { it.enabled }
|
||||||
|
.map { ProductItem.Section.Repository(it.id, it.name) }.toList()) }
|
||||||
|
updateSection()
|
||||||
|
|
||||||
val categoriesList = RecyclerView(toolbar.context).apply {
|
val sectionsList = RecyclerView(toolbar.context).apply {
|
||||||
id = R.id.categories_list
|
id = R.id.sections_list
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
isMotionEventSplittingEnabled = false
|
isMotionEventSplittingEnabled = false
|
||||||
isVerticalScrollBarEnabled = false
|
isVerticalScrollBarEnabled = false
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
this.adapter = CategoriesAdapter({ categories }) {
|
val adapter = SectionsAdapter({ sections }) {
|
||||||
if (showCategories) {
|
if (showSections) {
|
||||||
showCategories = false
|
showSections = false
|
||||||
category = it
|
section = it
|
||||||
updateCategory()
|
updateSection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.adapter = adapter
|
||||||
|
addItemDecoration(DividerItemDecoration(context, adapter::configureDivider))
|
||||||
setBackgroundColor(context.getColorFromAttr(android.R.attr.colorPrimaryDark).defaultColor)
|
setBackgroundColor(context.getColorFromAttr(android.R.attr.colorPrimaryDark).defaultColor)
|
||||||
elevation = resources.sizeScaled(4).toFloat()
|
elevation = resources.sizeScaled(4).toFloat()
|
||||||
content.addView(this, FrameLayout.LayoutParams.MATCH_PARENT, 0)
|
content.addView(this, FrameLayout.LayoutParams.MATCH_PARENT, 0)
|
||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
}
|
}
|
||||||
this.categoriesList = categoriesList
|
this.sectionsList = sectionsList
|
||||||
|
|
||||||
var lastContentHeight = -1
|
var lastContentHeight = -1
|
||||||
content.viewTreeObserver.addOnGlobalLayoutListener {
|
content.viewTreeObserver.addOnGlobalLayoutListener {
|
||||||
@@ -280,11 +287,11 @@ class TabsFragment: ScreenFragment() {
|
|||||||
if (lastContentHeight != contentHeight) {
|
if (lastContentHeight != contentHeight) {
|
||||||
lastContentHeight = contentHeight
|
lastContentHeight = contentHeight
|
||||||
if (initial) {
|
if (initial) {
|
||||||
categoriesList.layoutParams.height = if (showCategories) contentHeight else 0
|
sectionsList.layoutParams.height = if (showSections) contentHeight else 0
|
||||||
categoriesList.visibility = if (showCategories) View.VISIBLE else View.GONE
|
sectionsList.visibility = if (showSections) View.VISIBLE else View.GONE
|
||||||
categoriesList.requestLayout()
|
sectionsList.requestLayout()
|
||||||
} else {
|
} else {
|
||||||
animateCategoriesList()
|
animateSectionsList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,7 +305,7 @@ class TabsFragment: ScreenFragment() {
|
|||||||
sortOrderMenu = null
|
sortOrderMenu = null
|
||||||
syncRepositoriesMenuItem = null
|
syncRepositoriesMenuItem = null
|
||||||
layout = null
|
layout = null
|
||||||
categoriesList = null
|
sectionsList = null
|
||||||
viewPager = null
|
viewPager = null
|
||||||
|
|
||||||
syncConnection.unbind(requireContext())
|
syncConnection.unbind(requireContext())
|
||||||
@@ -306,8 +313,10 @@ class TabsFragment: ScreenFragment() {
|
|||||||
sortOrderDisposable = null
|
sortOrderDisposable = null
|
||||||
categoriesDisposable?.dispose()
|
categoriesDisposable?.dispose()
|
||||||
categoriesDisposable = null
|
categoriesDisposable = null
|
||||||
categoriesAnimator?.cancel()
|
repositoriesDisposable?.dispose()
|
||||||
categoriesAnimator = null
|
repositoriesDisposable = null
|
||||||
|
sectionsAnimator?.cancel()
|
||||||
|
sectionsAnimator = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
@@ -315,9 +324,9 @@ class TabsFragment: ScreenFragment() {
|
|||||||
|
|
||||||
outState.putBoolean(STATE_SEARCH_FOCUSED, searchMenuItem?.actionView!!.hasFocus())
|
outState.putBoolean(STATE_SEARCH_FOCUSED, searchMenuItem?.actionView!!.hasFocus())
|
||||||
outState.putString(STATE_SEARCH_QUERY, searchQuery)
|
outState.putString(STATE_SEARCH_QUERY, searchQuery)
|
||||||
outState.putByte(STATE_SHOW_CATEGORIES, if (showCategories) 1 else 0)
|
outState.putByte(STATE_SHOW_SECTIONS, if (showSections) 1 else 0)
|
||||||
outState.putStringArrayList(STATE_CATEGORIES, ArrayList(categories))
|
outState.putParcelableArrayList(STATE_SECTIONS, ArrayList(sections))
|
||||||
outState.putString(STATE_CATEGORY, category)
|
outState.putParcelable(STATE_SECTION, section)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
||||||
@@ -335,7 +344,7 @@ class TabsFragment: ScreenFragment() {
|
|||||||
|
|
||||||
if (view != null && childFragment is ProductsFragment) {
|
if (view != null && childFragment is ProductsFragment) {
|
||||||
childFragment.setSearchQuery(searchQuery)
|
childFragment.setSearchQuery(searchQuery)
|
||||||
childFragment.setCategory(category)
|
childFragment.setSection(section)
|
||||||
childFragment.setOrder(Preferences[Preferences.Key.SortOrder].order)
|
childFragment.setOrder(Preferences[Preferences.Key.SortOrder].order)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -346,8 +355,8 @@ class TabsFragment: ScreenFragment() {
|
|||||||
searchMenuItem?.collapseActionView()
|
searchMenuItem?.collapseActionView()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
showCategories -> {
|
showSections -> {
|
||||||
showCategories = false
|
showSections = false
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@@ -387,31 +396,52 @@ class TabsFragment: ScreenFragment() {
|
|||||||
productFragments.forEach { it.setOrder(order) }
|
productFragments.forEach { it.setOrder(order) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCategory() {
|
private inline fun <reified T: ProductItem.Section> collectOldSections(list: List<T>?): List<T>? {
|
||||||
if (category.isNotEmpty() && categories.indexOf(category) < 0) {
|
val oldList = sections.mapNotNull { it as? T }
|
||||||
category = ""
|
return if (list == null || oldList == list) oldList else null
|
||||||
}
|
|
||||||
layout?.categoryName?.text = category.nullIfEmpty() ?: getString(R.string.all_applications)
|
|
||||||
layout?.categoryIcon?.visibility = if (categories.isEmpty()) View.GONE else View.VISIBLE
|
|
||||||
productFragments.forEach { it.setCategory(category) }
|
|
||||||
categoriesList?.adapter?.notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateCategoriesList() {
|
private fun setSectionsAndUpdate(categories: List<ProductItem.Section.Category>?,
|
||||||
val categoriesList = categoriesList!!
|
repositories: List<ProductItem.Section.Repository>?) {
|
||||||
val value = if (categoriesList.visibility != View.VISIBLE) 0f else
|
val oldCategories = collectOldSections(categories)
|
||||||
categoriesList.height.toFloat() / (categoriesList.parent as View).height
|
val oldRepositories = collectOldSections(repositories)
|
||||||
val target = if (showCategories) 1f else 0f
|
if (oldCategories == null || oldRepositories == null) {
|
||||||
categoriesAnimator?.cancel()
|
sections = listOf(ProductItem.Section.All) +
|
||||||
categoriesAnimator = null
|
(categories ?: oldCategories).orEmpty() +
|
||||||
|
(repositories ?: oldRepositories).orEmpty()
|
||||||
|
updateSection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateSection() {
|
||||||
|
if (section !in sections) {
|
||||||
|
section = ProductItem.Section.All
|
||||||
|
}
|
||||||
|
layout?.sectionName?.text = when (val section = section) {
|
||||||
|
is ProductItem.Section.All -> getString(R.string.all_applications)
|
||||||
|
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
|
||||||
|
productFragments.forEach { it.setSection(section) }
|
||||||
|
sectionsList?.adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun animateSectionsList() {
|
||||||
|
val sectionsList = sectionsList!!
|
||||||
|
val value = if (sectionsList.visibility != View.VISIBLE) 0f else
|
||||||
|
sectionsList.height.toFloat() / (sectionsList.parent as View).height
|
||||||
|
val target = if (showSections) 1f else 0f
|
||||||
|
sectionsAnimator?.cancel()
|
||||||
|
sectionsAnimator = null
|
||||||
|
|
||||||
if (value != target) {
|
if (value != target) {
|
||||||
categoriesAnimator = ValueAnimator.ofFloat(value, target).apply {
|
sectionsAnimator = ValueAnimator.ofFloat(value, target).apply {
|
||||||
duration = (250 * abs(target - value)).toLong()
|
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 {
|
addUpdateListener {
|
||||||
val newValue = animatedValue as Float
|
val newValue = animatedValue as Float
|
||||||
categoriesList.apply {
|
sectionsList.apply {
|
||||||
val height = ((parent as View).height * newValue).toInt()
|
val height = ((parent as View).height * newValue).toInt()
|
||||||
val visible = height > 0
|
val visible = height > 0
|
||||||
if ((visibility == View.VISIBLE) != visible) {
|
if ((visibility == View.VISIBLE) != visible) {
|
||||||
@@ -423,7 +453,7 @@ class TabsFragment: ScreenFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target <= 0f && newValue <= 0f || target >= 1f && newValue >= 1f) {
|
if (target <= 0f && newValue <= 0f || target >= 1f && newValue >= 1f) {
|
||||||
categoriesAnimator = null
|
sectionsAnimator = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
start()
|
start()
|
||||||
@@ -434,24 +464,24 @@ class TabsFragment: ScreenFragment() {
|
|||||||
private val pageChangeCallback = object: ViewPager2.OnPageChangeCallback() {
|
private val pageChangeCallback = object: ViewPager2.OnPageChangeCallback() {
|
||||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||||
val layout = layout!!
|
val layout = layout!!
|
||||||
val fromCategories = ProductsFragment.Source.values()[position].categories
|
val fromSections = ProductsFragment.Source.values()[position].sections
|
||||||
val toCategories = if (positionOffset <= 0f) fromCategories else
|
val toSections = if (positionOffset <= 0f) fromSections else
|
||||||
ProductsFragment.Source.values()[position + 1].categories
|
ProductsFragment.Source.values()[position + 1].sections
|
||||||
val offset = if (fromCategories != toCategories) {
|
val offset = if (fromSections != toSections) {
|
||||||
if (fromCategories) 1f - positionOffset else positionOffset
|
if (fromSections) 1f - positionOffset else positionOffset
|
||||||
} else {
|
} else {
|
||||||
if (fromCategories) 1f else 0f
|
if (fromSections) 1f else 0f
|
||||||
}
|
}
|
||||||
(layout.tabs.background as TabsBackgroundDrawable)
|
(layout.tabs.background as TabsBackgroundDrawable)
|
||||||
.update(position + positionOffset, layout.tabs.childCount)
|
.update(position + positionOffset, layout.tabs.childCount)
|
||||||
assert(layout.categoryLayout.childCount == 1)
|
assert(layout.sectionLayout.childCount == 1)
|
||||||
val child = layout.categoryLayout.getChildAt(0)
|
val child = layout.sectionLayout.getChildAt(0)
|
||||||
val height = child.layoutParams.height
|
val height = child.layoutParams.height
|
||||||
assert(height > 0)
|
assert(height > 0)
|
||||||
val currentHeight = (offset * height).roundToInt()
|
val currentHeight = (offset * height).roundToInt()
|
||||||
if (layout.categoryLayout.layoutParams.height != currentHeight) {
|
if (layout.sectionLayout.layoutParams.height != currentHeight) {
|
||||||
layout.categoryLayout.layoutParams.height = currentHeight
|
layout.sectionLayout.layoutParams.height = currentHeight
|
||||||
layout.categoryLayout.requestLayout()
|
layout.sectionLayout.requestLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,14 +492,14 @@ class TabsFragment: ScreenFragment() {
|
|||||||
syncRepositoriesMenuItem!!.setShowAsActionFlags(if (!source.order ||
|
syncRepositoriesMenuItem!!.setShowAsActionFlags(if (!source.order ||
|
||||||
resources.configuration.screenWidthDp >= 480) MenuItem.SHOW_AS_ACTION_ALWAYS else 0)
|
resources.configuration.screenWidthDp >= 480) MenuItem.SHOW_AS_ACTION_ALWAYS else 0)
|
||||||
setSelectedTab(source)
|
setSelectedTab(source)
|
||||||
if (showCategories && !source.categories) {
|
if (showSections && !source.sections) {
|
||||||
showCategories = false
|
showSections = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageScrollStateChanged(state: Int) {
|
override fun onPageScrollStateChanged(state: Int) {
|
||||||
val source = ProductsFragment.Source.values()[viewPager!!.currentItem]
|
val source = ProductsFragment.Source.values()[viewPager!!.currentItem]
|
||||||
layout!!.categoryChange.isEnabled = state != ViewPager2.SCROLL_STATE_DRAGGING && source.categories
|
layout!!.sectionChange.isEnabled = state != ViewPager2.SCROLL_STATE_DRAGGING && source.sections
|
||||||
if (state == ViewPager2.SCROLL_STATE_IDLE) {
|
if (state == ViewPager2.SCROLL_STATE_IDLE) {
|
||||||
// onPageSelected can be called earlier than fragments created
|
// onPageSelected can be called earlier than fragments created
|
||||||
updateUpdateNotificationBlocker(source)
|
updateUpdateNotificationBlocker(source)
|
||||||
@@ -512,11 +542,12 @@ class TabsFragment: ScreenFragment() {
|
|||||||
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
|
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CategoriesAdapter(private val categories: () -> List<String>, private val onClick: (String) -> Unit):
|
private class SectionsAdapter(private val sections: () -> List<ProductItem.Section>,
|
||||||
EnumRecyclerAdapter<CategoriesAdapter.ViewType, RecyclerView.ViewHolder>() {
|
private val onClick: (ProductItem.Section) -> Unit): EnumRecyclerAdapter<SectionsAdapter.ViewType,
|
||||||
enum class ViewType { CATEGORY }
|
RecyclerView.ViewHolder>() {
|
||||||
|
enum class ViewType { SECTION }
|
||||||
|
|
||||||
private class CategoryViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
|
private class SectionViewHolder(context: Context): RecyclerView.ViewHolder(TextView(context)) {
|
||||||
val title: TextView
|
val title: TextView
|
||||||
get() = itemView as TextView
|
get() = itemView as TextView
|
||||||
|
|
||||||
@@ -532,22 +563,48 @@ class TabsFragment: ScreenFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
configuration.set(false, false, 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override val viewTypeClass: Class<ViewType>
|
override val viewTypeClass: Class<ViewType>
|
||||||
get() = ViewType::class.java
|
get() = ViewType::class.java
|
||||||
|
|
||||||
override fun getItemCount(): Int = 1 + categories().size
|
override fun getItemCount(): Int = sections().size
|
||||||
override fun getItemEnumViewType(position: Int): ViewType = ViewType.CATEGORY
|
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 CategoryViewHolder(parent.context).apply {
|
return SectionViewHolder(parent.context).apply {
|
||||||
itemView.setOnClickListener { onClick(categories().getOrNull(adapterPosition - 1).orEmpty()) }
|
itemView.setOnClickListener { onClick(sections()[adapterPosition]) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
holder as CategoryViewHolder
|
holder as SectionViewHolder
|
||||||
holder.title.text = categories().getOrNull(position - 1)
|
val section = sections()[position]
|
||||||
?: holder.itemView.resources.getString(R.string.all_applications)
|
val previousSection = sections().getOrNull(position - 1)
|
||||||
|
val nextSection = sections().getOrNull(position + 1)
|
||||||
|
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
|
||||||
|
layoutParams.bottomMargin = if (nextSection == null ||
|
||||||
|
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
|
||||||
|
is ProductItem.Section.Repository -> section.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
|
|||||||
if (hasUpdates && Preferences[Preferences.Key.UpdateNotify]) {
|
if (hasUpdates && Preferences[Preferences.Key.UpdateNotify]) {
|
||||||
val disposable = RxUtils
|
val disposable = RxUtils
|
||||||
.querySingle { Database.ProductAdapter
|
.querySingle { Database.ProductAdapter
|
||||||
.query(true, true, "", "", ProductItem.Order.NAME, it)
|
.query(true, true, "", ProductItem.Section.All, ProductItem.Order.NAME, it)
|
||||||
.use { it.asSequence().map(Database.ProductAdapter::transformItem).toList() } }
|
.use { it.asSequence().map(Database.ProductAdapter::transformItem).toList() } }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
android:orientation="horizontal" />
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/category_layout"
|
android:id="@+id/section_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp">
|
android:layout_height="0dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/category_change"
|
android:id="@+id/section_change"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/category_name"
|
android:id="@+id/section_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/category_icon"
|
android:id="@+id/section_icon"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<item type="id" name="categories_list" />
|
<item type="id" name="sections_list" />
|
||||||
<item type="id" name="divider_configuration" />
|
<item type="id" name="divider_configuration" />
|
||||||
<item type="id" name="fragment_pager" />
|
<item type="id" name="fragment_pager" />
|
||||||
<item type="id" name="main_content" />
|
<item type="id" name="main_content" />
|
||||||
|
|||||||
Reference in New Issue
Block a user