diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index b36fdb3ca1..e8af4e5a8e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java @@ -76,6 +76,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; +import kotlin.Unit; import network.loki.messenger.R; /** @@ -318,9 +319,9 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { @SuppressWarnings("CodeBlock2Expr") @SuppressLint({"InlinedApi", "StaticFieldLeak"}) private void handleSaveMedia(@NonNull Collection mediaRecords) { - final Context context = getContext(); + final Context context = requireContext(); - SaveAttachmentTask.showWarningDialog(context, (dialogInterface, which) -> { + SaveAttachmentTask.showWarningDialog(context, mediaRecords.size(), () -> { Permissions.with(this) .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) .maxSdkVersion(Build.VERSION_CODES.P) @@ -362,7 +363,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { }.execute(); }) .execute(); - }, mediaRecords.size()); + return Unit.INSTANCE; + }); } private void sendMediaSavedNotificationIfNeeded() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 6544c2ab89..a370478aeb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java @@ -85,6 +85,7 @@ import java.io.IOException; import java.util.Locale; import java.util.WeakHashMap; +import kotlin.Unit; import network.loki.messenger.R; /** @@ -416,7 +417,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im MediaItem mediaItem = getCurrentMediaItem(); if (mediaItem == null) return; - SaveAttachmentTask.showWarningDialog(this, (dialogInterface, i) -> { + SaveAttachmentTask.showWarningDialog(this, () -> { Permissions.with(this) .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) .maxSdkVersion(Build.VERSION_CODES.P) @@ -433,6 +434,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im } }) .execute(); + return Unit.INSTANCE; }); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt index 71b48c6bb6..c9e752229f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -8,70 +8,128 @@ import android.widget.Button import android.widget.LinearLayout import android.widget.LinearLayout.VERTICAL import android.widget.TextView +import androidx.annotation.AttrRes +import androidx.annotation.LayoutRes import androidx.annotation.StringRes import androidx.annotation.StyleRes import androidx.appcompat.app.AlertDialog import androidx.core.view.setMargins +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 +@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) +annotation class DialogDsl + +@DialogDsl class SessionDialogBuilder(val context: Context) { - private val dialog: AlertDialog = AlertDialog.Builder(context).create() + private val dialogBuilder: AlertDialog.Builder = AlertDialog.Builder(context) + + private var dialog: AlertDialog? = null + private fun dismiss() = dialog?.dismiss() + + private val topView = LinearLayout(context).apply { orientation = VERTICAL } + .also(dialogBuilder::setCustomTitle) private val root = LinearLayout(context).apply { orientation = VERTICAL } - .also(dialog::setView) + .also(dialogBuilder::setView) - fun title(@StringRes id: Int) { - TextView(context, null, 0, R.style.TextAppearance_AppCompat_Title) - .apply { textAlignment = View.TEXT_ALIGNMENT_CENTER } - .apply { setText(id) } - .apply { layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) - .apply { setMargins(toPx(20, resources)) } - }.let(root::addView) + fun title(@StringRes id: Int) = title(context.getString(id)) + + fun title(text: CharSequence?) = title(text?.toString()) + fun title(text: String?) { + text(text, R.style.TextAppearance_AppCompat_Title) { setPadding(toPx(20, resources)) } } - fun text(@StringRes id: Int, style: Int = 0) { - TextView(context, null, 0, style) - .apply { textAlignment = View.TEXT_ALIGNMENT_CENTER } - .apply { setText(id) } - .apply { layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) + fun text(@StringRes id: Int, style: Int = 0) = text(context.getString(id), style) + fun text(text: CharSequence?) = text(text?.toString()) + fun text(text: String?, @StyleRes style: Int = 0) { + text(text, style) { + layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) .apply { toPx(40, resources).let { updateMargins(it, 0, it, 0) } } - }.let(root::addView) + } } - fun buttons(build: ButtonsBuilder.() -> Unit) { - ButtonsBuilder(context, dialog).build(build).let(root::addView) + private fun text(text: String?, @StyleRes style: Int, modify: TextView.() -> Unit) { + text ?: return + TextView(context, null, 0, style) + .apply { + setText(text) + textAlignment = View.TEXT_ALIGNMENT_CENTER + modify() + }.let(topView::addView) } - fun show(): AlertDialog = dialog.apply { show() } + fun view(view: View) { + dialogBuilder.setView(view) + } + + fun view(@LayoutRes layout: Int) { + dialogBuilder.setView(layout) + } + + fun setIconAttribute(@AttrRes icon: Int) { + dialogBuilder.setIconAttribute(icon) + } + + fun singleChoiceItems( + options: Array, + 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() } -class ButtonsBuilder(val context: Context, val dialog: AlertDialog) { +@DialogDsl +class ButtonsBuilder(val context: Context, val dismiss: () -> Unit) { val root = LinearLayout(context) - fun destructiveButton(@StringRes text: Int, @StringRes contentDescription: Int, listener: () -> Unit = {}) { - button(text, contentDescription, R.style.Widget_Session_Button_Dialog_DestructiveText, listener) + fun destructiveButton( + @StringRes text: Int, + @StringRes contentDescription: Int, + listener: () -> Unit = {} + ) { + button( + text, + contentDescription, + R.style.Widget_Session_Button_Dialog_DestructiveText, + listener + ) } - fun cancelButton() = button(android.R.string.cancel) + fun cancelButton(listener: (() -> Unit) = {}) = button(android.R.string.cancel, R.string.AccessibilityId_cancel_button, listener = listener) fun button( @StringRes text: Int, @StringRes contentDescriptionRes: Int = 0, @StyleRes style: Int = R.style.Widget_Session_Button_Dialog_UnimportantText, - listener: (() -> Unit) = {}) { + listener: (() -> Unit) = {} + ) { Button(context, null, 0, style) - .apply { setText(text) } - .apply { setOnClickListener { - listener.invoke() - dialog.dismiss() - contentDescription = resources.getString(contentDescriptionRes) - } } .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) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 0ad0542eaf..1db4f98f78 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1461,23 +1461,24 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe private fun showGIFPicker() { val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning() if (!hasSeenGIFMetaDataWarning) { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.giphy_permission_title) - builder.setMessage(R.string.giphy_permission_message) - builder.setPositiveButton(R.string.continue_2) { dialog: DialogInterface, _: Int -> - textSecurePreferences.setHasSeenGIFMetaDataWarning() - AttachmentManager.selectGif(this, PICK_GIF) - dialog.dismiss() + sessionDialog { + title(R.string.giphy_permission_title) + text(R.string.giphy_permission_message) + buttons { + button(R.string.continue_2) { + textSecurePreferences.setHasSeenGIFMetaDataWarning() + selectGif() + } + cancelButton() + } } - builder.setNegativeButton(R.string.cancel) { dialog: DialogInterface, _: Int -> - dialog.dismiss() - } - builder.create().show() } else { - AttachmentManager.selectGif(this, PICK_GIF) + selectGif() } } + private fun selectGif() = AttachmentManager.selectGif(this, PICK_GIF) + private fun showDocumentPicker() { AttachmentManager.selectDocument(this, PICK_DOCUMENT) } @@ -1624,35 +1625,25 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id) != null } if (recipient.isOpenGroupRecipient) { val messageCount = 1 - val builder = AlertDialog.Builder(this) - builder.setTitle(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount)) - builder.setMessage(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount)) - builder.setCancelable(true) - builder.setPositiveButton(R.string.delete) { _, _ -> - for (message in messages) { - viewModel.deleteForEveryone(message) + + 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() } } - endActionMode() } - builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> - dialog.dismiss() - endActionMode() - } - builder.show() } else if (allSentByCurrentUser && allHasHash) { val bottomSheet = DeleteOptionsBottomSheet() bottomSheet.recipient = recipient bottomSheet.onDeleteForMeTapped = { - for (message in messages) { - viewModel.deleteLocally(message) - } + messages.forEach(viewModel::deleteLocally) bottomSheet.dismiss() endActionMode() } bottomSheet.onDeleteForEveryoneTapped = { - for (message in messages) { - viewModel.deleteForEveryone(message) - } + messages.forEach(viewModel::deleteForEveryone) bottomSheet.dismiss() endActionMode() } @@ -1663,54 +1654,38 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe bottomSheet.show(supportFragmentManager, bottomSheet.tag) } else { val messageCount = 1 - val builder = AlertDialog.Builder(this) - builder.setTitle(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount)) - builder.setMessage(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount)) - builder.setCancelable(true) - builder.setPositiveButton(R.string.delete) { _, _ -> - for (message in messages) { - viewModel.deleteLocally(message) + + 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) } - endActionMode() } - builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> - dialog.dismiss() - endActionMode() - } - builder.show() } } override fun banUser(messages: Set) { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.ConversationFragment_ban_selected_user) - builder.setMessage("This will ban the selected user from this room. It won't ban them from other rooms.") - builder.setCancelable(true) - builder.setPositiveButton(R.string.ban) { _, _ -> - viewModel.banUser(messages.first().individualRecipient) - endActionMode() + 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) + } } - builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> - dialog.dismiss() - endActionMode() - } - builder.show() } override fun banAndDeleteAll(messages: Set) { - val builder = AlertDialog.Builder(this) - builder.setTitle(R.string.ConversationFragment_ban_selected_user) - builder.setMessage("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.") - builder.setCancelable(true) - builder.setPositiveButton(R.string.ban) { _, _ -> - viewModel.banAndDeleteAll(messages.first().individualRecipient) - endActionMode() + 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) + } } - builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> - dialog.dismiss() - endActionMode() - } - builder.show() } override fun copyMessages(messages: Set) { @@ -1774,7 +1749,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe override fun saveAttachment(messages: Set) { val message = messages.first() as MmsMessageRecord - SaveAttachmentTask.showWarningDialog(this, { _, _ -> + SaveAttachmentTask.showWarningDialog(this) { Permissions.with(this) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE) .maxSdkVersion(Build.VERSION_CODES.P) @@ -1802,7 +1777,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe Toast.LENGTH_LONG).show() } .execute() - }) + } } override fun reply(messages: Set) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt index 85d3c8e6de..69c54003b5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationAdapter.kt @@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity +import org.thoughtcrime.securesms.sessionDialog class ConversationAdapter( context: Context, @@ -146,17 +147,17 @@ class ConversationAdapter( viewHolder.view.bind(message, messageBefore) if (message.isCallLog && message.isFirstMissedCall) { viewHolder.view.setOnClickListener { - AlertDialog.Builder(context) - .setTitle(R.string.CallNotificationBuilder_first_call_title) - .setMessage(R.string.CallNotificationBuilder_first_call_message) - .setPositiveButton(R.string.activity_settings_title) { _, _ -> - val intent = Intent(context, PrivacySettingsActivity::class.java) - context.startActivity(intent) + 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() } - .setNeutralButton(R.string.cancel) { d, _ -> - d.dismiss() - } - .show() + } } } else { viewHolder.view.setOnClickListener(null) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt index ce29efa3a0..22fb12e163 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt @@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.groups.EditClosedGroupActivity import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.groupIDKey import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity import org.thoughtcrime.securesms.service.WebRtcCallService +import org.thoughtcrime.securesms.sessionDialog import org.thoughtcrime.securesms.util.BitmapUtil import java.io.IOException @@ -186,29 +187,25 @@ object ConversationMenuHelper { private fun call(context: Context, thread: Recipient) { if (!TextSecurePreferences.isCallNotificationsEnabled(context)) { - val dialog = AlertDialog.Builder(context) - .setTitle(R.string.ConversationActivity_call_title) - .setMessage(R.string.ConversationActivity_call_prompt) - .setPositiveButton(R.string.activity_settings_title) { _, _ -> - val intent = Intent(context, PrivacySettingsActivity::class.java) - context.startActivity(intent) + 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() } - .setNeutralButton(R.string.cancel) { d, _ -> - d.dismiss() - }.create() - dialog.getButton(DialogInterface.BUTTON_POSITIVE)?.contentDescription = context.getString(R.string.AccessibilityId_settings) - dialog.getButton(DialogInterface.BUTTON_NEGATIVE)?.contentDescription = context.getString(R.string.AccessibilityId_cancel_button) - dialog.show() + } return } - val service = WebRtcCallService.createCall(context, thread) - context.startService(service) + WebRtcCallService.createCall(context, thread) + .let(context::startService) - val activity = Intent(context, WebRtcCallActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK - } - context.startActivity(activity) + Intent(context, WebRtcCallActivity::class.java) + .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK } + .let(context::startActivity) } @@ -295,9 +292,7 @@ object ConversationMenuHelper { private fun leaveClosedGroup(context: Context, thread: Recipient) { if (!thread.isClosedGroupRecipient) { return } - val builder = AlertDialog.Builder(context) - builder.setTitle(context.resources.getString(R.string.ConversationActivity_leave_group)) - builder.setCancelable(true) + val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull() val admins = group.admins val sessionID = TextSecurePreferences.getLocalNumber(context) @@ -307,29 +302,27 @@ object ConversationMenuHelper { } else { context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group) } - builder.setMessage(message) - builder.setPositiveButton(R.string.yes) { _, _ -> - var groupPublicKey: String? - var isClosedGroup: Boolean - try { - groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString() - isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey) - } catch (e: IOException) { - groupPublicKey = null - isClosedGroup = false - } - try { - if (isClosedGroup) { - MessageSender.leave(groupPublicKey!!, true) - } else { - Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show() + + fun onLeaveFailed() = Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show() + + 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) + + if (isClosedGroup) MessageSender.leave(groupPublicKey, true) + else onLeaveFailed() + } catch (e: Exception) { + onLeaveFailed() + } } - } catch (e: Exception) { - Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show() + button(R.string.no) } } - builder.setNegativeButton(R.string.no, null) - builder.show() } private fun inviteContacts(context: Context, thread: Recipient) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/NotificationUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/NotificationUtils.kt index dbbcfb51ef..0a6f839405 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/NotificationUtils.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/NotificationUtils.kt @@ -1,21 +1,18 @@ package org.thoughtcrime.securesms.conversation.v2.utilities import android.content.Context -import androidx.appcompat.app.AlertDialog import network.loki.messenger.R import org.session.libsession.utilities.recipients.Recipient +import org.thoughtcrime.securesms.sessionDialog object NotificationUtils { fun showNotifyDialog(context: Context, thread: Recipient, notifyTypeHandler: (Int)->Unit) { - val notifyTypes = context.resources.getStringArray(R.array.notify_types) - val currentSelected = thread.notifyType - - AlertDialog.Builder(context) - .setSingleChoiceItems(notifyTypes,currentSelected) { d, newSelection -> - notifyTypeHandler(newSelection) - d.dismiss() - } - .setTitle(R.string.RecipientPreferenceActivity_notification_settings) - .show() + context.sessionDialog { + title(R.string.RecipientPreferenceActivity_notification_settings) + singleChoiceItems( + context.resources.getStringArray(R.array.notify_types), + thread.notifyType + ) { notifyTypeHandler(it) } + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 9b1e01a54a..1964756a3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -63,6 +63,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.onboarding.SeedActivity import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate import org.thoughtcrime.securesms.preferences.SettingsActivity +import org.thoughtcrime.securesms.sessionDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.IP2Country @@ -488,46 +489,43 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } private fun blockConversation(thread: ThreadRecord) { - AlertDialog.Builder(this) - .setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ -> + 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() - dialog.dismiss() + binding.recyclerView.adapter?.notifyDataSetChanged() } } - }.show() + } + cancelButton() + } + } } private fun unblockConversation(thread: ThreadRecord) { - AlertDialog.Builder(this) - .setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) - .setMessage(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ -> + 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() - dialog.dismiss() + binding.recyclerView.adapter?.notifyDataSetChanged() } } - }.show() + } + cancelButton() + } + } } private fun setConversationMuted(thread: ThreadRecord, isMuted: Boolean) { - if (!isMuted) { - lifecycleScope.launch(Dispatchers.IO) { - recipientDatabase.setMuted(thread.recipient, 0) - withContext(Dispatchers.Main) { - binding.recyclerView.adapter!!.notifyDataSetChanged() - } - } - } else { + if (isMuted) { MuteDialog.show(this) { until: Long -> lifecycleScope.launch(Dispatchers.IO) { recipientDatabase.setMuted(thread.recipient, until) @@ -536,6 +534,13 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } } } + } else { + lifecycleScope.launch(Dispatchers.IO) { + recipientDatabase.setMuted(thread.recipient, 0) + withContext(Dispatchers.Main) { + binding.recyclerView.adapter!!.notifyDataSetChanged() + } + } } } @@ -574,48 +579,64 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } else { resources.getString(R.string.activity_home_delete_conversation_dialog_message) } - val dialog = AlertDialog.Builder(this) - dialog.setMessage(message) - dialog.setPositiveButton(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) + + 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) + } + } + // 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() } } - // 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) } } - dialog.setNegativeButton(R.string.no) { _, _ -> - // Do nothing - } - dialog.create().show() } private fun openSettings() { @@ -624,22 +645,21 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } private fun showMessageRequests() { - val intent = Intent(this, MessageRequestsActivity::class.java) - push(intent) + Intent(this, MessageRequestsActivity::class.java).let(::push) } private fun hideMessageRequests() { - AlertDialog.Builder(this) - .setMessage("Hide message requests?") - .setPositiveButton(R.string.yes) { _, _ -> - textSecurePreferences.setHasHiddenMessageRequests() - setupMessageRequestsBanner() - homeViewModel.tryUpdateChannel() + sessionDialog { + text("Hide message requests?") + buttons { + button(R.string.yes) { + textSecurePreferences.setHasHiddenMessageRequests() + setupMessageRequestsBanner() + homeViewModel.tryUpdateChannel() + } + button(R.string.no) } - .setNegativeButton(R.string.no) { _, _ -> - // Do nothing - } - .create().show() + } } private fun showNewConversation() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt index 50ed4628ea..2591cb4716 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.sessionDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.push import javax.inject.Inject @@ -77,34 +78,38 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat } override fun onBlockConversationClick(thread: ThreadRecord) { - val dialog = AlertDialog.Builder(this) - dialog.setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) - .setMessage(R.string.message_requests_block_message) - .setPositiveButton(R.string.recipient_preferences__block) { _, _ -> - viewModel.blockMessageRequest(thread) - LoaderManager.getInstance(this).restartLoader(0, null, this) - } - .setNegativeButton(R.string.no) { _, _ -> - // Do nothing - } - dialog.create().show() + fun doBlock() { + viewModel.blockMessageRequest(thread) + LoaderManager.getInstance(this).restartLoader(0, null, this) + } + + 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) + } + } } override fun onDeleteConversationClick(thread: ThreadRecord) { - val dialog = AlertDialog.Builder(this) - dialog.setTitle(R.string.decline) - .setMessage(resources.getString(R.string.message_requests_decline_message)) - .setPositiveButton(R.string.decline) { _,_ -> - viewModel.deleteMessageRequest(thread) - LoaderManager.getInstance(this).restartLoader(0, null, this) - lifecycleScope.launch(Dispatchers.IO) { - ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@MessageRequestsActivity) - } + fun doDecline() { + viewModel.deleteMessageRequest(thread) + LoaderManager.getInstance(this).restartLoader(0, null, this) + lifecycleScope.launch(Dispatchers.IO) { + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@MessageRequestsActivity) } - .setNegativeButton(R.string.no) { _, _ -> - // Do nothing + } + + sessionDialog { + title(R.string.decline) + text(resources.getString(R.string.message_requests_decline_message)) + buttons { + button(R.string.decline) { doDecline() } + button(R.string.no) } - dialog.create().show() + } } private fun updateEmptyState() { @@ -114,18 +119,20 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat } private fun deleteAllAndBlock() { - val dialog = AlertDialog.Builder(this) - dialog.setMessage(resources.getString(R.string.message_requests_clear_all_message)) - dialog.setPositiveButton(R.string.yes) { _, _ -> + fun doDeleteAllAndBlock() { viewModel.clearAllMessageRequests() LoaderManager.getInstance(this).restartLoader(0, null, this) lifecycleScope.launch(Dispatchers.IO) { ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@MessageRequestsActivity) } } - dialog.setNegativeButton(R.string.no) { _, _ -> - // Do nothing + + sessionDialog { + text(resources.getString(R.string.message_requests_clear_all_message)) + buttons { + button(R.string.yes) { doDeleteAllAndBlock() } + button(R.string.no) + } } - dialog.create().show() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt index 9cf9c3d049..eb0f4719b0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt @@ -20,6 +20,7 @@ import org.session.libsession.utilities.ThemeUtil import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.home.HomeActivity +import org.thoughtcrime.securesms.sessionDialog import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.PNModeView import org.thoughtcrime.securesms.util.disableClipping @@ -151,12 +152,15 @@ class PNModeActivity : BaseActionBarActivity() { private fun register() { if (selectedOptionView == null) { - val dialog = AlertDialog.Builder(this) - dialog.setTitle(R.string.activity_pn_mode_no_option_picked_dialog_title) - dialog.setPositiveButton(R.string.ok) { _, _ -> } - dialog.create().show() + sessionDialog { + title(R.string.activity_pn_mode_no_option_picked_dialog_title) + buttons { + button(R.string.ok) + } + } return } + TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == binding.fcmOptionView)) val application = ApplicationContext.getInstance(this) application.startPollingIfNeeded() diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsActivity.kt index 504194d3a4..3c62ccd709 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsActivity.kt @@ -9,6 +9,7 @@ import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R import network.loki.messenger.databinding.ActivityBlockedContactsBinding import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.sessionDialog @AndroidEntryPoint class BlockedContactsActivity: PassphraseRequiredActionBarActivity(), View.OnClickListener { @@ -51,17 +52,14 @@ class BlockedContactsActivity: PassphraseRequiredActionBarActivity(), View.OnCli getString(R.string.Unblock_dialog__message, stringBuilder.toString()) } - AlertDialog.Builder(this) - .setTitle(title) - .setMessage(message) - .setPositiveButton(R.string.continue_2) { d, _ -> - viewModel.unblock(contactsToUnblock) - d.dismiss() + sessionDialog { + title(title) + text(message) + buttons { + button(R.string.continue_2) { viewModel.unblock(contactsToUnblock) } + cancelButton() } - .setNegativeButton(R.string.cancel) { d, _ -> - d.dismiss() - } - .show() + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ListPreferenceDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ListPreferenceDialog.kt index 2ba48f6e41..f49cfc39d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ListPreferenceDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ListPreferenceDialog.kt @@ -1,41 +1,24 @@ package org.thoughtcrime.securesms.preferences import android.content.Context -import android.view.LayoutInflater import androidx.appcompat.app.AlertDialog import androidx.preference.ListPreference -import network.loki.messenger.databinding.DialogListPreferenceBinding +import org.thoughtcrime.securesms.sessionDialog fun listPreferenceDialog( context: Context, listPreference: ListPreference, - dialogListener: () -> Unit -) : AlertDialog { + onChange: () -> Unit +) : AlertDialog = listPreference.run { + context.sessionDialog { + val index = entryValues.indexOf(value) + val options = entries.map(CharSequence::toString).toTypedArray() - val builder = AlertDialog.Builder(context) - - val binding = DialogListPreferenceBinding.inflate(LayoutInflater.from(context)) - binding.titleTextView.text = listPreference.dialogTitle - binding.messageTextView.text = listPreference.dialogMessage - - builder.setView(binding.root) - - val dialog = builder.show() - - val valueIndex = listPreference.findIndexOfValue(listPreference.value) - RadioOptionAdapter(valueIndex) { - listPreference.value = it.value - dialog.dismiss() - dialogListener() - } - .apply { - listPreference.entryValues.zip(listPreference.entries) { value, title -> - RadioOption(value.toString(), title.toString()) - }.let(this::submitList) + title(dialogTitle) + text(dialogMessage) + singleChoiceItems(options, index) { + listPreference.setValueIndex(it) + onChange() } - .let { binding.recyclerView.adapter = it } - - binding.closeButton.setOnClickListener { dialog.dismiss() } - - return dialog + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt index 6b6497982d..93a1af4595 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints +import org.thoughtcrime.securesms.sessionDialog import org.thoughtcrime.securesms.util.BitmapDecodingException import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities @@ -260,21 +261,19 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { } private fun showEditProfilePictureUI() { - AlertDialog.Builder(this) - .setTitle(R.string.activity_settings_set_display_picture) - .setView(R.layout.dialog_change_avatar) - .setPositiveButton(R.string.activity_settings_upload) { _, _ -> - startAvatarSelection() - } - .setNegativeButton(R.string.cancel) { _, _ -> } - .apply { + 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) { - setNeutralButton(R.string.activity_settings_remove) { _, _ -> removeAvatar() } + button(R.string.activity_settings_remove) { removeAvatar() } } + cancelButton() } - .show().apply { - findViewById(R.id.profile_picture_view)?.let(::setupProfilePictureView) - } + }.apply { + findViewById(R.id.profile_picture_view)?.let(::setupProfilePictureView) + } } private fun removeAvatar() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt b/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt index 59658f12a0..e32488bbee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt @@ -18,6 +18,7 @@ import org.session.libsession.utilities.task.ProgressDialogAsyncTask import org.session.libsignal.utilities.ExternalStorageUtil import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.sessionDialog import java.io.File import java.io.FileOutputStream import java.io.IOException @@ -41,18 +42,19 @@ class SaveAttachmentTask : ProgressDialogAsyncTask Unit) { + context.sessionDialog { + title(R.string.ConversationFragment_save_to_sd_card) + setIconAttribute(R.attr.dialog_alert_icon) + text(context.resources.getQuantityString( R.plurals.ConversationFragment_saving_n_media_to_storage_warning, count, count)) - builder.setPositiveButton(R.string.yes, onAcceptListener) - builder.setNegativeButton(R.string.no, null) - builder.show() + buttons { + button(R.string.yes) { onAcceptListener() } + button(R.string.no) + } + } } }