diff --git a/andnext_markdown/src/main/java/club/andnext/markdown/MarkdownWebView.java b/andnext_markdown/src/main/java/club/andnext/markdown/MarkdownWebView.java
index 7058af4d2..f4914e979 100644
--- a/andnext_markdown/src/main/java/club/andnext/markdown/MarkdownWebView.java
+++ b/andnext_markdown/src/main/java/club/andnext/markdown/MarkdownWebView.java
@@ -47,7 +47,7 @@ public MarkdownWebView(Context context, AttributeSet attrs, int defStyleAttr, in
@SuppressLint("SetJavaScriptEnabled")
void init(Context context) {
- this.setBackgroundColor(Color.TRANSPARENT);
+ this.setBackgroundColor(Color.WHITE);
this.getSettings().setBlockNetworkLoads(true);
this.getSettings().setLoadWithOverviewMode(true);
diff --git a/res/layout/dialog_progress.xml b/res/layout/dialog_progress.xml
new file mode 100644
index 000000000..e04febd5c
--- /dev/null
+++ b/res/layout/dialog_progress.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/edlplan/ui/fragment/BaseFragment.kt b/src/com/edlplan/ui/fragment/BaseFragment.kt
index 877633dba..c1c9d7b3b 100644
--- a/src/com/edlplan/ui/fragment/BaseFragment.kt
+++ b/src/com/edlplan/ui/fragment/BaseFragment.kt
@@ -10,27 +10,32 @@ import androidx.fragment.app.Fragment
import com.edlplan.framework.easing.Easing
import com.edlplan.ui.ActivityOverlay
import com.edlplan.ui.EasingHelper
-import com.reco1l.osu.*
import ru.nsu.ccfit.zuev.osuplus.R
abstract class BaseFragment : Fragment(), BackPressListener {
var root: View? = null
private set
- private var background: View? = null
var onDismissListener: OnDismissListener? = null
var isDismissOnBackgroundClick = false
- var isCreated = false
- private set
var isDismissOnBackPress = true
+ /**
+ * Whether the fragment is created. This is set to true after [onCreateView] is called.
+ */
+ var isCreated = false
+ private set
/**
* If true, the fragment will intercept back press event when it's received.
*/
var interceptBackPress = true
+ /**
+ * Whether the fragment is loaded. This is set to true after [onLoadView] is called.
+ */
+ var isLoaded = false
+ private set
- private var isLoaded = false
private var isDismissCalled = false
diff --git a/src/com/edlplan/ui/fragment/MarkdownFragment.java b/src/com/edlplan/ui/fragment/MarkdownFragment.java
index f5a669d95..96ccc5a4d 100644
--- a/src/com/edlplan/ui/fragment/MarkdownFragment.java
+++ b/src/com/edlplan/ui/fragment/MarkdownFragment.java
@@ -64,7 +64,7 @@ public MarkdownFragment setTitle(@StringRes int title) {
}
protected void playOnLoadAnim() {
- View body = findViewById(R.id.frg_body);
+ View body = findViewById(R.id.fullLayout);
body.setTranslationY(500);
body.animate().cancel();
body.animate()
@@ -76,7 +76,7 @@ protected void playOnLoadAnim() {
}
protected void playOnDismissAnim(Runnable runnable) {
- View body = findViewById(R.id.frg_body);
+ View body = findViewById(R.id.fullLayout);
body.animate().cancel();
body.animate()
.translationY(500)
diff --git a/src/com/reco1l/framework/net/FileRequest.kt b/src/com/reco1l/framework/net/FileRequest.kt
index fb91dfd7b..a25eb6b2c 100644
--- a/src/com/reco1l/framework/net/FileRequest.kt
+++ b/src/com/reco1l/framework/net/FileRequest.kt
@@ -13,7 +13,7 @@ class FileRequest(val file: File, url: HttpUrl): WebRequest(url) {
/**
* The download observer.
*/
- var observer: IDownloaderObserver? = null
+ var observer: IFileRequestObserver? = null
/**
* Whether the download is in progress, this is set to true once [execute] is called.
@@ -116,13 +116,9 @@ class FileRequest(val file: File, url: HttpUrl): WebRequest(url) {
}
-interface IDownloaderObserver
-{
- fun onDownloadEnd(downloader: FileRequest) = Unit
-
- fun onDownloadCancel(downloader: FileRequest) = Unit
-
- fun onDownloadUpdate(downloader: FileRequest) = Unit
-
- fun onDownloadFail(downloader: FileRequest, exception: Exception) = Unit
+interface IFileRequestObserver {
+ fun onDownloadEnd(request: FileRequest) = Unit
+ fun onDownloadCancel(request: FileRequest) = Unit
+ fun onDownloadUpdate(request: FileRequest) = Unit
+ fun onDownloadFail(request: FileRequest, exception: Exception) = Unit
}
diff --git a/src/com/reco1l/osu/UpdateManager.kt b/src/com/reco1l/osu/UpdateManager.kt
index 408e8b9c4..60ece52a0 100644
--- a/src/com/reco1l/osu/UpdateManager.kt
+++ b/src/com/reco1l/osu/UpdateManager.kt
@@ -2,315 +2,170 @@ package com.reco1l.osu
import android.content.Intent
import android.content.Intent.*
+import android.util.Log
import androidx.core.content.FileProvider
-import androidx.preference.PreferenceManager
import com.osudroid.resources.R
import com.edlplan.ui.fragment.MarkdownFragment
-import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_INDEFINITE
-import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_SHORT
-import com.google.android.material.snackbar.Snackbar
import com.reco1l.framework.net.FileRequest
-import com.reco1l.framework.net.IDownloaderObserver
-import okhttp3.Request
-import org.json.JSONObject
+import com.reco1l.framework.net.IFileRequestObserver
+import com.reco1l.framework.net.JsonObjectRequest
+import com.reco1l.osu.ui.MessageDialog
+import com.reco1l.osu.ui.ProgressDialog
+import ru.nsu.ccfit.zuev.osu.Config
import ru.nsu.ccfit.zuev.osu.GlobalManager
+import ru.nsu.ccfit.zuev.osu.ToastLogger
import ru.nsu.ccfit.zuev.osu.helper.StringTable
-import ru.nsu.ccfit.zuev.osu.online.OnlineManager
import ru.nsu.ccfit.zuev.osu.online.OnlineManager.updateEndpoint
+import ru.nsu.ccfit.zuev.osuplus.BuildConfig
import ru.nsu.ccfit.zuev.osuplus.BuildConfig.APPLICATION_ID
import java.io.File
-object UpdateManager: IDownloaderObserver
+object UpdateManager: IFileRequestObserver
{
- private val mainActivity = GlobalManager.getInstance().mainActivity
+ private val apksDirectory = File(Config.getCachePath(), "updates").apply(File::mkdirs)
- private val preferences
- get() = PreferenceManager.getDefaultSharedPreferences(mainActivity)
- private val cacheDirectory = File(mainActivity.cacheDir, "updates").apply { mkdirs() }
+ private var progressDialog: ProgressDialog? = null
- private val snackBar = Snackbar.make(mainActivity.window.decorView, "", LENGTH_INDEFINITE)
-
-
- private var downloadURL: String? = null
-
- private var newVersionCode: Long = mainActivity.versionCode
-
+ @JvmStatic
fun onActivityStart() = mainThread {
- // Finding if there's a "pending changelog". This means the game was previously updated, we're
- // showing the changelog after update with a prompt asking user to show.
- preferences.apply {
-
- val latestUpdate = getLong("latestVersionCode", mainActivity.versionCode)
- val pendingChangelog = getString("pendingChangelog", null)
- if (!pendingChangelog.isNullOrEmpty()) {
- if (latestUpdate > mainActivity.versionCode) {
- snackBar.apply {
+ val activity = GlobalManager.getInstance().mainActivity
+ val version = Config.getLong("version", activity.versionCode)
- // Will only dismiss if user wants.
- duration = LENGTH_INDEFINITE
+ // Ignoring debug because otherwise every compiled build will show the dialog.
+ if (!BuildConfig.DEBUG && version < activity.versionCode) {
- // Show changelog button.
- setAction(R.string.changelog_title) {
+ MessageDialog()
+ .setTitle(StringTable.get(R.string.update_info_updated))
+ .setMessage("Game was updated to a newer version.\nDo you want to see the changelog?")
+ .addButton("Yes") {
+ it.dismiss()
- MarkdownFragment().apply {
- setTitle(R.string.changelog_title)
- setMarkdown(pendingChangelog)
- show()
- }
- }
- setText(R.string.update_info_updated)
- show()
- }
+ MarkdownFragment()
+ .setTitle(R.string.changelog_title)
+ .setMarkdown(activity.assets.open("app/changelog.md").reader().readText())
+ .show()
}
- // Now we're removing the cached changelog.
- edit().putString("pendingChangelog", null).apply()
- }
+ .addButton("No", clickListener = MessageDialog::dismiss)
+ .show()
+
+ Config.setLong("version", activity.versionCode)
}
checkNewUpdates(true)
}
-
/**
* Check for new game updates.
*
- * @param silently If `true` no prompt will be shown unless there's new updates.
+ * @param silently If `true`, no prompts will be shown unless there's new updates.
*/
- fun checkNewUpdates(silently: Boolean)
- {
- if (!silently) {
- snackBar.apply {
+ @JvmStatic
+ fun checkNewUpdates(silently: Boolean) {
- duration = LENGTH_INDEFINITE
-
- setText(R.string.update_info_checking)
- setAction(null, null)
- show()
- }
+ if (!silently) {
+ ToastLogger.showText(R.string.update_info_checking, false)
}
async {
- // Cleaning update directory first, checking if there's a newer package downloaded and
- // then installing it.
- cacheDirectory.listFiles()?.also { list ->
-
- var newestVersionDownloaded: Long = mainActivity.versionCode
-
- list.forEach {
-
- val version = it.nameWithoutExtension.toLongOrNull() ?: return@forEach
-
- // Deleting the file corresponding to this version if still present.
- if (version == mainActivity.versionCode)
- it.delete()
-
- // Finding the newest package.
- if (version > newestVersionDownloaded)
- newestVersionDownloaded = version
- }
+ // Cleaning up old updates.
+ apksDirectory.listFiles()?.forEach { it.delete() }
- // Directly navigate to installation if there's already a newer package.
- if (newestVersionDownloaded > mainActivity.versionCode) {
- newVersionCode = newestVersionDownloaded
- onFoundNewUpdate(silently)
- return@async
- }
- }
-
- // Requesting to server asking for new updates.
try {
-
- // Avoid new request if one was already done.
- if (downloadURL != null) {
- onFoundNewUpdate(silently)
- return@async
- }
-
- val request = Request.Builder()
- .url(updateEndpoint + mainActivity.resources.configuration.locale.language)
- .build()
-
- OnlineManager.client.newCall(request).execute().use {
-
- val response = JSONObject(it.body!!.string())
- val changelogUrl = response.getString("changelog")
+ JsonObjectRequest(updateEndpoint).use { request ->
- downloadURL = response.getString("link")
- newVersionCode = response.getLong("version_code")
+ val response = request.execute().json
- // Previous implementation has this check, server returning an older version
- // shouldn't happen.
- if (newVersionCode <= mainActivity.versionCode) {
- onAlreadyLatestVersion(silently)
- return@async
- }
+ val newVersion = response.getLong("version_code")
+ val link = response.getString("link")
- // Storing change log link to show once the user update to next version.
- preferences.apply {
- edit().putString("pendingChangelog", changelogUrl).apply()
+ if (newVersion <= GlobalManager.getInstance().mainActivity.versionCode) {
+ if (!silently) {
+ ToastLogger.showText(R.string.update_info_latest, false)
+ }
+ return@use
}
- onFoundNewUpdate(silently)
- }
- } catch (exception: Exception) {
+ MessageDialog()
+ .setTitle("New update available!")
+ .setMessage(StringTable.get(R.string.update_dialog_message))
+ .addButton(StringTable.get(R.string.update_dialog_button_update)) { dialog ->
- exception.printStackTrace()
+ dialog.dismiss()
- onUpdateCheckFailed(silently)
- }
- }
- }
-
-
- private fun onInstallNewUpdate(file: File) {
-
- val intent = Intent(ACTION_VIEW).apply {
-
- val uri = FileProvider.getUriForFile(mainActivity, "$APPLICATION_ID.fileProvider", file)
-
- setDataAndType(uri, "application/vnd.android.package-archive")
- addFlags(FLAG_GRANT_READ_URI_PERMISSION)
- }
- mainActivity.startActivity(intent)
- }
-
- private fun onDownloadNewUpdate(file: File) {
-
- // Empty string: At this point download URL shouldn't be null but if it is the case (which is weird) we set an
- // empty string so the downloader invokes onDownloadFail() and a prompt is shown to user rather than nothing.
- val url = downloadURL ?: ""
-
- val downloader = FileRequest(file, url)
- downloader.observer = this
-
- async {
- downloader.execute()
-
- mainThread {
- snackBar.apply {
+ progressDialog = ProgressDialog().apply {
+ indeterminate = true
+ allowDismiss = false
+ max = 100
+ title = "Downloading update"
+ message = StringTable.format(R.string.update_info_downloading, 0)
+ show()
+ }
- duration = LENGTH_INDEFINITE
+ async {
+ val file = File(apksDirectory, "${newVersion}.apk")
+ if (file.exists()) {
+ file.delete()
+ }
+ file.createNewFile()
- setText(StringTable.format(R.string.update_info_downloading, 0))
- setAction(R.string.beatmap_downloader_cancel) { downloader.cancel() }
- show()
+ val fileRequest = FileRequest(file, link)
+ fileRequest.observer = this@UpdateManager
+ fileRequest.execute()
+ }
+ }
+ .addButton("Update later") { it.dismiss() }
+ .show()
}
- }
- }
- }
-
-
- private fun onFoundNewUpdate(silently: Boolean) = mainThread {
-
- if (newVersionCode <= mainActivity.versionCode) {
- onAlreadyLatestVersion(silently)
- return@mainThread
- }
+ } catch (e: Exception) {
+ Log.e("UpdateManager", "Failed to check for updates.", e)
- preferences.apply {
- edit().putLong("latestVersionCode", newVersionCode).apply()
- }
-
- snackBar.apply {
-
- duration = 5000
-
- setText(R.string.update_dialog_message)
- setAction(R.string.update_dialog_button_update) {
-
- val file = File(cacheDirectory, "$newVersionCode.apk")
-
- // Files is already downloaded, navigating to installation.
- if (file.exists()) {
- onInstallNewUpdate(file)
- return@setAction
- }
-
- file.createNewFile()
- onDownloadNewUpdate(file)
+ if (!silently) {
+ ToastLogger.showText(R.string.update_info_check_failed, false)
+ }
}
- show()
}
}
- private fun onUpdateCheckFailed(silently: Boolean) = mainThread {
- if (silently) {
- snackBar.dismiss()
- return@mainThread
- }
-
- snackBar.apply {
- duration = LENGTH_SHORT
+ override fun onDownloadUpdate(request: FileRequest) = mainThread {
+ val progress = request.progress.toInt()
- setText(R.string.update_info_check_failed)
- setAction(null, null)
- show()
- }
- }
-
- private fun onAlreadyLatestVersion(silently: Boolean) = mainThread {
-
- if (silently) {
- snackBar.dismiss()
- return@mainThread
- }
-
- snackBar.apply {
-
- duration = LENGTH_SHORT
-
- setText(R.string.update_info_latest)
- setAction(null, null)
- show()
- }
+ progressDialog?.indeterminate = false
+ progressDialog?.progress = progress
+ progressDialog?.setMessage(StringTable.format(R.string.update_info_downloading, progress))
}
+ override fun onDownloadEnd(request: FileRequest) {
+ mainThread { progressDialog?.dismiss() }
- override fun onDownloadUpdate(downloader: FileRequest) = mainThread {
+ val activity = GlobalManager.getInstance().mainActivity
+ val uri = FileProvider.getUriForFile(activity, "$APPLICATION_ID.fileProvider", request.file)
- snackBar.setText(StringTable.format(R.string.update_info_downloading, downloader.progress.toInt()))
- }
+ val intent = Intent(ACTION_VIEW)
+ intent.setDataAndType(uri, "application/vnd.android.package-archive")
+ intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
- override fun onDownloadEnd(downloader: FileRequest) {
- mainThread(snackBar::dismiss)
- onInstallNewUpdate(downloader.file)
+ activity.startActivity(intent)
}
- override fun onDownloadFail(downloader: FileRequest, exception: Exception) {
-
- exception.printStackTrace()
-
- mainThread {
- snackBar.apply {
+ override fun onDownloadFail(request: FileRequest, exception: Exception) {
+ Log.e("UpdateManager", "Failed to download update.", exception)
+ ToastLogger.showText(R.string.update_info_download_failed, false)
- duration = LENGTH_SHORT
-
- setText(R.string.update_info_download_failed)
- setAction(null, null)
- show()
- }
- }
- downloader.file.delete()
+ mainThread { progressDialog?.dismiss() }
+ request.file.delete()
}
- override fun onDownloadCancel(downloader: FileRequest) {
- mainThread {
- snackBar.apply {
-
- duration = LENGTH_SHORT
+ override fun onDownloadCancel(request: FileRequest) {
+ ToastLogger.showText(R.string.update_info_download_canceled, false)
- setText(R.string.update_info_download_canceled)
- setAction(null, null)
- show()
- }
- }
- downloader.file.delete()
+ mainThread { progressDialog?.dismiss() }
+ request.file.delete()
}
}
\ No newline at end of file
diff --git a/src/com/reco1l/osu/beatmaplisting/BeatmapDownloader.kt b/src/com/reco1l/osu/beatmaplisting/BeatmapDownloader.kt
index bac5f4d47..e0def6a0e 100644
--- a/src/com/reco1l/osu/beatmaplisting/BeatmapDownloader.kt
+++ b/src/com/reco1l/osu/beatmaplisting/BeatmapDownloader.kt
@@ -4,7 +4,7 @@ import android.os.Environment.DIRECTORY_DOWNLOADS
import android.view.View
import com.osudroid.resources.R.*
import com.reco1l.framework.net.FileRequest
-import com.reco1l.framework.net.IDownloaderObserver
+import com.reco1l.framework.net.IFileRequestObserver
import com.reco1l.osu.mainThread
import com.reco1l.osu.multiplayer.Multiplayer
import com.reco1l.osu.multiplayer.RoomScene
@@ -18,7 +18,7 @@ import ru.nsu.ccfit.zuev.osu.helper.FileUtils
import ru.nsu.ccfit.zuev.osu.helper.StringTable
import java.io.IOException
-object BeatmapDownloader : IDownloaderObserver {
+object BeatmapDownloader : IFileRequestObserver {
private lateinit var fragment: DownloadFragment
diff --git a/src/com/reco1l/osu/beatmaplisting/BeatmapListing.kt b/src/com/reco1l/osu/beatmaplisting/BeatmapListing.kt
index f4e9efe9f..0e4986b11 100644
--- a/src/com/reco1l/osu/beatmaplisting/BeatmapListing.kt
+++ b/src/com/reco1l/osu/beatmaplisting/BeatmapListing.kt
@@ -2,7 +2,6 @@ package com.reco1l.osu.beatmaplisting
import android.graphics.BitmapFactory
import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
import android.util.Log
import android.view.Choreographer
import android.view.Choreographer.FrameCallback
@@ -34,7 +33,7 @@ import com.edlplan.ui.fragment.BaseFragment
import com.google.android.material.progressindicator.CircularProgressIndicator
import com.reco1l.*
import com.reco1l.framework.bass.URLBassStream
-import com.reco1l.framework.net.IDownloaderObserver
+import com.reco1l.framework.net.IFileRequestObserver
import com.reco1l.framework.net.JsonArrayRequest
import com.reco1l.osu.*
import com.reco1l.osu.beatmaplisting.BeatmapMirrorSearchRequestModel.OrderType
@@ -42,7 +41,6 @@ import com.reco1l.osu.beatmaplisting.BeatmapMirrorSearchRequestModel.SortType
import com.reco1l.osu.ui.Option
import com.reco1l.osu.ui.SelectDialog
import com.reco1l.osu.ui.SelectDropdown
-import com.reco1l.toolkt.android.backgroundColor
import com.reco1l.toolkt.android.cornerRadius
import com.reco1l.toolkt.android.dp
import com.reco1l.toolkt.android.drawableLeft
@@ -67,7 +65,7 @@ import ru.nsu.ccfit.zuev.osu.RankedStatus
class BeatmapListing : BaseFragment(),
- IDownloaderObserver,
+ IFileRequestObserver,
OnEditorActionListener,
OnKeyListener,
FrameCallback {
@@ -796,9 +794,13 @@ class BeatmapListingFiltersFragment(private val beatmapListing: BeatmapListing)
statusButton.setOnClickListener {
+ val options = RankedStatus.entries.map { status ->
+ Option(requireContext().getString(status.stringId), status)
+ }.toMutableList()
+ options.add(0, Option("All", null))
+
SelectDropdown(statusButton)
- .addOption(Option("All", null))
- .setOptions(RankedStatus.entries.map { status -> Option(requireContext().getString(status.stringId), status) }, false)
+ .setOptions(options)
.setSelected(rankedStatus)
.setOnSelectListener {
it as RankedStatus?
diff --git a/src/com/reco1l/osu/multiplayer/LobbyRoomList.kt b/src/com/reco1l/osu/multiplayer/LobbyRoomList.kt
index 9eb59c8da..e6a4aa872 100644
--- a/src/com/reco1l/osu/multiplayer/LobbyRoomList.kt
+++ b/src/com/reco1l/osu/multiplayer/LobbyRoomList.kt
@@ -43,10 +43,7 @@ class LobbyRoomList : ScrollableList() {
setTitle(room.name)
setMessage("Please enter the room password:")
setAllowDismiss(false)
-
- setOnTextInputBind {
- it.inputType = EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
- }
+ setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)
addButton("Join") {
val password = (it as PromptDialog).input
diff --git a/src/com/reco1l/osu/ui/Dialog.kt b/src/com/reco1l/osu/ui/Dialog.kt
index b86ed31e1..ae5be65f9 100644
--- a/src/com/reco1l/osu/ui/Dialog.kt
+++ b/src/com/reco1l/osu/ui/Dialog.kt
@@ -1,18 +1,16 @@
package com.reco1l.osu.ui
import android.graphics.Color
-import android.text.Html
import android.text.method.LinkMovementMethod
+import android.util.Log
import android.view.ContextThemeWrapper
import android.view.Gravity.*
import android.view.View
import android.widget.Button
-import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.text.HtmlCompat
-import androidx.core.view.isVisible
-import androidx.core.widget.doOnTextChanged
+import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import com.edlplan.framework.easing.Easing
import com.edlplan.ui.EasingHelper
import com.edlplan.ui.fragment.BaseFragment
@@ -26,66 +24,131 @@ import com.reco1l.toolkt.animation.toScaleY
import ru.nsu.ccfit.zuev.osuplus.R
+/**
+ * A button to be displayed in a dialog.
+ */
data class DialogButton(
+
+ /**
+ * The text to be displayed in the button.
+ */
val text: String,
+
+ /**
+ * The color of the button text and icon.
+ */
val tint: Int = Color.WHITE,
+
+ /**
+ * The function to be called when the button is clicked.
+ */
val clickListener: (MessageDialog) -> Unit
)
+/**
+ * A dialog that displays a message to the user.
+ */
open class MessageDialog : BaseFragment() {
override val layoutID = R.layout.dialog_message_fragment
- protected var title: CharSequence = "Alert"
+ /**
+ * The title to be displayed in the dialog.
+ */
+ var title: CharSequence = "Alert"
+ set(value) {
+ field = value
+ if (isCreated) {
+ findViewById(R.id.title)?.text = value
+ }
+ }
- protected var message: CharSequence = ""
+ /**
+ * The message to be displayed in the dialog.
+ */
+ open var message: CharSequence = ""
+ set(value) {
+ field = value
+ if (isCreated) {
+ findViewById(R.id.message)?.text = (
+ if (isHTMLMessage) HtmlCompat.fromHtml(value.toString(), FROM_HTML_MODE_LEGACY)
+ else value
+ )
+ }
+ }
- protected var isHTMLMessage = false
+ /**
+ * Whether the message is HTML formatted or not.
+ */
+ var isHTMLMessage = false
+ set(value) {
+ field = value
+ if (isCreated) {
+ message = message
+ }
+ }
- protected var allowDismiss = true
+ /**
+ * The buttons to be displayed in the dialog.
+ */
+ var buttons = mutableListOf()
+ set(value) {
+ field = value
+ if (isCreated) {
+
+ val layout = findViewById(R.id.button_layout)
+ if (layout == null) {
+ Log.e("MessageDialog", "Buttons layout not found.")
+ return
+ }
+
+ layout.removeAllViews()
+
+ for (button in value) {
+
+ val buttonView = Button(ContextThemeWrapper(context, R.style.button_borderless))
+ buttonView.minWidth = 300.dp
+ buttonView.minHeight = 56.dp
+ buttonView.gravity = CENTER
+ buttonView.background = requireContext().getDrawable(R.drawable.ripple)
+ buttonView.text = button.text
+ buttonView.fontColor = button.tint
+ buttonView.compoundDrawablePadding = 0
+ buttonView.setOnClickListener { button.clickListener(this@MessageDialog) }
+
+ layout.addView(buttonView)
+ }
+ }
+ }
- protected var onDismiss: (() -> Unit)? = null
+ /**
+ * Whether the dialog is cancelable or not.
+ */
+ var allowDismiss = true
- protected var buttons = mutableListOf()
+ /**
+ * The function to be called when the dialog is dismissed.
+ */
+ var onDismiss: (() -> Unit)? = null
override fun onLoadView() {
- findViewById(R.id.title)?.text = title
+ title = title
+ message = message
+ buttons = buttons
- if (isHTMLMessage) {
- findViewById(R.id.message)?.apply {
- text = HtmlCompat.fromHtml(message.toString(), HtmlCompat.FROM_HTML_MODE_LEGACY)
- isClickable = true
- movementMethod = LinkMovementMethod.getInstance()
- }
- } else {
- findViewById(R.id.message)?.text = message
+ findViewById(R.id.message)?.apply {
+ isClickable = true
+ movementMethod = LinkMovementMethod.getInstance()
}
- findViewById(R.id.button_layout)?.also { buttonLayout ->
-
- for (button in buttons) {
- buttonLayout.addView(Button(ContextThemeWrapper(context, R.style.button_borderless)).apply {
-
- minWidth = 300.dp
- minHeight = 56.dp
- gravity = CENTER
- background = context.getDrawable(R.drawable.ripple)
- text = button.text
- fontColor = button.tint
- compoundDrawablePadding = 0
-
- setOnClickListener { button.clickListener(this@MessageDialog) }
- })
- }
- }
val background = findViewById(R.id.frg_background)!!
- background.setOnClickListener { callDismissOnBackPress() }
+ background.setOnClickListener { callDismissOnBackPress() }
background.cancelAnimators()
.toAlpha(0f)
.toAlpha(1f, 200, ease = EasingHelper.asInterpolator(Easing.Out))
@@ -141,6 +204,7 @@ open class MessageDialog : BaseFragment() {
@JvmOverloads
open fun addButton(text: String, tint: Int = Color.WHITE, clickListener: (MessageDialog) -> Unit): MessageDialog {
buttons.add(DialogButton(text, tint, clickListener))
+ buttons = buttons
return this
}
@@ -158,79 +222,4 @@ open class MessageDialog : BaseFragment() {
}
-open class PromptDialog : MessageDialog() {
-
- override val layoutID = R.layout.dialog_input_fragment
-
-
- /**
- * The text input by user.
- */
- var input: String? = null
- protected set
-
-
- private var hint: String? = null
-
- private var onTextChanged: ((PromptDialog) -> Unit)? = null
-
- private var onTextInputBind: ((EditText) -> Unit)? = null
-
-
- override fun onLoadView() {
-
- super.onLoadView()
-
- findViewById(R.id.message)!!.isVisible = message.isNotBlank()
- findViewById(R.id.input)!!.apply {
-
- setText(input)
-
- doOnTextChanged { text, _, _, _ ->
- input = text.toString()
- onTextChanged?.invoke(this@PromptDialog)
- }
-
- onTextInputBind?.invoke(this)
- }
-
- }
-
-
-
- fun setInput(text: String?): PromptDialog {
- input = text
- return this
- }
-
- /**
- * The text to be show displayed in the input hint.
- */
- fun setHint(text: String): PromptDialog {
- hint = text
- return this
- }
-
- /**
- * The function to be called when the text input is changed.
- */
- fun setOnTextChanged(action: (PromptDialog) -> Unit): PromptDialog {
- onTextChanged = action
- return this
- }
-
- /**
- * The function to be called when the EditText is created.
- */
- fun setOnTextInputBind(action: (EditText) -> Unit): PromptDialog {
- onTextInputBind = action
- return this
- }
-
-
- override fun addButton(text: String, tint: Int, clickListener: (MessageDialog) -> Unit) = super.addButton(text, tint, clickListener) as PromptDialog
-
-}
-
-
diff --git a/src/com/reco1l/osu/ui/ListDialog.kt b/src/com/reco1l/osu/ui/ListDialog.kt
index 1e4cc917c..ce9bce8fb 100644
--- a/src/com/reco1l/osu/ui/ListDialog.kt
+++ b/src/com/reco1l/osu/ui/ListDialog.kt
@@ -40,13 +40,18 @@ data class Option(
val icon: Drawable? = null
)
-
+/**
+ * A dialog that allows the user to select an option.
+ */
open class SelectDialog : MessageDialog() {
override val layoutID = R.layout.dialog_select_fragment
+ /**
+ * The options to be displayed in the dialog.
+ */
var options = mutableListOf