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