Archived
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:
@@ -4,17 +4,17 @@ WeserPlaner is a work-in-progress Android app for students at the University of
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||

|
||||
|
||||
+2
-2
@@ -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"]
|
||||
|
||||
|
||||
@@ -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,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"/>
|
||||
|
||||
@@ -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,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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user