Commit 0969e614 authored by Nihar Thakkar's avatar Nihar Thakkar
Browse files

Redo categories UI

parent cae7b82d
......@@ -26,10 +26,8 @@ import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
private var currentFragmentId = 0
private val homeFragment = HomeFragment()
private val categoriesFragment = CategoriesFragment()
private val searchFragment = SearchFragment()
private val updatesFragment = UpdatesFragment()
private val settingsFragment = SettingsFragment()
private val installManagerGetter = InstallManagerGetter()
override fun onCreate(savedInstanceState: Bundle?) {
......@@ -83,7 +81,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
return true
}
R.id.menu_categories -> {
showFragment(categoriesFragment)
showFragment(CategoriesFragment())
return true
}
R.id.menu_search -> {
......@@ -95,7 +93,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
return true
}
R.id.menu_settings -> {
showFragment(settingsFragment)
showFragment(SettingsFragment())
return true
}
}
......
package io.eelo.appinstaller.categories
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import io.eelo.appinstaller.R
import io.eelo.appinstaller.categories.viewmodel.CategoriesViewModel
import io.eelo.appinstaller.utils.Common
import kotlinx.android.synthetic.main.error_layout.view.*
import kotlinx.android.synthetic.main.fragment_application_categories.view.*
class ApplicationsFragment : Fragment() {
private lateinit var categoriesViewModel: CategoriesViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
categoriesViewModel = ViewModelProviders.of(activity!!).get(CategoriesViewModel::class.java)
val view = inflater.inflate(R.layout.fragment_application_categories, container, false)
view.categories_list.layoutManager = LinearLayoutManager(context)
view.categories_list.visibility = View.GONE
view.progress_bar.visibility = View.VISIBLE
view.error_container.visibility = View.GONE
view.findViewById<TextView>(R.id.error_resolve).setOnClickListener {
view.progress_bar.visibility = View.VISIBLE
categoriesViewModel.loadCategories(context!!)
}
// Bind to the list of applications categories
categoriesViewModel.getApplicationsCategories().observe(this, Observer {
if (it!!.isNotEmpty()) {
view.categories_list.adapter = CategoriesListAdapter(it)
view.categories_list.visibility = View.VISIBLE
view.progress_bar.visibility = View.GONE
}
})
// Bind to the screen error
categoriesViewModel.getScreenError().observe(this, Observer {
if (it != null) {
view.error_description.text = activity!!.getString(Common.getScreenErrorDescriptionId(it))
view.error_container.visibility = View.VISIBLE
view.progress_bar.visibility = View.GONE
} else {
view.error_container.visibility = View.GONE
}
})
if (categoriesViewModel.getApplicationsCategories().value!!.isEmpty()) {
categoriesViewModel.loadCategories(context!!)
}
return view
}
}
package io.eelo.appinstaller.categories
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.content.res.Configuration
import android.os.Bundle
import android.support.design.widget.TabLayout
import android.support.v4.app.Fragment
import android.text.TextUtils
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.Gravity
import android.support.v4.view.ViewPager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import io.eelo.appinstaller.R
import io.eelo.appinstaller.categories.viewmodel.CategoriesViewModel
import io.eelo.appinstaller.utils.Common
import kotlin.math.roundToInt
class CategoriesFragment : Fragment() {
private lateinit var categoriesViewModel: CategoriesViewModel
private lateinit var applicationsCategoriesList: GridLayout
private lateinit var gamesCategoriesList: GridLayout
private lateinit var categoriesContainer: LinearLayout
private lateinit var progressBar: ProgressBar
private lateinit var itemParams: LinearLayout.LayoutParams
private var itemWidth = 0
private var itemPadding = 0
private var itemMargin = 0
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_categories, container, false)
val tabLayout = view.findViewById<TabLayout>(R.id.tab_layout)
val viewPager = view.findViewById<ViewPager>(R.id.view_pager)
categoriesViewModel = ViewModelProviders.of(activity!!).get(CategoriesViewModel::class.java)
applicationsCategoriesList = view.findViewById(R.id.applications_categories_list)
gamesCategoriesList = view.findViewById(R.id.games_categories_list)
categoriesContainer = view.findViewById(R.id.categories_container)
progressBar = view.findViewById(R.id.progress_bar)
val errorContainer = view.findViewById<LinearLayout>(R.id.error_container)
val errorDescription = view.findViewById<TextView>(R.id.error_description)
// Initialise UI elements
initialiseDimensions()
handleDeviceOrientation()
categoriesContainer.visibility = View.GONE
progressBar.visibility = View.VISIBLE
errorContainer.visibility = View.GONE
view.findViewById<TextView>(R.id.error_resolve).setOnClickListener {
progressBar.visibility = View.VISIBLE
categoriesViewModel.loadCategories(context!!)
}
// Bind to the list of applications categories
categoriesViewModel.getApplicationsCategories().observe(this, Observer {
showApplicationsCategories()
})
// Bind to the list of games categories
categoriesViewModel.getGamesCategories().observe(this, Observer {
showGamesCategories()
})
viewPager.adapter = CategoriesViewPagerAdapter(activity!!.supportFragmentManager, tabLayout.tabCount)
viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
// Bind to the screen error
categoriesViewModel.getScreenError().observe(this, Observer {
if (it != null) {
errorDescription.text = activity!!.getString(Common.getScreenErrorDescriptionId(it))
errorContainer.visibility = View.VISIBLE
progressBar.visibility = View.GONE
} else {
errorContainer.visibility = View.GONE
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewPager.currentItem = tab.position
}
})
if (categoriesViewModel.getApplicationsCategories().value!!.isEmpty() ||
categoriesViewModel.getGamesCategories().value!!.isEmpty()) {
categoriesViewModel.loadCategories(context!!)
}
return view
}
private fun initialiseDimensions() {
// Do some math and figure out item width, padding and margin
val metrics = DisplayMetrics()
activity!!.windowManager.defaultDisplay.getMetrics(metrics)
val logicalDensity = metrics.density
itemWidth = Math.ceil(160 * logicalDensity.toDouble()).roundToInt()
itemPadding = Math.ceil(8 * logicalDensity.toDouble()).roundToInt()
itemMargin = Math.ceil(4 * logicalDensity.toDouble()).roundToInt()
override fun onTabUnselected(tab: TabLayout.Tab) {
itemParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
itemParams.topMargin = itemPadding
itemParams.bottomMargin = itemPadding
itemParams.marginStart = itemPadding
itemParams.marginEnd = itemPadding
}
private fun handleDeviceOrientation() {
// Check device orientation and increase/decrease number of columns
val orientation = resources.configuration.orientation
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
// In landscape
applicationsCategoriesList.columnCount = 3
gamesCategoriesList.columnCount = 3
} else {
// In portrait
applicationsCategoriesList.columnCount = 2
gamesCategoriesList.columnCount = 2
}
}
private fun showApplicationsCategories() {
applicationsCategoriesList.removeAllViews()
categoriesViewModel.getApplicationsCategories().value!!.forEach {
val textView = TextView(context)
textView.layoutParams = itemParams
textView.width = itemWidth
textView.setPadding(itemPadding, itemPadding, itemPadding, itemPadding)
textView.text = it.title
textView.setTextColor(resources.getColor(android.R.color.black))
textView.textSize = 16.0f
textView.gravity = Gravity.CENTER
textView.maxLines = 1
textView.ellipsize = TextUtils.TruncateAt.END
textView.isClickable = true
if (android.os.Build.VERSION.SDK_INT >= 23) {
textView.foreground = activity!!.getDrawable(R.drawable.app_category_border)
}
val outValue = TypedValue()
context!!.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
textView.setBackgroundResource(outValue.resourceId)
applicationsCategoriesList.addView(textView)
textView.setOnClickListener { _ ->
categoriesViewModel.onCategoryClick(context!!, it)
}
categoriesContainer.visibility = View.VISIBLE
progressBar.visibility = View.GONE
}
}
private fun showGamesCategories() {
gamesCategoriesList.removeAllViews()
categoriesViewModel.getGamesCategories().value!!.forEach {
val textView = TextView(context)
textView.layoutParams = itemParams
textView.width = itemWidth
textView.setPadding(itemPadding, itemPadding, itemPadding, itemPadding)
textView.text = it.title
textView.setTextColor(resources.getColor(android.R.color.black))
textView.textSize = 16.0f
textView.gravity = Gravity.CENTER
textView.maxLines = 1
textView.ellipsize = TextUtils.TruncateAt.END
textView.isClickable = true
if (android.os.Build.VERSION.SDK_INT >= 23) {
textView.foreground = activity!!.getDrawable(R.drawable.app_category_border)
}
val outValue = TypedValue()
context!!.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
textView.setBackgroundResource(outValue.resourceId)
gamesCategoriesList.addView(textView)
textView.setOnClickListener { _ ->
categoriesViewModel.onCategoryClick(context!!, it)
override fun onTabReselected(tab: TabLayout.Tab) {
}
categoriesContainer.visibility = View.VISIBLE
progressBar.visibility = View.GONE
}
})
return view
}
}
package io.eelo.appinstaller.categories
import android.content.Intent
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.TextView
import io.eelo.appinstaller.R
import io.eelo.appinstaller.categories.category.CategoryActivity
import io.eelo.appinstaller.categories.model.Category
import io.eelo.appinstaller.utils.Common
import io.eelo.appinstaller.utils.Constants
class CategoriesListAdapter(private val categories: ArrayList<Category>)
: RecyclerView.Adapter<CategoriesListAdapter.CategoryViewHolder>() {
class CategoryViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val categoryContainer: RelativeLayout = view.findViewById(R.id.category_container)
val categoryTitle: TextView = view.findViewById(R.id.category_title)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
val categoryContainer = LayoutInflater.from(parent.context).inflate(R.layout.category_list_item,
parent, false)
return CategoryViewHolder(categoryContainer)
}
override fun getItemCount(): Int {
return categories.size
}
override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
if (categories[position].title.isNullOrEmpty()) {
categories[position].title = Common.getCategoryTitle(categories[position].id)
}
holder.categoryTitle.text = categories[position].title
holder.categoryContainer.setOnClickListener {
val intent = Intent(holder.categoryContainer.context, CategoryActivity::class.java)
intent.putExtra(Constants.CATEGORY_KEY, categories[position])
holder.categoryContainer.context.startActivity(intent)
}
}
}
package io.eelo.appinstaller.categories
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter
class CategoriesViewPagerAdapter(fragmentManager: FragmentManager, private val numberOfTabs: Int) :
FragmentStatePagerAdapter(fragmentManager) {
private val applicationsFragment = ApplicationsFragment()
private val gamesFragment = GamesFragment()
override fun getItem(position: Int): Fragment? {
when (position) {
0 -> {
return applicationsFragment
}
1 -> {
return gamesFragment
}
}
return null
}
override fun getCount(): Int {
return numberOfTabs
}
}
package io.eelo.appinstaller.categories
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import io.eelo.appinstaller.R
import io.eelo.appinstaller.categories.viewmodel.CategoriesViewModel
import io.eelo.appinstaller.utils.Common
import kotlinx.android.synthetic.main.error_layout.view.*
import kotlinx.android.synthetic.main.fragment_application_categories.view.*
class GamesFragment : Fragment() {
private lateinit var categoriesViewModel: CategoriesViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
categoriesViewModel = ViewModelProviders.of(activity!!).get(CategoriesViewModel::class.java)
val view = inflater.inflate(R.layout.fragment_games_categories, container, false)
view.categories_list.layoutManager = LinearLayoutManager(context)
view.categories_list.visibility = View.GONE
view.progress_bar.visibility = View.VISIBLE
view.error_container.visibility = View.GONE
view.findViewById<TextView>(R.id.error_resolve).setOnClickListener {
view.progress_bar.visibility = View.VISIBLE
categoriesViewModel.loadCategories(context!!)
}
// Bind to the list of games categories
categoriesViewModel.getGamesCategories().observe(this, Observer {
if (it!!.isNotEmpty()) {
view.categories_list.adapter = CategoriesListAdapter(it)
view.categories_list.visibility = View.VISIBLE
view.progress_bar.visibility = View.GONE
}
})
// Bind to the screen error
categoriesViewModel.getScreenError().observe(this, Observer {
if (it != null) {
view.error_description.text = activity!!.getString(Common.getScreenErrorDescriptionId(it))
view.error_container.visibility = View.VISIBLE
view.progress_bar.visibility = View.GONE
} else {
view.error_container.visibility = View.GONE
}
})
if (categoriesViewModel.getGamesCategories().value!!.isEmpty()) {
categoriesViewModel.loadCategories(context!!)
}
return view
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="384dp"
android:height="512dp"
android:viewportWidth="384"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M373.679,338.344l-35.16,-44.832c20.236,-16.151 24.079,-45.619 8.359,-66.438l-34.932,-46.261c21.067,-17.62 23.055,-49.355 4.372,-69.475l-89.143,-96.006c-18.969,-20.428 -51.352,-20.457 -70.348,0l-89.143,96.006c-18.704,20.143 -16.67,51.876 4.372,69.475l-34.932,46.261c-15.73,20.831 -11.864,50.298 8.359,66.438l-35.167,44.832C-14.37,369.762 8.032,416 48.058,416H160c0,36.341 -3.919,56.605 -29.657,82.343C125.318,503.368 128.88,512 136,512h112c7.106,0 10.692,-8.622 5.657,-13.657C227.943,472.629 224,452.386 224,416h111.935c39.956,0 62.472,-46.182 37.744,-77.656zM224,368v-24c0,-8.837 -7.163,-16 -16,-16h-32c-8.837,0 -16,7.163 -16,16v24H48l88,-112H75.429L160,144h-57.143L192,48l89.143,96H224l84.571,112H248l88,112H224z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/category_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:paddingStart="@dimen/layout_padding_medium"
android:paddingTop="@dimen/layout_padding_medium"
android:paddingEnd="@dimen/layout_padding_medium">
<ImageView
android:id="@+id/category_icon"
android:layout_width="@dimen/category_icon_size"
android:layout_height="@dimen/category_icon_size"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_category_default" />
<TextView
android:id="@+id/category_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="@dimen/layout_margin_medium"
android:layout_toEndOf="@id/category_icon"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/text_size_large" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/default_divider_height"
android:layout_below="@id/category_icon"
android:layout_marginStart="@dimen/layout_margin_medium"
android:layout_marginTop="@dimen/layout_margin_medium"
android:layout_marginEnd="@dimen/layout_margin_medium"
android:background="@color/colorDivider" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/categories_list"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
layout="@layout/error_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -4,82 +4,28 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/categories_container"
android:layout_width="match_parent"
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/applications_categories_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_margin_large"
android:layout_marginTop="@dimen/layout_margin_large"
android:maxLines="1"
android:text="@string/applications_categories_title"
android:textColor="@android:color/black"
android:textSize="@dimen/text_size_large"
android:textStyle="bold" />
<GridLayout
android:id="@+id/applications_categories_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/layout_margin_medium"
app:layout_constraintTop_toBottomOf="@id/applications_categories_title" />
<TextView
android:id="@+id/games_categories_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_margin_large"
android:layout_marginTop="@dimen/layout_margin_large"
android:maxLines="1"
android:text="@string/games_categories_title"
android:textColor="@android:color/black"
android:textSize="@dimen/text_size_large"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/applications_categories_list" />
<GridLayout
android:id="@+id/games_categories_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/layout_margin_medium"
app:layout_constraintTop_toBottomOf="@id/games_categories_title" />
android:text="@string/applications_categories_title" />
</LinearLayout>
</ScrollView>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/games_categories_title" />
<include
layout="@layout/error_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"