WeserPlaner 1.0.3: adjusted layout of AllergenSheet.kt to display allergens and additives separately; FetcherActivity.kt now features a button to go directly to the user's schedule, if logged in; replaced toast messages with snack bars; ScheduleUpdateSheet.kt now notifies the user about database transactions via snack bar; schedule view now tells the user if there are no events scheduled for a day

This commit is contained in:
denizk0461
2023-05-14 23:09:06 +02:00
parent 8f69b5f381
commit 1c31d422fa
22 changed files with 261 additions and 85 deletions
+6 -6
View File
@@ -4,17 +4,17 @@ WeserPlaner is a work-in-progress Android app for students at the University of
![Screenshots of the WeserPlaner app](https://denizk0461.github.io/weserplaner_showcase.png)
## Progress
- Downloading and creating events, as well as displaying them is fully functional.
- Downloading canteen offers, filtering them, and viewing details is fully functional.
- Viewing additional information on the canteen (opening hours, news) works.
- Downloading and creating events, as well as displaying them.
- Downloading canteen offers, filtering them, and viewing details.
- Viewing additional information on the canteen (opening hours, news).
## What's broken
Currently, nothing is outright broken, but a few things may be bodged together.
Currently, nothing is outright broken, but a few things may be bodged together badly.
## TODO
- Adding exams, homework, etc.: users will be able to add any tasks they need to do as reminders. Those can be associated with events that either have been downloaded from the user's Stud.IP schedule, or ones the user has created manually. An overview will be provided that allows the user to have, well, an overview of everything they need to do.
- Optimisations to data handling will be made. The app currently only supports storing a single timetable, which will be changed in future releases. A planned implementation will see the user being able to download schedules as well as create ones from scratch, with the schedules able to be selected via a dropdown menu.
- Some UI restructuring to declutter certain screens and improve the UX.
- Some improvements to the Play Store screenshots are sorely needed...
## Why Android-only?
Because:
@@ -27,6 +27,6 @@ I may reconsider once Apple finally opens up iOS for third-party apps.
The colour scheme is inspired by the University of Bremen's colour scheme, as well as the city of Bremen's colour scheme. The logo represents both the University of Bremen as well as (kind of) the Weserstadion, our local sports stadium.
I admit the Weserstadion idea came to me after I had created the logo.
I admit the Weserstadion idea only came to me after I had created the logo.
![WeserPlaner app icon](https://denizk0461.github.io/weserplaner_app_icon.png)
+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId "com.denizk0461.weserplaner"
minSdk 24
targetSdk 33
versionCode 6
versionName "1.0.2"
versionCode 7
versionName "1.0.3"
resourceConfigurations += ["en", "de"]
+2 -2
View File
@@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 6,
"versionName": "1.0.2",
"versionCode": 7,
"versionName": "1.0.3",
"outputFile": "app-release.apk"
}
],
@@ -10,6 +10,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModelProvider
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.showErrorSnackBar
import com.denizk0461.weserplaner.data.showSnackBar
import com.denizk0461.weserplaner.data.showToast
import com.denizk0461.weserplaner.databinding.ActivityFetcherBinding
import com.denizk0461.weserplaner.model.TextSheetContentId
@@ -50,7 +51,11 @@ class FetcherActivity : FragmentActivity() {
setContentView(binding.root)
// Remind the user of the help button
showToast(this, getString(R.string.toast_info_reminder))
theme.showSnackBar(
binding.coordinatorLayout,
getString(R.string.fetch_snack_info_reminder),
binding.bottomAppBar,
)
// Enabling JavaScript. See comment above lint suppression for reason why this is done.
binding.webview.settings.apply {
@@ -104,15 +109,19 @@ class FetcherActivity : FragmentActivity() {
}.show(supportFragmentManager, TextSheet::class.java.simpleName)
true
}
R.id.schedule -> {
binding.webview.loadUrl(scheduleUrl)
true
}
else -> false
}
}
/*
* Attempt to load the user-set Stud.IP front page. Redirects to login page if user is
* Attempt to load the user's Stud.IP schedule page. Redirects to login page if user is
* not logged in.
*/
binding.webview.loadUrl("https://elearning.uni-bremen.de/index.php?again=yes")
binding.webview.loadUrl(scheduleUrl)
binding.fab.setOnClickListener {
@@ -146,18 +155,18 @@ class FetcherActivity : FragmentActivity() {
} catch (e: IOException) {
// Let the user know that an error occurred
theme.showErrorSnackBar(
binding.rootView,
binding.coordinatorLayout,
getString(R.string.fetch_error_snack),
binding.bottomAppBar
binding.bottomAppBar,
)
}
}
} else {
// Tell the user that they must navigate to their timetable
theme.showErrorSnackBar(
binding.rootView,
binding.coordinatorLayout,
getString(R.string.fetch_error_webpage_snack),
binding.bottomAppBar
binding.bottomAppBar,
)
}
}
@@ -76,19 +76,15 @@ class CanteenOfferItemAdapter(
// Display
if (position == (offers.size - 1) && difference > 0) {
holder.binding.textDifference.apply {
visibility = View.VISIBLE
text = if (difference == 1) {
holder.binding.differenceContainer.visibility = View.VISIBLE
holder.binding.textDifference.text = if (difference == 1) {
context.getString(R.string.canteen_item_difference_text_one)
} else {
context.getString(R.string.canteen_item_difference_text_other, difference)
}
}
} else {
holder.binding.textDifference.apply {
visibility = View.GONE
text = ""
}
holder.binding.differenceContainer.visibility = View.GONE
holder.binding.textDifference.text = ""
}
/*
@@ -2,12 +2,13 @@ package com.denizk0461.weserplaner.adapter
import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources.getDrawable
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.denizk0461.weserplaner.data.AppDiffUtilCallback
import com.google.android.material.R
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.getThemedColor
import com.denizk0461.weserplaner.data.parseToMinutes
import com.denizk0461.weserplaner.model.StudIPEvent
@@ -19,7 +20,7 @@ import java.util.*
*
* @param currentDay current day that is used to show only a given day's events (0 = Monday,
* 4 = Friday)
* 4 = Friday, 6 = Sunday)
* @param onClickListener used for listening to clicks and long presses
*/
class StudIPEventItemAdapter(
@@ -65,9 +66,28 @@ class StudIPEventItemAdapter(
override fun getItemCount(): Int = events.size
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
// Retrieve item for current position
val currentItem = events[position]
// Context for easier reference
val context = holder.binding.root.context
// Display a notice to the user saying they have no events scheduled for the day
if (currentItem.eventId == -1) {
holder.binding.cardBackground.visibility = View.GONE
holder.binding.noEventsContainer.visibility = View.VISIBLE
holder.binding.textNoEvents.text = context.getString(
R.string.schedule_no_events,
context.resources.getStringArray(R.array.weekdays)[currentDay]
)
return
}
// Set visibility of the elements accordingly if there are events scheduled
holder.binding.cardBackground.visibility = View.VISIBLE
holder.binding.noEventsContainer.visibility = View.GONE
// Set up text fields with corresponding values
holder.binding.textTitle.text = currentItem.title
holder.binding.textLecturers.text = currentItem.lecturer
@@ -77,14 +97,17 @@ class StudIPEventItemAdapter(
holder.binding.textTimeslot.text = currentItem.timeslot()
// Get current theme
val theme = holder.binding.root.context.theme
val theme = context.theme
/*
* Highlight the next upcoming course of the day if the user selected the option, if the day
* of the adapter matches the current day, and if no other course has been highlighted, to
* avoid double highlighting.
*/
if (highlightNextCourse && !isAnyCourseHighlighted && currentItem.isCurrentCourse(currentCalendar)) {
if (highlightNextCourse &&
!isAnyCourseHighlighted &&
currentItem.isCurrentCourse(currentCalendar)
) {
// Ensure that no other course will be highlighted
isAnyCourseHighlighted = true
@@ -98,12 +121,12 @@ class StudIPEventItemAdapter(
}
// Set the divider colour to the text colour
holder.binding.divider.dividerColor =
theme.getThemedColor(com.denizk0461.weserplaner.R.attr.colorText)
theme.getThemedColor(R.attr.colorText)
// Set ripple colour
holder.binding.linearLayout.background = getDrawable(
holder.binding.root.context,
com.denizk0461.weserplaner.R.drawable.selectable_item_background_highlighted,
R.drawable.selectable_item_background_highlighted,
)
// Otherwise, apply colours to ensure that the item will not be highlighted
@@ -111,7 +134,9 @@ class StudIPEventItemAdapter(
holder.binding.cardBackground.apply {
// Set the card's background colour to its default value
backgroundTintList =
ColorStateList.valueOf(theme.getThemedColor(R.attr.colorSurface))
ColorStateList.valueOf(theme.getThemedColor(
com.google.android.material.R.attr.colorSurface
))
// Set the card's stroke to its default colour
strokeColor = theme.getThemedColor(R.attr.colorOutlineVariant)
@@ -122,7 +147,7 @@ class StudIPEventItemAdapter(
// Set ripple colour
holder.binding.linearLayout.background = getDrawable(
holder.binding.root.context,
com.denizk0461.weserplaner.R.drawable.selectable_item_background,
R.drawable.selectable_item_background,
)
}
@@ -10,6 +10,7 @@ import androidx.viewpager2.widget.ViewPager2
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.activity.FetcherActivity
import com.denizk0461.weserplaner.adapter.StudIPEventPageAdapter
import com.denizk0461.weserplaner.data.showSnackBar
import com.denizk0461.weserplaner.databinding.FragmentEventBinding
import com.denizk0461.weserplaner.sheet.ScheduleUpdateSheet
import com.denizk0461.weserplaner.viewmodel.EventViewModel
@@ -144,6 +145,15 @@ class EventFragment : AppFragment() {
binding.fabAddEvent.extend()
}
/**
* Creates and shows a snack bar.
*
* @param text content to display in the snack bar
*/
fun showSnackBar(text: String) {
context.theme.showSnackBar(binding.coordinatorLayout, text)
}
// Invalidate the view binding
override fun onDestroyView() {
super.onDestroyView()
@@ -96,7 +96,20 @@ class EventPageFragment : AppFragment(), StudIPEventItemAdapter.OnClickListener
// Set up LiveData observer to refresh the view on update
viewModel.getEventsForDay(currentDay).observe(viewLifecycleOwner) { events ->
eventAdapter.setNewData(events)
eventAdapter.setNewData(events.ifEmpty { listOf(
// Use an event with eventId = -1 to denote that there are no events for this day
StudIPEvent(
-1,
"",
"",
"",
0,
"",
"",
0,
0,
)
) })
}
}
@@ -238,8 +238,7 @@ class SettingsFragment : AppFragment() {
licencesLongClick = 0
viewModel.preferenceExperimentalSettingsEnabled = true
binding.cardExperimentalSettings.visibility = View.VISIBLE
context.theme.showSnackBar(
binding.coordinatorLayout,
showSnackBar(
getString(R.string.settings_unlocked_experiments),
)
} else {
@@ -320,8 +319,7 @@ class SettingsFragment : AppFragment() {
binding.buttonHideExperiments.setOnClickListener {
viewModel.preferenceExperimentalSettingsEnabled = false
binding.cardExperimentalSettings.visibility = View.GONE
context.theme.showSnackBar(
binding.coordinatorLayout,
showSnackBar(
getString(R.string.settings_hide_experiments),
)
}
@@ -397,8 +395,7 @@ class SettingsFragment : AppFragment() {
viewModel.nukeEverything()
showToast(context, "Nuked everything")
}
else -> context.theme.showSnackBar(
binding.coordinatorLayout,
else -> showSnackBar(
getString(R.string.settings_dev_codes_invalid)
)
}
@@ -418,6 +415,15 @@ class SettingsFragment : AppFragment() {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(link)))
}
/**
* Creates and shows a snack bar.
*
* @param text content to display in the snack bar
*/
fun showSnackBar(text: String) {
context.theme.showSnackBar(binding.coordinatorLayout, text)
}
/**
* Decodes Base64 to a regular string.
*
@@ -4,10 +4,11 @@ import android.os.Bundle
import android.view.View
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.NavHostFragment
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.showToast
import com.denizk0461.weserplaner.data.viewBinding
import com.denizk0461.weserplaner.databinding.SheetAllergenConfigBinding
import com.denizk0461.weserplaner.fragment.SettingsFragment
import com.denizk0461.weserplaner.model.AllergenPreferences
import com.denizk0461.weserplaner.viewmodel.AllergenConfigViewModel
@@ -26,6 +27,17 @@ class AllergenConfigSheet : AppSheet(R.layout.sheet_allergen_config) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set up FAB behaviour for parent fragment's FAB if activity is not null
val parentFragment = activity?.let { activity ->
// Retrieve parent fragment to access its functions
val navHostFragment = activity
.supportFragmentManager
.fragments[0] as NavHostFragment
navHostFragment
.childFragmentManager
.primaryNavigationFragment as SettingsFragment
}
// Retrieve allergens as currently set by the user from view model
val currentAllergenConfig = AllergenPreferences.construct(
viewModel.preferenceAllergenConfig
@@ -73,7 +85,7 @@ class AllergenConfigSheet : AppSheet(R.layout.sheet_allergen_config) {
parentFragment?.setFragmentResult("allergenConfig", Bundle())
// Tell the user that saving was successful
showToast(context, getString(R.string.allergens_config_confirmation))
parentFragment?.showSnackBar(getString(R.string.allergens_config_confirmation))
// Close the sheet
dismiss()
@@ -84,20 +84,28 @@ class AllergenSheet : AppSheet(R.layout.sheet_allergen) {
}
}
// Allergen and additive notice
// textContent.text = offer.allergens.compileAllergenString()
val (a, b) = offer.allergens.compileStrings()
// Retrieve allergen and additive strings
val (allergens, additives) = offer.allergens.compileStrings()
if (a.isBlank() && b.isBlank()) {
// Display notices for allergens and additives, if necessary
if (allergens.isBlank() && additives.isBlank()) {
// Tell the user that no allergens and no additives are present in the offer
textAllergensTitle.text = getString(R.string.allergens_none)
textAllergensContent.visibility = View.GONE
textAdditivesTitle.visibility = View.GONE
textAdditivesContent.visibility = View.GONE
} else {
// Set allergen and additives accordingly if any are present
textAllergensTitle.text = getString(R.string.allergens_allergens_title)
textAllergensContent.text = getString(R.string.allergens_content_template, a)
textAllergensContent.text = getString(
R.string.allergens_content_template,
allergens,
)
textAdditivesTitle.text = getString(R.string.allergens_additives_title)
textAdditivesContent.text = getString(R.string.allergens_content_template, b)
textAdditivesContent.text = getString(
R.string.allergens_content_template,
additives,
)
}
// Show a price, if one is available
@@ -124,22 +132,23 @@ class AllergenSheet : AppSheet(R.layout.sheet_allergen) {
}
/**
* Retrieves localised strings for all allergens present in a given offer and compiles them into
* a human-readable string, if any allergens are present.
* Retrieves localised strings for all allergens and additives present in a given offer and
* compiles them into human-readable strings, if any are present.
*
* @return a string of all allergens
* @receiver string to parse allergens and additives from
* @return allergens and additives formatted as strings
*/
private fun String.compileStrings(): Pair<String, String> {
// Let the user know if no allergens are present
// Return empty values if the received string is blank
if (this.isBlank()) return Pair("", "")
// Stores all localised additives
val additiveList = mutableListOf<String>()
// Stores all localised allergens
val allergenList = mutableListOf<String>()
// Stores all localised additives
val additiveList = mutableListOf<String>()
// Split all allergens to iterate through them
val allergens = this.replace(" ", "").split(",")
@@ -156,16 +165,10 @@ class AllergenSheet : AppSheet(R.layout.sheet_allergen) {
}
}
// Insert the items into a template string
// return getString(
// R.string.allergens_contains,
// allergenList.joinToString(", ").ifBlank {
// getString(R.string.allergens_single_none)
// },
// additiveList.joinToString(", ").ifBlank {
// getString(R.string.allergens_single_none)
// },
// )
/*
* Join the lists to formatted strings, separated by commas. Put a localised "none" if
* either no allergens or additives could be found respectively.
*/
return Pair(
allergenList.joinToString(", ").ifBlank {
getString(R.string.allergens_single_none)
@@ -6,6 +6,7 @@ import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.NavHostFragment
import androidx.transition.TransitionManager
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.getParcelableCompat
@@ -18,6 +19,7 @@ import com.denizk0461.weserplaner.data.viewBinding
import com.denizk0461.weserplaner.databinding.SheetScheduleUpdateBinding
import com.denizk0461.weserplaner.exception.AcademicQuarterNotApplicableException
import com.denizk0461.weserplaner.exception.ParcelNotFoundException
import com.denizk0461.weserplaner.fragment.EventFragment
import com.denizk0461.weserplaner.model.StudIPEvent
import com.denizk0461.weserplaner.viewmodel.ScheduleUpdateViewModel
import com.google.android.material.timepicker.MaterialTimePicker
@@ -51,6 +53,17 @@ class ScheduleUpdateSheet : AppSheet(R.layout.sheet_schedule_update) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set up FAB behaviour for parent fragment's FAB if activity is not null
val parentFragment = activity?.let { activity ->
// Retrieve parent fragment to access its functions
val navHostFragment = activity
.supportFragmentManager
.fragments[0] as NavHostFragment
navHostFragment
.childFragmentManager
.primaryNavigationFragment as EventFragment
}
if (arguments?.getBoolean("isEditing") == true) {
try {
@@ -88,7 +101,15 @@ class ScheduleUpdateSheet : AppSheet(R.layout.sheet_schedule_update) {
* accidentally deleting an event.
*/
if (hasClickedDelete) {
// Delete the event
viewModel.delete(event)
// Tell the user that the event has been deleted
parentFragment?.showSnackBar(
getString(R.string.sheet_schedule_snack_delete)
)
// Dismiss the sheet upon deletion
dismiss()
} else {
@@ -133,6 +154,12 @@ class ScheduleUpdateSheet : AppSheet(R.layout.sheet_schedule_update) {
colour = colour,
)
)
// Tell the user that the event has been updated
parentFragment?.showSnackBar(
getString(R.string.sheet_schedule_snack_update)
)
// Dismiss the sheet upon update
dismiss()
}
@@ -188,6 +215,11 @@ class ScheduleUpdateSheet : AppSheet(R.layout.sheet_schedule_update) {
)
)
// Tell the user that the event has been updated
parentFragment?.showSnackBar(
getString(R.string.sheet_schedule_snack_add)
)
// Dismiss the sheet upon update
dismiss()
}
+1 -1
View File
@@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector android:height="24dp" android:tint="?attr/colorOnSurfaceVariant"
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="M16.24,11.51l1.57,-1.57l-3.75,-3.75l-1.57,1.57L8.35,3.63c-0.78,-0.78 -2.05,-0.78 -2.83,0l-1.9,1.9c-0.78,0.78 -0.78,2.05 0,2.83l4.13,4.13L3,17.25V21h3.75l4.76,-4.76l4.13,4.13c0.95,0.95 2.23,0.6 2.83,0l1.9,-1.9c0.78,-0.78 0.78,-2.05 0,-2.83L16.24,11.51zM9.18,11.07L5.04,6.94l1.89,-1.9c0,0 0,0 0,0l1.27,1.27L7.02,7.5l1.41,1.41l1.19,-1.19l1.45,1.45L9.18,11.07zM17.06,18.96l-4.13,-4.13l1.9,-1.9l1.45,1.45l-1.19,1.19l1.41,1.41l1.19,-1.19l1.27,1.27L17.06,18.96z"/>
+5
View File
@@ -0,0 +1,5 @@
<vector 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.126,8.125l1.937,-1.937 3.747,3.747 -1.937,1.938zM20.71,5.63l-2.34,-2.34a1,1 0,0 0,-1.41 0l-1.83,1.83 3.75,3.75L20.71,7a1,1 0,0 0,0 -1.37zM2,5l6.63,6.63L3,17.25V21h3.75l5.63,-5.62L18,21l2,-2L4,3 2,5z"/>
</vector>
@@ -0,0 +1,5 @@
<vector 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,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
</vector>
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_view"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
@@ -2,6 +2,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.EventFragment">
+17
View File
@@ -47,6 +47,21 @@
</com.google.android.material.card.MaterialCardView>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/difference_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginHorizontal="4dp"
android:scaleType="centerInside"
app:tint="?attr/colorText"
android:src="@drawable/visibility_off"/>
<TextView
android:id="@+id/text_difference"
android:layout_width="wrap_content"
@@ -54,3 +69,5 @@
android:layout_gravity="center_horizontal"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
+26 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp"
android:orientation="vertical">
@@ -55,4 +55,28 @@
</com.google.android.material.card.MaterialCardView>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/no_events_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginHorizontal="4dp"
android:scaleType="centerInside"
app:tint="?attr/colorText"
android:src="@drawable/edit_off"/>
<TextView
android:id="@+id/text_no_events"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/schedule_no_events"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
+12 -6
View File
@@ -4,19 +4,25 @@
<item
android:id="@+id/close"
android:icon="@drawable/cross"
android:title="@string/fetch_bar_close_title"
android:title="@string/fetch_bar_close_desc"
android:contentDescription="@string/fetch_bar_close_desc"
app:showAsAction="ifRoom"/>
app:showAsAction="always"/>
<item
android:id="@+id/refresh"
android:icon="@drawable/refresh"
android:title="@string/fetch_bar_refresh_title"
android:title="@string/fetch_bar_refresh_desc"
android:contentDescription="@string/fetch_bar_refresh_desc"
app:showAsAction="ifRoom"/>
app:showAsAction="always"/>
<item
android:id="@+id/help"
android:icon="@drawable/info"
android:title="@string/fetch_bar_help_title"
android:title="@string/fetch_bar_help_desc"
android:contentDescription="@string/fetch_bar_help_desc"
app:showAsAction="ifRoom"/>
app:showAsAction="always"/>
<item
android:id="@+id/schedule"
android:icon="@drawable/design"
android:title="@string/fetch_bar_schedule_desc"
android:contentDescription="@string/fetch_bar_schedule_desc"
app:showAsAction="always"/>
</menu>
+8 -2
View File
@@ -34,24 +34,30 @@
<string name="toast_fetch_finished_one">Dein Stundenplan wurde aktualisiert, doch eine Veranstaltung konnte nicht heruntergeladen werden.</string>
<string name="toast_fetch_finished_other">Dein Stundenplan wurde aktualisiert, doch %d Veranstaltungen konnten nicht heruntergeladen werden.</string>
<string name="toast_info_reminder">Klick\' das Info-Icon unten, falls du Hilfe benötigst.</string>
<string name="fetch_snack_info_reminder">Klick\' das Info-Icon unten, falls du Hilfe benötigst.</string>
<string name="fetch_bar_close_title">Schließen</string>
<string name="fetch_bar_close_desc">Schließe die Webseite, ohne etwas zu speichern</string>
<string name="fetch_bar_refresh_title">Aktualisieren</string>
<string name="fetch_bar_refresh_desc">Aktualisiere die Webseite</string>
<string name="fetch_bar_help_title">Hilfe</string>
<string name="fetch_bar_help_desc">Zeige Hilfe</string>
<string name="fetch_bar_schedule_title">Stundenplan</string>
<string name="fetch_bar_schedule_desc">Gehe zu deinem Stundenplan</string>
<string name="fetch_bar_help_sheet_title">Wie du deinen Stundenplan in der App speichern kannst</string>
<string name="fetch_bar_help_sheet_content"> • 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!</string>
<string name="fetch_bar_help_sheet_content"> • Logge dich mit deinem Stud.IP-Login ein\n • Öffne deinen Stundenplan, falls er sich nicht automatisch öffnet, indem du in der unteren Leiste auf das rechte Logo klickst, das wie ein Stift und ein Lineal aussieht\n • Klicke auf den \'Speichern\'-Knopf, der unten in der rechten Ecke auftaucht\n • Fertig!</string>
<string name="fetch_bar_save_desc">Speichere den Stundenplan in der App ab</string>
<string name="schedule_fab_add_text">Neue Veranstaltung</string>
<string name="schedule_no_events">%ss hast du keine Veranstaltungen.</string>
<string name="save">Speichern</string>
<string name="delete">Löschen</string>
<string name="sheet_schedule_update_delete_confirm">Sicher?</string>
<string name="cancel">Abbrechen</string>
<string name="confirm">Bestätigen</string>
<string name="sheet_schedule_snack_delete">Veranstaltung wurde gelöscht.</string>
<string name="sheet_schedule_snack_add">Veranstaltung wurde hinzugefügt.</string>
<string name="sheet_schedule_snack_update">Veranstaltung wurde aktualisiert.</string>
<string name="sheet_schedule_update_header_edit">Bestehende Veranstaltung bearbeiten</string>
<string name="sheet_schedule_update_header_add">Neue Veranstaltung hinzufügen</string>
<string name="sheet_schedule_update_title_hint">Veranstaltungstitel</string>
+8 -2
View File
@@ -35,24 +35,30 @@
<string name="toast_fetch_finished_one">Your schedule has been refreshed, but one item could not be saved.</string>
<string name="toast_fetch_finished_other">Your schedule has been refreshed, but %d items could not be saved.</string>
<string name="toast_info_reminder">Click the info icon at the bottom if you need any help here.</string>
<string name="fetch_snack_info_reminder">Click the info icon at the bottom if you need any help here.</string>
<string name="fetch_bar_close_title">Close</string>
<string name="fetch_bar_close_desc">Close the website without saving anything</string>
<string name="fetch_bar_refresh_title">Refresh</string>
<string name="fetch_bar_refresh_desc">Refresh the page</string>
<string name="fetch_bar_help_title">Help</string>
<string name="fetch_bar_help_desc">Show help</string>
<string name="fetch_bar_schedule_title">Schedule</string>
<string name="fetch_bar_schedule_desc">Go to your schedule</string>
<string name="fetch_bar_help_sheet_title">How to save your timetable in the app</string>
<string name="fetch_bar_help_sheet_content"> • 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!</string>
<string name="fetch_bar_help_sheet_content"> • Log in with your Stud.IP login\n • Open your timetable view, in case it doesn\'t open automatically, by clicking the rightmost icon in the bottom bar that looks like a pencil and a ruler\n • Click the \'save\' button that appears in the bottom right corner\n • Done!</string>
<string name="fetch_bar_save_desc">Save the timetable in the app</string>
<string name="schedule_fab_add_text">New event</string>
<string name="schedule_no_events">You don\'t have any events for %s.</string>
<string name="save">Save</string>
<string name="delete">Delete</string>
<string name="sheet_schedule_update_delete_confirm">Sure?</string>
<string name="cancel">Cancel</string>
<string name="confirm">Confirm</string>
<string name="sheet_schedule_snack_delete">Event has been deleted.</string>
<string name="sheet_schedule_snack_add">Event has been added.</string>
<string name="sheet_schedule_snack_update">Event has been updated.</string>
<string name="sheet_schedule_update_header_edit">Edit existing event</string>
<string name="sheet_schedule_update_header_add">Add new event</string>
<string name="sheet_schedule_update_title_hint">Event title</string>