Finished IntroductionActivity.kt; fixed a bug in StwParser.kt that would throw an error upon accessing the UI due to a missing withContext() call. This error seemed to (visibly?) happen on API level 24, but not on 30+

This commit is contained in:
denizk0461
2023-06-04 15:34:32 +02:00
parent 01acb1c4b5
commit 26c89108bf
12 changed files with 202 additions and 8 deletions
+1 -1
View File
@@ -12,6 +12,6 @@
</deviceKey> </deviceKey>
</Target> </Target>
</targetSelectedWithDropDown> </targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-05-23T10:00:38.080775800Z" /> <timeTargetWasSelectedWithDropDown value="2023-06-04T13:25:31.807787600Z" />
</component> </component>
</project> </project>
+7
View File
@@ -42,6 +42,13 @@
android:theme="@style/Theme.StudIPTimetable"> android:theme="@style/Theme.StudIPTimetable">
</activity> </activity>
<activity
android:name=".activity.IntroductionActivity"
android:exported="false"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/Theme.StudIPTimetable">
</activity>
<activity <activity
android:name=".activity.TimetableOverviewActivity" android:name=".activity.TimetableOverviewActivity"
android:exported="false" android:exported="false"
@@ -1,20 +1,61 @@
package com.denizk0461.weserplaner.activity package com.denizk0461.weserplaner.activity
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.denizk0461.weserplaner.databinding.ActivityIntroductionBinding import com.denizk0461.weserplaner.databinding.ActivityIntroductionBinding
import com.denizk0461.weserplaner.viewmodel.IntroductionViewModel
/**
* Introductory activity for briefly telling the user about the app's features, and to allow quick
* configuration of common settings.
*/
class IntroductionActivity : AppCompatActivity() { class IntroductionActivity : AppCompatActivity() {
// View binding
private lateinit var binding: ActivityIntroductionBinding private lateinit var binding: ActivityIntroductionBinding
// View model
private lateinit var viewModel: IntroductionViewModel
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// Instantiate view model
viewModel = ViewModelProvider.AndroidViewModelFactory(application)
.create(IntroductionViewModel::class.java)
// Inflate view binding and bind to this activity // Inflate view binding and bind to this activity
binding = ActivityIntroductionBinding.inflate(layoutInflater) binding = ActivityIntroductionBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
binding.buttonDownloadSchedule.setOnClickListener {
// Save that the user has completed the tutorial
viewModel.completedIntroduction()
// Close the activity
finish()
/*
* Start the main activity so that the user won't be thrown out of the app after they
* complete the fetch or press the back button.
*/
startActivity(Intent(this, MainActivity::class.java))
// Start the fetcher activity
startActivity(Intent(this, FetcherActivity::class.java))
}
binding.buttonContinue.setOnClickListener {
// Save that the user has completed the tutorial
viewModel.completedIntroduction()
// Close the activity
finish()
// Start the main activity
startActivity(Intent(this, MainActivity::class.java))
}
} }
} }
@@ -40,7 +40,13 @@ class MainActivity : FragmentActivity() {
* tutorial. This value will be changed to 'false' upon completing the tutorial, thus not * tutorial. This value will be changed to 'false' upon completing the tutorial, thus not
* requiring any changes to the value here. * requiring any changes to the value here.
*/ */
if (true/*viewModel.preferenceFirstLaunch*/) { if (viewModel.preferenceFirstLaunch) {
/*
* Close this activity to prevent skipping the introduction activity by pressing the
* back button.
*/
finish()
// Launch the tutorial activity // Launch the tutorial activity
startActivity(Intent(this, IntroductionActivity::class.java)) startActivity(Intent(this, IntroductionActivity::class.java))
} }
@@ -95,11 +95,15 @@ class StwParser(application: Application) {
insertItems(items) insertItems(items)
} }
// Call action to let the user know the fetch has finished /*
onFinish() * Call action to let the user know the fetch has finished. Do this on the main thread
* to access UI.
*/
withContext(Dispatchers.Main) {
onFinish()
}
} catch (e: RuntimeException) { } catch (e: RuntimeException) {
// e.printStackTrace()
/* /*
* Call action to let the user know an error occurred. Do this on the main thread to * Call action to let the user know an error occurred. Do this on the main thread to
* access UI. * access UI.
@@ -0,0 +1,16 @@
package com.denizk0461.weserplaner.viewmodel
import android.app.Application
/**
* View model for [com.denizk0461.weserplaner.activity.IntroductionActivity].
*/
class IntroductionViewModel(application: Application) : AppViewModel(application) {
/**
* Save that the user has launched the app and completed the introduction.
*/
fun completedIntroduction() {
repo.setPreferenceFirstLaunch(false)
}
}
@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
</vector>
+5
View File
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:viewportHeight="357.65"
android:viewportWidth="357.65" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000"
android:pathData="m9.82,264.18c-8.93,-19.87 -11.77,-40.45 -8.52,-61.92 0.83,-5.46 3.9,-16.85 5.97,-22.12 4.64,-11.83 11.28,-22.68 19.53,-31.95 4.28,-4.8 9.07,-9.23 15.08,-13.94 3.26,-2.55 3.29,-2.57 53.48,-31.72 27.62,-16.04 63.71,-36.87 80.2,-46.3 32.2,-18.4 30.68,-17.59 38.54,-20.44 20.37,-7.38 42.35,-8.15 63.73,-2.22 11.83,3.28 20.84,7.35 30.69,13.85 14.51,9.59 25.56,21.39 34.42,36.77 8.75,15.19 13.5,30.84 14.5,47.85 0.72,12.08 -0.34,22.3 -3.55,34.32 -2.06,7.73 -4.14,13.31 -7.37,19.8 -7.48,15.01 -16.87,26.39 -30.75,37.23 -3.17,2.48 -4.07,3.02 -32.86,19.81 -16.28,9.49 -52.37,30.33 -80.2,46.31 -54.89,31.51 -51.03,29.4 -59.16,32.34 -16.94,6.14 -35.33,7.71 -53.54,4.58 -4.8,-0.82 -14.51,-3.42 -18.76,-5.01 -20.91,-7.84 -38.04,-20.99 -50.97,-39.13 -2.99,-4.2 -8.45,-13.65 -10.46,-18.13zM42.11,266.92c8.95,12.71 21.97,22.9 36.73,28.75 4.04,1.6 12.72,3.94 17.07,4.6 5.35,0.81 11.08,1.03 16.98,0.64 9.55,-0.63 16,-2.1 25.51,-5.82 2.67,-1.05 7.87,-4 77.26,-43.97 75.11,-43.26 80.44,-46.4 85.04,-50.09 15.54,-12.46 25.59,-29.32 28.95,-48.62 2.81,-16.08 0.72,-32.39 -6.11,-47.81C321.9,100.86 317.37,93.17 314.76,89.65 306.96,79.16 297.19,70.9 285.75,65.14 280.47,62.48 276.22,60.88 270.23,59.28 261.09,56.85 253.97,56.14 244.75,56.75c-9.56,0.63 -16.02,2.1 -25.51,5.82 -2.67,1.05 -7.94,4.04 -77.27,43.97 -75.16,43.29 -80.43,46.39 -85.04,50.09 -15.54,12.46 -25.59,29.32 -28.95,48.62 -3.76,21.54 1.25,43.39 14.14,61.68zM58.8,242.34c-5.27,-11.33 -6.45,-24.6 -3.24,-36.72 1.58,-5.97 3.7,-10.64 7.21,-15.86 3.53,-5.25 7.28,-9.17 12.39,-12.94 1.25,-0.92 28.23,-16.61 77.11,-44.83C234.8,84.33 229.71,87.16 236.14,85.38 253.23,80.64 271.91,85.08 285.31,97.07c18.47,16.53 23.79,43.04 13.03,65.01 -1.56,3.19 -5.3,8.73 -7.73,11.43 -2.19,2.44 -5.26,5.21 -8.14,7.33 -1.26,0.93 -28.03,16.49 -77.13,44.84 -82.51,47.64 -77.44,44.82 -83.85,46.6 -24.98,6.93 -51.4,-5.69 -62.7,-29.95zM86.7,237.05c3.49,4.37 8.08,7.53 13.22,9.12 5.55,1.72 11.81,1.44 17.24,-0.76 2.69,-1.09 146.37,-84.05 149.27,-86.18 10.22,-7.52 13.5,-21.92 7.68,-33.7 -3.92,-7.94 -12,-13.84 -20.51,-14.97 -4.15,-0.55 -9.19,0.1 -13.11,1.69 -2.69,1.09 -146.37,84.04 -149.27,86.18 -10.64,7.84 -13.71,22.99 -7.07,34.92 0.53,0.95 1.68,2.63 2.55,3.72z" android:strokeWidth="0.264583"/>
</vector>
@@ -1,6 +1,95 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <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_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:layoutAnimation="@anim/layout_animation_fall_down">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/image_header"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="w,1:1"
android:src="@drawable/pill"
android:tint="?attr/colorPrimary"
android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/text_header"
app:layout_constraintBottom_toBottomOf="@id/text_header"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/text_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="36sp"
android:fontFamily="@font/lato_blackitalic"
android:text="@string/introduction_welcome_header"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/image_header"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="64dp"/>
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/text_header"
app:layout_constraintBottom_toTopOf="@id/button_download_schedule"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
android:paddingHorizontal="16dp"
android:paddingTop="24dp"
android:paddingBottom="16dp">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/text_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/introduction_welcome_description"
android:textSize="16sp"/>
</ScrollView>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TonalButton"
android:id="@+id/button_download_schedule"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/cloud_download"
android:text="@string/introduction_button_download"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="8dp"
android:layout_gravity="center_horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/text_buttons_filler"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/text_buttons_filler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/introduction_or"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/button_continue"/>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.OutlinedButton"
android:id="@+id/button_continue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/arrow_forward"
app:iconGravity="end"
android:text="@string/introduction_button_continue"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+2 -1
View File
@@ -42,12 +42,13 @@
app:layout_constraintEnd_toStartOf="@id/button_view_overview"/> app:layout_constraintEnd_toStartOf="@id/button_view_overview"/>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:visibility="gone"
style="@style/Widget.Material3.Button.IconButton.Outlined" style="@style/Widget.Material3.Button.IconButton.Outlined"
android:id="@+id/button_view_overview" android:id="@+id/button_view_overview"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="0dp" android:minWidth="0dp"
app:icon="@drawable/sort" app:icon="@drawable/engineering"
app:iconPadding="0dp" app:iconPadding="0dp"
app:strokeColor="?attr/colorOutlineVariant" app:strokeColor="?attr/colorOutlineVariant"
app:layout_constraintTop_toTopOf="@id/app_title_bar" app:layout_constraintTop_toTopOf="@id/app_title_bar"
+10
View File
@@ -1,3 +1,7 @@
<!DOCTYPE resources [
<!ENTITY app_name "WeserPlaner">
]>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="error_generic">Ein Fehler ist aufgetreten.</string> <string name="error_generic">Ein Fehler ist aufgetreten.</string>
@@ -20,6 +24,12 @@
<item>Sonntag</item> <item>Sonntag</item>
</string-array> </string-array>
<string name="introduction_welcome_header">Willkommen zu &app_name;!</string>
<string name="introduction_welcome_description">&app_name; ist eine inoffizielle App für Studierende der Universität Bremen, mit welcher du deinen Stundenplan immer griffbereit hast. Erstelle deine eigenen wöchentlichen Veranstaltungen und bearbeite sie, oder lade sie direkt von Stud.IP herunter.\n\nDu kannst außerdem alle Angebote aus den Mensen in Bremen und Bremerhaven einsehen. Filtere nach Präferenzen mit den Knöpfen oben am Bildschirm, oder gehe in die Einstellungen, um zu konfigurieren, gegen welche Stoffe du allergisch bist, und die App wird so gut wie möglich versuchen, dir nur Angebote zu zeigen, die relevant für dich sind.\n\nKlicke auf den Knopf unten, um deinen Stud.IP-Stundenplan direkt herunterzuladen! Solltest du dies überspringen, kannst du dies jederzeit in den Einstellungen der App nachholen.</string>
<string name="introduction_button_download">Lade deinen Stud.IP-Stundenplan herunter</string>
<string name="introduction_or"> oder </string>
<string name="introduction_button_continue">Gehe direkt zur App</string>
<string name="fetch_error_snack">Ein Fehler ist aufgetreten!</string> <string name="fetch_error_snack">Ein Fehler ist aufgetreten!</string>
<string name="fetch_error_webpage_snack">Du musst deinen Stundenplan öffnen!</string> <string name="fetch_error_webpage_snack">Du musst deinen Stundenplan öffnen!</string>
<string name="fetch_error_login_snack">Du musst dich zuerst einloggen!</string> <string name="fetch_error_login_snack">Du musst dich zuerst einloggen!</string>
+11 -1
View File
@@ -1,5 +1,9 @@
<!DOCTYPE resources [
<!ENTITY app_name "WeserPlaner">
]>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">WeserPlaner</string> <string name="app_name" translatable="false">&app_name;</string>
<string name="error_generic">An error occurred.</string> <string name="error_generic">An error occurred.</string>
@@ -21,6 +25,12 @@
<item>Sunday</item> <item>Sunday</item>
</string-array> </string-array>
<string name="introduction_welcome_header">Welcome to &app_name;!</string>
<string name="introduction_welcome_description">&app_name; is an unofficial companion app for students of the University of Bremen that allows you to keep your own schedule in close reach. Create your own weekly events and edit them as you wish, or download them straight from Stud.IP.\n\nYou can also view the offers for all of the canteens in Bremen and Bremerhaven. Filter by preferences using the buttons at the top of the canteen screen, or go into the settings to configure what substances you are allergic against, and the app will do its best to only show you offers that are relevant for you.\n\nClick the button below to download your schedule right away! If you skip this, you\'ll always have the option to download your schedule from the app\'s settings later.</string>
<string name="introduction_button_download">Download your Stud.IP schedule</string>
<string name="introduction_or"> or </string>
<string name="introduction_button_continue">Continue to the app</string>
<string name="fetch_error_snack">Something went wrong!</string> <string name="fetch_error_snack">Something went wrong!</string>
<string name="fetch_error_webpage_snack">You must navigate to your schedule!</string> <string name="fetch_error_webpage_snack">You must navigate to your schedule!</string>
<string name="fetch_error_login_snack">You must log in first!</string> <string name="fetch_error_login_snack">You must log in first!</string>