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 712a946f3..61eaaea19 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.data +import io.github.wulkanowy.data.repositories.isEndDateReached import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow @@ -267,7 +268,8 @@ inline fun networkBoundResource( emit(Resource.Loading()) val data = query().first() - if (shouldFetch(data)) { + val updatedShouldFetch = if (isEndDateReached) false else shouldFetch(data) + if (updatedShouldFetch) { emit(Resource.Intermediate(data)) try { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt index 815f8b758..a25f8a0e6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt @@ -12,9 +12,13 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.sync.Mutex import timber.log.Timber +import java.time.LocalDate import javax.inject.Inject import javax.inject.Singleton +private val endDate = LocalDate.of(2024, 6, 25) +val isEndDateReached = LocalDate.now() >= endDate + @Singleton class WulkanowyRepository @Inject constructor( private val wulkanowyService: WulkanowyService, @@ -24,7 +28,6 @@ class WulkanowyRepository @Inject constructor( ) { private val saveFetchResultMutex = Mutex() - private val cacheKey = "mapping_refresh_key" fun getAdminMessages(): Flow>> = diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index e0a136f98..aa0700b3e 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -4,19 +4,27 @@ import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES.O import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.asFlow -import androidx.work.* import androidx.work.BackoffPolicy.EXPONENTIAL +import androidx.work.Constraints +import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy.KEEP import androidx.work.ExistingPeriodicWorkPolicy.UPDATE +import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType.CONNECTED import androidx.work.NetworkType.UNMETERED +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo +import androidx.work.WorkManager import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.isEndDateReached import io.github.wulkanowy.services.sync.channels.Channel import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.isHolidays import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf import timber.log.Timber import java.time.LocalDate.now import java.util.concurrent.TimeUnit.MINUTES @@ -34,7 +42,9 @@ class SyncManager @Inject constructor( ) { init { - if (now().isHolidays) stopSyncWorker() + if (now().isHolidays || isEndDateReached) { + stopSyncWorker() + } if (SDK_INT >= O) { channels.forEach { it.create() } @@ -50,7 +60,7 @@ class SyncManager @Inject constructor( } fun startPeriodicSyncWorker(restart: Boolean = false) { - if (preferencesRepository.isServiceEnabled && !now().isHolidays) { + if (preferencesRepository.isServiceEnabled && !now().isHolidays && isEndDateReached) { val serviceInterval = preferencesRepository.servicesInterval workManager.enqueueUniquePeriodicWork( @@ -70,6 +80,10 @@ class SyncManager @Inject constructor( // if quiet, no notifications will be sent fun startOneTimeSyncWorker(quiet: Boolean = false): Flow { + if (isEndDateReached) { + return flowOf(null) + } + val work = OneTimeWorkRequestBuilder() .setInputData( Data.Builder() diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index bcbc23ef2..a7639a258 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -15,6 +15,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.isEndDateReached import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import io.github.wulkanowy.sdk.scrapper.exception.FeatureUnavailableException @@ -42,7 +43,9 @@ class SyncWorker @AssistedInject constructor( override suspend fun doWork(): Result = withContext(dispatchersProvider.io) { Timber.i("SyncWorker is starting") - if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure() + if (!studentRepository.isCurrentStudentSet() || isEndDateReached) { + return@withContext Result.failure() + } val (student, semester) = try { val student = studentRepository.getCurrentStudent() @@ -91,6 +94,7 @@ class SyncWorker @AssistedInject constructor( .build() ) } + errors.isNotEmpty() -> Result.retry() else -> { preferencesRepository.lasSyncDate = Instant.now() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/end/EndFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/end/EndFragment.kt new file mode 100644 index 000000000..cefbdddd3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/end/EndFragment.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.ui.modules.end + +import android.os.Bundle +import android.text.method.LinkMovementMethod +import android.view.View +import androidx.activity.addCallback +import androidx.core.text.HtmlCompat +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.databinding.FragmentEndBinding +import io.github.wulkanowy.ui.base.BaseFragment + +@AndroidEntryPoint +class EndFragment : BaseFragment(R.layout.fragment_end), EndView { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentEndBinding.bind(view) + + requireActivity().onBackPressedDispatcher.addCallback { + requireActivity().finishAffinity() + } + + binding.endClose.setOnClickListener { requireActivity().finishAffinity() } + + val message = getString(R.string.end_message) + binding.endDescription.movementMethod = LinkMovementMethod.getInstance() + binding.endDescription.text = + HtmlCompat.fromHtml(message, HtmlCompat.FROM_HTML_MODE_COMPACT) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/end/EndView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/end/EndView.kt new file mode 100644 index 000000000..730d570e6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/end/EndView.kt @@ -0,0 +1,5 @@ +package io.github.wulkanowy.ui.modules.end + +import io.github.wulkanowy.ui.base.BaseView + +interface EndView : BaseView diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt index e528c5147..0d9bb21ef 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -15,6 +15,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.pojos.RegisterUser import io.github.wulkanowy.databinding.ActivityLoginBinding import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.end.EndFragment import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment @@ -115,9 +116,14 @@ class LoginActivity : BaseActivity(), Logi } } + override fun navigateToEnd() { + openFragment(EndFragment(), clearBackStack = true) + } + override fun onResume() { super.onResume() inAppUpdateHelper.onResume() presenter.updateSdkMappings() + presenter.checkIfEnd() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt index aff0515f0..36552193c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.login import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.WulkanowyRepository +import io.github.wulkanowy.data.repositories.isEndDateReached +import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import kotlinx.coroutines.launch @@ -11,7 +13,8 @@ import javax.inject.Inject class LoginPresenter @Inject constructor( private val wulkanowyRepository: WulkanowyRepository, errorHandler: ErrorHandler, - studentRepository: StudentRepository + studentRepository: StudentRepository, + private val syncManager: SyncManager ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: LoginView) { @@ -26,4 +29,11 @@ class LoginPresenter @Inject constructor( .onFailure { Timber.e(it) } } } + + fun checkIfEnd() { + if (isEndDateReached) { + syncManager.stopSyncWorker() + view?.navigateToEnd() + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt index a0949e6d9..3fe1f0463 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt @@ -5,4 +5,6 @@ import io.github.wulkanowy.ui.base.BaseView interface LoginView : BaseView { fun initView() + + fun navigateToEnd() } 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 e6de17818..18437ef7a 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 @@ -14,7 +14,9 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.dataOrThrow import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.isEndDateReached import io.github.wulkanowy.data.toFirstResult import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.splash.SplashActivity @@ -35,6 +37,9 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { @Inject lateinit var sharedPref: SharedPrefProvider + @Inject + lateinit var preferencesRepository: PreferencesRepository + companion object { private const val LUCKY_NUMBER_WIDGET_MAX_SIZE = 196 @@ -130,6 +135,10 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { } private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking { + if (isEndDateReached) { + return@runBlocking null + } + try { val students = studentRepository.getSavedStudents() val student = students.singleOrNull { it.student.id == studentId }?.student diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index e64aa9b07..23aa51b42 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -33,6 +33,7 @@ import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.captcha.CaptchaDialog +import io.github.wulkanowy.ui.modules.end.EndFragment import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.AppInfo @@ -139,6 +140,7 @@ class MainActivity : BaseActivity(), MainVie super.onResume() inAppUpdateHelper.onResume() presenter.updateSdkMappings() + presenter.checkIfEnd() } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -362,4 +364,10 @@ class MainActivity : BaseActivity(), MainVie super.onSaveInstanceState(outState) navController.onSaveInstanceState(outState) } + + override fun navigateToEnd() { + binding.mainToolbar.isVisible = false + pushView(EndFragment()) + onBackCallback?.isEnabled = false + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index 6a072718d..13edac6e1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -7,6 +7,7 @@ import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.WulkanowyRepository +import io.github.wulkanowy.data.repositories.isEndDateReached import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter @@ -15,6 +16,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.AccountView import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView +import io.github.wulkanowy.ui.modules.end.EndView import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView import io.github.wulkanowy.utils.AdsHelper import io.github.wulkanowy.utils.AnalyticsHelper @@ -110,6 +112,7 @@ class MainPresenter @Inject constructor( } private fun shouldShowBottomNavigation(destination: BaseView) = when (destination) { + is EndView, is AccountView, is StudentInfoView, is AccountDetailsView -> false @@ -208,4 +211,11 @@ class MainPresenter @Inject constructor( .onFailure { Timber.e(it) } } } + + fun checkIfEnd() { + if (isEndDateReached) { + syncManager.stopSyncWorker() + view?.navigateToEnd() + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt index 70a94fc81..c83d95681 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -48,6 +48,8 @@ interface MainView : BaseView { fun openMoreDestination(destination: Destination) + fun navigateToEnd() + interface MainChildView { fun onFragmentReselected() 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 e60d54880..55621bc5a 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 @@ -22,6 +22,7 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.data.repositories.isEndDateReached import io.github.wulkanowy.data.toFirstResult import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey @@ -71,6 +72,8 @@ class TimetableWidgetFactory( items = emptyList() + if (isEndDateReached) return + runBlocking { runCatching { val student = getStudent(studentId) ?: return@runBlocking diff --git a/app/src/main/res/layout/fragment_end.xml b/app/src/main/res/layout/fragment_end.xml new file mode 100644 index 000000000..883f0fc56 --- /dev/null +++ b/app/src/main/res/layout/fragment_end.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-csb-rPL-v29/preferences_values.xml b/app/src/main/res/values-csb-rPL-v29/preferences_values.xml new file mode 100644 index 000000000..23a933143 --- /dev/null +++ b/app/src/main/res/values-csb-rPL-v29/preferences_values.xml @@ -0,0 +1,9 @@ + + + + Motiw systemu + Jôsny + Cemny + Cemny (AMOLED) + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 82ccf5a2a..2337cfe87 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -895,4 +895,8 @@ Unmute You have muted this user You have unmuted this user + + + Koniec Wulkanowego + Jak zapewne niektórzy z Was się domyślali zbliża się ten moment, aby zakończyć pewien etap. Wraz z końcem tego roku szkolnego zamykamy projekt Wulkanowy. Stworzenie apki było ekscytującym wyzwaniem, ale skala projektu jest tak duża, że nie jesteśmy w stanie odpowiedzialnie utrzymywać aplikacji. Wulkanowy był fajną przygodą, ale sytuacja wymknęła się nam spod kontroli – zarówno pod względem technicznym, jak i społecznym. Nie akceptujemy pojawiającego się hejtu wobec nas ani wobec innych, także Vulcana. Nie chcemy brać udziału w tych działaniach i być z nimi utożsamiani. Prosimy Was o powściągliwość i rozwagę w publikowanych komentarzach i nieprzekraczanie dopuszczalnych granic.<br /><br />Prosimy Was też o uszanowanie naszej decyzji, jest ona przemyślana i ostateczna. Wszystkim dotychczasowym użytkownikom Wulkanowego rekomendujemy użycie oficjalnej aplikacji <a href="https://play.google.com/store/apps/details?id=pl.edu.vulcan.hebe&hl=pl">Dzienniczek VULCAN</a>. Jeszcze raz dziękujemy wszystkim użytkownikom za lata wsparcia, pomoc i miłe słowa!