diff --git a/app/build.gradle b/app/build.gradle index b0866de1e4..702336cdac 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -220,7 +220,8 @@ dependencies { implementation libs.disk.lru.cache implementation libs.markwon implementation libs.kizzyrpc - + implementation libs.konfetti.xml + implementation libs.konfetti.core implementation libs.conscrypt.android implementation libs.sentry.android diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 62357e3251..f1ec24034a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -307,9 +307,6 @@ - diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/core/github/AppUpdateRepository.kt b/app/src/main/kotlin/io/github/landwarderer/futon/core/github/AppUpdateRepository.kt index 0deceba200..4aa90d5bce 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/core/github/AppUpdateRepository.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/core/github/AppUpdateRepository.kt @@ -2,82 +2,29 @@ package io.github.landwarderer.futon.core.github import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.landwarderer.futon.BuildConfig import io.github.landwarderer.futon.R import io.github.landwarderer.futon.core.network.BaseHttpClient -import io.github.landwarderer.futon.core.prefs.AppSettings import io.github.landwarderer.futon.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request -import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton -private const val BUILD_TYPE_RELEASE = "release" - @Singleton class AppUpdateRepository @Inject constructor( - private val settings: AppSettings, @BaseHttpClient private val okHttp: OkHttpClient, @ApplicationContext context: Context, ) { -// TODO("Fix update checking.") - private val availableUpdate = MutableStateFlow(null) - private val latestReleaseUrl = buildString { - append("https://api.github.com/repos/") - append(context.getString(R.string.github_updates_repo)) - append("/releases/latest") - } - private val changelogUrl = buildString { append("https://raw.githubusercontent.com/") append(context.getString(R.string.github_updates_repo)) append("/refs/heads/devel/CHANGELOG.md") } - val isUpdateAvailable: Boolean - get() = availableUpdate.value != null - - fun observeAvailableUpdate() = availableUpdate.asStateFlow() - - suspend fun fetchUpdate(): AppVersion? = withContext(Dispatchers.IO) { - runCatchingCancellable { - val request = Request.Builder() - .get() - .url(latestReleaseUrl) - .build() - val response = okHttp.newCall(request).await() - val json = JSONObject(response.body?.string() ?: "{}") - - val currentVersion = VersionId(BuildConfig.VERSION_NAME) - val releaseVersion = VersionId(json.getString("tag_name").removePrefix("v")) - - // Only return update if there's a newer version available - if (releaseVersion <= currentVersion) { - return@runCatchingCancellable null - } - - AppVersion( - id = json.getLong("id"), - url = json.getString("html_url"), - name = json.getString("name").removePrefix("v"), - apkSize = 0L, // No longer downloading, so size not needed - apkUrl = "", // No longer downloading - description = json.getString("body"), - ) - }.onFailure { - it.printStackTraceDebug("AppUpdateRepository::fetchUpdate") - }.onSuccess { - availableUpdate.value = it - }.getOrNull() - } - suspend fun fetchChangelog(): String? = withContext(Dispatchers.IO) { runCatchingCancellable { val request = Request.Builder() @@ -89,9 +36,4 @@ class AppUpdateRepository @Inject constructor( it.printStackTraceDebug("AppUpdateRepository::fetchChangelog") }.getOrNull() } - - @Suppress("KotlinConstantConditions") - suspend fun isUpdateSupported(): Boolean { - return true // Updates are always available now (just checking for newer version) - } } diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/core/nav/AppRouter.kt b/app/src/main/kotlin/io/github/landwarderer/futon/core/nav/AppRouter.kt index 6c1fbd8f5a..a483076902 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/core/nav/AppRouter.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/core/nav/AppRouter.kt @@ -86,7 +86,6 @@ import io.github.landwarderer.futon.search.domain.SearchKind import io.github.landwarderer.futon.search.ui.MangaListActivity import io.github.landwarderer.futon.search.ui.multi.SearchActivity import io.github.landwarderer.futon.settings.SettingsActivity -import io.github.landwarderer.futon.settings.about.AppUpdateActivity import io.github.landwarderer.futon.settings.override.OverrideConfigActivity import io.github.landwarderer.futon.settings.reader.ReaderTapGridConfigActivity import io.github.landwarderer.futon.settings.sources.auth.SourceAuthActivity @@ -200,7 +199,6 @@ class AppRouter private constructor( fun openBookmarks() = startActivity(AllBookmarksActivity::class.java) - fun openAppUpdate() = startActivity(AppUpdateActivity::class.java) fun openSuggestions() { startActivity(suggestionsIntent(contextOrNull() ?: return)) diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/core/ui/dialog/ErrorDetailsDialog.kt b/app/src/main/kotlin/io/github/landwarderer/futon/core/ui/dialog/ErrorDetailsDialog.kt index e4afcc6335..fd979d7268 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/core/ui/dialog/ErrorDetailsDialog.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/core/ui/dialog/ErrorDetailsDialog.kt @@ -8,7 +8,6 @@ import androidx.core.view.isVisible import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.landwarderer.futon.R -import io.github.landwarderer.futon.core.github.AppUpdateRepository import io.github.landwarderer.futon.core.nav.AppRouter import io.github.landwarderer.futon.core.nav.router import io.github.landwarderer.futon.core.ui.AlertDialogFragment @@ -20,16 +19,12 @@ import io.github.landwarderer.futon.core.util.ext.report import io.github.landwarderer.futon.core.util.ext.requireSerializable import io.github.landwarderer.futon.core.util.ext.setTextAndVisible import io.github.landwarderer.futon.databinding.DialogErrorDetailsBinding -import javax.inject.Inject @AndroidEntryPoint class ErrorDetailsDialog : AlertDialogFragment(), View.OnClickListener { private lateinit var exception: Throwable - @Inject - lateinit var appUpdateRepository: AppUpdateRepository - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val args = requireArguments() @@ -48,9 +43,7 @@ class ErrorDetailsDialog : AlertDialogFragment(), Vie binding.buttonBrowser.isVisible = isUrlAvailable binding.textViewBrowser.isVisible = isUrlAvailable binding.textViewDescription.setTextAndVisible( - if (appUpdateRepository.isUpdateAvailable) { - R.string.error_disclaimer_app_outdated - } else if (exception.isReportable()) { + if (exception.isReportable()) { R.string.error_disclaimer_report } else { 0 @@ -67,12 +60,7 @@ class ErrorDetailsDialog : AlertDialogFragment(), Vie .setNeutralButton(androidx.preference.R.string.copy) { _, _ -> context?.copyToClipboard(getString(R.string.error), exception.stackTraceToString()) } - if (appUpdateRepository.isUpdateAvailable) { - builder.setPositiveButton(R.string.update) { _, _ -> - router.openAppUpdate() - dismiss() - } - } else if (exception.isReportable()) { + if (exception.isReportable()) { builder.setPositiveButton(R.string.report) { _, _ -> exception.report(silent = true) dismiss() diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainActivity.kt b/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainActivity.kt index f68ad31fdc..d9f5c20648 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainActivity.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainActivity.kt @@ -154,7 +154,6 @@ class MainActivity : BaseActivity(), AppBarOwner, BottomNav viewModel.isLoading.observe(this@MainActivity, this@MainActivity::onLoadingStateChanged) viewModel.isResumeEnabled.observe(this@MainActivity, this@MainActivity::onResumeEnabledChanged) viewModel.feedCounter.observe(this@MainActivity, ::onFeedCounterChanged) - viewModel.appUpdate.observe(this@MainActivity, MenuInvalidator(this@MainActivity)) viewModel.onFirstStart.observeEvent(this@MainActivity) { router.showWelcomeSheet() } viewModel.isBottomNavPinned.observe(this@MainActivity, ::setNavbarPinned) searchSuggestionViewModel.isIncognitoModeEnabled.observe(this@MainActivity, this@MainActivity::onIncognitoModeChanged) diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainMenuProvider.kt b/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainMenuProvider.kt index 53a9647421..6900ad03a7 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainMenuProvider.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainMenuProvider.kt @@ -19,8 +19,7 @@ class MainMenuProvider( override fun onPrepareMenu(menu: Menu) { menu.findItem(R.id.action_incognito)?.isChecked = viewModel.isIncognitoModeEnabled.value - val hasAppUpdate = viewModel.appUpdate.value != null - menu.findItem(R.id.action_app_update)?.isVisible = hasAppUpdate + menu.findItem(R.id.action_app_update)?.isVisible = false } override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { @@ -34,11 +33,6 @@ class MainMenuProvider( true } - R.id.action_app_update -> { - router.openAppUpdate() - true - } - R.id.action_downloads -> { router.openDownloads() true diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainViewModel.kt b/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainViewModel.kt index fafbafc1cc..38e10c10a8 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainViewModel.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/main/ui/MainViewModel.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import io.github.landwarderer.futon.core.exceptions.EmptyHistoryException -import io.github.landwarderer.futon.core.github.AppUpdateRepository import io.github.landwarderer.futon.core.prefs.AppSettings import io.github.landwarderer.futon.core.prefs.observeAsFlow import io.github.landwarderer.futon.core.prefs.observeAsStateFlow @@ -25,7 +24,6 @@ import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( private val historyRepository: HistoryRepository, - private val appUpdateRepository: AppUpdateRepository, trackingRepository: TrackingRepository, private val settings: AppSettings, readingResumeEnabledUseCase: ReadingResumeEnabledUseCase, @@ -43,7 +41,6 @@ class MainViewModel @Inject constructor( initialValue = false, ) - val appUpdate = appUpdateRepository.observeAvailableUpdate() val feedCounter = trackingRepository.observeUnreadUpdatesCount() .withErrorHandling() diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsFragment.kt b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsFragment.kt index 3e0d6295cf..487c596e00 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsFragment.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsFragment.kt @@ -1,30 +1,32 @@ package io.github.landwarderer.futon.settings.about -import android.content.Intent import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout import androidx.annotation.StringRes +import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.preference.Preference -import androidx.preference.SwitchPreferenceCompat import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.combine import io.github.landwarderer.futon.BuildConfig import io.github.landwarderer.futon.R -import io.github.landwarderer.futon.core.github.AppVersion -import io.github.landwarderer.futon.core.github.VersionId -import io.github.landwarderer.futon.core.github.isStable import io.github.landwarderer.futon.core.nav.router import io.github.landwarderer.futon.core.prefs.AppSettings import io.github.landwarderer.futon.core.ui.BasePreferenceFragment -import io.github.landwarderer.futon.core.util.ext.observe -import io.github.landwarderer.futon.core.util.ext.observeEvent +import nl.dionsegijn.konfetti.core.models.Shape +import nl.dionsegijn.konfetti.xml.KonfettiView +import nl.dionsegijn.konfetti.xml.image.DrawableImage +import kotlin.random.Random @AndroidEntryPoint class AboutSettingsFragment : BasePreferenceFragment(R.string.about) { private val viewModel by viewModels() + private var versionClickCount = 0 + private lateinit var konfettiView: KonfettiView override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_about) @@ -32,27 +34,50 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) { title = getString(R.string.app_version, BuildConfig.VERSION_NAME) } findPreference(AppSettings.KEY_LINK_TELEGRAM)?.isVisible = false - findPreference(AppSettings.KEY_UPDATES_UNSTABLE)?.run { - isEnabled = VersionId(BuildConfig.VERSION_NAME).isStable - if (!isEnabled) isChecked = true + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val list = super.onCreateView(inflater, container, savedInstanceState) + konfettiView = KonfettiView(requireContext()).apply { + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + // Ensure it doesn't consume clicks + isClickable = false + isFocusable = false + } + return FrameLayout(requireContext()).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + addView(list) + addView(konfettiView) } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - combine(viewModel.isUpdateSupported, viewModel.isLoading, ::Pair) - .observe(viewLifecycleOwner) { (isUpdateSupported, isLoading) -> - findPreference(AppSettings.KEY_UPDATES_UNSTABLE)?.isVisible = isUpdateSupported - findPreference(AppSettings.KEY_APP_VERSION)?.isEnabled = isUpdateSupported && !isLoading + } - } - viewModel.onUpdateAvailable.observeEvent(viewLifecycleOwner, ::onUpdateAvailable) + override fun onDestroyView() { + (view as? ViewGroup)?.removeView(konfettiView) + super.onDestroyView() } override fun onPreferenceTreeClick(preference: Preference): Boolean { return when (preference.key) { AppSettings.KEY_APP_VERSION -> { - viewModel.checkForUpdates() + versionClickCount++ + if (versionClickCount == 8) { + versionClickCount = 0 + triggerEasterEgg() + } true } @@ -82,12 +107,25 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) { } } - private fun onUpdateAvailable(version: AppVersion?) { - if (version == null) { - Snackbar.make(listView, R.string.no_update_available, Snackbar.LENGTH_SHORT).show() - } else { - startActivity(Intent(requireContext(), AppUpdateActivity::class.java)) + private fun triggerEasterEgg() { + val drawable = ContextCompat.getDrawable(requireContext(), R.drawable.unicorn) + if (drawable == null) { + Snackbar.make(listView, "Failed to load unicorn drawable", Snackbar.LENGTH_SHORT).show() + return } + + val coreImage = DrawableImage(drawable, drawable.intrinsicWidth, drawable.intrinsicHeight) + val drawableShape = Shape.DrawableShape(coreImage, tint = false, applyAlpha = true) + + val presets = listOf( + Presets.festive(drawableShape), + Presets.explode(drawableShape), + Presets.parade(drawableShape), + Presets.rain(drawableShape) + ) + + val randomPreset = presets[Random.nextInt(presets.size)] + konfettiView.start(randomPreset) } private fun openLink( diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsViewModel.kt b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsViewModel.kt index 14b9b26a5d..02b7f36a09 100644 --- a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsViewModel.kt +++ b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AboutSettingsViewModel.kt @@ -1,32 +1,10 @@ package io.github.landwarderer.futon.settings.about -import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.stateIn -import io.github.landwarderer.futon.core.github.AppUpdateRepository -import io.github.landwarderer.futon.core.github.AppVersion import io.github.landwarderer.futon.core.ui.BaseViewModel -import io.github.landwarderer.futon.core.util.ext.MutableEventFlow -import io.github.landwarderer.futon.core.util.ext.call import javax.inject.Inject @HiltViewModel -class AboutSettingsViewModel @Inject constructor( - private val appUpdateRepository: AppUpdateRepository, -) : BaseViewModel() { +class AboutSettingsViewModel @Inject constructor() : BaseViewModel() { - val isUpdateSupported = flow { - emit(appUpdateRepository.isUpdateSupported()) - }.stateIn(viewModelScope, SharingStarted.Eagerly, false) - - val onUpdateAvailable = MutableEventFlow() - - fun checkForUpdates() { - launchLoadingJob { - val update = appUpdateRepository.fetchUpdate() - onUpdateAvailable.call(update) - } - } } diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AppUpdateActivity.kt b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AppUpdateActivity.kt deleted file mode 100644 index b42438fe3b..0000000000 --- a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AppUpdateActivity.kt +++ /dev/null @@ -1,102 +0,0 @@ -package io.github.landwarderer.futon.settings.about - -import android.os.Bundle -import android.view.View -import android.view.ViewGroup.MarginLayoutParams -import androidx.activity.viewModels -import androidx.core.text.buildSpannedString -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updateLayoutParams -import androidx.core.view.updatePadding -import com.google.android.material.snackbar.Snackbar -import dagger.hilt.android.AndroidEntryPoint -import io.noties.markwon.Markwon -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import io.github.landwarderer.futon.R -import io.github.landwarderer.futon.core.github.AppVersion -import io.github.landwarderer.futon.core.nav.router -import io.github.landwarderer.futon.core.ui.BaseActivity -import io.github.landwarderer.futon.core.util.ext.consumeAllSystemBarsInsets -import io.github.landwarderer.futon.core.util.ext.getDisplayMessage -import io.github.landwarderer.futon.core.util.ext.observe -import io.github.landwarderer.futon.core.util.ext.observeEvent -import io.github.landwarderer.futon.core.util.ext.setTextAndVisible -import io.github.landwarderer.futon.core.util.ext.systemBarsInsets -import io.github.landwarderer.futon.databinding.ActivityAppUpdateBinding - -@AndroidEntryPoint -class AppUpdateActivity : BaseActivity(), View.OnClickListener { - - private val viewModel: AppUpdateViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(ActivityAppUpdateBinding.inflate(layoutInflater)) - viewModel.nextVersion.observe(this, ::onNextVersionChanged) - viewBinding.buttonCancel.setOnClickListener(this) - viewBinding.buttonUpdate.setOnClickListener(this) - - viewModel.isLoading.observe(this) { isLoading -> - viewBinding.buttonUpdate.isEnabled = viewModel.nextVersion.value != null && !isLoading - } - viewModel.onError.observeEvent(this, ::onError) - } - - override fun onApplyWindowInsets( - v: View, - insets: WindowInsetsCompat - ): WindowInsetsCompat { - val barsInsets = insets.systemBarsInsets - viewBinding.root.updatePadding(top = barsInsets.top) - viewBinding.dockedToolbarChild.updateLayoutParams { - leftMargin = barsInsets.left - rightMargin = barsInsets.right - bottomMargin = barsInsets.bottom - } - viewBinding.scrollView.updatePadding( - left = barsInsets.left, - right = barsInsets.right, - ) - return insets.consumeAllSystemBarsInsets() - } - - override fun onClick(v: View) { - when (v.id) { - R.id.button_cancel -> finishAfterTransition() - R.id.button_update -> openGitHub() - } - } - - private suspend fun onNextVersionChanged(version: AppVersion?) { - viewBinding.buttonUpdate.isEnabled = version != null && !viewModel.isLoading.value - if (version == null) { - viewBinding.textViewContent.setText(R.string.loading_) - return - } - val markwon = Markwon.create(this) - val message = withContext(Dispatchers.IO) { - buildSpannedString { - append(getString(R.string.new_version_s, version.name)) - appendLine() - appendLine() - append(markwon.toMarkdown(version.description)) - appendLine() - appendLine() - append(getString(R.string.github_download_warning)) - } - } - markwon.setParsedMarkdown(viewBinding.textViewContent, message) - } - - private fun openGitHub() { - val latestVersion = viewModel.nextVersion.value ?: return - if (!router.openExternalBrowser(latestVersion.url, getString(R.string.open_in_browser))) { - Snackbar.make(viewBinding.scrollView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() - } - } - - private fun onError(e: Throwable) { - viewBinding.textViewError.setTextAndVisible(R.string.error_occurred) - } -} diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AppUpdateViewModel.kt b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AppUpdateViewModel.kt deleted file mode 100644 index fb495345bc..0000000000 --- a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/AppUpdateViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.landwarderer.futon.settings.about - -import dagger.hilt.android.lifecycle.HiltViewModel -import io.github.landwarderer.futon.core.github.AppUpdateRepository -import io.github.landwarderer.futon.core.ui.BaseViewModel -import javax.inject.Inject - -@HiltViewModel -class AppUpdateViewModel @Inject constructor( - private val repository: AppUpdateRepository, -) : BaseViewModel() { - - val nextVersion = repository.observeAvailableUpdate() - - init { - if (nextVersion.value == null) { - launchLoadingJob { - repository.fetchUpdate() - } - } - } -} diff --git a/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/Presets.kt b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/Presets.kt new file mode 100644 index 0000000000..cdefe6d8b5 --- /dev/null +++ b/app/src/main/kotlin/io/github/landwarderer/futon/settings/about/Presets.kt @@ -0,0 +1,114 @@ +package io.github.landwarderer.futon.settings.about + +import android.graphics.drawable.Drawable +import nl.dionsegijn.konfetti.core.Angle +import nl.dionsegijn.konfetti.core.Party +import nl.dionsegijn.konfetti.core.Position +import nl.dionsegijn.konfetti.core.Rotation +import nl.dionsegijn.konfetti.core.Spread +import nl.dionsegijn.konfetti.core.emitter.Emitter +import nl.dionsegijn.konfetti.core.models.Shape +import nl.dionsegijn.konfetti.core.models.Size +import java.util.concurrent.TimeUnit + +class Presets { + companion object { + fun festive(drawable: Shape.DrawableShape? = null): List { + val party = Party( + speed = 30f, + maxSpeed = 50f, + damping = 0.9f, + angle = Angle.TOP, + spread = 45, + size = if (drawable != null) listOf(Size(24), Size(32)) else listOf(Size.SMALL, Size.MEDIUM), + shapes = if (drawable != null) listOf(drawable) else listOf(Shape.Square, Shape.Circle), + timeToLive = 3000L, + rotation = Rotation(multiplier3D = 0f), + colors = listOf(0xfce18a, 0xff726d, 0xf4306d, 0xb48def), + emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).max(30), + position = Position.Relative(0.5, 1.0) + ) + + return listOf( + party, + party.copy( + speed = 55f, + maxSpeed = 65f, + spread = 10, + emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).max(10), + ), + party.copy( + speed = 50f, + maxSpeed = 60f, + spread = 120, + emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).max(40), + ), + party.copy( + speed = 65f, + maxSpeed = 80f, + spread = 10, + emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).max(10), + ) + ) + } + + fun explode(drawable: Shape.DrawableShape? = null): List { + return listOf( + Party( + speed = 0f, + maxSpeed = 30f, + damping = 0.9f, + spread = 360, + size = if (drawable != null) listOf(Size(24), Size(32)) else listOf(Size.SMALL, Size.MEDIUM), + shapes = if (drawable != null) listOf(drawable) else listOf(Shape.Square, Shape.Circle), + colors = listOf(0xfce18a, 0xff726d, 0xf4306d, 0xb48def), + emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).max(100), + rotation = Rotation(multiplier3D = 0f), + position = Position.Relative(0.5, 0.3) + ) + ) + } + + fun parade(drawable: Shape.DrawableShape? = null): List { + val party = Party( + speed = 10f, + maxSpeed = 30f, + damping = 0.9f, + angle = Angle.RIGHT - 45, + spread = Spread.SMALL, + size = if (drawable != null) listOf(Size(24), Size(32)) else listOf(Size.SMALL, Size.MEDIUM), + shapes = if (drawable != null) listOf(drawable) else listOf(Shape.Square, Shape.Circle), + colors = listOf(0xfce18a, 0xff726d, 0xf4306d, 0xb48def), + emitter = Emitter(duration = 5, TimeUnit.SECONDS).perSecond(30), + rotation = Rotation(multiplier3D = 0f), + position = Position.Relative(0.0, 0.5) + ) + + return listOf( + party, + party.copy( + angle = party.angle - 90, // flip angle from right to left + position = Position.Relative(1.0, 0.5) + ), + ) + } + + fun rain(drawable: Shape.DrawableShape? = null): List { + return listOf( + Party( + speed = 0f, + maxSpeed = 15f, + damping = 0.9f, + angle = Angle.BOTTOM, + spread = Spread.ROUND, + size = if (drawable != null) listOf(Size(24), Size(32)) else listOf(Size.SMALL, Size.MEDIUM), + shapes = if (drawable != null) listOf(drawable) else listOf(Shape.Square, Shape.Circle), + colors = listOf(0xfce18a, 0xff726d, 0xf4306d, 0xb48def), + emitter = Emitter(duration = 5, TimeUnit.SECONDS).perSecond(100), + rotation = Rotation(multiplier3D = 0f), + position = Position.Relative(0.0, 0.0).between(Position.Relative(1.0, 0.0)) + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/unicorn.png b/app/src/main/res/drawable/unicorn.png new file mode 100644 index 0000000000..e95bfca72f Binary files /dev/null and b/app/src/main/res/drawable/unicorn.png differ diff --git a/app/src/main/res/layout/activity_app_update.xml b/app/src/main/res/layout/activity_app_update.xml deleted file mode 100644 index 84f64f3038..0000000000 --- a/app/src/main/res/layout/activity_app_update.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - -