diff --git a/README.md b/README.md index 668a09f..5b2b909 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/app/build.gradle b/app/build.gradle index 261a709..8b64bea 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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"] diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index e1fe1ac..94e6b8d 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 6, - "versionName": "1.0.2", + "versionCode": 7, + "versionName": "1.0.3", "outputFile": "app-release.apk" } ], diff --git a/app/src/main/java/com/denizk0461/weserplaner/activity/FetcherActivity.kt b/app/src/main/java/com/denizk0461/weserplaner/activity/FetcherActivity.kt index 734026e..9f97ff4 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/activity/FetcherActivity.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/activity/FetcherActivity.kt @@ -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, ) } } diff --git a/app/src/main/java/com/denizk0461/weserplaner/adapter/CanteenOfferItemAdapter.kt b/app/src/main/java/com/denizk0461/weserplaner/adapter/CanteenOfferItemAdapter.kt index fa39276..f0ee4b6 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/adapter/CanteenOfferItemAdapter.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/adapter/CanteenOfferItemAdapter.kt @@ -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) { - context.getString(R.string.canteen_item_difference_text_one) - } else { - context.getString(R.string.canteen_item_difference_text_other, difference) - } + 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 = "" } /* diff --git a/app/src/main/java/com/denizk0461/weserplaner/adapter/StudIPEventItemAdapter.kt b/app/src/main/java/com/denizk0461/weserplaner/adapter/StudIPEventItemAdapter.kt index 159c72c..b0f32d3 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/adapter/StudIPEventItemAdapter.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/adapter/StudIPEventItemAdapter.kt @@ -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, ) } diff --git a/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt b/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt index b2ca0e7..1284e5d 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt @@ -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() diff --git a/app/src/main/java/com/denizk0461/weserplaner/fragment/EventPageFragment.kt b/app/src/main/java/com/denizk0461/weserplaner/fragment/EventPageFragment.kt index 8901502..ffe258a 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/EventPageFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/EventPageFragment.kt @@ -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, + ) + ) }) } } diff --git a/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt b/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt index 329f946..3e80ee3 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt @@ -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. * diff --git a/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenConfigSheet.kt b/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenConfigSheet.kt index 64e646e..d2f9450 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenConfigSheet.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenConfigSheet.kt @@ -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() diff --git a/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenSheet.kt b/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenSheet.kt index cb1c13f..cf2c87e 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenSheet.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/sheet/AllergenSheet.kt @@ -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 { - // 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() - // Stores all localised allergens val allergenList = mutableListOf() + // Stores all localised additives + val additiveList = mutableListOf() + // 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) diff --git a/app/src/main/java/com/denizk0461/weserplaner/sheet/ScheduleUpdateSheet.kt b/app/src/main/java/com/denizk0461/weserplaner/sheet/ScheduleUpdateSheet.kt index cd05e8f..b043578 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/sheet/ScheduleUpdateSheet.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/sheet/ScheduleUpdateSheet.kt @@ -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() } diff --git a/app/src/main/res/drawable/design.xml b/app/src/main/res/drawable/design.xml index d3ca206..7fe3e65 100644 --- a/app/src/main/res/drawable/design.xml +++ b/app/src/main/res/drawable/design.xml @@ -1,4 +1,4 @@ - diff --git a/app/src/main/res/drawable/edit_off.xml b/app/src/main/res/drawable/edit_off.xml new file mode 100644 index 0000000..f03dc42 --- /dev/null +++ b/app/src/main/res/drawable/edit_off.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/visibility_off.xml b/app/src/main/res/drawable/visibility_off.xml new file mode 100644 index 0000000..00c8a20 --- /dev/null +++ b/app/src/main/res/drawable/visibility_off.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_fetcher.xml b/app/src/main/res/layout/activity_fetcher.xml index bb8b72a..42e7331 100644 --- a/app/src/main/res/layout/activity_fetcher.xml +++ b/app/src/main/res/layout/activity_fetcher.xml @@ -1,7 +1,7 @@ diff --git a/app/src/main/res/layout/fragment_event.xml b/app/src/main/res/layout/fragment_event.xml index 8573219..83b51cb 100644 --- a/app/src/main/res/layout/fragment_event.xml +++ b/app/src/main/res/layout/fragment_event.xml @@ -2,6 +2,7 @@ diff --git a/app/src/main/res/layout/item_canteen.xml b/app/src/main/res/layout/item_canteen.xml index 5fe16be..d54863d 100644 --- a/app/src/main/res/layout/item_canteen.xml +++ b/app/src/main/res/layout/item_canteen.xml @@ -47,10 +47,27 @@ - + android:layout_gravity="center_horizontal" + android:layout_marginTop="4dp"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_event.xml b/app/src/main/res/layout/item_event.xml index ea87337..0e401f4 100644 --- a/app/src/main/res/layout/item_event.xml +++ b/app/src/main/res/layout/item_event.xml @@ -1,8 +1,8 @@ - @@ -55,4 +55,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_fetcher_bar.xml b/app/src/main/res/menu/menu_fetcher_bar.xml index d090257..e0da8b4 100644 --- a/app/src/main/res/menu/menu_fetcher_bar.xml +++ b/app/src/main/res/menu/menu_fetcher_bar.xml @@ -4,19 +4,25 @@ + app:showAsAction="always"/> + app:showAsAction="always"/> + app:showAsAction="always"/> + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5a8574b..97bc7fc 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -34,24 +34,30 @@ 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. + Klick\' das Info-Icon unten, falls du Hilfe benötigst. Schließen Schließe die Webseite, ohne etwas zu speichern Aktualisieren Aktualisiere die Webseite Hilfe Zeige Hilfe + Stundenplan + Gehe zu deinem Stundenplan 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, der unten in der rechten Ecke auftauchen sollte\n • Fertig! + • 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! Speichere den Stundenplan in der App ab Neue Veranstaltung + %ss hast du keine Veranstaltungen. Speichern Löschen Sicher? Abbrechen Bestätigen + Veranstaltung wurde gelöscht. + Veranstaltung wurde hinzugefügt. + Veranstaltung wurde aktualisiert. Bestehende Veranstaltung bearbeiten Neue Veranstaltung hinzufügen Veranstaltungstitel diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e981d32..2db38f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,24 +35,30 @@ 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. + Click the info icon at the bottom if you need any help here. Close Close the website without saving anything Refresh Refresh the page Help Show help + Schedule + Go to your schedule 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 that should appear 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, 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! Save the timetable in the app New event + You don\'t have any events for %s. Save Delete Sure? Cancel Confirm + Event has been deleted. + Event has been added. + Event has been updated. Edit existing event Add new event Event title