diff --git a/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt b/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt index 4d54a7b..78faf0a 100644 --- a/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt +++ b/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt @@ -2,6 +2,7 @@ package com.denizk0461.studip.activity import android.annotation.SuppressLint import android.os.Bundle +import android.view.View import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient @@ -28,6 +29,9 @@ class FetcherActivity : FragmentActivity() { // View model private lateinit var viewModel: FetcherViewModel + // URL of the schedule + private val scheduleUrl: String = "https://elearning.uni-bremen.de/dispatch.php/calendar/schedule" + /** * 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. @@ -44,10 +48,14 @@ class FetcherActivity : FragmentActivity() { binding = ActivityFetcherBinding.inflate(layoutInflater) setContentView(binding.root) + // Remind the user of the help button + showToast(this, getString(R.string.toast_info_reminder)) + // 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( @@ -56,6 +64,18 @@ class FetcherActivity : FragmentActivity() { ): Boolean { return false } + + override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) { + // Check whether the button to save the timetable should be shown + if (url?.contains(scheduleUrl) == true) { + binding.fab.visibility = View.VISIBLE + } else { + binding.fab.visibility = View.GONE + } + + // Super call + super.doUpdateVisitedHistory(view, url, isReload) + } } // Set up bottom app bar for various actions @@ -77,6 +97,7 @@ class FetcherActivity : FragmentActivity() { val bundle = Bundle() bundle.putString("header", getString(R.string.fetch_bar_help_sheet_title)) bundle.putString("content", getString(R.string.fetch_bar_help_sheet_content)) + bundle.putBoolean("isCancellable", true) sheet.arguments = bundle }.show(supportFragmentManager, TextSheet::class.java.simpleName) true @@ -96,7 +117,7 @@ class FetcherActivity : FragmentActivity() { binding.fab.setOnClickListener { if (binding.webview.url?.contains( - "https://elearning.uni-bremen.de/dispatch.php/calendar/schedule" + scheduleUrl ) == true) { /* * Retrieve encoded HTML via JavaScript function. Encoding is done as otherwise not all diff --git a/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt b/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt index fcf93df..b526761 100644 --- a/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt +++ b/app/src/main/java/com/denizk0461/studip/activity/MainActivity.kt @@ -39,6 +39,7 @@ class MainActivity : FragmentActivity() { binding.navHostFragmentContentMain.getFragment().findNavController() ) + // Set up navigation component setNavigationGraph() } diff --git a/app/src/main/java/com/denizk0461/studip/data/Extensions.kt b/app/src/main/java/com/denizk0461/studip/data/Extensions.kt index ac6875e..52bac06 100644 --- a/app/src/main/java/com/denizk0461/studip/data/Extensions.kt +++ b/app/src/main/java/com/denizk0461/studip/data/Extensions.kt @@ -66,7 +66,7 @@ fun Resources.Theme.showErrorSnackBar(view: CoordinatorLayout, text: String, anc } /** - * Retrieve a [TextSheet] with text content added as arguments bundle. + * Retrieve a cancellable [TextSheet] with text content added as arguments bundle. * * @param header header of the sheet * @param content text content of the sheet @@ -76,6 +76,7 @@ fun getTextSheet(header: String, content: String): TextSheet = TextSheet().also val bundle = Bundle() bundle.putString("header", header) bundle.putString("content", content) + bundle.putBoolean("isCancellable", true) sheet.arguments = bundle } diff --git a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt index 52264a6..9588f95 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt @@ -37,8 +37,15 @@ class CanteenFragment : AppFragment() { // View model reference for providing access to the database private val viewModel: CanteenViewModel by viewModels() + /** + * Locally saved opening hours to quickly access them without querying the database on every + * opening of the bottom sheet. + */ private var openingHours = "" + /** + * Locally stored dates to populate the TabLayout with. + */ private var dates: List = listOf() // Instantiate the view binding diff --git a/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt index c900404..1f3eb64 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt @@ -65,12 +65,8 @@ class EventFragment : AppFragment() { // Set the view pager's current page to the current day, if the user chose this option if (viewModel.preferenceCurrentDay) { - /* - * Determine the current day and go to the respective page. - * BUG: this selects the correct tab and changes the page, but it doesn't highlight - * the tab that has been selected. binding.viewPager.currentPage encounters the same bug - */ - binding.dayTabLayout.getTabAt( + // Determine the current day and go to the respective page. + binding.viewPager.setCurrentItem( when (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) { Calendar.TUESDAY -> 1 Calendar.WEDNESDAY -> 2 @@ -79,11 +75,16 @@ class EventFragment : AppFragment() { Calendar.SATURDAY -> 5 Calendar.SUNDAY -> 6 else -> 0 // assume Monday - } - )?.select() + }, false + ) } + // Set up button for adding a new event binding.fabAddEvent.setOnClickListener { + /* + * Open the bottom sheet used for editing an event, but tell the sheet that it will be + * used for creating a new event instead. + */ openBottomSheet( ScheduleUpdateSheet().also { sheet -> val bundle = Bundle() @@ -92,29 +93,17 @@ class EventFragment : AppFragment() { } ) } - } - /** - * Called when the layout changes (e.g. device rotation) but NOT when the fragment is selected - * from the bottom navigation view. - */ -// override fun onSaveInstanceState(outState: Bundle) { -// super.onSaveInstanceState(outState) -// -// outState.putInt( -// "viewPagerCurrentPage", -// binding.viewPager.currentItem -// ) -// } -// -// override fun onViewStateRestored(savedInstanceState: Bundle?) { -// super.onViewStateRestored(savedInstanceState) -// -// // Retrieve -// val restoredPage = savedInstanceState?.getInt("viewPagerCurrentPage") ?: 0 -// -//// binding.viewPager.currentItem = restoredPage -// } + // Show the user a little message if they're opening the app for the first time + if (viewModel.preferenceFirstLaunch) { + + // Show toast +// showToast(context, getString(R.string.toast_first_launch)) + + // Remember that the app has been launched before + viewModel.preferenceFirstLaunch = false + } + } // Invalidate the view binding override fun onDestroyView() { diff --git a/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt b/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt index 5337f06..fe811a5 100644 --- a/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt +++ b/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt @@ -8,6 +8,11 @@ package com.denizk0461.studip.model */ enum class SettingsPreferences(val key: String) { + /** + * Whether the user is launching this app for the first time. + */ + FIRST_LAUNCH("check_first_launch"), + /** * Which allergens the user wants displayed or hidden. */ diff --git a/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt index ee7e6e7..ada4748 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt @@ -57,6 +57,7 @@ class DevCodeSheet : AppSheet(R.layout.sheet_dev_code) { "UE9XRUxM".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS9XUGMtVkVxQlBISQ==".d64) "Q0FMTE1F".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS8xbTV1WnJNMnktMA==".d64) "SkFNSVJP".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS9fMVZoT2c0N3ozNA==".d64) + "TE9ORUxZ".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS9uLURFNHNfc3d5Zw==".d64) "Qk9KQUNL".d64 -> { startActivity(Intent(context, ImageActivity::class.java).also { intent -> val bundle = Bundle() diff --git a/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt index 38f0d70..3b9a73e 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt @@ -23,16 +23,26 @@ class TextSheet : AppSheet(R.layout.sheet_text) { // Retrieve content for the window val content = arguments?.getString("content") ?: "" + // Set whether the sheet should be cancellable by actions such as swiping + val isSheetCancellable = arguments?.getBoolean("isCancellable") == true + // Set text values binding.apply { textHeader.text = header textContent.text = content } - // Set up close button - binding.buttonCancel.setOnClickListener { - // Do nothing and dismiss the sheet - dismiss() + // Set up an appropriate close button depending on whether the sheet is cancellable + if (isSheetCancellable) { + // Set up a simple X button to be able to close the sheet + binding.buttonCancel.visibility = View.VISIBLE + binding.buttonCancel.setOnClickListener { + // Do nothing and dismiss the sheet + dismiss() + } + } else { + // Show a more prominent button to close the sheet + binding.buttonCancel.visibility = View.GONE } } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt b/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt index 65c68ba..f2c5820 100644 --- a/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/EventViewModel.kt @@ -16,6 +16,13 @@ class EventViewModel(app: Application) : AppViewModel(app) { val preferenceCurrentDay: Boolean get() = repo.getBooleanPreference( SettingsPreferences.CURRENT_DAY, - defaultValue = true + defaultValue = true, ) + + var preferenceFirstLaunch: Boolean + get() = repo.getBooleanPreference( + SettingsPreferences.FIRST_LAUNCH, + defaultValue = true, + ) + set(value) { repo.setPreference(SettingsPreferences.FIRST_LAUNCH, value) } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_fetcher.xml b/app/src/main/res/layout/activity_fetcher.xml index 41bbcb0..bb8b72a 100644 --- a/app/src/main/res/layout/activity_fetcher.xml +++ b/app/src/main/res/layout/activity_fetcher.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/root_view" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:animateLayoutChanges="true"> - - - + app:layout_anchor="@id/bottom_app_bar" + android:visibility="gone"/> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 8851b23..2885e3c 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -404,13 +404,14 @@ - + + android:paddingVertical="8dp" + android:visibility="gone"> - - diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 124441d..89b3389 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,6 +1,8 @@ Stud.IP Timetable + Hey, danke, dass Du meine App ausprobierst! ~Deniz + Montag Dienstag Mittwoch @@ -28,6 +30,7 @@ Dein Stundenplan wurde aktualisiert, doch eine Veranstaltung konnte nicht heruntergeladen werden. Dein Stundenplan wurde aktualisiert, doch %d Veranstaltungen konnten nicht heruntergeladen werden. + Klick\' das Info-Icon unten, falls du Hilfe benötigst. Schließen Schließe die Webseite, ohne etwas zu speichern Aktualisieren @@ -35,7 +38,7 @@ Hilfe Zeige Hilfe Wie Du Deinen Stundenplan in der App speichern kannst - • Logge dich mit deinem Stud.IP-Login ein\n • Öffne deinen Stundenplan (falls er sich nicht automatisch öffnet)\n • Klicke auf den \'Speichern\'-Knopf rechts in der unteren Ecke\n • Fertig! + • Logge dich mit deinem Stud.IP-Login ein\n • Öffne deinen Stundenplan (falls er sich nicht automatisch öffnet)\n • Klicke auf den \'Speichern\'-Knopf, der unten in der rechten Ecke auftauchen sollte\n • Fertig! Speichere den Stundenplan in der App ab Speichern @@ -181,8 +184,11 @@ Was passiert mit meinen Daten? Stiehlst du meinen Stud.IP-Login? - TL;DR: nein, aber Google könnte mir Bescheid geben, falls die App mal abstürzt. - Die App funktioniert standalone, was bedeutet, dass sie sich niemals mit einem Server verbindet, der mir gehört. Sie ist ein Web-Scraper, also ein Programm, welches eine Internetseite öffnet und dann all die Informationen dieser Webseite herunterlädt und analysiert. In diesem Fall sind es die Webseiten der Mensen und Cafeterien des Studierendenwerks Bremen (stw-bremen.de) sowie die Stud.IP-Seite (elearning.uni-bremen.de).\n\nDies funktioniert relativ einfach für die Essenspläne, da diese Pläne öffentlich zugänglich sind. Um jedoch Deinen persönlichen Stud.IP-Stundenplan herunterzuladen, braucht die App Zugriff auf diese Webseite während Dein Account eingeloggt ist. Dies erfolgt, indem die App Dich mit deinem Account einloggen lässt - dies geschieht, wenn Du den Knopf zum Aktualisieren des Plans drückst und die App Dir die Login-Seite zeigt - und Du dann den Knopf klickst, um den Inhalt, den HTML-Quellcode, herunterzuladen. Der HTML-Quellcode beinhält Informationen darüber, was auf einer Webseite angezeigt wird und wie es aussehen soll, aber er kann niemals persönliche Details beinhalten, wie zum Beispiel Passwörter oder andere Daten, die auf Deinem Gerät vorhanden sind.\n\nDer Quellcode wird dann lokal, also auf Deinem Gerät, zu den Daten verarbeitet, die Du dann in der App sehen kannst, ohne, dass jegliche Daten irgendwohin geschickt werden müssten.\n\nEinige Daten – welche nichts mit Deinen Stud.IP-Daten zu tun haben – könnten jedoch dennoch nach außen geschickt werden. Diese App nutzt \"Crashlytics\" von Google. Dies ist ein Tool, welches Absturzreporte an Google-Server schickt, wenn in der App etwas schief läuft. Diese werden genutzt, um Probleme in der App zu lösen.\n\nDies ist jedoch gemäß europäischer Datenschutz-Grundverordnung Opt-In, was bedeutet, dass Du zustimmen musst, bevor Daten nach außen geschickt werden. Du wurdest danach gefragt, als Du die App zum ersten Mal gestartet hast, und du kannst jederzeit deine Meinung ändern, indem Du den Schalter hierfür in den Einstellungen umstellst. Solltest Du dich entscheiden, keine Daten verschicken zu lassen, wird auch nichts mit Google (und mir) geteilt.\n\nSolltest du dich versichern wollen, dass wirklich keine Daten an irgendwelche Server verschickt wird (abgesehen von den Absturzreports an Google), kannst Du jederzeit das Projekt erkunden, da es Open-Source und auf meinen GitHub-Profil öffentlich zugänglich ist:\nhttps://github.com/denizk0461/studip-timetable/\n\nSchreib mir ansonsten gerne eine E-Mail:\ndenizk0461@gmail.com + TL;DR: nein. + Die App funktioniert standalone, was bedeutet, dass sie sich niemals mit einem Server verbindet, der mir gehört. Sie ist ein Web-Scraper, also ein Programm, welches eine Internetseite öffnet und dann all die Informationen dieser Webseite herunterlädt und analysiert. In diesem Fall sind es die Webseiten der Mensen und Cafeterien des Studierendenwerks Bremen (stw-bremen.de) sowie die Stud.IP-Seite (elearning.uni-bremen.de).\n\nDies funktioniert relativ einfach für die Essenspläne, da diese Pläne öffentlich zugänglich sind. Um jedoch Deinen persönlichen Stud.IP-Stundenplan herunterzuladen, braucht die App Zugriff auf diese Webseite während Dein Account eingeloggt ist. Dies erfolgt, indem die App Dich mit deinem Account einloggen lässt - dies geschieht, wenn Du den Knopf zum Aktualisieren des Plans drückst und die App Dir die Login-Seite zeigt - und Du dann den Knopf klickst, um den Inhalt, den HTML-Quellcode, herunterzuladen. Der HTML-Quellcode beinhält Informationen darüber, was auf einer Webseite angezeigt wird und wie es aussehen soll, aber er kann niemals persönliche Details beinhalten, wie zum Beispiel Passwörter oder andere Daten, die auf Deinem Gerät vorhanden sind.\n\nDer Quellcode wird dann lokal, also auf Deinem Gerät, zu den Daten verarbeitet, die Du dann in der App sehen kannst, ohne, dass jegliche Daten irgendwohin geschickt werden müssten.\n\nSolltest du dich versichern wollen, dass wirklich keine Daten an irgendwelche Server verschickt wird, kannst Du jederzeit das Projekt erkunden, da es Open-Source und auf meinen GitHub-Profil öffentlich zugänglich ist:\nhttps://github.com/denizk0461/studip-timetable/\n\nSchreib mir ansonsten gerne eine E-Mail:\ndenizk0461@gmail.com\ndeniz7@uni-bremen.de + + TL;DR: nein, aber Google könnte mir Bescheid geben, falls die App mal abstürzt. + Die App funktioniert standalone, was bedeutet, dass sie sich niemals mit einem Server verbindet, der mir gehört. Sie ist ein Web-Scraper, also ein Programm, welches eine Internetseite öffnet und dann all die Informationen dieser Webseite herunterlädt und analysiert. In diesem Fall sind es die Webseiten der Mensen und Cafeterien des Studierendenwerks Bremen (stw-bremen.de) sowie die Stud.IP-Seite (elearning.uni-bremen.de).\n\nDies funktioniert relativ einfach für die Essenspläne, da diese Pläne öffentlich zugänglich sind. Um jedoch Deinen persönlichen Stud.IP-Stundenplan herunterzuladen, braucht die App Zugriff auf diese Webseite während Dein Account eingeloggt ist. Dies erfolgt, indem die App Dich mit deinem Account einloggen lässt - dies geschieht, wenn Du den Knopf zum Aktualisieren des Plans drückst und die App Dir die Login-Seite zeigt - und Du dann den Knopf klickst, um den Inhalt, den HTML-Quellcode, herunterzuladen. Der HTML-Quellcode beinhält Informationen darüber, was auf einer Webseite angezeigt wird und wie es aussehen soll, aber er kann niemals persönliche Details beinhalten, wie zum Beispiel Passwörter oder andere Daten, die auf Deinem Gerät vorhanden sind.\n\nDer Quellcode wird dann lokal, also auf Deinem Gerät, zu den Daten verarbeitet, die Du dann in der App sehen kannst, ohne, dass jegliche Daten irgendwohin geschickt werden müssten.\n\nEinige Daten – welche nichts mit Deinen Stud.IP-Daten zu tun haben – könnten jedoch dennoch nach außen geschickt werden. Diese App nutzt \"Crashlytics\" von Google. Dies ist ein Tool, welches Absturzreporte an Google-Server schickt, wenn in der App etwas schief läuft. Diese werden genutzt, um Probleme in der App zu lösen.\n\nDies ist jedoch gemäß europäischer Datenschutz-Grundverordnung Opt-In, was bedeutet, dass Du zustimmen musst, bevor Daten nach außen geschickt werden. Du wurdest danach gefragt, als Du die App zum ersten Mal gestartet hast, und du kannst jederzeit deine Meinung ändern, indem Du den Schalter hierfür in den Einstellungen umstellst. Solltest Du dich entscheiden, keine Daten verschicken zu lassen, wird auch nichts mit Google (und mir) geteilt.\n\nSolltest du dich versichern wollen, dass wirklich keine Daten an irgendwelche Server verschickt wird (abgesehen von den Absturzreports an Google), kannst Du jederzeit das Projekt erkunden, da es Open-Source und auf meinen GitHub-Profil öffentlich zugänglich ist:\nhttps://github.com/denizk0461/studip-timetable/\n\nSchreib mir ansonsten gerne eine E-Mail:\ndenizk0461@gmail.com\ndeniz7@uni-bremen.de Siehe Lizenzen Ressourcen, die diese App möglich gemacht haben @@ -191,6 +197,4 @@ klick weiter Solltest du nicht für deine Klausuren lernen? - - mit ♡️ aus Bremen-Osterholz\nvon denizk0461 \\__* \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index da27b0c..863e737 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,8 @@ Stud.IP Timetable + Hey, thank you for trying out my app! ~Deniz + Monday Tuesday Wednesday @@ -28,6 +30,7 @@ Your schedule has been refreshed, but one item could not be saved. Your schedule has been refreshed, but %d items could not be saved. + Click the info icon at the bottom if you need any help here. Close Close the website without saving anything Refresh @@ -35,7 +38,7 @@ Help Show help How to save your timetable in the app - • Log in with your Stud.IP login\n • Open your timetable view (in case it doesn\'t open automatically)\n • Click the \'save\' button in the bottom right corner\n • Done! + • Log in with your Stud.IP login\n • Open your timetable view (in case it doesn\'t open automatically)\n • Click the \'save\' button that should appear in the bottom right corner\n • Done! Save the timetable in the app Save @@ -185,8 +188,11 @@ What happens with my data? Will you steal my Stud.IP login? - TL;DR: no, but Google may let me know if the app crashes on your device. - This app works standalone, which is to say, it doesn\'t ever connect to a server I own. Instead, it is a web scraper, which means that it opens a website and then downloads and analyses all the data from that website by itself. In this case, those websites are the canteen websites of the Studierendenwerk Bremen (stw-bremen.de) and the Stud.IP page (elearning.uni-bremen.de).\n\nThis works fairly easily for the canteen offers, since the plans are public and anyone can access them. However, to access your personal Stud.IP schedule, the app needs access to that website, with your account logged in. This is done by letting you log into the website by yourself (which happens when you click the button to refresh the schedule and the app opens the login page) and then clicking a button to download the content, the HTML source code, of the website. The HTML source code contains information about which elements are shown on the website and how they look, but it can never contain your personal details such as passwords, or any other sensitive data you may store on your device.\n\nThe HTML source is then processed locally on your device, which means that your device will handle converting the HTML source to the data you will be shown in the app by itself, without needing to send any data anywhere.\n\nHowever, some data – which has nothing to do with your Stud.IP data – may still be sent outside of your device. This app uses \"Crashlytics\" by Google, which is a tool that sends crash reports to Google servers if something breaks. These are used to fix problems that occur in the app.\n\nThis, in compliance with the European General Data Protection Regulation, is of course opt-in, which means that you need to accept this before any data is sent outside of your device. You have been prompted for this when you first started the app, and you can always change your mind in the app settings by clicking the corresponding switch. Should you choose to not share crash data with Google (and me), no data will be sent outside of your device.\n\nShould you want to reassure yourself that no data is sent to any servers (outside of the crash reports from Google), feel free to explore the project, since it is open-source and documented on my GitHub profile:\nhttps://github.com/denizk0461/studip-timetable/\n\nYou\'re also welcome to send me an email:\ndenizk0461@gmail.com + TL;DR: no. + This app works standalone, which is to say, it doesn\'t ever connect to a server I own. Instead, it is a web scraper, which means that it opens a website and then downloads and analyses all the data from that website by itself. In this case, those websites are the canteen websites of the Studierendenwerk Bremen (stw-bremen.de) and the Stud.IP page (elearning.uni-bremen.de).\n\nThis works fairly easily for the canteen offers, since the plans are public and anyone can access them. However, to access your personal Stud.IP schedule, the app needs access to that website, with your account logged in. This is done by letting you log into the website by yourself (which happens when you click the button to refresh the schedule and the app opens the login page) and then clicking a button to download the content, the HTML source code, of the website. The HTML source code contains information about which elements are shown on the website and how they look, but it can never contain your personal details such as passwords, or any other sensitive data you may store on your device.\n\nThe HTML source is then processed locally on your device, which means that your device will handle converting the HTML source to the data you will be shown in the app by itself, without needing to send any data anywhere.\n\nShould you want to reassure yourself that no data is sent to any servers, feel free to explore the project, since it is open-source and documented on my GitHub profile:\nhttps://github.com/denizk0461/studip-timetable/\n\nYou\'re also welcome to send me an email:\ndenizk0461@gmail.com\ndeniz7@uni-bremen.de + + TL;DR: no, but Google may let me know if the app crashes on your device. + This app works standalone, which is to say, it doesn\'t ever connect to a server I own. Instead, it is a web scraper, which means that it opens a website and then downloads and analyses all the data from that website by itself. In this case, those websites are the canteen websites of the Studierendenwerk Bremen (stw-bremen.de) and the Stud.IP page (elearning.uni-bremen.de).\n\nThis works fairly easily for the canteen offers, since the plans are public and anyone can access them. However, to access your personal Stud.IP schedule, the app needs access to that website, with your account logged in. This is done by letting you log into the website by yourself (which happens when you click the button to refresh the schedule and the app opens the login page) and then clicking a button to download the content, the HTML source code, of the website. The HTML source code contains information about which elements are shown on the website and how they look, but it can never contain your personal details such as passwords, or any other sensitive data you may store on your device.\n\nThe HTML source is then processed locally on your device, which means that your device will handle converting the HTML source to the data you will be shown in the app by itself, without needing to send any data anywhere.\n\nHowever, some data – which has nothing to do with your Stud.IP data – may still be sent outside of your device. This app uses \"Crashlytics\" by Google, which is a tool that sends crash reports to Google servers if something breaks. These are used to fix problems that occur in the app.\n\nThis, in compliance with the European General Data Protection Regulation, is of course opt-in, which means that you need to accept this before any data is sent outside of your device. You have been prompted for this when you first started the app, and you can always change your mind in the app settings by clicking the corresponding switch. Should you choose to not share crash data with Google (and me), no data will be sent outside of your device.\n\nShould you want to reassure yourself that no data is sent to any servers (outside of the crash reports from Google), feel free to explore the project, since it is open-source and documented on my GitHub profile:\nhttps://github.com/denizk0461/studip-timetable/\n\nYou\'re also welcome to send me an email:\ndenizk0461@gmail.com\ndeniz7@uni-bremen.de View licences Resources that made this app possible @@ -203,8 +209,6 @@ ◢◤ \\__* - with ♡️ from Bremen-Osterholz\nmade by denizk0461 \\__* - it\'s only real when it hurts can\'t go back in time, every second is a new one