Archived
Added comments and cleaned up code in activities and StudIPEvent.kt
This commit is contained in:
@@ -14,9 +14,15 @@ import com.denizk0461.studip.databinding.ActivityFetcherBinding
|
||||
import com.denizk0461.studip.viewmodel.FetcherViewModel
|
||||
import java.net.URLDecoder
|
||||
|
||||
/**
|
||||
* Activity for allowing the user to log in to access and fetch their schedule from their Stud.IP
|
||||
* profile. Launched through a button in the app's settings.
|
||||
*/
|
||||
class FetcherActivity : Activity() {
|
||||
|
||||
// View binding
|
||||
private lateinit var binding: ActivityFetcherBinding
|
||||
// View model
|
||||
private lateinit var viewModel: FetcherViewModel
|
||||
|
||||
/*
|
||||
@@ -27,39 +33,51 @@ class FetcherActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Instantiate view model
|
||||
viewModel = ViewModelProvider.AndroidViewModelFactory(this.application).create(FetcherViewModel::class.java)
|
||||
|
||||
// Inflate view binding and bind to this activity
|
||||
binding = ActivityFetcherBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
// Enabling JavaScript. See comment above lint suppression for reason why this is done.
|
||||
binding.webview.settings.apply {
|
||||
javaScriptEnabled = true
|
||||
}
|
||||
binding.webview.webViewClient = object : WebViewClient() {
|
||||
// Disallow redirecting to prevent the app from launching Chrome after the user logs in
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView,
|
||||
request: WebResourceRequest
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
binding.webview.loadUrl(
|
||||
"javascript:window.HtmlViewer.showHTML" +
|
||||
"('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to load the user-set Stud.IP front page. Redirects to login page if user is
|
||||
* not logged in.
|
||||
*/
|
||||
binding.webview.loadUrl("https://elearning.uni-bremen.de/index.php?again=yes")
|
||||
|
||||
binding.fab.setOnClickListener { view ->
|
||||
/*
|
||||
* Retrieve encoded HTML via JavaScript function. Encoding is done as otherwise not all
|
||||
* symbols will be accurately fetched.
|
||||
*/
|
||||
binding.webview.evaluateJavascript(
|
||||
"(function(){return encodeURI(document.getElementsByTagName('html')[0].innerHTML)})();"
|
||||
) { p0 ->
|
||||
// Delete all previously fetched elements
|
||||
viewModel.nukeEvents()
|
||||
// Decode HTML and parse it into a list of StudIPEvent.kt
|
||||
StudIPParser().parse(URLDecoder.decode(p0, "UTF-8")) { events ->
|
||||
// Insert the list into the database
|
||||
viewModel.insertEvents(events)
|
||||
Toast.makeText(this, R.string.toast_fetch_finished, Toast.LENGTH_SHORT).show()
|
||||
// Notify the user that the fetch was successful. TODO: notify on error
|
||||
Toast.makeText(
|
||||
this, R.string.toast_fetch_finished, Toast.LENGTH_SHORT
|
||||
).show()
|
||||
// Close the activity
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ package com.denizk0461.studip.activity
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.denizk0461.studip.R
|
||||
@@ -16,124 +14,100 @@ import com.denizk0461.studip.fragment.EventFragment
|
||||
import com.denizk0461.studip.fragment.CanteenFragment
|
||||
import com.denizk0461.studip.fragment.SettingsFragment
|
||||
|
||||
/**
|
||||
* Main activity that handles all common fragments. This is opened on app launch.
|
||||
*/
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
// View binding
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private var currentFragment = FragmentType.SCHEDULE
|
||||
private val fragments = listOf(EventFragment(), CanteenFragment(), SettingsFragment())
|
||||
/*
|
||||
* ID of the bottom navigation view button that launched the currently active fragment; set on
|
||||
* activity launch to the fragment that will be first shown
|
||||
*/
|
||||
private var currentFragment: Int = R.id.plan
|
||||
// All fragments are instantiated at app launch
|
||||
private val fragments: List<Fragment> =
|
||||
listOf(EventFragment(), CanteenFragment(), SettingsFragment())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
/*
|
||||
* Instantiate repository object that is accessed by the fragments' view models to retrieve
|
||||
* data.
|
||||
*/
|
||||
Dependencies.repo = EventRepository(application)
|
||||
|
||||
// Inflate view binding and bind to this activity
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
/*
|
||||
* Set up navigation view bar to launch fragments.
|
||||
* TODO: why do fragments launch slowly when quickly clicking between them?
|
||||
*/
|
||||
binding.contentMain.navView.setOnItemSelectedListener { item ->
|
||||
loadFragment(bottomIdToType(item.itemId))
|
||||
loadFragment(item.itemId)
|
||||
}
|
||||
|
||||
// Launch the fragment defined in currentFragment
|
||||
loadFragment(currentFragment)
|
||||
|
||||
// setSupportActionBar(binding.toolbar)
|
||||
|
||||
// val navController = findNavController(R.id.nav_host_fragment_content_main)
|
||||
// appBarConfiguration = AppBarConfiguration(navController.graph)
|
||||
// setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
|
||||
// binding.fab.setOnClickListener { view ->
|
||||
// findNavController(R.id.nav_host_fragment_content_main).navigate(R.id.action_FirstFragment_to_SecondFragment)
|
||||
//// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
//// .setAnchorView(R.id.fab)
|
||||
//// .setAction("Action", null).show()
|
||||
// }
|
||||
}
|
||||
|
||||
private fun loadFragment(type: FragmentType): Boolean {
|
||||
// val fragment = supportFragmentManager.findFragmentByTag(type.type) ?: getFragmentByType(type)
|
||||
val fragment = getFragment(type)
|
||||
/**
|
||||
* Loads a given fragment.
|
||||
*
|
||||
* @param id the ID of the bottom navigation view button that was clicked
|
||||
* @return true on success, false if another fragment is currently instantiating
|
||||
*/
|
||||
private fun loadFragment(id: Int): Boolean {
|
||||
// Get fragment to be loaded
|
||||
val fragment = getFragment(id)
|
||||
|
||||
// Check if another fragment exists and is not fully initialised
|
||||
if (fragment.lifecycle.currentState != Lifecycle.State.INITIALIZED) {
|
||||
// Return false to avoid interrupting the fragment's lifecycle
|
||||
return false
|
||||
}
|
||||
|
||||
// if (supportFragmentManager.fragments.size > 1) {
|
||||
//
|
||||
// val currentFragment = supportFragmentManager.findFragmentByTag(currentFragment.type)
|
||||
// if (currentFragment != null) {
|
||||
// supportFragmentManager.beginTransaction().remove(currentFragment).commit()
|
||||
// supportFragmentManager.popBackStack()
|
||||
// }
|
||||
// }
|
||||
|
||||
// supportFragmentManager.popBackStack()
|
||||
// Prepare and commit a transaction between the current and the next fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.nav_host_fragment_content_main, fragment, type.type)
|
||||
.replace(R.id.nav_host_fragment_content_main, fragment, id.toString())
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.commit()
|
||||
|
||||
binding.contentMain.appTitleBar.text = getTitleString(type)
|
||||
|
||||
currentFragment = type
|
||||
// Set text of the app bar accordingly
|
||||
binding.contentMain.appTitleBar.text = getTitleString(id)
|
||||
// Note down the new current fragment
|
||||
currentFragment = id
|
||||
|
||||
// Transaction successful
|
||||
return true
|
||||
}
|
||||
|
||||
// override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// // Inflate the menu; this adds items to the action bar if it is present.
|
||||
// menuInflater.inflate(R.menu.menu_main, menu)
|
||||
// return true
|
||||
// }
|
||||
|
||||
// override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
// // Handle action bar item clicks here. The action bar will
|
||||
// // automatically handle clicks on the Home/Up button, so long
|
||||
// // as you specify a parent activity in AndroidManifest.xml.
|
||||
// return when (item.itemId) {
|
||||
// R.id.action_settings -> true
|
||||
// else -> super.onOptionsItemSelected(item)
|
||||
// }
|
||||
// }
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
val navController = findNavController(R.id.nav_host_fragment_content_main)
|
||||
return navController.navigateUp(appBarConfiguration)
|
||||
|| super.onSupportNavigateUp()
|
||||
/**
|
||||
* Retrieve fragment by the ID of the bottom navigation view's button.
|
||||
*
|
||||
* @param id the ID of the bottom navigation button
|
||||
* @return the corresponding fragment
|
||||
*/
|
||||
private fun getFragment(id: Int) = when (id) {
|
||||
R.id.food -> fragments[1] // CanteenFragment.kt
|
||||
R.id.settings -> fragments[2] // SettingsFragment.kt
|
||||
else -> fragments[0] // on unknown value or R.id.plan, launch EventFragment.kt
|
||||
}
|
||||
|
||||
// private fun getFragmentByType(type: FragmentType): Fragment {
|
||||
//// Log.d("AAA!", "help")
|
||||
// return when (type) {
|
||||
// FragmentType.SCHEDULE -> EventFragment()
|
||||
// FragmentType.FOOD -> FoodFragment()
|
||||
// FragmentType.SETTINGS -> SettingsFragment()
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun bottomIdToType(id: Int): FragmentType = when (id) {
|
||||
R.id.food -> FragmentType.FOOD
|
||||
R.id.settings -> FragmentType.SETTINGS
|
||||
else -> FragmentType.SCHEDULE
|
||||
}
|
||||
|
||||
private fun getFragment(type: FragmentType) = when (type) {
|
||||
FragmentType.SCHEDULE -> fragments[0]
|
||||
FragmentType.FOOD -> fragments[1]
|
||||
FragmentType.SETTINGS -> fragments[2]
|
||||
}
|
||||
|
||||
private fun getTitleString(type: FragmentType) = when (type) {
|
||||
FragmentType.SCHEDULE -> getString(R.string.title_schedule)
|
||||
FragmentType.FOOD -> getString(R.string.title_food)
|
||||
FragmentType.SETTINGS -> getString(R.string.title_settings)
|
||||
}
|
||||
|
||||
enum class FragmentType(val type: String) {
|
||||
SCHEDULE("schedule"),
|
||||
FOOD("food"),
|
||||
SETTINGS("settings"),
|
||||
/**
|
||||
* Retrieve app bar text by the ID of the bottom navigation view's button.
|
||||
*
|
||||
* @param id the ID of the bottom navigation button
|
||||
* @return the corresponding fragment's title
|
||||
*/
|
||||
private fun getTitleString(id: Int) = when (id) {
|
||||
R.id.food -> getString(R.string.title_food)
|
||||
R.id.settings -> getString(R.string.title_settings)
|
||||
else -> getString(R.string.title_schedule) // on unknown value or R.id.plan, get text for EventFragment.kt
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,9 @@ class CanteenFragment : Fragment() {
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
// BUG: if the fragment is changed before the refresh is finished, the app crashes with a NPE
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var liveData: LiveData<List<CanteenOffer>>
|
||||
|
||||
private lateinit var viewPagerAdapter: CanteenOfferPageAdapter
|
||||
private val viewModel: CanteenViewModel by viewModels()
|
||||
|
||||
|
||||
@@ -3,23 +3,40 @@ package com.denizk0461.studip.model
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
/**
|
||||
* Entity for storing entries of the user's Stud.IP schedule. Registered in the app database.
|
||||
*
|
||||
* @param id primary key that uniquely identifies the entry
|
||||
* @param title title of the event
|
||||
* @param lecturer lecturer(s) organising the event
|
||||
* @param room room the event takes place in
|
||||
* @param day day the event takes place on; 0 = Monday, 4 = Friday
|
||||
* @param timeslotStart time the event starts at
|
||||
* @param timeslotEnd time the event ends at
|
||||
* @param colour user-defined colour the event will be shown in - UNIMPLEMENTED
|
||||
*/
|
||||
@Entity(tableName = "events")
|
||||
data class StudIPEvent(
|
||||
@PrimaryKey val id: Int,
|
||||
val title: String, // the name of the event
|
||||
val lecturer: String, // the lecturer(s)
|
||||
val room: String, // the room the event takes place in
|
||||
val day: Int, // 0 = Monday, 1 = Tuesday, 2 = Wednesday, 3 = Thursday, 4 = Friday
|
||||
val timeslotStart: String, // the time the course starts
|
||||
val timeslotEnd: String, // the time the course ends
|
||||
val colour: String = "000000", // user-defined colour, TODO implement this
|
||||
val title: String,
|
||||
val lecturer: String,
|
||||
val room: String,
|
||||
val day: Int,
|
||||
val timeslotStart: String,
|
||||
val timeslotEnd: String,
|
||||
val colour: String = "000000",
|
||||
) {
|
||||
|
||||
// @Ignore private val timeSlotStartTimes: List<String> = listOf("06:15", "08:15", "10:15", "12:15", "14:15", "16:15", "18:15", "20:15")
|
||||
// @Ignore private val timeSlotEndTimes: List<String> = listOf("07:45", "09:45", "11:45", "13:45", "15:45", "17:45", "19:45", "21:45")
|
||||
|
||||
/**
|
||||
* Parse timeslotStart and timeslotEnd into an easily readable string in the following format:
|
||||
* 12:15 – 13:45
|
||||
*
|
||||
* @return formatted time string
|
||||
*/
|
||||
fun timeslot(): String = "$timeslotStart – $timeslotEnd"
|
||||
|
||||
// Auto-generated methods
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
@@ -39,6 +56,7 @@ data class StudIPEvent(
|
||||
result = 31 * result + day
|
||||
result = 31 * result + timeslotStart.hashCode()
|
||||
result = 31 * result + timeslotEnd.hashCode()
|
||||
result = 31 * result + colour.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user