Moved the fetching task to a separate activity; changed the time attributes in StudIPEvent.kt to strings

This commit is contained in:
denizk0461
2023-04-12 18:58:10 +02:00
parent 4f78eb8373
commit d55a304cec
11 changed files with 131 additions and 29 deletions
+5 -1
View File
@@ -21,10 +21,14 @@
android:theme="@style/Theme.StudIPTimetable"> android:theme="@style/Theme.StudIPTimetable">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".activity.FetcherActivity"
android:exported="false"
android:theme="@style/Theme.StudIPTimetable">
</activity>
</application> </application>
</manifest> </manifest>
@@ -0,0 +1,68 @@
package com.denizk0461.studip.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import com.denizk0461.studip.R
import com.denizk0461.studip.data.StudIPParser
import com.denizk0461.studip.databinding.ActivityFetcherBinding
import com.denizk0461.studip.viewmodel.FetcherViewModel
import java.net.URLDecoder
class FetcherActivity : Activity() {
private lateinit var binding: ActivityFetcherBinding
private lateinit var viewModel: FetcherViewModel
/*
* Enabling JavaScript is necessary to get the HTML source in this context. As the HTML can only
* be accessed once the user has logged in, a simple HTML request would not suffice here.
*/
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider.AndroidViewModelFactory(this.application).create(FetcherViewModel::class.java)
binding = ActivityFetcherBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.webview.settings.apply {
javaScriptEnabled = true
}
binding.webview.webViewClient = object : WebViewClient() {
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>');"
)
}
}
binding.webview.loadUrl("https://elearning.uni-bremen.de/index.php?again=yes")
binding.fab.setOnClickListener { view ->
binding.webview.evaluateJavascript(
"(function(){return encodeURI(document.getElementsByTagName('html')[0].innerHTML)})();"
) { p0 ->
viewModel.nukeEvents()
StudIPParser().parse(URLDecoder.decode(p0, "UTF-8")) { events ->
viewModel.insertEvents(events)
Toast.makeText(this, R.string.toast_fetch_finished, Toast.LENGTH_SHORT).show()
finish()
}
}
}
}
}
@@ -4,15 +4,15 @@ import com.denizk0461.studip.model.StudIPEvent
object Misc { object Misc {
val events: Array<StudIPEvent> = arrayOf( // val events: Array<StudIPEvent> = arrayOf(
StudIPEvent(1, "Literatures", "Nittel", "MZH 1380/1400", 0, 3, 1), // StudIPEvent(1, "Literatures", "Nittel", "MZH 1380/1400", 0, 3, 1),
StudIPEvent(2, "Linguistics", "Du Bois", "SFG 1020", 1, 1, 1), // StudIPEvent(2, "Linguistics", "Du Bois", "SFG 1020", 1, 1, 1),
StudIPEvent(3, "System Erde", "Zolitschka, Labuhn-Deroubaix", "GW2 B1410", 1, 3, 1), // StudIPEvent(3, "System Erde", "Zolitschka, Labuhn-Deroubaix", "GW2 B1410", 1, 3, 1),
StudIPEvent(4, "Key Moments", "Esders-Angermund", "GW2 B2890", 2, 2, 1), // StudIPEvent(4, "Key Moments", "Esders-Angermund", "GW2 B2890", 2, 2, 1),
StudIPEvent(5, "ULS-1", "Herrmann", "GW2 B2890", 2, 3, 1), // StudIPEvent(5, "ULS-1", "Herrmann", "GW2 B2890", 2, 3, 1),
StudIPEvent(6, "Gesellschaft und Raum", "Lossau, Mossig", "GW2 B1820", 3, 1, 1), // StudIPEvent(6, "Gesellschaft und Raum", "Lossau, Mossig", "GW2 B1820", 3, 1, 1),
StudIPEvent(7, "Gesellschaft und Raum2", "Lossau, Mossig", "GW2 B1820", 4, 4, 2), // StudIPEvent(7, "Gesellschaft und Raum2", "Lossau, Mossig", "GW2 B1820", 4, 4, 2),
) // )
private val timeslotStartMap: Map<String, Int> = mapOf( private val timeslotStartMap: Map<String, Int> = mapOf(
"6:00" to 0, "6:00" to 0,
@@ -9,7 +9,7 @@ class StudIPParser {
var id = 0 var id = 0
val doc = Jsoup.parse(html) val doc = Jsoup.parse(html)
val newEvents = mutableListOf<StudIPEvent>() val newEvents = mutableListOf<StudIPEvent>()
val columns = arrayOf( val columns = arrayOf( // TODO THIS NEEDS TO BE DYNAMIC!
doc.getElementById("calendar_view_1_column_0"), doc.getElementById("calendar_view_1_column_0"),
doc.getElementById("calendar_view_1_column_1"), doc.getElementById("calendar_view_1_column_1"),
doc.getElementById("calendar_view_1_column_2"), doc.getElementById("calendar_view_1_column_2"),
@@ -24,7 +24,8 @@ class StudIPParser {
val parsedLecturers = entryHeader.substring(delimiter + 1 until entryHeader.length - 1) val parsedLecturers = entryHeader.substring(delimiter + 1 until entryHeader.length - 1)
val entryInfos = entry.getElementsByTag("dd")[0].text().split(", ", limit = 2) val entryInfos = entry.getElementsByTag("dd")[0].text().split(", ", limit = 2)
val timeSlot = Misc.parseTimeslot(entryInfos[0]) // val timeSlot = Misc.parseTimeslot(entryInfos[0])
val timeSlot = entryInfos[0].split(" - ")
val event = StudIPEvent( val event = StudIPEvent(
id = id, id = id,
@@ -32,8 +33,8 @@ class StudIPParser {
lecturer = parsedLecturers, lecturer = parsedLecturers,
room = entryInfos[1], room = entryInfos[1],
day = index, day = index,
timeslotStart = timeSlot.first, timeslotStart = timeSlot[0],
timeslotEnd = timeSlot.second, timeslotEnd = timeSlot[1],
) )
newEvents.add(event) newEvents.add(event)
id += 1 id += 1
@@ -6,7 +6,7 @@ import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.denizk0461.studip.model.StudIPEvent import com.denizk0461.studip.model.StudIPEvent
@Database(entities = [StudIPEvent::class], version = 2) @Database(entities = [StudIPEvent::class], version = 3)
abstract class EventDatabase : RoomDatabase() { abstract class EventDatabase : RoomDatabase() {
abstract fun dao(): EventDAO abstract fun dao(): EventDAO
@@ -1,5 +1,6 @@
package com.denizk0461.studip.fragment package com.denizk0461.studip.fragment
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -8,6 +9,7 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.NavHostFragment.Companion.findNavController import androidx.navigation.fragment.NavHostFragment.Companion.findNavController
import com.denizk0461.studip.R import com.denizk0461.studip.R
import com.denizk0461.studip.activity.FetcherActivity
import com.denizk0461.studip.adapter.StudIPEventPageAdapter import com.denizk0461.studip.adapter.StudIPEventPageAdapter
import com.denizk0461.studip.databinding.FragmentEventBinding import com.denizk0461.studip.databinding.FragmentEventBinding
import com.denizk0461.studip.viewmodel.EventViewModel import com.denizk0461.studip.viewmodel.EventViewModel
@@ -90,7 +92,8 @@ class EventFragment : Fragment() {
} }
private fun launchWebview() { private fun launchWebview() {
findNavController(this).navigate(R.id.action_FirstFragment_to_SecondFragment) // findNavController(this).navigate(R.id.action_FirstFragment_to_SecondFragment)
startActivity(Intent(context, FetcherActivity::class.java))
} }
/** /**
@@ -11,7 +11,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.denizk0461.studip.data.StudIPParser import com.denizk0461.studip.data.StudIPParser
import com.denizk0461.studip.databinding.FragmentParserBinding import com.denizk0461.studip.databinding.FragmentParserBinding
import com.denizk0461.studip.viewmodel.ParserViewModel import com.denizk0461.studip.viewmodel.FetcherViewModel
import java.net.URLDecoder import java.net.URLDecoder
/** /**
@@ -19,14 +19,14 @@ import java.net.URLDecoder
*/ */
class ParserFragment : Fragment() { class ParserFragment : Fragment() {
private var _binding: FragmentParserBinding? = null
private var html = "" // temporary storage for the website HTML private var html = "" // temporary storage for the website HTML
private var _binding: FragmentParserBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private val viewModel: ParserViewModel by viewModels() private val viewModel: FetcherViewModel by viewModels()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@@ -11,14 +11,14 @@ data class StudIPEvent(
val lecturer: String, // the lecturer(s) val lecturer: String, // the lecturer(s)
val room: String, // the room the event takes place in val room: String, // the room the event takes place in
val day: Int, // 0 = Monday, 1 = Tuesday, 2 = Wednesday, 3 = Thursday, 4 = Friday val day: Int, // 0 = Monday, 1 = Tuesday, 2 = Wednesday, 3 = Thursday, 4 = Friday
val timeslotStart: Int, // 1 = 08:15, 2 = 10:15, 3 = 12:15, 4 = 14:15, 5 = 16:15, 6 = 18:15 val timeslotStart: String, // the time the course starts
val timeslotEnd: Int, // 1 = 09:45 etc. val timeslotEnd: String, // the time the course ends
) { ) {
@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 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") // @Ignore private val timeSlotEndTimes: List<String> = listOf("07:45", "09:45", "11:45", "13:45", "15:45", "17:45", "19:45", "21:45")
fun timeslot(): String = "${timeSlotStartTimes[timeslotStart]} ${timeSlotEndTimes[timeslotEnd]}" fun timeslot(): String = "$timeslotStart $timeslotEnd"
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -37,10 +37,8 @@ data class StudIPEvent(
result = 31 * result + lecturer.hashCode() result = 31 * result + lecturer.hashCode()
result = 31 * result + room.hashCode() result = 31 * result + room.hashCode()
result = 31 * result + day result = 31 * result + day
result = 31 * result + timeslotStart result = 31 * result + timeslotStart.hashCode()
result = 31 * result + timeslotEnd result = 31 * result + timeslotEnd.hashCode()
result = 31 * result + timeSlotStartTimes.hashCode()
result = 31 * result + timeSlotEndTimes.hashCode()
return result return result
} }
} }
@@ -3,7 +3,7 @@ package com.denizk0461.studip.viewmodel
import android.app.Application import android.app.Application
import com.denizk0461.studip.model.StudIPEvent import com.denizk0461.studip.model.StudIPEvent
class ParserViewModel(app: Application) : TemplateViewModel(app) { class FetcherViewModel(app: Application) : TemplateViewModel(app) {
fun insertEvent(event: StudIPEvent) { repo.insertEvent(event) } fun insertEvent(event: StudIPEvent) { repo.insertEvent(event) }
fun insertEvents(events: List<StudIPEvent>) { doAsync { repo.insertEvents(events) } } fun insertEvents(events: List<StudIPEvent>) { doAsync { repo.insertEvents(events) } }
@@ -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">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="@dimen/fab_margin"
app:srcCompat="@drawable/check" />
</androidx.constraintlayout.widget.ConstraintLayout>
+2
View File
@@ -19,6 +19,8 @@
<string name="nav_food">Food</string> <string name="nav_food">Food</string>
<string name="nav_settings">Settings</string> <string name="nav_settings">Settings</string>
<string name="toast_fetch_finished">Your schedule has been refreshed!</string>
<string name="lorem_ipsum"> <string name="lorem_ipsum">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris
volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus