Add NavigationDrawer to MainActivity to switch between budgets or create new ones
This commit is contained in:
parent
ace85f324f
commit
ed734651f9
13 changed files with 233 additions and 41 deletions
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import com.wbrawner.budget.common.util.ErrorHandler
|
import com.wbrawner.budget.common.util.ErrorHandler
|
||||||
import com.wbrawner.budget.lib.network.NetworkModule
|
import com.wbrawner.budget.lib.network.NetworkModule
|
||||||
import com.wbrawner.budget.storage.StorageModule
|
import com.wbrawner.budget.storage.StorageModule
|
||||||
|
import com.wbrawner.budget.ui.MainViewModel
|
||||||
import com.wbrawner.budget.ui.SplashViewModel
|
import com.wbrawner.budget.ui.SplashViewModel
|
||||||
import com.wbrawner.budget.ui.budgets.BudgetFormViewModel
|
import com.wbrawner.budget.ui.budgets.BudgetFormViewModel
|
||||||
import com.wbrawner.budget.ui.budgets.BudgetListViewModel
|
import com.wbrawner.budget.ui.budgets.BudgetListViewModel
|
||||||
|
@ -15,7 +16,6 @@ import com.wbrawner.budget.ui.transactions.TransactionFormViewModel
|
||||||
import com.wbrawner.budget.ui.transactions.TransactionListViewModel
|
import com.wbrawner.budget.ui.transactions.TransactionListViewModel
|
||||||
import dagger.BindsInstance
|
import dagger.BindsInstance
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
import javax.inject.Named
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -23,6 +23,7 @@ import javax.inject.Singleton
|
||||||
interface AppComponent {
|
interface AppComponent {
|
||||||
fun inject(viewModel: OverviewViewModel)
|
fun inject(viewModel: OverviewViewModel)
|
||||||
fun inject(viewModel: SplashViewModel)
|
fun inject(viewModel: SplashViewModel)
|
||||||
|
fun inject(viewModel: MainViewModel)
|
||||||
fun inject(viewMode: BudgetListViewModel)
|
fun inject(viewMode: BudgetListViewModel)
|
||||||
fun inject(viewModel: BudgetFormViewModel)
|
fun inject(viewModel: BudgetFormViewModel)
|
||||||
fun inject(viewModel: CategoryListViewModel)
|
fun inject(viewModel: CategoryListViewModel)
|
||||||
|
|
|
@ -2,43 +2,95 @@ package com.wbrawner.budget.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.emoji.text.EmojiCompat
|
import androidx.emoji.text.EmojiCompat
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.google.android.material.navigation.NavigationView
|
||||||
|
import com.wbrawner.budget.AllowanceApplication
|
||||||
import com.wbrawner.budget.R
|
import com.wbrawner.budget.R
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
private const val MENU_GROUP_BUDGETS = 50
|
||||||
|
private const val MENU_ITEM_ADD_BUDGET = 100
|
||||||
|
private const val MENU_ITEM_SETTINGS = 101
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
||||||
|
private lateinit var toggle: ActionBarDrawerToggle
|
||||||
|
private val viewModel: MainViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
EmojiCompat.init(androidx.emoji.bundled.BundledEmojiCompatConfig(this))
|
EmojiCompat.init(androidx.emoji.bundled.BundledEmojiCompatConfig(this))
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
(application as AllowanceApplication).appComponent.inject(viewModel)
|
||||||
setSupportActionBar(action_bar)
|
setSupportActionBar(action_bar)
|
||||||
|
toggle = ActionBarDrawerToggle(this, drawerLayout, R.string.action_open, R.string.action_close)
|
||||||
|
toggle.isDrawerIndicatorEnabled = true
|
||||||
|
toggle.isDrawerSlideAnimationEnabled = true
|
||||||
|
drawerLayout.addDrawerListener(toggle)
|
||||||
|
navigationView.setNavigationItemSelectedListener(this)
|
||||||
|
supportActionBar?.setHomeButtonEnabled(true)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
val navController = findNavController(R.id.content_container)
|
val navController = findNavController(R.id.content_container)
|
||||||
menu_main.setupWithNavController(navController)
|
menu_main.setupWithNavController(navController)
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
title = destination.label
|
title = destination.label
|
||||||
val showHomeAsUp = when (destination.label) {
|
val homeAsUpIndicator = when (destination.label) {
|
||||||
getString(R.string.title_overview) -> false
|
getString(R.string.title_overview) -> R.drawable.ic_menu
|
||||||
getString(R.string.title_transactions) -> false
|
getString(R.string.title_transactions) -> R.drawable.ic_menu
|
||||||
getString(R.string.title_profile) -> false
|
getString(R.string.title_profile) -> R.drawable.ic_menu
|
||||||
getString(R.string.title_categories) -> false
|
getString(R.string.title_categories) -> R.drawable.ic_menu
|
||||||
else -> true
|
else -> 0
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(showHomeAsUp)
|
supportActionBar?.setHomeAsUpIndicator(homeAsUpIndicator)
|
||||||
}
|
}
|
||||||
|
viewModel.loadBudgets().observe(this, { list ->
|
||||||
|
val menu = navigationView.menu
|
||||||
|
menu.clear()
|
||||||
|
val budgetsMenu = navigationView.menu.addSubMenu(0, 0, 0, "Budgets")
|
||||||
|
list.budgets.forEachIndexed { index, budget ->
|
||||||
|
budgetsMenu.add(MENU_GROUP_BUDGETS, index, index, budget.name)
|
||||||
|
.setIcon(R.drawable.ic_folder_selectable)
|
||||||
|
}
|
||||||
|
budgetsMenu.setGroupCheckable(MENU_GROUP_BUDGETS, true, true)
|
||||||
|
list.selectedIndex?.let {
|
||||||
|
budgetsMenu.getItem(it).isChecked = true
|
||||||
|
}
|
||||||
|
menu.add(0, MENU_ITEM_ADD_BUDGET, list.budgets.size, R.string.title_add_budget)
|
||||||
|
.setIcon(R.drawable.ic_add_white_24dp)
|
||||||
|
menu.add(1, MENU_ITEM_SETTINGS, list.budgets.size + 1, "Settings")
|
||||||
|
.setIcon(R.drawable.ic_settings)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == android.R.id.home) {
|
if (item.itemId != android.R.id.home) return super.onOptionsItemSelected(item)
|
||||||
findNavController(R.id.content_container).navigateUp()
|
with(findNavController(R.id.content_container)) {
|
||||||
return true
|
when (currentDestination?.label) {
|
||||||
|
getString(R.string.title_overview) -> drawerLayout.open()
|
||||||
|
getString(R.string.title_transactions) -> drawerLayout.open()
|
||||||
|
getString(R.string.title_profile) -> drawerLayout.open()
|
||||||
|
getString(R.string.title_categories) -> drawerLayout.open()
|
||||||
|
else -> navigateUp()
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_OPEN_FRAGMENT = "com.wbrawner.budget.MainActivity.EXTRA_OPEN_FRAGMENT"
|
const val EXTRA_OPEN_FRAGMENT = "com.wbrawner.budget.MainActivity.EXTRA_OPEN_FRAGMENT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
MENU_ITEM_ADD_BUDGET -> findNavController(R.id.content_container).navigate(R.id.addEditBudget)
|
||||||
|
MENU_ITEM_SETTINGS -> findNavController(R.id.content_container).navigate(R.id.addEditBudget)
|
||||||
|
else -> viewModel.loadBudget(item.itemId)
|
||||||
|
}
|
||||||
|
drawerLayout.close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.wbrawner.budget.ui
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.wbrawner.budget.common.budget.Budget
|
||||||
|
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||||
|
import com.wbrawner.budget.common.user.UserRepository
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class MainViewModel : ViewModel() {
|
||||||
|
@Inject
|
||||||
|
lateinit var budgetRepository: BudgetRepository
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var userRepository: UserRepository
|
||||||
|
|
||||||
|
private val budgets = MutableLiveData<BudgetList>()
|
||||||
|
|
||||||
|
fun loadBudgets(): LiveData<BudgetList> {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val list = budgetRepository.findAll().sortedBy { it.name }
|
||||||
|
budgets.postValue(BudgetList(
|
||||||
|
list,
|
||||||
|
budgetRepository.currentBudget.value?.let {
|
||||||
|
list.indexOf(it)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return budgets
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadBudget(index: Int) {
|
||||||
|
val list = budgets.value ?: return
|
||||||
|
viewModelScope.launch {
|
||||||
|
budgetRepository.findById(list.budgets[index].id!!, true)
|
||||||
|
budgets.postValue(list.copy(selectedIndex = index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class BudgetList(
|
||||||
|
val budgets: List<Budget>,
|
||||||
|
val selectedIndex: Int? = null
|
||||||
|
)
|
10
android/src/main/res/drawable/ic_folder.xml
Normal file
10
android/src/main/res/drawable/ic_folder.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" />
|
||||||
|
</vector>
|
10
android/src/main/res/drawable/ic_folder_open.xml
Normal file
10
android/src/main/res/drawable/ic_folder_open.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z" />
|
||||||
|
</vector>
|
5
android/src/main/res/drawable/ic_folder_selectable.xml
Normal file
5
android/src/main/res/drawable/ic_folder_selectable.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_checked="true" android:drawable="@drawable/ic_folder" />
|
||||||
|
<item android:drawable="@drawable/ic_folder_open" />
|
||||||
|
</selector>
|
10
android/src/main/res/drawable/ic_menu.xml
Normal file
10
android/src/main/res/drawable/ic_menu.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
|
||||||
|
</vector>
|
10
android/src/main/res/drawable/ic_settings.xml
Normal file
10
android/src/main/res/drawable/ic_settings.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
|
||||||
|
</vector>
|
|
@ -1,11 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/drawerLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
tools:context=".ui.MainActivity">
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/action_bar"
|
android:id="@+id/action_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -22,7 +28,7 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:menu="@menu/main_navigation" />
|
app:menu="@menu/bottom_navigation" />
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/content_container"
|
android:id="@+id/content_container"
|
||||||
|
@ -35,5 +41,16 @@
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/action_bar"
|
app:layout_constraintTop_toBottomOf="@+id/action_bar"
|
||||||
app:navGraph="@navigation/nav_graph" />
|
app:navGraph="@navigation/nav_graph" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.navigation.NavigationView
|
||||||
|
android:id="@+id/navigationView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
app:headerLayout="@layout/header_navigation_menu"
|
||||||
|
app:menu="@menu/drawer_navigation" />
|
||||||
|
|
||||||
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
|
|
26
android/src/main/res/layout/header_navigation_menu.xml
Normal file
26
android/src/main/res/layout/header_navigation_menu.xml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.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">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/twigsIcon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/ic_twigs_color"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/twigsIcon"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/twigsIcon"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/twigsIcon" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
2
android/src/main/res/menu/drawer_navigation.xml
Normal file
2
android/src/main/res/menu/drawer_navigation.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu />
|
|
@ -59,4 +59,6 @@
|
||||||
<string name="label_balance">Balance</string>
|
<string name="label_balance">Balance</string>
|
||||||
<string name="label_remaining">Remaining</string>
|
<string name="label_remaining">Remaining</string>
|
||||||
<string name="prompt_server">Server</string>
|
<string name="prompt_server">Server</string>
|
||||||
|
<string name="action_open">Open</string>
|
||||||
|
<string name="action_close">Close</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue