Archived
Replaced fragment switching functionality in MainActivity.kt with Navigation component, simplifying logic significantly; upgraded to AGP 8.0.1
This commit is contained in:
@@ -3,15 +3,10 @@ package com.denizk0461.studip.activity
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.denizk0461.studip.R
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.denizk0461.studip.databinding.ActivityMainBinding
|
import com.denizk0461.studip.databinding.ActivityMainBinding
|
||||||
import com.denizk0461.studip.db.AppRepository
|
|
||||||
import com.denizk0461.studip.fragment.EventFragment
|
|
||||||
import com.denizk0461.studip.fragment.CanteenFragment
|
|
||||||
import com.denizk0461.studip.fragment.SettingsFragment
|
|
||||||
import com.denizk0461.studip.model.SettingsPreferences
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main activity that handles all common fragments. This is opened on app launch.
|
* Main activity that handles all common fragments. This is opened on app launch.
|
||||||
@@ -21,91 +16,17 @@ class MainActivity : FragmentActivity() {
|
|||||||
// View binding
|
// View binding
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
/*
|
|
||||||
* Tag for the currently active fragment; set on activity launch to the fragment that will be
|
|
||||||
* shown first.
|
|
||||||
*/
|
|
||||||
private var currentFragment: String = "event"
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
/*
|
|
||||||
* Instantiate repository object that is accessed by the fragments' view models to retrieve
|
|
||||||
* data.
|
|
||||||
*/
|
|
||||||
// Dependencies.repo = AppRepository(application)
|
|
||||||
|
|
||||||
// Inflate view binding and bind to this activity
|
// Inflate view binding and bind to this activity
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
// Set up navigation view bar to launch fragments
|
// Set up bottom navigation view to navigate between fragments
|
||||||
binding.navView.setOnItemSelectedListener { item ->
|
binding.navView.setupWithNavController(
|
||||||
// Launch fragment based on the item that has been clicked
|
binding.navHostFragmentContentMain.getFragment<NavHostFragment>().findNavController()
|
||||||
loadFragment(when (item.itemId) {
|
)
|
||||||
R.id.food -> "canteen"
|
|
||||||
R.id.settings -> "settings"
|
|
||||||
else -> "event"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up bottom nav bar and the initial fragment when the app launches
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
// Open the fragment that the user specified to open on app start
|
|
||||||
if (AppRepository.getRepositoryInstance(application)
|
|
||||||
.getBooleanPreference(SettingsPreferences.LAUNCH_CANTEEN_ON_START)
|
|
||||||
) {
|
|
||||||
// Open canteen fragment
|
|
||||||
binding.navView.selectedItemId = R.id.food
|
|
||||||
currentFragment = "canteen"
|
|
||||||
} else {
|
|
||||||
// Open event fragment
|
|
||||||
binding.navView.selectedItemId = R.id.plan
|
|
||||||
currentFragment = "event"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch the fragment defined in currentFragment
|
|
||||||
loadFragment(currentFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a given fragment.
|
|
||||||
*
|
|
||||||
* @param type tag for the fragment that is to be opened
|
|
||||||
* @return true on success, false if another fragment is currently instantiating
|
|
||||||
*/
|
|
||||||
private fun loadFragment(type: String): Boolean {
|
|
||||||
|
|
||||||
// Find the fragment that is to be opened, or create it if it hasn't been instantiated yet
|
|
||||||
val fragment = supportFragmentManager.findFragmentByTag(type) ?: getFragment(type)
|
|
||||||
|
|
||||||
// Don't interrupt the current fragment's lifecycle if it isn't fully initialised
|
|
||||||
if (fragment.lifecycle.currentState != Lifecycle.State.INITIALIZED) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the transaction to show the new fragment
|
|
||||||
supportFragmentManager.beginTransaction()
|
|
||||||
.replace(R.id.nav_host_fragment_content_main, fragment, type)
|
|
||||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
|
||||||
.commit()
|
|
||||||
|
|
||||||
// Success
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve fragment by its tag.
|
|
||||||
*
|
|
||||||
* @param type tag for the corresponding fragment
|
|
||||||
* @return the corresponding fragment
|
|
||||||
*/
|
|
||||||
private fun getFragment(type: String) = when (type) {
|
|
||||||
"canteen" -> CanteenFragment() // CanteenFragment.kt
|
|
||||||
"settings" -> SettingsFragment() // SettingsFragment.kt
|
|
||||||
else -> EventFragment() // on unknown value or "event", launch EventFragment.kt
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import java.util.*
|
|||||||
*/
|
*/
|
||||||
class StudIPEventItemAdapter(
|
class StudIPEventItemAdapter(
|
||||||
private val currentDay: Int,
|
private val currentDay: Int,
|
||||||
|
private val highlightNextCourse: Boolean,
|
||||||
private val onClickListener: OnClickListener,
|
private val onClickListener: OnClickListener,
|
||||||
) : RecyclerView.Adapter<StudIPEventItemAdapter.EventViewHolder>() {
|
) : RecyclerView.Adapter<StudIPEventItemAdapter.EventViewHolder>() {
|
||||||
|
|
||||||
@@ -80,10 +81,11 @@ class StudIPEventItemAdapter(
|
|||||||
val theme = holder.binding.root.context.theme
|
val theme = holder.binding.root.context.theme
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Highlight the next upcoming course of the day if the day of the adapter matches the
|
* Highlight the next upcoming course of the day if the user selected the option, if the day
|
||||||
* current day, and if no other course has been highlighted, to avoid double highlighting.
|
* of the adapter matches the current day, and if no other course has been highlighted, to
|
||||||
|
* avoid double highlighting.
|
||||||
*/
|
*/
|
||||||
if (!isAnyCourseHighlighted && currentItem.isCurrentCourse(currentCalendar)) {
|
if (highlightNextCourse && !isAnyCourseHighlighted && currentItem.isCurrentCourse(currentCalendar)) {
|
||||||
// Ensure that no other course will be highlighted
|
// Ensure that no other course will be highlighted
|
||||||
isAnyCourseHighlighted = true
|
isAnyCourseHighlighted = true
|
||||||
|
|
||||||
|
|||||||
@@ -124,17 +124,6 @@ class CanteenFragment : AppFragment() {
|
|||||||
chip.setOnCheckedChangeListener { buttonView, newValue ->
|
chip.setOnCheckedChangeListener { buttonView, newValue ->
|
||||||
// Save the preference change to persistent storage
|
// Save the preference change to persistent storage
|
||||||
setPreference(pref, newValue)
|
setPreference(pref, newValue)
|
||||||
|
|
||||||
/*
|
|
||||||
* Force an animation on click. This is a subpar method from StackOverflow. Since
|
|
||||||
* it removes the view before immediately adding it back, this method causes a
|
|
||||||
* flicker.
|
|
||||||
* TODO implement a more efficient / better-looking method
|
|
||||||
* TODO order the chips alphabetically?
|
|
||||||
*/
|
|
||||||
// val index = binding.chipsPreference.indexOfChild(buttonView)
|
|
||||||
// binding.chipsPreference.removeView(buttonView)
|
|
||||||
// binding.chipsPreference.addView(buttonView, index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +141,8 @@ class CanteenFragment : AppFragment() {
|
|||||||
|
|
||||||
// Let the adapter know of the new amount of dates (pages) to display
|
// Let the adapter know of the new amount of dates (pages) to display
|
||||||
viewPagerAdapter.itemCount = dates.size
|
viewPagerAdapter.itemCount = dates.size
|
||||||
|
|
||||||
|
openingHours = viewModel.getCanteenOpeningHours()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the adapter to the view pager
|
// Assign the adapter to the view pager
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ class EventPageFragment : AppFragment(), StudIPEventItemAdapter.OnClickListener
|
|||||||
currentDay = arguments?.getInt("currentDay") ?: -1
|
currentDay = arguments?.getInt("currentDay") ?: -1
|
||||||
|
|
||||||
// Instantiate event adapter with the current day
|
// Instantiate event adapter with the current day
|
||||||
eventAdapter = StudIPEventItemAdapter(currentDay, this)
|
eventAdapter = StudIPEventItemAdapter(
|
||||||
|
currentDay,
|
||||||
|
viewModel.preferenceCourseHighlighting,
|
||||||
|
this,
|
||||||
|
)
|
||||||
|
|
||||||
// Inflate view binding
|
// Inflate view binding
|
||||||
_binding = RecyclerViewBinding.inflate(inflater, container, false)
|
_binding = RecyclerViewBinding.inflate(inflater, container, false)
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class CanteenPageViewModel(app: Application) : AppViewModel(app) {
|
|||||||
/**
|
/**
|
||||||
* This value determines which canteen the user has selected.
|
* This value determines which canteen the user has selected.
|
||||||
*/
|
*/
|
||||||
var preferenceColour: Boolean
|
val preferenceColour: Boolean
|
||||||
get() = repo.getBooleanPreference(SettingsPreferences.COLOUR_PREFS)
|
get() = repo.getBooleanPreference(SettingsPreferences.COLOUR_PREFS, defaultValue = true)
|
||||||
set(newValue) { repo.setPreference(SettingsPreferences.COLOUR_PREFS, newValue) }
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.denizk0461.studip.viewmodel
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.denizk0461.studip.model.SettingsPreferences
|
||||||
import com.denizk0461.studip.model.StudIPEvent
|
import com.denizk0461.studip.model.StudIPEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,4 +18,14 @@ class EventPageViewModel(app: Application) : AppViewModel(app) {
|
|||||||
* @return Stud.IP events for a certain day exposed through a LiveData object
|
* @return Stud.IP events for a certain day exposed through a LiveData object
|
||||||
*/
|
*/
|
||||||
fun getEventsForDay(day: Int): LiveData<List<StudIPEvent>> = repo.getEventsForDay(day)
|
fun getEventsForDay(day: Int): LiveData<List<StudIPEvent>> = repo.getEventsForDay(day)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value determines whether the user wants to have the next course in their schedule
|
||||||
|
* highlighted.
|
||||||
|
*/
|
||||||
|
val preferenceCourseHighlighting: Boolean
|
||||||
|
get() = repo.getBooleanPreference(
|
||||||
|
SettingsPreferences.COURSE_HIGHLIGHTING,
|
||||||
|
defaultValue = true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,10 @@ class SettingsViewModel(app: Application) : AppViewModel(app) {
|
|||||||
* highlighted.
|
* highlighted.
|
||||||
*/
|
*/
|
||||||
var preferenceCourseHighlighting: Boolean
|
var preferenceCourseHighlighting: Boolean
|
||||||
get() = repo.getBooleanPreference(SettingsPreferences.COURSE_HIGHLIGHTING)
|
get() = repo.getBooleanPreference(
|
||||||
|
SettingsPreferences.COURSE_HIGHLIGHTING,
|
||||||
|
defaultValue = true
|
||||||
|
)
|
||||||
set(newValue) { repo.setPreference(SettingsPreferences.COURSE_HIGHLIGHTING, newValue) }
|
set(newValue) { repo.setPreference(SettingsPreferences.COURSE_HIGHLIGHTING, newValue) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +39,7 @@ class SettingsViewModel(app: Application) : AppViewModel(app) {
|
|||||||
* This value determines which canteen the user has selected.
|
* This value determines which canteen the user has selected.
|
||||||
*/
|
*/
|
||||||
var preferenceColour: Boolean
|
var preferenceColour: Boolean
|
||||||
get() = repo.getBooleanPreference(SettingsPreferences.COLOUR_PREFS)
|
get() = repo.getBooleanPreference(SettingsPreferences.COLOUR_PREFS, defaultValue = true)
|
||||||
set(newValue) { repo.setPreference(SettingsPreferences.COLOUR_PREFS, newValue) }
|
set(newValue) { repo.setPreference(SettingsPreferences.COLOUR_PREFS, newValue) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:defaultNavHost="true"
|
app:defaultNavHost="true"
|
||||||
|
app:navGraph="@navigation/main_nav_graph"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/nav_view"
|
app:layout_constraintBottom_toTopOf="@+id/nav_view"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@@ -44,6 +44,15 @@
|
|||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/app_title_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="@string/title_food"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:fontFamily="@font/lato_bolditalic"
|
||||||
|
android:layout_marginHorizontal="16dp"/>
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:id="@+id/scroll_view_chip"
|
android:id="@+id/scroll_view_chip"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -181,15 +190,6 @@
|
|||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/app_title_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:text="@string/title_food"
|
|
||||||
android:textSize="32sp"
|
|
||||||
android:fontFamily="@font/lato_bolditalic"
|
|
||||||
android:layout_marginHorizontal="16dp"/>
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/plan"
|
android:id="@+id/schedule"
|
||||||
android:icon="@drawable/design"
|
android:icon="@drawable/design"
|
||||||
android:title="@string/nav_schedule"
|
android:title="@string/nav_schedule"
|
||||||
android:enabled="true"/>
|
android:enabled="true"/>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/food"
|
android:id="@+id/canteen"
|
||||||
android:icon="@drawable/restaurant"
|
android:icon="@drawable/restaurant"
|
||||||
android:title="@string/nav_food"
|
android:title="@string/nav_food"
|
||||||
android:enabled="true"/>
|
android:enabled="true"/>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation 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:id="@+id/main_nav_graph"
|
||||||
|
app:startDestination="@id/schedule">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/schedule"
|
||||||
|
android:name="com.denizk0461.studip.fragment.EventFragment"
|
||||||
|
android:label="EventFragment"
|
||||||
|
tools:layout="@layout/fragment_event"/>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/canteen"
|
||||||
|
android:name="com.denizk0461.studip.fragment.CanteenFragment"
|
||||||
|
android:label="CanteenFragment"/>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/settings"
|
||||||
|
android:name="com.denizk0461.studip.fragment.SettingsFragment"
|
||||||
|
android:label="SettingsFragment"
|
||||||
|
tools:layout="@layout/fragment_settings"/>
|
||||||
|
</navigation>
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '8.0.0' apply false
|
id 'com.android.application' version '8.0.1' apply false
|
||||||
id 'com.android.library' version '8.0.0' apply false
|
id 'com.android.library' version '8.0.1' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
|
id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user