diff --git a/app/build.gradle b/app/build.gradle index e6d35f548..26c2547e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,8 +27,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 34 - versionCode 147 - versionName "2.4.1" + versionCode 148 + versionName "2.4.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" @@ -164,8 +164,8 @@ play { defaultToAppBundles = false track = 'production' releaseStatus = ReleaseStatus.IN_PROGRESS - userFraction = 0.50d - updatePriority = 1 + userFraction = 0.99d + updatePriority = 2 enabled.set(false) } @@ -191,7 +191,7 @@ ext { room = "2.6.1" chucker = "4.0.0" mockk = "1.13.9" - coroutines = "1.7.3" + coroutines = "1.8.0" } dependencies { @@ -199,7 +199,7 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" implementation 'androidx.core:core-ktx:1.12.0' @@ -262,7 +262,7 @@ dependencies { playImplementation "com.google.android.play:integrity:1.3.0" playImplementation 'com.google.android.play:app-update-ktx:2.1.0' playImplementation 'com.google.android.play:review-ktx:2.0.1' - playImplementation "com.google.android.ump:user-messaging-platform:2.2.0" + playImplementation "com.google.android.ump:user-messaging-platform:2.1.0" hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.301' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.303' diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt index d7c2aeed9..108b0d58e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -30,8 +30,15 @@ val Resource.dataOrNull: T? get() = when (this) { is Resource.Success -> this.data is Resource.Intermediate -> this.data - is Resource.Loading -> null - is Resource.Error -> null + else -> null + } + +val Resource.dataOrThrow: T + get() = when (this) { + is Resource.Success -> this.data + is Resource.Intermediate -> this.data + is Resource.Loading -> throw IllegalStateException("Resource is in loading state") + is Resource.Error -> throw this.error } val Resource.errorOrNull: Throwable? diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt index bafb2d7e5..1ab079a3a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt @@ -11,10 +11,8 @@ import android.view.View import android.widget.RemoteViews import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.dataOrThrow import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.repositories.LuckyNumberRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.toFirstResult @@ -69,8 +67,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { appWidgetIds?.forEach { widgetId -> val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0) - val luckyNumberResource = getLuckyNumber(studentId, widgetId) - val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber?.toString() + val luckyNumber = getLuckyNumber(studentId, widgetId)?.luckyNumber?.toString() val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber) .apply { setTextViewText(R.id.luckyNumberWidgetValue, luckyNumber ?: "-") @@ -143,18 +140,18 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) } } + else -> null } if (currentStudent != null) { luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false) .toFirstResult() - } else { - Resource.Success(null) - } + .dataOrThrow + } else null } catch (e: Exception) { Timber.e(e, "An error has occurred in lucky number provider") - Resource.Error(e) + null } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt index 4e0578e2b..4cfc03229 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -11,7 +11,7 @@ import android.view.View.VISIBLE import android.widget.RemoteViews import android.widget.RemoteViewsService import io.github.wulkanowy.R -import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.dataOrThrow import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student @@ -27,6 +27,7 @@ import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Co import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.getErrorString import io.github.wulkanowy.utils.getPlural import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.runBlocking @@ -67,25 +68,31 @@ class TimetableWidgetFactory( override fun onDestroy() {} override fun onDataSetChanged() { - intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId -> - val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) - val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) + val appWidgetId = intent?.extras?.getInt(EXTRA_APPWIDGET_ID) ?: return + val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) + val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) + items = emptyList() + + runBlocking { runCatching { - runBlocking { - val student = getStudent(studentId) ?: return@runBlocking - val semester = semesterRepository.getCurrentSemester(student) - items = createItems( - lessons = getLessons(student, semester, date), - lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date) - ) + val student = getStudent(studentId) ?: return@runBlocking + val semester = semesterRepository.getCurrentSemester(student) + val lessons = getLessons(student, semester, date) + val lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date) + + createItems(lessons, lastSync) + } + .onFailure { + items = listOf(TimetableWidgetItem.Error(it)) + Timber.e(it, "An error has occurred in timetable widget factory") + } + .onSuccess { + items = it if (date == LocalDate.now()) { updateTodayLastLessonEnd(appWidgetId) } } - }.onFailure { - Timber.e(it, "An error has occurred in timetable widget factory") - } } } @@ -98,7 +105,7 @@ class TimetableWidgetFactory( student: Student, semester: Semester, date: LocalDate ): List { val timetable = timetableRepository.getTimetable(student, semester, date, date, false) - val lessons = timetable.toFirstResult().dataOrNull?.lessons.orEmpty() + val lessons = timetable.toFirstResult().dataOrThrow.lessons return lessons.sortedBy { it.number } } @@ -110,6 +117,7 @@ class TimetableWidgetFactory( BETWEEN_AND_BEFORE_LESSONS -> 0 else -> null } + return buildList { lessons.forEach { if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) { @@ -133,15 +141,12 @@ class TimetableWidgetFactory( sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true) } - companion object { - const val TIME_FORMAT_STYLE = "HH:mm" - } - override fun getViewAt(position: Int): RemoteViews? { return when (val item = items.getOrNull(position) ?: return null) { is TimetableWidgetItem.Normal -> getNormalItemRemoteView(item) is TimetableWidgetItem.Empty -> getEmptyItemRemoteView(item) is TimetableWidgetItem.Synchronized -> getSynchronizedItemRemoteView(item) + is TimetableWidgetItem.Error -> getErrorItemRemoteView(item) } } @@ -213,6 +218,18 @@ class TimetableWidgetFactory( } } + private fun getErrorItemRemoteView(item: TimetableWidgetItem.Error): RemoteViews { + return RemoteViews( + context.packageName, + R.layout.item_widget_timetable_error + ).apply { + setTextViewText( + R.id.timetable_widget_item_error_message, + context.resources.getErrorString(item.error) + ) + } + } + private fun updateTheme() { when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { Configuration.UI_MODE_NIGHT_YES -> { @@ -300,4 +317,8 @@ class TimetableWidgetFactory( synchronizationTime, ) } + + private companion object { + private const val TIME_FORMAT_STYLE = "HH:mm" + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt index 166b1a8fb..fb02f8919 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt @@ -17,10 +17,15 @@ sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) { data class Synchronized( val timestamp: Instant, ) : TimetableWidgetItem(TimetableWidgetItemType.SYNCHRONIZED) + + data class Error( + val error: Throwable + ) : TimetableWidgetItem(TimetableWidgetItemType.ERROR) } enum class TimetableWidgetItemType { NORMAL, EMPTY, SYNCHRONIZED, + ERROR, } diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 5736992bf..ef6308b6c 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,5 +1,7 @@ -Wersja 2.4.1 +Wersja 2.4.2 -- drobne poprawki stabilności aplikacji i odświeżania danych +- naprawiliśmy crash przy przełączaniu uczniów, motywów i języków +- naprawiliśmy crash przy dodawaniu dodatkowych lekcji +- naprawiliśmy obsługę błędów widżetach Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/layout/dialog_additional_add.xml b/app/src/main/res/layout/dialog_additional_add.xml index 4be436d76..78967394b 100644 --- a/app/src/main/res/layout/dialog_additional_add.xml +++ b/app/src/main/res/layout/dialog_additional_add.xml @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:editable="false" android:focusable="false" - android:inputType="text" + android:inputType="none" tools:ignore="Deprecated" /> @@ -67,7 +67,7 @@ android:layout_height="wrap_content" android:editable="false" android:focusable="false" - android:inputType="text" + android:inputType="none" tools:ignore="Deprecated" /> @@ -87,7 +87,7 @@ android:layout_height="wrap_content" android:editable="false" android:focusable="false" - android:inputType="text" + android:inputType="none" tools:ignore="Deprecated" /> diff --git a/app/src/main/res/layout/item_widget_timetable_error.xml b/app/src/main/res/layout/item_widget_timetable_error.xml new file mode 100644 index 000000000..6f9ab067a --- /dev/null +++ b/app/src/main/res/layout/item_widget_timetable_error.xml @@ -0,0 +1,12 @@ + + diff --git a/app/src/main/res/layout/widget_timetable.xml b/app/src/main/res/layout/widget_timetable.xml index b07cc78f6..b438da6c3 100644 --- a/app/src/main/res/layout/widget_timetable.xml +++ b/app/src/main/res/layout/widget_timetable.xml @@ -114,7 +114,5 @@ android:text="@string/widget_timetable_no_items" android:textAppearance="?attr/textAppearanceBody1" android:visibility="gone" /> - -