Make kotlin code use dialog dsl

This commit is contained in:
andrew 2023-05-30 21:39:03 +09:30
parent 4ee68cbbb1
commit 1d8d678047
12 changed files with 160 additions and 225 deletions

View file

@ -417,7 +417,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem == null) return;
SaveAttachmentTask.showWarningDialog(this, () -> {
SaveAttachmentTask.showWarningDialog(this, 1, () -> {
Permissions.with(this)
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P)

View file

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
@ -18,7 +19,6 @@ import androidx.core.view.setPadding
import androidx.core.view.updateMargins
import network.loki.messenger.R
import org.thoughtcrime.securesms.util.toPx
import java.lang.ref.Reference
@DslMarker
@ -35,9 +35,15 @@ class SessionDialogBuilder(val context: Context) {
private val topView = LinearLayout(context).apply { orientation = VERTICAL }
.also(dialogBuilder::setCustomTitle)
private val contentView = LinearLayout(context).apply { orientation = VERTICAL }
private val buttonLayout = LinearLayout(context)
private val root = LinearLayout(context).apply { orientation = VERTICAL }
.also(dialogBuilder::setView)
.apply {
addView(contentView)
addView(buttonLayout)
}
fun title(@StringRes id: Int) = title(context.getString(id))
@ -65,52 +71,31 @@ class SessionDialogBuilder(val context: Context) {
}.let(topView::addView)
}
fun view(view: View) {
dialogBuilder.setView(view)
}
fun view(view: View) = contentView.addView(view)
fun view(@LayoutRes layout: Int) {
dialogBuilder.setView(layout)
}
fun view(@LayoutRes layout: Int) = contentView.addView(LayoutInflater.from(context).inflate(layout, contentView))
fun setIconAttribute(@AttrRes icon: Int) {
dialogBuilder.setIconAttribute(icon)
}
fun iconAttribute(@AttrRes icon: Int): AlertDialog.Builder = dialogBuilder.setIconAttribute(icon)
fun singleChoiceItems(
options: Array<String>,
currentSelected: Int,
onSelect: (Int) -> Unit
) {
dialogBuilder.setSingleChoiceItems(
options,
currentSelected
) { dialog, it -> onSelect(it); dialog.dismiss() }
}
fun buttons(build: (@DialogDsl ButtonsBuilder).() -> Unit) {
ButtonsBuilder(context, ::dismiss).build(build).let(root::addView)
}
fun show(): AlertDialog = dialogBuilder.show()
}
@DialogDsl
class ButtonsBuilder(val context: Context, val dismiss: () -> Unit) {
val root = LinearLayout(context)
): AlertDialog.Builder = dialogBuilder.setSingleChoiceItems(
options,
currentSelected
) { dialog, it -> onSelect(it); dialog.dismiss() }
fun destructiveButton(
@StringRes text: Int,
@StringRes contentDescription: Int,
listener: () -> Unit = {}
) {
button(
text,
contentDescription,
R.style.Widget_Session_Button_Dialog_DestructiveText,
listener
)
}
) = button(
text,
contentDescription,
R.style.Widget_Session_Button_Dialog_DestructiveText,
listener
)
fun cancelButton(listener: (() -> Unit) = {}) = button(android.R.string.cancel, R.string.AccessibilityId_cancel_button, listener = listener)
@ -119,25 +104,18 @@ class ButtonsBuilder(val context: Context, val dismiss: () -> Unit) {
@StringRes contentDescriptionRes: Int = 0,
@StyleRes style: Int = R.style.Widget_Session_Button_Dialog_UnimportantText,
listener: (() -> Unit) = {}
) {
Button(context, null, 0, style)
.apply {
setText(text)
contentDescription = resources.getString(contentDescriptionRes)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1f)
.apply { setMargins(toPx(20, resources)) }
setOnClickListener {
listener.invoke()
dismiss()
}
) = Button(context, null, 0, style).apply {
setText(text)
contentDescription = resources.getString(contentDescriptionRes)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1f)
.apply { setMargins(toPx(20, resources)) }
setOnClickListener {
listener.invoke()
dismiss()
}
.let(root::addView)
}
}.let(buttonLayout::addView)
internal fun build(build: ButtonsBuilder.() -> Unit): LinearLayout {
build()
return root
}
fun show(): AlertDialog = dialogBuilder.show()
}
fun Context.sessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog =

View file

@ -965,16 +965,14 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(R.string.RecipientPreferenceActivity_block_this_contact_question)
text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
buttons {
destructiveButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) {
viewModel.block()
if (deleteThread) {
viewModel.deleteThread()
finish()
}
destructiveButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) {
viewModel.block()
if (deleteThread) {
viewModel.deleteThread()
finish()
}
cancelButton()
}
cancelButton()
}
}
@ -1018,13 +1016,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(R.string.ConversationActivity_unblock_this_contact_question)
text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
buttons {
destructiveButton(
R.string.ConversationActivity_unblock,
R.string.AccessibilityId_block_confirm
) { viewModel.unblock() }
cancelButton()
}
destructiveButton(
R.string.ConversationActivity_unblock,
R.string.AccessibilityId_block_confirm
) { viewModel.unblock() }
cancelButton()
}
}
@ -1464,13 +1460,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(R.string.giphy_permission_title)
text(R.string.giphy_permission_message)
buttons {
button(R.string.continue_2) {
textSecurePreferences.setHasSeenGIFMetaDataWarning()
selectGif()
}
cancelButton()
button(R.string.continue_2) {
textSecurePreferences.setHasSeenGIFMetaDataWarning()
selectGif()
}
cancelButton()
}
} else {
selectGif()
@ -1629,10 +1623,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
buttons {
button(R.string.delete) { messages.forEach(viewModel::deleteForEveryone); endActionMode() }
cancelButton { endActionMode() }
}
button(R.string.delete) { messages.forEach(viewModel::deleteForEveryone); endActionMode() }
cancelButton { endActionMode() }
}
} else if (allSentByCurrentUser && allHasHash) {
val bottomSheet = DeleteOptionsBottomSheet()
@ -1658,10 +1650,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
buttons {
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
cancelButton(::endActionMode)
}
button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
cancelButton(::endActionMode)
}
}
}
@ -1670,10 +1660,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(R.string.ConversationFragment_ban_selected_user)
text("This will ban the selected user from this room. It won't ban them from other rooms.")
buttons {
button(R.string.ban) { viewModel.banUser(messages.first().individualRecipient); endActionMode() }
cancelButton(::endActionMode)
}
button(R.string.ban) { viewModel.banUser(messages.first().individualRecipient); endActionMode() }
cancelButton(::endActionMode)
}
}
@ -1681,10 +1669,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
sessionDialog {
title(R.string.ConversationFragment_ban_selected_user)
text("This will ban the selected user from this room and delete all messages sent by them. It won't ban them from other rooms or delete the messages they sent there.")
buttons {
button(R.string.ban) { viewModel.banAndDeleteAll(messages.first().individualRecipient); endActionMode() }
cancelButton(::endActionMode)
}
button(R.string.ban) { viewModel.banAndDeleteAll(messages.first().individualRecipient); endActionMode() }
cancelButton(::endActionMode)
}
}

View file

@ -150,13 +150,11 @@ class ConversationAdapter(
context.sessionDialog {
title(R.string.CallNotificationBuilder_first_call_title)
text(R.string.CallNotificationBuilder_first_call_message)
buttons {
button(R.string.activity_settings_title) {
Intent(context, PrivacySettingsActivity::class.java)
.let(context::startActivity)
}
cancelButton()
button(R.string.activity_settings_title) {
Intent(context, PrivacySettingsActivity::class.java)
.let(context::startActivity)
}
cancelButton()
}
}
} else {

View file

@ -190,12 +190,10 @@ object ConversationMenuHelper {
context.sessionDialog {
title(R.string.ConversationActivity_call_title)
text(R.string.ConversationActivity_call_prompt)
buttons {
button(R.string.activity_settings_title, R.string.AccessibilityId_settings) {
Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity)
}
cancelButton()
button(R.string.activity_settings_title, R.string.AccessibilityId_settings) {
Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity)
}
cancelButton()
}
return
}
@ -308,20 +306,18 @@ object ConversationMenuHelper {
context.sessionDialog {
title(R.string.ConversationActivity_leave_group)
text(message)
buttons {
button(R.string.yes) {
try {
val groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString()
val isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey)
button(R.string.yes) {
try {
val groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString()
val isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey)
if (isClosedGroup) MessageSender.leave(groupPublicKey, true)
else onLeaveFailed()
} catch (e: Exception) {
onLeaveFailed()
}
if (isClosedGroup) MessageSender.leave(groupPublicKey, true)
else onLeaveFailed()
} catch (e: Exception) {
onLeaveFailed()
}
button(R.string.no)
}
button(R.string.no)
}
}

View file

@ -23,4 +23,4 @@ open class BaseDialog : DialogFragment() {
open fun setContentView(builder: AlertDialog.Builder) {
// To be overridden by subclasses
}
}
}

View file

@ -492,17 +492,15 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
sessionDialog {
title(R.string.RecipientPreferenceActivity_block_this_contact_question)
text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
buttons {
button(R.string.RecipientPreferenceActivity_block) {
lifecycleScope.launch(Dispatchers.IO) {
recipientDatabase.setBlocked(thread.recipient, true)
withContext(Dispatchers.Main) {
binding.recyclerView.adapter?.notifyDataSetChanged()
}
button(R.string.RecipientPreferenceActivity_block) {
lifecycleScope.launch(Dispatchers.IO) {
recipientDatabase.setBlocked(thread.recipient, true)
withContext(Dispatchers.Main) {
binding.recyclerView.adapter?.notifyDataSetChanged()
}
}
cancelButton()
}
cancelButton()
}
}
@ -510,17 +508,15 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
sessionDialog {
title(R.string.RecipientPreferenceActivity_unblock_this_contact_question)
text(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
buttons {
button(R.string.RecipientPreferenceActivity_unblock) {
lifecycleScope.launch(Dispatchers.IO) {
recipientDatabase.setBlocked(thread.recipient, false)
withContext(Dispatchers.Main) {
binding.recyclerView.adapter?.notifyDataSetChanged()
}
button(R.string.RecipientPreferenceActivity_unblock) {
lifecycleScope.launch(Dispatchers.IO) {
recipientDatabase.setBlocked(thread.recipient, false)
withContext(Dispatchers.Main) {
binding.recyclerView.adapter?.notifyDataSetChanged()
}
}
cancelButton()
}
cancelButton()
}
}
@ -582,60 +578,58 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
sessionDialog {
text(message)
buttons {
button(R.string.yes) {
lifecycleScope.launch(Dispatchers.Main) {
val context = this@HomeActivity as Context
// Cancel any outstanding jobs
DatabaseComponent.get(context).sessionJobDatabase()
.cancelPendingMessageSendJobs(threadID)
// Send a leave group message if this is an active closed group
if (recipient.address.isClosedGroup && DatabaseComponent.get(context)
.groupDatabase().isActive(recipient.address.toGroupString())
) {
var isClosedGroup: Boolean
var groupPublicKey: String?
try {
groupPublicKey =
GroupUtil.doubleDecodeGroupID(recipient.address.toString())
.toHexString()
isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase()
.isClosedGroup(groupPublicKey)
} catch (e: IOException) {
groupPublicKey = null
isClosedGroup = false
}
if (isClosedGroup) {
MessageSender.explicitLeave(groupPublicKey!!, false)
}
button(R.string.yes) {
lifecycleScope.launch(Dispatchers.Main) {
val context = this@HomeActivity as Context
// Cancel any outstanding jobs
DatabaseComponent.get(context).sessionJobDatabase()
.cancelPendingMessageSendJobs(threadID)
// Send a leave group message if this is an active closed group
if (recipient.address.isClosedGroup && DatabaseComponent.get(context)
.groupDatabase().isActive(recipient.address.toGroupString())
) {
var isClosedGroup: Boolean
var groupPublicKey: String?
try {
groupPublicKey =
GroupUtil.doubleDecodeGroupID(recipient.address.toString())
.toHexString()
isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase()
.isClosedGroup(groupPublicKey)
} catch (e: IOException) {
groupPublicKey = null
isClosedGroup = false
}
// Delete the conversation
val v2OpenGroup =
DatabaseComponent.get(this@HomeActivity).lokiThreadDatabase()
.getOpenGroupChat(threadID)
if (v2OpenGroup != null) {
OpenGroupManager.delete(
v2OpenGroup.server,
v2OpenGroup.room,
this@HomeActivity
)
} else {
lifecycleScope.launch(Dispatchers.IO) {
threadDb.deleteConversation(threadID)
}
if (isClosedGroup) {
MessageSender.explicitLeave(groupPublicKey!!, false)
}
// Update the badge count
ApplicationContext.getInstance(context).messageNotifier.updateNotification(
context
)
// Notify the user
val toastMessage =
if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show()
}
// Delete the conversation
val v2OpenGroup =
DatabaseComponent.get(this@HomeActivity).lokiThreadDatabase()
.getOpenGroupChat(threadID)
if (v2OpenGroup != null) {
OpenGroupManager.delete(
v2OpenGroup.server,
v2OpenGroup.room,
this@HomeActivity
)
} else {
lifecycleScope.launch(Dispatchers.IO) {
threadDb.deleteConversation(threadID)
}
}
// Update the badge count
ApplicationContext.getInstance(context).messageNotifier.updateNotification(
context
)
// Notify the user
val toastMessage =
if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show()
}
button(R.string.no)
}
button(R.string.no)
}
}
@ -651,14 +645,12 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
private fun hideMessageRequests() {
sessionDialog {
text("Hide message requests?")
buttons {
button(R.string.yes) {
textSecurePreferences.setHasHiddenMessageRequests()
setupMessageRequestsBanner()
homeViewModel.tryUpdateChannel()
}
button(R.string.no)
button(R.string.yes) {
textSecurePreferences.setHasHiddenMessageRequests()
setupMessageRequestsBanner()
homeViewModel.tryUpdateChannel()
}
button(R.string.no)
}
}

View file

@ -86,10 +86,8 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat
sessionDialog {
title(R.string.RecipientPreferenceActivity_block_this_contact_question)
text(R.string.message_requests_block_message)
buttons {
button(R.string.recipient_preferences__block) { doBlock() }
button(R.string.no)
}
button(R.string.recipient_preferences__block) { doBlock() }
button(R.string.no)
}
}
@ -105,10 +103,8 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat
sessionDialog {
title(R.string.decline)
text(resources.getString(R.string.message_requests_decline_message))
buttons {
button(R.string.decline) { doDecline() }
button(R.string.no)
}
button(R.string.decline) { doDecline() }
button(R.string.no)
}
}
@ -129,10 +125,8 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat
sessionDialog {
text(resources.getString(R.string.message_requests_clear_all_message))
buttons {
button(R.string.yes) { doDeleteAllAndBlock() }
button(R.string.no)
}
button(R.string.yes) { doDeleteAllAndBlock() }
button(R.string.no)
}
}
}

View file

@ -154,9 +154,7 @@ class PNModeActivity : BaseActionBarActivity() {
if (selectedOptionView == null) {
sessionDialog {
title(R.string.activity_pn_mode_no_option_picked_dialog_title)
buttons {
button(R.string.ok)
}
button(R.string.ok)
}
return
}

View file

@ -55,10 +55,8 @@ class BlockedContactsActivity: PassphraseRequiredActionBarActivity(), View.OnCli
sessionDialog {
title(title)
text(message)
buttons {
button(R.string.continue_2) { viewModel.unblock(contactsToUnblock) }
cancelButton()
}
button(R.string.continue_2) { viewModel.unblock(contactsToUnblock) }
cancelButton()
}
}
}

View file

@ -264,13 +264,11 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
sessionDialog {
title(R.string.activity_settings_set_display_picture)
view(R.layout.dialog_change_avatar)
buttons {
button(R.string.activity_settings_upload) { startAvatarSelection() }
if (TextSecurePreferences.getProfileAvatarId(context) != 0) {
button(R.string.activity_settings_remove) { removeAvatar() }
}
cancelButton()
button(R.string.activity_settings_upload) { startAvatarSelection() }
if (TextSecurePreferences.getProfileAvatarId(context) != 0) {
button(R.string.activity_settings_remove) { removeAvatar() }
}
cancelButton()
}.apply {
findViewById<ProfilePictureView>(R.id.profile_picture_view)?.let(::setupProfilePictureView)
}

View file

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.util
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.DialogInterface.OnClickListener
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
@ -12,7 +11,6 @@ import android.provider.MediaStore
import android.text.TextUtils
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import network.loki.messenger.R
import org.session.libsession.utilities.task.ProgressDialogAsyncTask
import org.session.libsignal.utilities.ExternalStorageUtil
@ -31,7 +29,12 @@ import java.util.concurrent.TimeUnit
* Saves attachment files to an external storage using [MediaStore] API.
* Requires [android.Manifest.permission.WRITE_EXTERNAL_STORAGE] on API 28 and below.
*/
class SaveAttachmentTask : ProgressDialogAsyncTask<SaveAttachmentTask.Attachment, Void, Pair<Int, String?>> {
class SaveAttachmentTask @JvmOverloads constructor(context: Context, count: Int = 1) :
ProgressDialogAsyncTask<SaveAttachmentTask.Attachment, Void, Pair<Int, String?>>(
context,
context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_attachments, count, count),
context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_attachments_to_sd_card, count, count)
) {
companion object {
@JvmStatic
@ -42,31 +45,25 @@ class SaveAttachmentTask : ProgressDialogAsyncTask<SaveAttachmentTask.Attachment
@JvmStatic
@JvmOverloads
fun showWarningDialog(context: Context, count: Int = 1, onAcceptListener: () -> Unit) {
fun showWarningDialog(context: Context, count: Int = 1, onAcceptListener: () -> Unit = {}) {
context.sessionDialog {
title(R.string.ConversationFragment_save_to_sd_card)
setIconAttribute(R.attr.dialog_alert_icon)
iconAttribute(R.attr.dialog_alert_icon)
text(context.resources.getQuantityString(
R.plurals.ConversationFragment_saving_n_media_to_storage_warning,
count,
count))
buttons {
button(R.string.yes) { onAcceptListener() }
button(R.string.no)
}
button(R.string.yes) { onAcceptListener() }
button(R.string.no)
}
}
}
private val contextReference: WeakReference<Context>
private val attachmentCount: Int
private val attachmentCount: Int = count
@JvmOverloads
constructor(context: Context, count: Int = 1): super(context,
context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_attachments, count, count),
context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_attachments_to_sd_card, count, count)) {
init {
this.contextReference = WeakReference(context)
this.attachmentCount = count
}
override fun doInBackground(vararg attachments: Attachment?): Pair<Int, String?> {