Merge pull request #1 from denizk0461/temp

Changed TextSheet to use a ViewModel for showing content that is fetc…
This commit is contained in:
Deniz
2023-05-09 14:35:16 +02:00
committed by GitHub
13 changed files with 149 additions and 22 deletions
@@ -14,6 +14,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.denizk0461.weserplaner.R import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.exception.AcademicQuarterNotApplicableException import com.denizk0461.weserplaner.exception.AcademicQuarterNotApplicableException
import com.denizk0461.weserplaner.exception.ParcelNotFoundException import com.denizk0461.weserplaner.exception.ParcelNotFoundException
import com.denizk0461.weserplaner.model.TextSheetContentId
import com.denizk0461.weserplaner.sheet.TextSheet import com.denizk0461.weserplaner.sheet.TextSheet
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlin.jvm.Throws import kotlin.jvm.Throws
@@ -72,10 +73,15 @@ fun Resources.Theme.showErrorSnackBar(view: CoordinatorLayout, text: String, anc
* @param content text content of the sheet * @param content text content of the sheet
* @return sheet with text added * @return sheet with text added
*/ */
fun getTextSheet(header: String, content: String): TextSheet = TextSheet().also { sheet -> fun getTextSheet(
header: String,
content: String = "",
contentId: Int = TextSheetContentId.PASS_RAW_STRING
): TextSheet = TextSheet().also { sheet ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString("header", header) bundle.putString("header", header)
bundle.putString("content", content) bundle.putString("content", content)
bundle.putInt("contentId", contentId)
bundle.putBoolean("isCancellable", true) bundle.putBoolean("isCancellable", true)
sheet.arguments = bundle sheet.arguments = bundle
} }
@@ -194,4 +194,20 @@ interface AppDAO {
*/ */
@Query("SELECT openingHours, news FROM offer_canteen LIMIT 1") @Query("SELECT openingHours, news FROM offer_canteen LIMIT 1")
fun getCanteenInfo(): CanteenOfferTuple fun getCanteenInfo(): CanteenOfferTuple
/**
* Retrieve opening hours for the fetched canteen.
*
* @return opening hours for the canteen
*/
@Query("SELECT openingHours FROM offer_canteen LIMIT 1")
fun getCanteenOpeningHours(): LiveData<String>
/**
* Retrieve news for the fetched canteen.
*
* @return news for the canteen
*/
@Query("SELECT news FROM offer_canteen LIMIT 1")
fun getCanteenNews(): LiveData<String>
} }
@@ -78,6 +78,20 @@ class AppRepository(app: Application) {
*/ */
fun getCanteenInfo(): CanteenOfferTuple = dao.getCanteenInfo() fun getCanteenInfo(): CanteenOfferTuple = dao.getCanteenInfo()
/**
* Retrieve opening hours for the fetched canteen.
*
* @return opening hours for the canteen
*/
fun getCanteenOpeningHours(): LiveData<String> = dao.getCanteenOpeningHours()
/**
* Retrieve news for the fetched canteen.
*
* @return news for the canteen
*/
fun getCanteenNews(): LiveData<String> = dao.getCanteenNews()
/** /**
* Retrieves all canteen offers that match given day. * Retrieves all canteen offers that match given day.
* *
@@ -0,0 +1,6 @@
package com.denizk0461.weserplaner.exception
import java.io.IOException
class InvalidContentIdException : IOException() {
}
@@ -41,13 +41,13 @@ class CanteenFragment : AppFragment() {
* Locally saved opening hours to quickly access them without querying the database on every * Locally saved opening hours to quickly access them without querying the database on every
* opening of the bottom sheet. * opening of the bottom sheet.
*/ */
private var openingHours: String = "" // private var openingHours: String = ""
/** /**
* Locally saved news to quickly access them without querying the database on every opening of * Locally saved news to quickly access them without querying the database on every opening of
* the corresponding bottom sheet. * the corresponding bottom sheet.
*/ */
private var news: String = "" // private var news: String = ""
/** /**
* Locally stored dates to populate the TabLayout with. * Locally stored dates to populate the TabLayout with.
@@ -76,7 +76,7 @@ class CanteenFragment : AppFragment() {
binding.buttonNotifications.setOnClickListener { binding.buttonNotifications.setOnClickListener {
openBottomSheet(getTextSheet( openBottomSheet(getTextSheet(
getString(R.string.canteen_news_sheet_title), getString(R.string.canteen_news_sheet_title),
news, contentId = TextSheetContentId.NEWS,
)) ))
} }
@@ -114,7 +114,7 @@ class CanteenFragment : AppFragment() {
R.string.canteen_opening_hours, R.string.canteen_opening_hours,
getCurrentlySelectedCanteenName() getCurrentlySelectedCanteenName()
), ),
openingHours contentId = TextSheetContentId.OPENING_HOURS,
) )
) )
} }
@@ -181,10 +181,11 @@ class CanteenFragment : AppFragment() {
// Retrieve further information about this canteen from the database // Retrieve further information about this canteen from the database
viewModel.getCanteenInfo().also { info -> viewModel.getCanteenInfo().also { info ->
openingHours = info.openingHours /*
news = info.news * Set visibility of the notifications icon depending on whether news are available.
* TODO change this to a BadgeDrawable later
binding.buttonNotifications.visibility = if (news.isBlank()) { */
binding.buttonNotifications.visibility = if (info.news.isBlank()) {
View.GONE View.GONE
} else { } else {
View.VISIBLE View.VISIBLE
@@ -0,0 +1,8 @@
package com.denizk0461.weserplaner.model
object TextSheetContentId {
const val PASS_RAW_STRING = -1
const val OPENING_HOURS = 0
const val NEWS = 1
}
@@ -2,9 +2,13 @@ package com.denizk0461.weserplaner.sheet
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.fragment.app.viewModels
import com.denizk0461.weserplaner.R import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.viewBinding import com.denizk0461.weserplaner.data.viewBinding
import com.denizk0461.weserplaner.databinding.SheetTextBinding import com.denizk0461.weserplaner.databinding.SheetTextBinding
import com.denizk0461.weserplaner.exception.InvalidContentIdException
import com.denizk0461.weserplaner.model.TextSheetContentId
import com.denizk0461.weserplaner.viewmodel.TextSheetViewModel
/** /**
* Bottom sheet used for displaying any sort of text to the user. * Bottom sheet used for displaying any sort of text to the user.
@@ -14,22 +18,49 @@ class TextSheet : AppSheet(R.layout.sheet_text) {
// View binding // View binding
private val binding: SheetTextBinding by viewBinding(SheetTextBinding::bind) private val binding: SheetTextBinding by viewBinding(SheetTextBinding::bind)
// View model; used to retrieve certain texts as LiveData rather than receiving the raw string
private val viewModel: TextSheetViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// Retrieve headline for the window // Retrieve headline for the window
val header = arguments?.getString("header") ?: "" val header = arguments?.getString("header") ?: ""
// Retrieve content for the window /*
val content = arguments?.getString("content") ?: "" * Retrieve certain ID to denote which text content LiveData should be observed; if this is
* -1, assume that a raw string has been passed instead
*/
val contentId = arguments?.getInt("contentId") ?: TextSheetContentId.PASS_RAW_STRING
// Check whether an ID has passed to use LiveData
val content = if (contentId == TextSheetContentId.PASS_RAW_STRING) {
// Retrieve content for the window as raw string if this has been picked
arguments?.getString("content") ?: ""
} else "" // This empty string will be unused
// Set whether the sheet should be cancellable by actions such as swiping // Set whether the sheet should be cancellable by actions such as swiping
val isSheetCancellable = arguments?.getBoolean("isCancellable") == true val isSheetCancellable = arguments?.getBoolean("isCancellable") == true
// Set text values /*
binding.apply { * Set text header.
textHeader.text = header * TODO update this when contentId is set and canteen could change while the sheet is open!
textContent.text = content */
binding.textHeader.text = header
// Set content to raw string passed if ID says so
if (contentId == -1) {
binding.textContent.text = content
} else (when (contentId) {
// Observe LiveData if corresponding ID has been passed
TextSheetContentId.OPENING_HOURS -> viewModel.getCanteenOpeningHours()
TextSheetContentId.NEWS -> viewModel.getCanteenNews()
else -> throw InvalidContentIdException()
}).observe(viewLifecycleOwner) { observedContent ->
// Show the content
binding.textContent.text = (observedContent ?: "")
// If the content is empty, tell the user
.ifBlank { emptyContentStringFor(contentId) }
} }
// Set up an appropriate close button depending on whether the sheet is cancellable // Set up an appropriate close button depending on whether the sheet is cancellable
@@ -45,4 +76,16 @@ class TextSheet : AppSheet(R.layout.sheet_text) {
binding.buttonCancel.visibility = View.GONE binding.buttonCancel.visibility = View.GONE
} }
} }
/**
* Retrieves a string to show to the user if the content is empty.
*
* @param contentId used to determine which string res should be used
* @return localised string to show the user
*/
private fun emptyContentStringFor(contentId: Int) = getString(when (contentId) {
TextSheetContentId.OPENING_HOURS -> R.string.text_sheet_empty_opening_hours
TextSheetContentId.NEWS -> R.string.text_sheet_empty_news
else -> R.string.error_generic
})
} }
@@ -0,0 +1,23 @@
package com.denizk0461.weserplaner.viewmodel
import android.app.Application
import androidx.lifecycle.LiveData
class TextSheetViewModel(application: Application) : AppViewModel(application) {
/**
* Retrieve opening hours for the fetched canteen.
* Used when contentId = [com.denizk0461.weserplaner.model.TextSheetContentId.OPENING_HOURS]
*
* @return opening hours for the canteen
*/
fun getCanteenOpeningHours(): LiveData<String> = repo.getCanteenOpeningHours()
/**
* Retrieve news for the fetched canteen.
* Used when contentId = [com.denizk0461.weserplaner.model.TextSheetContentId.NEWS]
*
* @return news for the canteen
*/
fun getCanteenNews(): LiveData<String> = repo.getCanteenNews()
}
+1 -1
View File
@@ -31,7 +31,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/title_food" android:text="@string/title_food"
android:textSize="28sp" android:textSize="28sp"
android:fontFamily="@font/lato_bolditalic" android:fontFamily="@font/lato_blackitalic"
android:singleLine="true" android:singleLine="true"
android:ellipsize="marquee" android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever" android:marqueeRepeatLimit="marquee_forever"
+1 -1
View File
@@ -18,7 +18,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/title_schedule" android:text="@string/title_schedule"
android:textSize="28sp" android:textSize="28sp"
android:fontFamily="@font/lato_bolditalic" android:fontFamily="@font/lato_blackitalic"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
@@ -20,7 +20,7 @@
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:fontFamily="@font/lato_bolditalic" android:fontFamily="@font/lato_blackitalic"
android:text="@string/title_settings" android:text="@string/title_settings"
app:layout_scrollFlags="scroll|exitUntilCollapsed" app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:textSize="28sp"/> android:textSize="28sp"/>
+7 -2
View File
@@ -1,7 +1,8 @@
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- <string name="app_name">sixminutes</string>--> <!-- <string name="app_name">sixminutes</string>-->
<string name="toast_first_launch">Hey, danke, dass Du meine App ausprobierst! ~Deniz</string> <string name="toast_first_launch">Hey, danke, dass Du meine App ausprobierst! ~Deniz</string>
<string name="error_generic">Ein Fehler ist aufgetreten.</string>
<string name="monday">Montag</string> <string name="monday">Montag</string>
<string name="tuesday">Dienstag</string> <string name="tuesday">Dienstag</string>
@@ -68,6 +69,10 @@
<string name="canteen_fab_switch_canteen_content_desc">Wechsle Mensa</string> <string name="canteen_fab_switch_canteen_content_desc">Wechsle Mensa</string>
<string name="canteen_fab_refresh_content_desc">Aktualisiere Angebote</string> <string name="canteen_fab_refresh_content_desc">Aktualisiere Angebote</string>
<!-- TODO replace 'this canteen' with %s -->
<string name="text_sheet_empty_opening_hours">The Öffnungszeiten für diese Mensa können nicht angezeigt werden.</string>
<string name="text_sheet_empty_news">Es sind keine Neuigkeiten verfügbar.</string>
<string name="sheet_licences_header">Lizenzen</string> <string name="sheet_licences_header">Lizenzen</string>
<string name="title_schedule">Dein Stundenplan</string> <string name="title_schedule">Dein Stundenplan</string>
@@ -79,7 +84,7 @@
<string name="mensa_uni">Uni-Mensa</string> <string name="mensa_uni">Uni-Mensa</string>
<string name="cafe_central">Café Central</string> <string name="cafe_central">Café Central</string>
<string name="mensa_nw1">Mensa NW1</string> <string name="mensa_nw1" tools:ignore="Typos">Mensa NW1</string>
<string name="cafeteria_gw2">Cafeteria GW2</string> <string name="cafeteria_gw2">Cafeteria GW2</string>
<string name="mensa_neustadt">Mensa Neustadtswall</string> <string name="mensa_neustadt">Mensa Neustadtswall</string>
<string name="mensa_werder">Mensa Werderstraße</string> <string name="mensa_werder">Mensa Werderstraße</string>
+7 -2
View File
@@ -1,7 +1,8 @@
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">WeserPlaner</string> <string name="app_name" translatable="false">WeserPlaner</string>
<string name="toast_first_launch">Hey, thank you for trying out my app! ~Deniz</string> <string name="toast_first_launch">Hey, thank you for trying out my app! ~Deniz</string>
<string name="error_generic">An error occurred.</string>
<string name="monday">Monday</string> <string name="monday">Monday</string>
<string name="tuesday">Tuesday</string> <string name="tuesday">Tuesday</string>
@@ -69,6 +70,10 @@
<string name="canteen_fab_switch_canteen_content_desc">Switch canteen</string> <string name="canteen_fab_switch_canteen_content_desc">Switch canteen</string>
<string name="canteen_fab_refresh_content_desc">Refresh offers</string> <string name="canteen_fab_refresh_content_desc">Refresh offers</string>
<!-- TODO replace 'this canteen' with %s -->
<string name="text_sheet_empty_opening_hours">The opening hours for this canteen could not be downloaded.</string>
<string name="text_sheet_empty_news">No news are available.</string>
<string name="sheet_licences_header">Licences</string> <string name="sheet_licences_header">Licences</string>
<string name="sheet_licences_content" translatable="false">Android developer resources by Google.\nhttps://developer.android.com/\nandroidx.appcompat:appcompat:1.6.1\nandroidx.constraintlayout:constraintlayout:2.1.4\nandroidx.core:core-ktx:1.10.0\nandroidx.fragment:fragment-ktx:1.6.0-beta01\nandroidx.navigation:navigation-fragment-ktx:2.5.3\nandroidx.navigation:navigation-ui-ktx:2.5.3\nandroidx.preference:preference-ktx:1.2.0\nandroidx.room:room-runtime:2.5.1\nandroidx.room:room-compiler:2.5.1\nandroidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01\n\nDesign guidelines, icons, and colour palette by Google.\nhttps://m3.material.io/\ncom.google.android.material:material:1.9.0\n\nAdditional icons by Flaticon.\nhttps://www.flaticon.com/\n\nWeb scraping functionality by Jonathan Hedley.\nhttps://jhy.io/\nhttps://jsoup.org/\norg.jsoup:jsoup:1.15.4\n\nApp developed by Deniz Düzgören.\ndenizk0461@gmail.com\ndeniz7@uni-bremen.de</string> <string name="sheet_licences_content" translatable="false">Android developer resources by Google.\nhttps://developer.android.com/\nandroidx.appcompat:appcompat:1.6.1\nandroidx.constraintlayout:constraintlayout:2.1.4\nandroidx.core:core-ktx:1.10.0\nandroidx.fragment:fragment-ktx:1.6.0-beta01\nandroidx.navigation:navigation-fragment-ktx:2.5.3\nandroidx.navigation:navigation-ui-ktx:2.5.3\nandroidx.preference:preference-ktx:1.2.0\nandroidx.room:room-runtime:2.5.1\nandroidx.room:room-compiler:2.5.1\nandroidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01\n\nDesign guidelines, icons, and colour palette by Google.\nhttps://m3.material.io/\ncom.google.android.material:material:1.9.0\n\nAdditional icons by Flaticon.\nhttps://www.flaticon.com/\n\nWeb scraping functionality by Jonathan Hedley.\nhttps://jhy.io/\nhttps://jsoup.org/\norg.jsoup:jsoup:1.15.4\n\nApp developed by Deniz Düzgören.\ndenizk0461@gmail.com\ndeniz7@uni-bremen.de</string>
@@ -81,7 +86,7 @@
<string name="mensa_uni">Uni-Mensa</string> <string name="mensa_uni">Uni-Mensa</string>
<string name="cafe_central">Café Central</string> <string name="cafe_central">Café Central</string>
<string name="mensa_nw1">Mensa NW1</string> <string name="mensa_nw1" tools:ignore="Typos">Mensa NW1</string>
<string name="cafeteria_gw2">Cafeteria GW2</string> <string name="cafeteria_gw2">Cafeteria GW2</string>
<string name="mensa_neustadt">Mensa Neustadtswall</string> <string name="mensa_neustadt">Mensa Neustadtswall</string>
<string name="mensa_werder">Mensa Werderstraße</string> <string name="mensa_werder">Mensa Werderstraße</string>