add navigation menus

This commit is contained in:
2025-07-24 21:45:15 +05:00
parent b505daf2a9
commit 41aefc447a
26 changed files with 571 additions and 11 deletions

View File

@@ -44,6 +44,7 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.coordinatorlayout)
implementation(libs.androidx.lifecycle.livedata.ktx)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.androidx.navigation.fragment.ktx)

View File

@@ -1,7 +1,10 @@
package sh.sar.gridflow
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
@@ -41,12 +44,50 @@ class MainActivity : AppCompatActivity() {
// Update navigation header with user info
updateNavHeader(navView)
// Only Home in the navigation
// Set up navigation with all main fragments
appBarConfiguration = AppBarConfiguration(
setOf(R.id.nav_home), drawerLayout
setOf(
R.id.nav_dashboard,
R.id.nav_subscriptions,
R.id.nav_band_rates,
R.id.nav_bill_history,
R.id.nav_pay_any_bill
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
// Handle special menu items (Terms of Service and Privacy Policy)
navView.setNavigationItemSelectedListener { menuItem ->
when (menuItem.itemId) {
R.id.nav_terms_of_service -> {
openUrl("https://fenaka.mv/terms-and-conditions")
drawerLayout.closeDrawers()
true
}
R.id.nav_privacy_policy -> {
openUrl("https://fenaka.mv/privacy-policy")
drawerLayout.closeDrawers()
true
}
else -> {
// Let the default navigation handle other items
try {
navController.navigate(menuItem.itemId)
menuItem.isChecked = true
drawerLayout.closeDrawers()
true
} catch (e: Exception) {
false
}
}
}
}
}
private fun openUrl(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
}
private fun updateNavHeader(navView: NavigationView) {

View File

@@ -0,0 +1,39 @@
package sh.sar.gridflow.ui.bandrates
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import sh.sar.gridflow.databinding.FragmentBandRatesBinding
class BandRatesFragment : Fragment() {
private var _binding: FragmentBandRatesBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val bandRatesViewModel =
ViewModelProvider(this).get(BandRatesViewModel::class.java)
_binding = FragmentBandRatesBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView = binding.textBandRates
bandRatesViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@@ -0,0 +1,13 @@
package sh.sar.gridflow.ui.bandrates
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class BandRatesViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "Band Rates\n\nComing soon..."
}
val text: LiveData<String> = _text
}

View File

@@ -0,0 +1,39 @@
package sh.sar.gridflow.ui.billhistory
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import sh.sar.gridflow.databinding.FragmentBillHistoryBinding
class BillHistoryFragment : Fragment() {
private var _binding: FragmentBillHistoryBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val billHistoryViewModel =
ViewModelProvider(this).get(BillHistoryViewModel::class.java)
_binding = FragmentBillHistoryBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView = binding.textBillHistory
billHistoryViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@@ -0,0 +1,13 @@
package sh.sar.gridflow.ui.billhistory
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class BillHistoryViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "Bill History\n\nComing soon..."
}
val text: LiveData<String> = _text
}

View File

@@ -0,0 +1,39 @@
package sh.sar.gridflow.ui.payanybill
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import sh.sar.gridflow.databinding.FragmentPayAnyBillBinding
class PayAnyBillFragment : Fragment() {
private var _binding: FragmentPayAnyBillBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val payAnyBillViewModel =
ViewModelProvider(this).get(PayAnyBillViewModel::class.java)
_binding = FragmentPayAnyBillBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView = binding.textPayAnyBill
payAnyBillViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@@ -0,0 +1,13 @@
package sh.sar.gridflow.ui.payanybill
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class PayAnyBillViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "Pay Any Bill\n\nComing soon..."
}
val text: LiveData<String> = _text
}

View File

@@ -0,0 +1,45 @@
package sh.sar.gridflow.ui.subscriptions
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import sh.sar.gridflow.databinding.FragmentSubscriptionsBinding
class SubscriptionsFragment : Fragment() {
private var _binding: FragmentSubscriptionsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val subscriptionsViewModel =
ViewModelProvider(this).get(SubscriptionsViewModel::class.java)
_binding = FragmentSubscriptionsBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView = binding.textSubscriptions
subscriptionsViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
// Handle FAB click
binding.fabAddSubscription.setOnClickListener {
Toast.makeText(context, "Add subscription coming soon", Toast.LENGTH_SHORT).show()
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@@ -0,0 +1,13 @@
package sh.sar.gridflow.ui.subscriptions
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SubscriptionsViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "Subscriptions\n\nComing soon..."
}
val text: LiveData<String> = _text
}

View 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/colorOnPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@@ -0,0 +1,31 @@
<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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M3,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M5.5,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M8,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M10.5,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M13,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M18,8l1.5,0l0,8l-1.5,0z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M20.5,8l1.5,0l0,8l-1.5,0z"/>
</vector>

View File

@@ -0,0 +1,22 @@
<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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 2,2h8c1.1,0 2,-0.9 2,-2L16,4c0,-1.1 -0.9,-2 -2,-2zM14,20L6,20L6,4h8v16z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M19,17h2v2h-2z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M19,5h2v2h-2z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M19,9h2v2h-2z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M19,13h2v2h-2z"/>
</vector>

View 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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z"/>
</vector>

View 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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/>
</vector>

View 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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M12,1l3.5,3.5l-3.5,3.5l-3.5,-3.5L12,1zM12,10.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5s-2.5,-1.12 -2.5,-2.5S10.62,10.5 12,10.5zM6,16c1.11,0 2,0.89 2,2s-0.89,2 -2,2s-2,-0.89 -2,-2S4.89,16 6,16zM18,16c1.11,0 2,0.89 2,2s-0.89,2 -2,2s-2,-0.89 -2,-2S16.89,16 18,16z"/>
</vector>

View 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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM8,6c1.11,0 2,0.89 2,2s-0.89,2 -2,2 -2,-0.89 -2,-2 0.89,-2 2,-2zM14,18L4,18v-1c0,-1.33 2.67,-2 4,-2s4,0.67 4,2v1zM17.5,17h-2.2l1.4,-4.65c0.18,-0.65 0.9,-1.1 1.6,-1 0.95,0.15 1.6,1.05 1.45,2l-0.9,3.65zM19.5,11.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>

View 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/colorOnSurface">
<path
android:fillColor="@android:color/white"
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM17,17L7,17v-2h10v2zM17,13L7,13v-2h10v2zM17,9L7,9L7,7h10v2z"/>
</vector>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:background="?android:attr/colorBackground"
tools:context=".ui.bandrates.BandRatesFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
android:gravity="center">
<TextView
android:id="@+id/text_band_rates"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center"
android:layout_marginTop="100dp"
tools:text="Band Rates\n\nComing soon..." />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:background="?android:attr/colorBackground"
tools:context=".ui.billhistory.BillHistoryFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
android:gravity="center">
<TextView
android:id="@+id/text_bill_history"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center"
android:layout_marginTop="100dp"
tools:text="Bill History\n\nComing soon..." />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:background="?android:attr/colorBackground"
tools:context=".ui.payanybill.PayAnyBillFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
android:gravity="center">
<TextView
android:id="@+id/text_pay_any_bill"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center"
android:layout_marginTop="100dp"
tools:text="Pay Any Bill\n\nComing soon..." />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
tools:context=".ui.subscriptions.SubscriptionsFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp"
android:gravity="center">
<TextView
android:id="@+id/text_subscriptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center"
android:layout_marginTop="100dp"
tools:text="Subscriptions\n\nComing soon..." />
</LinearLayout>
</ScrollView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_add_subscription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:src="@drawable/ic_add_24"
android:contentDescription="Add subscription"
app:tint="?android:attr/colorBackground" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -5,8 +5,35 @@
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_menu_camera"
android:title="@string/menu_home" />
android:id="@+id/nav_dashboard"
android:icon="@drawable/ic_dashboard_24"
android:title="@string/menu_dashboard" />
<item
android:id="@+id/nav_subscriptions"
android:icon="@drawable/ic_subscriptions_24"
android:title="@string/menu_subscriptions" />
<item
android:id="@+id/nav_band_rates"
android:icon="@drawable/ic_band_rates_24"
android:title="@string/menu_band_rates" />
<item
android:id="@+id/nav_bill_history"
android:icon="@drawable/ic_bill_history_24"
android:title="@string/menu_bill_history" />
<item
android:id="@+id/nav_pay_any_bill"
android:icon="@drawable/ic_pay_any_bill_24"
android:title="@string/menu_pay_any_bill" />
</group>
<group android:id="@+id/group_legal">
<item
android:id="@+id/nav_terms_of_service"
android:icon="@drawable/ic_terms_24"
android:title="@string/menu_terms_of_service" />
<item
android:id="@+id/nav_privacy_policy"
android:icon="@drawable/ic_privacy_24"
android:title="@string/menu_privacy_policy" />
</group>
</menu>

View File

@@ -3,12 +3,36 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/nav_home">
app:startDestination="@+id/nav_dashboard">
<fragment
android:id="@+id/nav_home"
android:id="@+id/nav_dashboard"
android:name="sh.sar.gridflow.ui.home.HomeFragment"
android:label="@string/menu_home"
android:label="@string/menu_dashboard"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_subscriptions"
android:name="sh.sar.gridflow.ui.subscriptions.SubscriptionsFragment"
android:label="@string/menu_subscriptions"
tools:layout="@layout/fragment_subscriptions" />
<fragment
android:id="@+id/nav_band_rates"
android:name="sh.sar.gridflow.ui.bandrates.BandRatesFragment"
android:label="@string/menu_band_rates"
tools:layout="@layout/fragment_band_rates" />
<fragment
android:id="@+id/nav_bill_history"
android:name="sh.sar.gridflow.ui.billhistory.BillHistoryFragment"
android:label="@string/menu_bill_history"
tools:layout="@layout/fragment_bill_history" />
<fragment
android:id="@+id/nav_pay_any_bill"
android:name="sh.sar.gridflow.ui.payanybill.PayAnyBillFragment"
android:label="@string/menu_pay_any_bill"
tools:layout="@layout/fragment_pay_any_bill" />
</navigation>

View File

@@ -7,7 +7,11 @@
<string name="nav_header_desc">Navigation header</string>
<string name="action_settings">Settings</string>
<string name="menu_home">Home</string>
<string name="menu_gallery">Gallery</string>
<string name="menu_slideshow">Slideshow</string>
<string name="menu_dashboard">Dashboard</string>
<string name="menu_subscriptions">Subscriptions</string>
<string name="menu_band_rates">Band Rates</string>
<string name="menu_bill_history">Bill History</string>
<string name="menu_pay_any_bill">Pay any bill</string>
<string name="menu_terms_of_service">Terms of Service</string>
<string name="menu_privacy_policy">Privacy Policy</string>
</resources>

View File

@@ -15,6 +15,7 @@ navigationUiKtx = "2.6.0"
okhttp = "4.12.0"
gson = "2.10.1"
security = "1.1.0-alpha06"
coordinatorlayout = "1.2.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -32,6 +33,7 @@ okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhtt
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "security" }
androidx-coordinatorlayout = { group = "androidx.coordinatorlayout", name = "coordinatorlayout", version.ref = "coordinatorlayout" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }