diff --git a/app/src/main/java/org/thoughtcrime/securesms/DeleteMediaDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/DeleteMediaDialog.kt new file mode 100644 index 0000000000..af38c31ff3 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/DeleteMediaDialog.kt @@ -0,0 +1,28 @@ +package org.thoughtcrime.securesms + +import android.content.Context +import network.loki.messenger.R + +class DeleteMediaDialog { + companion object { + @JvmStatic + fun show(context: Context, recordCount: Int, doDelete: Runnable) = context.showSessionDialog { + iconAttribute(R.attr.dialog_alert_icon) + title( + context.resources.getQuantityString( + R.plurals.MediaOverviewActivity_Media_delete_confirm_title, + recordCount, + recordCount + ) + ) + text( + context.resources.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_message, + recordCount, + recordCount + ) + ) + button(R.string.delete) { doDelete.run() } + cancelButton() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/DeleteMediaPreviewDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/DeleteMediaPreviewDialog.kt new file mode 100644 index 0000000000..0390a3007d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/DeleteMediaPreviewDialog.kt @@ -0,0 +1,19 @@ +package org.thoughtcrime.securesms + +import android.content.Context +import network.loki.messenger.R + +class DeleteMediaPreviewDialog { + companion object { + @JvmStatic + fun show(context: Context, doDelete: Runnable) { + context.showSessionDialog { + iconAttribute(R.attr.dialog_alert_icon) + title(R.string.MediaPreviewActivity_media_delete_confirmation_title) + text(R.string.MediaPreviewActivity_media_delete_confirmation_message) + button(R.string.delete) { doDelete.run() } + cancelButton() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java b/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java deleted file mode 100644 index 469629ed3f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import org.session.libsession.utilities.ExpirationUtil; - -import cn.carbswang.android.numberpickerview.library.NumberPickerView; -import network.loki.messenger.R; - -public class ExpirationDialog extends AlertDialog { - - protected ExpirationDialog(Context context) { - super(context); - } - - protected ExpirationDialog(Context context, int theme) { - super(context, theme); - } - - protected ExpirationDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { - super(context, cancelable, cancelListener); - } - - public static void show(final Context context, - final int currentExpiration, - final @NonNull OnClickListener listener) - { - final View view = createNumberPickerView(context, currentExpiration); - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(context.getString(R.string.ExpirationDialog_disappearing_messages)); - builder.setView(view); - builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { - int selected = ((NumberPickerView)view.findViewById(R.id.expiration_number_picker)).getValue(); - listener.onClick(context.getResources().getIntArray(R.array.expiration_times)[selected]); - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - } - - private static View createNumberPickerView(final Context context, final int currentExpiration) { - final LayoutInflater inflater = LayoutInflater.from(context); - final View view = inflater.inflate(R.layout.expiration_dialog, null); - final NumberPickerView numberPickerView = view.findViewById(R.id.expiration_number_picker); - final TextView textView = view.findViewById(R.id.expiration_details); - final int[] expirationTimes = context.getResources().getIntArray(R.array.expiration_times); - final String[] expirationDisplayValues = new String[expirationTimes.length]; - - int selectedIndex = expirationTimes.length - 1; - - for (int i=0;i= expirationTimes[i]) && - (i == expirationTimes.length -1 || currentExpiration < expirationTimes[i+1])) { - selectedIndex = i; - } - } - - numberPickerView.setDisplayedValues(expirationDisplayValues); - numberPickerView.setMinValue(0); - numberPickerView.setMaxValue(expirationTimes.length-1); - - NumberPickerView.OnValueChangeListener listener = (picker, oldVal, newVal) -> { - if (newVal == 0) { - textView.setText(R.string.ExpirationDialog_your_messages_will_not_expire); - } else { - textView.setText(context.getString(R.string.ExpirationDialog_your_messages_will_disappear_s_after_they_have_been_seen, picker.getDisplayedValues()[newVal])); - } - }; - - numberPickerView.setOnValueChangedListener(listener); - numberPickerView.setValue(selectedIndex); - listener.onValueChange(numberPickerView, selectedIndex, selectedIndex); - - return view; - } - - public interface OnClickListener { - public void onClick(int expirationTime); - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.kt new file mode 100644 index 0000000000..9a34c1ec4b --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ExpirationDialog.kt @@ -0,0 +1,51 @@ +package org.thoughtcrime.securesms + +import android.content.Context +import android.view.LayoutInflater +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import cn.carbswang.android.numberpickerview.library.NumberPickerView +import network.loki.messenger.R +import org.session.libsession.utilities.ExpirationUtil + +fun Context.showExpirationDialog( + expiration: Int, + onExpirationTime: (Int) -> Unit +): AlertDialog { + val view = LayoutInflater.from(this).inflate(R.layout.expiration_dialog, null) + val numberPickerView = view.findViewById(R.id.expiration_number_picker) + + fun updateText(index: Int) { + view.findViewById(R.id.expiration_details).text = when (index) { + 0 -> getString(R.string.ExpirationDialog_your_messages_will_not_expire) + else -> getString( + R.string.ExpirationDialog_your_messages_will_disappear_s_after_they_have_been_seen, + numberPickerView.displayedValues[index] + ) + } + } + + val expirationTimes = resources.getIntArray(R.array.expiration_times) + val expirationDisplayValues = expirationTimes + .map { ExpirationUtil.getExpirationDisplayValue(this, it) } + .toTypedArray() + + val selectedIndex = expirationTimes.run { indexOfFirst { it >= expiration }.coerceIn(indices) } + + numberPickerView.apply { + displayedValues = expirationDisplayValues + minValue = 0 + maxValue = expirationTimes.lastIndex + setOnValueChangedListener { _, _, index -> updateText(index) } + value = selectedIndex + } + + updateText(selectedIndex) + + return showSessionDialog { + title(getString(R.string.ExpirationDialog_disappearing_messages)) + view(view) + okButton { onExpirationTime(numberPickerView.let { expirationTimes[it.value] }) } + cancelButton() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java index b36fdb3ca1..95ba15c82e 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() { @@ -374,41 +376,26 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity { @SuppressLint("StaticFieldLeak") private void handleDeleteMedia(@NonNull Collection mediaRecords) { int recordCount = mediaRecords.size(); - Resources res = getContext().getResources(); - String confirmTitle = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_title, - recordCount, - recordCount); - String confirmMessage = res.getQuantityString(R.plurals.MediaOverviewActivity_Media_delete_confirm_message, - recordCount, - recordCount); - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setTitle(confirmTitle); - builder.setMessage(confirmMessage); - builder.setCancelable(true); - - builder.setPositiveButton(R.string.delete, (dialogInterface, i) -> { - new ProgressDialogAsyncTask(getContext(), - R.string.MediaOverviewActivity_Media_delete_progress_title, - R.string.MediaOverviewActivity_Media_delete_progress_message) - { - @Override - protected Void doInBackground(MediaDatabase.MediaRecord... records) { - if (records == null || records.length == 0) { - return null; - } - - for (MediaDatabase.MediaRecord record : records) { - AttachmentUtil.deleteAttachment(getContext(), record.getAttachment()); - } + DeleteMediaDialog.show( + requireContext(), + recordCount, + () -> new ProgressDialogAsyncTask( + requireContext(), + R.string.MediaOverviewActivity_Media_delete_progress_title, + R.string.MediaOverviewActivity_Media_delete_progress_message) { + @Override + protected Void doInBackground(MediaDatabase.MediaRecord... records) { + if (records == null || records.length == 0) { return null; } - }.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()])); - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); + for (MediaDatabase.MediaRecord record : records) { + AttachmentUtil.deleteAttachment(getContext(), record.getAttachment()); + } + return null; + } + }.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()]))); } private void handleSelectAllMedia() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaPreviewActivity.java index 6544c2ab89..008a45cfd1 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, 1, () -> { 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; }); } @@ -449,29 +451,20 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im return; } - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setIconAttribute(R.attr.dialog_alert_icon); - builder.setTitle(R.string.MediaPreviewActivity_media_delete_confirmation_title); - builder.setMessage(R.string.MediaPreviewActivity_media_delete_confirmation_message); - builder.setCancelable(true); - - builder.setPositiveButton(R.string.delete, (dialogInterface, which) -> { - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - if (mediaItem.attachment == null) { - return null; - } - AttachmentUtil.deleteAttachment(MediaPreviewActivity.this.getApplicationContext(), - mediaItem.attachment); - return null; - } - }.execute(); + DeleteMediaPreviewDialog.show(this, () -> { + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + DatabaseAttachment attachment = mediaItem.attachment; + if (attachment != null) { + AttachmentUtil.deleteAttachment(getApplicationContext(), attachment); + } + return null; + } + }.execute(); finish(); }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java deleted file mode 100644 index acca9f8375..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.content.Context; -import android.content.DialogInterface; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import java.util.concurrent.TimeUnit; - -import network.loki.messenger.R; - -public class MuteDialog extends AlertDialog { - - - protected MuteDialog(Context context) { - super(context); - } - - protected MuteDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { - super(context, cancelable, cancelListener); - } - - protected MuteDialog(Context context, int theme) { - super(context, theme); - } - - public static void show(final Context context, final @NonNull MuteSelectionListener listener) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.MuteDialog_mute_notifications); - builder.setItems(R.array.mute_durations, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, final int which) { - final long muteUntil; - - switch (which) { - case 1: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(2); break; - case 2: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1); break; - case 3: muteUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7); break; - case 4: muteUntil = Long.MAX_VALUE; break; - default: muteUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); break; - } - - listener.onMuted(muteUntil); - } - }); - - builder.show(); - - } - - public interface MuteSelectionListener { - public void onMuted(long until); - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt new file mode 100644 index 0000000000..f294e387ff --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt @@ -0,0 +1,27 @@ +package org.thoughtcrime.securesms + +import android.content.Context +import androidx.annotation.StringRes +import androidx.appcompat.app.AlertDialog +import network.loki.messenger.R +import java.util.concurrent.TimeUnit + +fun showMuteDialog( + context: Context, + onMuteDuration: (Long) -> Unit +): AlertDialog = context.showSessionDialog { + title(R.string.MuteDialog_mute_notifications) + items(Option.values().map { it.stringRes }.map(context::getString).toTypedArray()) { + onMuteDuration(Option.values()[it].getTime()) + } +} + +private enum class Option(@StringRes val stringRes: Int, val getTime: () -> Long) { + ONE_HOUR(R.string.arrays__mute_for_one_hour, duration = TimeUnit.HOURS.toMillis(1)), + TWO_HOURS(R.string.arrays__mute_for_two_hours, duration = TimeUnit.DAYS.toMillis(2)), + ONE_DAY(R.string.arrays__mute_for_one_day, duration = TimeUnit.DAYS.toMillis(1)), + SEVEN_DAYS(R.string.arrays__mute_for_seven_days, duration = TimeUnit.DAYS.toMillis(7)), + FOREVER(R.string.arrays__mute_forever, getTime = { Long.MAX_VALUE }); + + constructor(@StringRes stringRes: Int, duration: Long): this(stringRes, { System.currentTimeMillis() + duration }) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt new file mode 100644 index 0000000000..141a98e4ac --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -0,0 +1,146 @@ +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 +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 androidx.fragment.app.Fragment +import network.loki.messenger.R +import org.thoughtcrime.securesms.util.toPx + + +@DslMarker +@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) +annotation class DialogDsl + +@DialogDsl +class SessionDialogBuilder(val context: Context) { + + private val dp20 = toPx(20, context.resources) + private val dp40 = toPx(40, context.resources) + + 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 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)) + + fun title(text: CharSequence?) = title(text?.toString()) + fun title(text: String?) { + text(text, R.style.TextAppearance_AppCompat_Title) { setPadding(dp20) } + } + + fun text(@StringRes id: Int, style: Int = 0) = text(context.getString(id), style) + fun text(text: CharSequence?, @StyleRes style: Int = 0) { + text(text, style) { + layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT) + .apply { updateMargins(dp40, 0, dp40, dp20) } + } + } + + + private fun text(text: CharSequence?, @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 view(view: View) = contentView.addView(view) + + fun view(@LayoutRes layout: Int): View = LayoutInflater.from(context).inflate(layout, contentView) + + fun iconAttribute(@AttrRes icon: Int): AlertDialog.Builder = dialogBuilder.setIconAttribute(icon) + + fun singleChoiceItems( + options: Collection, + currentSelected: Int = 0, + onSelect: (Int) -> Unit + ) = singleChoiceItems(options.toTypedArray(), currentSelected, onSelect) + + fun singleChoiceItems( + options: Array, + currentSelected: Int = 0, + onSelect: (Int) -> Unit + ): AlertDialog.Builder = dialogBuilder.setSingleChoiceItems( + options, + currentSelected + ) { dialog, it -> onSelect(it); dialog.dismiss() } + + fun items( + options: Array, + onSelect: (Int) -> Unit + ): AlertDialog.Builder = dialogBuilder.setItems( + options, + ) { 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 + ) + + fun okButton(listener: (() -> Unit) = {}) = button(android.R.string.ok, listener = listener) + fun cancelButton(listener: (() -> Unit) = {}) = button(android.R.string.cancel, R.string.AccessibilityId_cancel_button, listener = listener) + + fun button( + @StringRes text: Int, + @StringRes contentDescriptionRes: Int = text, + @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() + } + }.let(buttonLayout::addView) + + fun create(): AlertDialog = dialogBuilder.create().also { dialog = it } + fun show(): AlertDialog = dialogBuilder.show().also { dialog = it } +} + +fun Context.showSessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog = + SessionDialogBuilder(this).apply { build() }.show() + +fun Fragment.showSessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog = + SessionDialogBuilder(requireContext()).apply { build() }.show() +fun Fragment.createSessionDialog(build: SessionDialogBuilder.() -> Unit): AlertDialog = + SessionDialogBuilder(requireContext()).apply { build() }.create() 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 92cf5ec639..b419658e78 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 @@ -20,8 +20,8 @@ import android.widget.RelativeLayout import android.widget.Toast import androidx.activity.viewModels import androidx.annotation.DimenRes -import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -70,7 +70,6 @@ import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.hexEncodedPrivateKey import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.ExpirationDialog import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.attachments.ScreenshotObserver import org.thoughtcrime.securesms.audio.AudioRecorder @@ -112,6 +111,8 @@ import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.reactions.ReactionsDialogFragment import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment +import org.thoughtcrime.securesms.showExpirationDialog +import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.util.* import java.lang.ref.WeakReference import java.util.* @@ -406,8 +407,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe push(intent, false) } - override fun showDialog(baseDialog: BaseDialog, tag: String?) { - baseDialog.show(supportFragmentManager, tag) + override fun showDialog(dialogFragment: DialogFragment, tag: String?) { + dialogFragment.show(supportFragmentManager, tag) } override fun onCreateLoader(id: Int, bundle: Bundle?): Loader { @@ -965,21 +966,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } override fun block(deleteThread: Boolean) { - val title = R.string.RecipientPreferenceActivity_block_this_contact_question - val message = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact - val dialog = AlertDialog.Builder(this) - .setTitle(title) - .setMessage(message) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.RecipientPreferenceActivity_block) { _, _ -> + showSessionDialog { + title(R.string.RecipientPreferenceActivity_block_this_contact_question) + text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) + destructiveButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) { viewModel.block(this@ConversationActivityV2) if (deleteThread) { viewModel.deleteThread() finish() } - }.show() - val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE) - button.setContentDescription("Confirm block") + } + cancelButton() + } } override fun copySessionID(sessionId: String) { @@ -1006,28 +1004,27 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val group = groupDb.getGroup(thread.address.toGroupString()).orNull() if (group?.isActive == false) { return } } - ExpirationDialog.show(this, thread.expireMessages) { expirationTime: Int -> + showExpirationDialog(thread.expireMessages) { expirationTime -> recipientDb.setExpireMessages(thread, expirationTime) val message = ExpirationTimerUpdate(expirationTime) message.recipient = thread.address.serialize() message.sentTimestamp = SnodeAPI.nowWithOffset - val expiringMessageManager = ApplicationContext.getInstance(this).expiringMessageManager - expiringMessageManager.setExpirationTimer(message) + ApplicationContext.getInstance(this).expiringMessageManager.setExpirationTimer(message) MessageSender.send(message, thread.address) invalidateOptionsMenu() } } override fun unblock() { - val title = R.string.ConversationActivity_unblock_this_contact_question - val message = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact - AlertDialog.Builder(this) - .setTitle(title) - .setMessage(message) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(R.string.ConversationActivity_unblock) { _, _ -> - viewModel.unblock(this@ConversationActivityV2) - }.show() + showSessionDialog { + 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) + destructiveButton( + R.string.ConversationActivity_unblock, + R.string.AccessibilityId_block_confirm + ) { viewModel.unblock(this@ConversationActivityV2) } + cancelButton() + } } // `position` is the adapter position; not the visual position @@ -1468,23 +1465,22 @@ 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() + showSessionDialog { + title(R.string.giphy_permission_title) + text(R.string.giphy_permission_message) + 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) } @@ -1631,35 +1627,23 @@ 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) - } - endActionMode() + + showSessionDialog { + 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)) + button(R.string.delete) { messages.forEach(viewModel::deleteForEveryone); endActionMode() } + cancelButton { 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() } @@ -1670,54 +1654,32 @@ 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) - } - endActionMode() + + showSessionDialog { + 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)) + button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() } + cancelButton(::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() + showSessionDialog { + 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.") + 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() + showSessionDialog { + 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.") + 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) { @@ -1781,7 +1743,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) @@ -1809,7 +1771,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..ca7e1de780 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 @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.conversation.v2 -import android.app.AlertDialog import android.content.Context import android.content.Intent import android.database.Cursor @@ -31,6 +30,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.showSessionDialog class ConversationAdapter( context: Context, @@ -146,17 +146,15 @@ 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.showSessionDialog { + title(R.string.CallNotificationBuilder_first_call_title) + text(R.string.CallNotificationBuilder_first_call_message) + button(R.string.activity_settings_title) { + Intent(context, PrivacySettingsActivity::class.java) + .let(context::startActivity) } - .setNeutralButton(R.string.cancel) { d, _ -> - d.dismiss() - } - .show() + cancelButton() + } } } else { viewHolder.view.setOnClickListener(null) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/BlockedDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/BlockedDialog.kt index bcabca98f1..c90f5b2be7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/BlockedDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/BlockedDialog.kt @@ -1,42 +1,41 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs import android.content.Context +import android.app.Dialog import android.graphics.Typeface +import android.os.Bundle import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.StyleSpan -import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import androidx.fragment.app.DialogFragment import network.loki.messenger.R -import network.loki.messenger.databinding.DialogBlockedBinding import org.session.libsession.messaging.contacts.Contact import org.session.libsession.utilities.recipients.Recipient -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities /** Shown upon sending a message to a user that's blocked. */ -class BlockedDialog(private val recipient: Recipient, private val context: Context) : BaseDialog() { +class BlockedDialog(private val recipient: Recipient, private val context: Context) : DialogFragment() { - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogBlockedBinding.inflate(LayoutInflater.from(requireContext())) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { val contactDB = DatabaseComponent.get(requireContext()).sessionContactDatabase() val sessionID = recipient.address.toString() val contact = contactDB.getContactWithSessionID(sessionID) val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID - val title = resources.getString(R.string.dialog_blocked_title, name) - binding.blockedTitleTextView.text = title + val explanation = resources.getString(R.string.dialog_blocked_explanation, name) val spannable = SpannableStringBuilder(explanation) val startIndex = explanation.indexOf(name) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - binding.blockedExplanationTextView.text = spannable - binding.cancelButton.setOnClickListener { dismiss() } - binding.unblockButton.setOnClickListener { unblock() } - builder.setView(binding.root) + + title(resources.getString(R.string.dialog_blocked_title, name)) + text(spannable) + button(R.string.ConversationActivity_unblock) { unblock() } + cancelButton { dismiss() } } private fun unblock() { @@ -48,4 +47,4 @@ class BlockedDialog(private val recipient: Recipient, private val context: Conte ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/DownloadDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/DownloadDialog.kt index 42cca1ad3e..5edd63f100 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/DownloadDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/DownloadDialog.kt @@ -1,19 +1,19 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs +import android.app.Dialog import android.graphics.Typeface +import android.os.Bundle import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.StyleSpan -import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R -import network.loki.messenger.databinding.DialogDownloadBinding import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.jobs.AttachmentDownloadJob import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.utilities.recipients.Recipient -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.database.SessionContactDatabase import org.thoughtcrime.securesms.dependencies.DatabaseComponent import javax.inject.Inject @@ -21,25 +21,24 @@ import javax.inject.Inject /** Shown when receiving media from a contact for the first time, to confirm that * they are to be trusted and files sent by them are to be downloaded. */ @AndroidEntryPoint -class DownloadDialog(private val recipient: Recipient) : BaseDialog() { +class DownloadDialog(private val recipient: Recipient) : DialogFragment() { @Inject lateinit var contactDB: SessionContactDatabase - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogDownloadBinding.inflate(LayoutInflater.from(requireContext())) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { val sessionID = recipient.address.toString() val contact = contactDB.getContactWithSessionID(sessionID) val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID - val title = resources.getString(R.string.dialog_download_title, name) - binding.downloadTitleTextView.text = title + title(resources.getString(R.string.dialog_download_title, name)) + val explanation = resources.getString(R.string.dialog_download_explanation, name) val spannable = SpannableStringBuilder(explanation) val startIndex = explanation.indexOf(name) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - binding.downloadExplanationTextView.text = spannable - binding.cancelButton.setOnClickListener { dismiss() } - binding.downloadButton.setOnClickListener { trust() } - builder.setView(binding.root) + text(spannable) + + button(R.string.dialog_download_button_title, R.string.AccessibilityId_download_media) { trust() } + cancelButton { dismiss() } } private fun trust() { @@ -50,4 +49,4 @@ class DownloadDialog(private val recipient: Recipient) : BaseDialog() { JobQueue.shared.resumePendingJobs(AttachmentDownloadJob.KEY) dismiss() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt index 444c389e04..e4cbd6ecee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/JoinOpenGroupDialog.kt @@ -1,45 +1,41 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs +import android.app.Dialog import android.graphics.Typeface +import android.os.Bundle import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.StyleSpan -import android.view.LayoutInflater import android.widget.Toast -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.DialogFragment import network.loki.messenger.R -import network.loki.messenger.databinding.DialogJoinOpenGroupBinding import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.utilities.OpenGroupUrlParser import org.session.libsignal.utilities.ThreadUtils -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities /** Shown upon tapping an open group invitation. */ -class JoinOpenGroupDialog(private val name: String, private val url: String) : BaseDialog() { +class JoinOpenGroupDialog(private val name: String, private val url: String) : DialogFragment() { - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogJoinOpenGroupBinding.inflate(LayoutInflater.from(requireContext())) - val title = resources.getString(R.string.dialog_join_open_group_title, name) - binding.joinOpenGroupTitleTextView.text = title + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { + title(resources.getString(R.string.dialog_join_open_group_title, name)) val explanation = resources.getString(R.string.dialog_join_open_group_explanation, name) val spannable = SpannableStringBuilder(explanation) val startIndex = explanation.indexOf(name) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - binding.joinOpenGroupExplanationTextView.text = spannable - binding.cancelButton.setOnClickListener { dismiss() } - binding.joinButton.setOnClickListener { join() } - builder.setView(binding.root) + text(spannable) + cancelButton { dismiss() } + button(R.string.open_group_invitation_view__join_accessibility_description) { join() } } private fun join() { val openGroup = OpenGroupUrlParser.parseUrl(url) - val activity = requireContext() as AppCompatActivity + val activity = requireActivity() ThreadUtils.queue { try { - OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity) + openGroup.apply { OpenGroupManager.add(server, room, serverPublicKey, activity) } MessagingModuleConfiguration.shared.storage.onOpenGroupAdded(openGroup.server) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity) } catch (e: Exception) { @@ -48,4 +44,4 @@ class JoinOpenGroupDialog(private val name: String, private val url: String) : B } dismiss() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt index a16ca86f79..996dd41f94 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/LinkPreviewDialog.kt @@ -1,20 +1,21 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs -import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog -import network.loki.messenger.databinding.DialogLinkPreviewBinding +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import org.thoughtcrime.securesms.createSessionDialog /** Shown the first time the user inputs a URL that could generate a link preview, to * let them know that Session offers the ability to send and receive link previews. */ -class LinkPreviewDialog(private val onEnabled: () -> Unit) : BaseDialog() { +class LinkPreviewDialog(private val onEnabled: () -> Unit) : DialogFragment() { - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogLinkPreviewBinding.inflate(LayoutInflater.from(requireContext())) - binding.cancelButton.setOnClickListener { dismiss() } - binding.enableLinkPreviewsButton.setOnClickListener { enable() } - builder.setView(binding.root) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { + title(R.string.dialog_link_preview_title) + text(R.string.dialog_link_preview_explanation) + button(R.string.dialog_link_preview_enable_button_title) { enable() } + cancelButton { dismiss() } } private fun enable() { @@ -22,4 +23,4 @@ class LinkPreviewDialog(private val onEnabled: () -> Unit) : BaseDialog() { dismiss() onEnabled() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt index f51261d499..6abb0814d6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt @@ -1,22 +1,23 @@ package org.thoughtcrime.securesms.conversation.v2.dialogs -import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog -import network.loki.messenger.databinding.DialogSendSeedBinding -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import android.app.Dialog +import android.os.Bundle +import androidx.fragment.app.DialogFragment +import network.loki.messenger.R +import org.thoughtcrime.securesms.createSessionDialog /** Shown if the user is about to send their recovery phrase to someone. */ -class SendSeedDialog(private val proceed: (() -> Unit)? = null) : BaseDialog() { +class SendSeedDialog(private val proceed: (() -> Unit)? = null) : DialogFragment() { - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogSendSeedBinding.inflate(LayoutInflater.from(requireContext())) - binding.cancelButton.setOnClickListener { dismiss() } - binding.sendSeedButton.setOnClickListener { send() } - builder.setView(binding.root) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { + title(R.string.dialog_send_seed_title) + text(R.string.dialog_send_seed_explanation) + button(R.string.dialog_send_seed_send_button_title) { send() } + cancelButton() } private fun send() { proceed?.invoke() dismiss() } -} \ No newline at end of file +} 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..11add657f2 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 @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.conversation.v2.menus import android.annotation.SuppressLint import android.content.Context -import android.content.DialogInterface import android.content.Intent import android.graphics.BitmapFactory import android.graphics.PorterDuff @@ -15,7 +14,6 @@ import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.annotation.ColorInt -import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.widget.SearchView @@ -34,7 +32,6 @@ import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.MediaOverviewActivity -import org.thoughtcrime.securesms.MuteDialog import org.thoughtcrime.securesms.ShortcutLauncherActivity import org.thoughtcrime.securesms.calls.WebRtcCallActivity import org.thoughtcrime.securesms.contacts.SelectContactsActivity @@ -45,6 +42,8 @@ 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.showSessionDialog +import org.thoughtcrime.securesms.showMuteDialog import org.thoughtcrime.securesms.util.BitmapUtil import java.io.IOException @@ -186,29 +185,23 @@ 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.showSessionDialog { + title(R.string.ConversationActivity_call_title) + text(R.string.ConversationActivity_call_prompt) + button(R.string.activity_settings_title, R.string.AccessibilityId_settings) { + Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity) } - .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() + cancelButton() + } 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 +288,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 +298,25 @@ 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.showSessionDialog { + title(R.string.ConversationActivity_leave_group) + text(message) + 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) { @@ -344,7 +331,7 @@ object ConversationMenuHelper { } private fun mute(context: Context, thread: Recipient) { - MuteDialog.show(ContextThemeWrapper(context, context.theme)) { until: Long -> + showMuteDialog(ContextThemeWrapper(context, context.theme)) { until -> DatabaseComponent.get(context).recipientDatabase().setMuted(thread, until) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/BaseDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/BaseDialog.kt deleted file mode 100644 index c3a9689a00..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/BaseDialog.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.thoughtcrime.securesms.conversation.v2.utilities - -import android.app.Dialog -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import androidx.appcompat.app.AlertDialog -import androidx.fragment.app.DialogFragment -import org.thoughtcrime.securesms.util.UiModeUtilities - -open class BaseDialog : DialogFragment() { - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = AlertDialog.Builder(requireContext()) - setContentView(builder) - val result = builder.create() - result.window?.setDimAmount(0.6f) - return result - } - - open fun setContentView(builder: AlertDialog.Builder) { - // To be overridden by subclasses - } -} \ No newline at end of file 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..c0ce83f631 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.showSessionDialog 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.showSessionDialog { + 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 0215040d37..700eb167d5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -10,7 +10,6 @@ import android.os.Bundle import android.text.SpannableString import android.widget.Toast import androidx.activity.viewModels -import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope @@ -41,7 +40,6 @@ import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.MuteDialog import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.start.NewConversationFragment import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 @@ -63,6 +61,8 @@ 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.showSessionDialog +import org.thoughtcrime.securesms.showMuteDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.IP2Country @@ -488,39 +488,39 @@ 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, _ -> - lifecycleScope.launch(Dispatchers.IO) { - recipientDatabase.setBlocked(thread.recipient, true) - // TODO: Remove in UserConfig branch - ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity) - withContext(Dispatchers.Main) { - binding.recyclerView.adapter!!.notifyDataSetChanged() - dialog.dismiss() - } + showSessionDialog { + title(R.string.RecipientPreferenceActivity_block_this_contact_question) + text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) + button(R.string.RecipientPreferenceActivity_block) { + lifecycleScope.launch(Dispatchers.IO) { + recipientDatabase.setBlocked(thread.recipient, true) + // TODO: Remove in UserConfig branch + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity) + withContext(Dispatchers.Main) { + 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, _ -> - lifecycleScope.launch(Dispatchers.IO) { - recipientDatabase.setBlocked(thread.recipient, false) - // TODO: Remove in UserConfig branch - ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity) - withContext(Dispatchers.Main) { - binding.recyclerView.adapter!!.notifyDataSetChanged() - dialog.dismiss() - } + showSessionDialog { + 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) + button(R.string.RecipientPreferenceActivity_unblock) { + lifecycleScope.launch(Dispatchers.IO) { + recipientDatabase.setBlocked(thread.recipient, false) + // TODO: Remove in UserConfig branch + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity) + withContext(Dispatchers.Main) { + binding.recyclerView.adapter!!.notifyDataSetChanged() } - }.show() + } + } + cancelButton() + } } private fun setConversationMuted(thread: ThreadRecord, isMuted: Boolean) { @@ -532,7 +532,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } } } else { - MuteDialog.show(this) { until: Long -> + showMuteDialog(this) { until -> lifecycleScope.launch(Dispatchers.IO) { recipientDatabase.setMuted(thread.recipient, until) withContext(Dispatchers.Main) { @@ -578,48 +578,41 @@ 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 + + showSessionDialog { + text(message) + button(R.string.yes) { + lifecycleScope.launch(Dispatchers.Main) { + val context = this@HomeActivity + // 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())) { + try { + GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString() + .takeIf(DatabaseComponent.get(context).lokiAPIDatabase()::isClosedGroup) + ?.let { MessageSender.explicitLeave(it, false) } + } catch (_: IOException) { + } } - if (isClosedGroup) { - MessageSender.explicitLeave(groupPublicKey!!, false) + // Delete the conversation + val v2OpenGroup = DatabaseComponent.get(context).lokiThreadDatabase().getOpenGroupChat(threadID) + if (v2OpenGroup != null) { + v2OpenGroup.apply { OpenGroupManager.delete(server, room, context) } + } 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() { @@ -633,17 +626,15 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } private fun hideMessageRequests() { - AlertDialog.Builder(this) - .setMessage("Hide message requests?") - .setPositiveButton(R.string.yes) { _, _ -> + showSessionDialog { + text("Hide message requests?") + button(R.string.yes) { textSecurePreferences.setHasHiddenMessageRequests() setupMessageRequestsBanner() homeViewModel.tryUpdateChannel() } - .setNegativeButton(R.string.no) { _, _ -> - // Do nothing - } - .create().show() + button(R.string.no) + } } 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..60942fd72b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsActivity.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.messagerequests -import android.app.AlertDialog import android.content.Intent import android.database.Cursor import android.os.Bundle @@ -20,6 +19,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.showSessionDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.push import javax.inject.Inject @@ -77,34 +77,34 @@ 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) + } + + showSessionDialog { + title(R.string.RecipientPreferenceActivity_block_this_contact_question) + text(R.string.message_requests_block_message) + 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 - } - dialog.create().show() + } + + showSessionDialog { + title(R.string.decline) + text(resources.getString(R.string.message_requests_decline_message)) + button(R.string.decline) { doDecline() } + button(R.string.no) + } } private fun updateEmptyState() { @@ -114,18 +114,18 @@ 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 + + showSessionDialog { + text(resources.getString(R.string.message_requests_clear_all_message)) + 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..42f3a85b47 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/PNModeActivity.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.onboarding import android.animation.ArgbEvaluator import android.animation.ValueAnimator -import android.app.AlertDialog import android.content.Intent import android.graphics.drawable.TransitionDrawable import android.net.Uri @@ -20,6 +19,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.showSessionDialog import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.PNModeView import org.thoughtcrime.securesms.util.disableClipping @@ -151,12 +151,13 @@ 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() + showSessionDialog { + title(R.string.activity_pn_mode_no_option_picked_dialog_title) + 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/permissions/Permissions.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java index 2d7e6dae59..88ee67cb4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/Permissions.java @@ -162,15 +162,13 @@ public class Permissions { request.onResult(requestedPermissions, grantResults, new boolean[requestedPermissions.length]); } - @SuppressWarnings("ConstantConditions") private void executePermissionsRequestWithRationale(PermissionsRequest request) { - AlertDialog dialog = RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogMessage, rationalDialogHeader) - .setPositiveButton(R.string.Permissions_continue, (d, which) -> executePermissionsRequest(request)) - .setNegativeButton(R.string.Permissions_not_now, (d, which) -> executeNoPermissionsRequest(request)) - .show(); - dialog.getWindow().setLayout((int)(permissionObject.getWindowWidth() * .75), ViewGroup.LayoutParams.WRAP_CONTENT); - Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - positiveButton.setContentDescription("Continue"); + RationaleDialog.show( + permissionObject.getContext(), + rationaleDialogMessage, + () -> executePermissionsRequest(request), + () -> executeNoPermissionsRequest(request), + rationalDialogHeader); } private void executePermissionsRequest(PermissionsRequest request) { @@ -257,7 +255,7 @@ public class Permissions { resultListener.onResult(permissions, grantResults, shouldShowRationaleDialog); } - private static Intent getApplicationSettingsIntent(@NonNull Context context) { + static Intent getApplicationSettingsIntent(@NonNull Context context) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", context.getPackageName(), null); @@ -354,20 +352,8 @@ public class Permissions { @Override public void run() { Context context = this.context.get(); - - if (context != null) { - AlertDialog alertDialog = new AlertDialog.Builder(context, R.style.ThemeOverlay_Session_AlertDialog) - .setTitle(R.string.Permissions_permission_required) - .setMessage(message) - .setPositiveButton(R.string.Permissions_continue, (dialog, which) -> context.startActivity(getApplicationSettingsIntent(context))) - .setNegativeButton(android.R.string.cancel, null) - .create(); - Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (positiveButton != null) { - positiveButton.setContentDescription(context.getString(R.string.AccessibilityId_continue)); - } - alertDialog.show(); - } + if (context == null) return; + SettingsDialog.show(context, message); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java b/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java deleted file mode 100644 index a346d591ac..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/permissions/RationaleDialog.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.thoughtcrime.securesms.permissions; - - -import android.app.AlertDialog; -import android.content.Context; -import android.graphics.Color; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout.LayoutParams; -import android.widget.TextView; - -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; - -import org.session.libsession.utilities.ViewUtil; - -import network.loki.messenger.R; - -public class RationaleDialog { - - public static AlertDialog.Builder createFor(@NonNull Context context, @NonNull String message, @DrawableRes int... drawables) { - View view = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null); - view.setClipToOutline(true); - ViewGroup header = view.findViewById(R.id.header_container); - TextView text = view.findViewById(R.id.message); - - for (int i=0;i(R.id.header_container) + view.findViewById(R.id.message).text = message + + fun addIcon(id: Int) { + ImageView(context).apply { + setImageDrawable(ResourcesCompat.getDrawable(context.resources, id, context.theme)) + layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT) + }.also(header::addView) + } + + fun addPlus() { + TextView(context).apply { + text = "+" + setTextSize(TypedValue.COMPLEX_UNIT_SP, 40f) + setTextColor(Color.WHITE) + layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { + ViewUtil.dpToPx(context, 20).let { setMargins(it, 0, it, 0) } + } + }.also(header::addView) + } + + drawables.firstOrNull()?.let(::addIcon) + drawables.drop(1).forEach { addPlus(); addIcon(it) } + + return context.showSessionDialog { + view(view) + button(R.string.Permissions_continue) { onPositive.run() } + button(R.string.Permissions_not_now) { onNegative.run() } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/permissions/SettingsDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/permissions/SettingsDialog.kt new file mode 100644 index 0000000000..a4efd8d870 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/permissions/SettingsDialog.kt @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.permissions + +import android.content.Context +import network.loki.messenger.R +import org.thoughtcrime.securesms.showSessionDialog + +class SettingsDialog { + companion object { + @JvmStatic + fun show(context: Context, message: String) { + context.showSessionDialog { + title(R.string.Permissions_permission_required) + text(message) + button(R.string.Permissions_continue, R.string.AccessibilityId_continue) { + context.startActivity(Permissions.getApplicationSettingsIntent(context)) + } + cancelButton() + } + } + } +} 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 a66dd7428c..aa7ee5a42d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsActivity.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.preferences -import android.app.AlertDialog import android.os.Bundle import androidx.activity.viewModels import androidx.core.view.isVisible @@ -8,6 +7,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.showSessionDialog @AndroidEntryPoint class BlockedContactsActivity: PassphraseRequiredActionBarActivity() { @@ -19,17 +19,12 @@ class BlockedContactsActivity: PassphraseRequiredActionBarActivity() { val adapter: BlockedContactsAdapter by lazy { BlockedContactsAdapter(viewModel) } fun unblock() { - // show dialog - val title = viewModel.getTitle(this) - - val message = viewModel.getMessage(this) - - AlertDialog.Builder(this) - .setTitle(title) - .setMessage(message) - .setPositiveButton(R.string.continue_2) { _, _ -> viewModel.unblock(this@BlockedContactsActivity) } - .setNegativeButton(R.string.cancel) { _, _ -> } - .show() + showSessionDialog { + title(viewModel.getTitle(this@BlockedContactsActivity)) + text(viewModel.getMessage(this@BlockedContactsActivity)) + button(R.string.continue_2) { viewModel.unblock(this@BlockedContactsActivity) } + cancelButton() + } } override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { @@ -51,4 +46,3 @@ class BlockedContactsActivity: PassphraseRequiredActionBarActivity() { } } - \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/CallToggleListener.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/CallToggleListener.kt new file mode 100644 index 0000000000..ea747798c8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/CallToggleListener.kt @@ -0,0 +1,45 @@ +package org.thoughtcrime.securesms.preferences + +import android.Manifest +import androidx.fragment.app.Fragment +import androidx.preference.Preference +import network.loki.messenger.R +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.TextSecurePreferences.Companion.setBooleanPreference +import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.showSessionDialog + +internal class CallToggleListener( + private val context: Fragment, + private val setCallback: (Boolean) -> Unit +) : Preference.OnPreferenceChangeListener { + + override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + if (newValue == false) return true + + // check if we've shown the info dialog and check for microphone permissions + context.showSessionDialog { + title(R.string.dialog_voice_video_title) + text(R.string.dialog_voice_video_message) + button(R.string.dialog_link_preview_enable_button_title, R.string.AccessibilityId_enable) { requestMicrophonePermission() } + cancelButton() + } + + return false + } + + private fun requestMicrophonePermission() { + Permissions.with(context) + .request(Manifest.permission.RECORD_AUDIO) + .onAllGranted { + setBooleanPreference( + context.requireContext(), + TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED, + true + ) + setCallback(true) + } + .onAnyDenied { setCallback(false) } + .execute() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ChangeUiModeDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ChangeUiModeDialog.kt deleted file mode 100644 index 3d5b9e2e99..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ChangeUiModeDialog.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.thoughtcrime.securesms.preferences - -import android.app.Dialog -import android.os.Bundle -import androidx.fragment.app.DialogFragment - -class ChangeUiModeDialog : DialogFragment() { - - companion object { - const val TAG = "ChangeUiModeDialog" - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val context = requireContext() - return android.app.AlertDialog.Builder(context) - .setTitle("TODO: remove this") - .show() - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt index fa3be71307..37a54a4afc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt @@ -1,9 +1,12 @@ package org.thoughtcrime.securesms.preferences +import android.app.Dialog +import android.os.Bundle import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog +import android.view.View import androidx.core.view.isGone import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DividerItemDecoration import kotlinx.coroutines.Dispatchers @@ -15,10 +18,10 @@ import network.loki.messenger.databinding.DialogClearAllDataBinding import org.session.libsession.snode.SnodeAPI import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities -class ClearAllDataDialog : BaseDialog() { +class ClearAllDataDialog : DialogFragment() { private lateinit var binding: DialogClearAllDataBinding enum class Steps { @@ -35,7 +38,11 @@ class ClearAllDataDialog : BaseDialog() { updateUI() } - override fun setContentView(builder: AlertDialog.Builder) { + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { + view(createView()) + } + + private fun createView(): View { binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext())) val device = RadioOption("deviceOnly", requireContext().getString(R.string.dialog_clear_all_data_clear_device_only)) val network = RadioOption("deviceAndNetwork", requireContext().getString(R.string.dialog_clear_all_data_clear_device_and_network)) @@ -62,8 +69,7 @@ class ClearAllDataDialog : BaseDialog() { Steps.DELETING -> { /* do nothing intentionally */ } } } - builder.setView(binding.root) - builder.setCancelable(false) + return binding.root } private fun updateUI() { 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..6f0998eecb 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.showSessionDialog fun listPreferenceDialog( context: Context, listPreference: ListPreference, - dialogListener: () -> Unit -) : AlertDialog { + onChange: () -> Unit +) : AlertDialog = listPreference.run { + context.showSessionDialog { + 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/PrivacySettingsPreferenceFragment.java b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.java deleted file mode 100644 index ac03efa362..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.java +++ /dev/null @@ -1,203 +0,0 @@ -package org.thoughtcrime.securesms.preferences; - -import android.Manifest; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.KeyguardManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.Settings; -import android.widget.Button; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.view.ContextThemeWrapper; -import androidx.fragment.app.Fragment; -import androidx.preference.Preference; - -import org.session.libsession.utilities.TextSecurePreferences; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.components.SwitchPreferenceCompat; -import org.thoughtcrime.securesms.permissions.Permissions; -import org.thoughtcrime.securesms.service.KeyCachingService; -import org.thoughtcrime.securesms.util.CallNotificationBuilder; -import org.thoughtcrime.securesms.util.IntentUtils; - -import kotlin.jvm.functions.Function1; -import network.loki.messenger.BuildConfig; -import network.loki.messenger.R; - -public class PrivacySettingsPreferenceFragment extends ListSummaryPreferenceFragment { - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - } - - @Override - public void onCreate(Bundle paramBundle) { - super.onCreate(paramBundle); - - this.findPreference(TextSecurePreferences.SCREEN_LOCK).setOnPreferenceChangeListener(new ScreenLockListener()); - - this.findPreference(TextSecurePreferences.READ_RECEIPTS_PREF).setOnPreferenceChangeListener(new ReadReceiptToggleListener()); - this.findPreference(TextSecurePreferences.TYPING_INDICATORS).setOnPreferenceChangeListener(new TypingIndicatorsToggleListener()); - this.findPreference(TextSecurePreferences.LINK_PREVIEWS).setOnPreferenceChangeListener(new LinkPreviewToggleListener()); - this.findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED).setOnPreferenceChangeListener(new CallToggleListener(this, this::setCall)); - - initializeVisibility(); - } - - private Void setCall(boolean isEnabled) { - ((SwitchPreferenceCompat)findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED)).setChecked(isEnabled); - if (isEnabled && !CallNotificationBuilder.areNotificationsEnabled(requireActivity())) { - // show a dialog saying that calls won't work properly if you don't have notifications on at a system level - new AlertDialog.Builder(new ContextThemeWrapper(requireActivity(), R.style.ThemeOverlay_Session_AlertDialog)) - .setTitle(R.string.CallNotificationBuilder_system_notification_title) - .setMessage(R.string.CallNotificationBuilder_system_notification_message) - .setPositiveButton(R.string.activity_notification_settings_title, (d, w) -> { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - Intent settingsIntent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID); - if (IntentUtils.isResolvable(requireContext(), settingsIntent)) { - startActivity(settingsIntent); - } - } else { - Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .setData(Uri.parse("package:"+BuildConfig.APPLICATION_ID)); - if (IntentUtils.isResolvable(requireContext(), settingsIntent)) { - startActivity(settingsIntent); - } - } - d.dismiss(); - }) - .setNeutralButton(R.string.dismiss, (d, w) -> { - // do nothing, user might have broken notifications - d.dismiss(); - }) - .show(); - } - return null; - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); - } - - @Override - public void onCreatePreferences(@Nullable Bundle savedInstanceState, String rootKey) { - addPreferencesFromResource(R.xml.preferences_app_protection); - } - - @Override - public void onResume() { - super.onResume(); - } - - private void initializeVisibility() { - if (TextSecurePreferences.isPasswordDisabled(getContext())) { - KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(Context.KEYGUARD_SERVICE); - if (!keyguardManager.isKeyguardSecure()) { - ((SwitchPreferenceCompat)findPreference(TextSecurePreferences.SCREEN_LOCK)).setChecked(false); - findPreference(TextSecurePreferences.SCREEN_LOCK).setEnabled(false); - } - } else { - findPreference(TextSecurePreferences.SCREEN_LOCK).setVisible(false); - findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT).setVisible(false); - } - } - - private class ScreenLockListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (Boolean)newValue; - - TextSecurePreferences.setScreenLockEnabled(getContext(), enabled); - - Intent intent = new Intent(getContext(), KeyCachingService.class); - intent.setAction(KeyCachingService.LOCK_TOGGLED_EVENT); - getContext().startService(intent); - return true; - } - } - - private class ReadReceiptToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - return true; - } - } - - private class TypingIndicatorsToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (boolean)newValue; - - if (!enabled) { - ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear(); - } - - return true; - } - } - - private class LinkPreviewToggleListener implements Preference.OnPreferenceChangeListener { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - return true; - } - } - - private class CallToggleListener implements Preference.OnPreferenceChangeListener { - - private final Fragment context; - private final Function1 setCallback; - - private CallToggleListener(Fragment context, Function1 setCallback) { - this.context = context; - this.setCallback = setCallback; - } - - private void requestMicrophonePermission() { - Permissions.with(context) - .request(Manifest.permission.RECORD_AUDIO) - .onAllGranted(() -> { - TextSecurePreferences.setBooleanPreference(context.requireContext(), TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED, true); - setCallback.invoke(true); - }) - .onAnyDenied(() -> setCallback.invoke(false)) - .execute(); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean val = (boolean) newValue; - if (val) { - // check if we've shown the info dialog and check for microphone permissions - AlertDialog dialog = new AlertDialog.Builder(new ContextThemeWrapper(context.requireContext(), R.style.ThemeOverlay_Session_AlertDialog)) - .setTitle(R.string.dialog_voice_video_title) - .setMessage(R.string.dialog_voice_video_message) - .setPositiveButton(R.string.dialog_link_preview_enable_button_title, (d, w) -> { - requestMicrophonePermission(); - }) - .setNegativeButton(R.string.cancel, (d, w) -> { - - }) - .show(); - Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); - positiveButton.setContentDescription("Enable"); - return false; - } else { - return true; - } - } - } - -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt new file mode 100644 index 0000000000..eaf48f8688 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/PrivacySettingsPreferenceFragment.kt @@ -0,0 +1,114 @@ +package org.thoughtcrime.securesms.preferences + +import android.app.KeyguardManager +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import androidx.preference.Preference +import network.loki.messenger.BuildConfig +import network.loki.messenger.R +import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.TextSecurePreferences.Companion.isPasswordDisabled +import org.session.libsession.utilities.TextSecurePreferences.Companion.setScreenLockEnabled +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.components.SwitchPreferenceCompat +import org.thoughtcrime.securesms.permissions.Permissions +import org.thoughtcrime.securesms.service.KeyCachingService +import org.thoughtcrime.securesms.showSessionDialog +import org.thoughtcrime.securesms.util.CallNotificationBuilder.Companion.areNotificationsEnabled +import org.thoughtcrime.securesms.util.IntentUtils + +class PrivacySettingsPreferenceFragment : ListSummaryPreferenceFragment() { + override fun onCreate(paramBundle: Bundle?) { + super.onCreate(paramBundle) + findPreference(TextSecurePreferences.SCREEN_LOCK)!! + .onPreferenceChangeListener = ScreenLockListener() + findPreference(TextSecurePreferences.TYPING_INDICATORS)!! + .onPreferenceChangeListener = TypingIndicatorsToggleListener() + findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED)!! + .onPreferenceChangeListener = CallToggleListener(this) { setCall(it) } + initializeVisibility() + } + + private fun setCall(isEnabled: Boolean) { + (findPreference(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED) as SwitchPreferenceCompat?)!!.isChecked = + isEnabled + if (isEnabled && !areNotificationsEnabled(requireActivity())) { + // show a dialog saying that calls won't work properly if you don't have notifications on at a system level + showSessionDialog { + title(R.string.CallNotificationBuilder_system_notification_title) + text(R.string.CallNotificationBuilder_system_notification_message) + button(R.string.activity_notification_settings_title) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) + .putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID) + } else { + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID)) + } + .apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } + .takeIf { IntentUtils.isResolvable(requireContext(), it) }.let { + startActivity(it) + } + } + button(R.string.dismiss) + } + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_app_protection) + } + + override fun onResume() { + super.onResume() + } + + private fun initializeVisibility() { + if (isPasswordDisabled(requireContext())) { + val keyguardManager = + requireContext().getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + if (!keyguardManager.isKeyguardSecure) { + findPreference(TextSecurePreferences.SCREEN_LOCK)!!.isChecked = false + findPreference(TextSecurePreferences.SCREEN_LOCK)!!.isEnabled = false + } + } else { + findPreference(TextSecurePreferences.SCREEN_LOCK)!!.isVisible = false + findPreference(TextSecurePreferences.SCREEN_LOCK_TIMEOUT)!!.isVisible = false + } + } + + private inner class ScreenLockListener : Preference.OnPreferenceChangeListener { + override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + val enabled = newValue as Boolean + setScreenLockEnabled(context!!, enabled) + val intent = Intent(context, KeyCachingService::class.java) + intent.action = KeyCachingService.LOCK_TOGGLED_EVENT + context!!.startService(intent) + return true + } + } + + private inner class TypingIndicatorsToggleListener : Preference.OnPreferenceChangeListener { + override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { + val enabled = newValue as Boolean + if (!enabled) { + ApplicationContext.getInstance(requireContext()).typingStatusRepository.clear() + } + return true + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SeedDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SeedDialog.kt index e7bfd60d3f..bae5f19605 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SeedDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SeedDialog.kt @@ -1,38 +1,34 @@ package org.thoughtcrime.securesms.preferences +import android.app.Dialog import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.view.LayoutInflater +import android.os.Bundle import android.widget.Toast -import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import network.loki.messenger.R -import network.loki.messenger.databinding.DialogSeedBinding import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.utilities.hexEncodedPrivateKey +import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.MnemonicUtilities -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog - -class SeedDialog : BaseDialog() { +class SeedDialog: DialogFragment() { private val seed by lazy { - var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED) - if (hexEncodedSeed == null) { - hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account - } - val loadFileContents: (String) -> String = { fileName -> - MnemonicUtilities.loadFileContents(requireContext(), fileName) - } - MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english) + val hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED) + ?: IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account + + MnemonicCodec { fileName -> MnemonicUtilities.loadFileContents(requireContext(), fileName) } + .encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.english) } - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogSeedBinding.inflate(LayoutInflater.from(requireContext())) - binding.seedTextView.text = seed - binding.closeButton.setOnClickListener { dismiss() } - binding.copyButton.setOnClickListener { copySeed() } - builder.setView(binding.root) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { + title(R.string.dialog_seed_title) + text(R.string.dialog_seed_explanation) + text(seed, R.style.SessionIDTextView) + button(R.string.copy, R.string.AccessibilityId_copy_recovery_phrase) { copySeed() } + button(R.string.close) { dismiss() } } private fun copySeed() { @@ -42,4 +38,4 @@ class SeedDialog : BaseDialog() { Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() dismiss() } -} \ No newline at end of file +} 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 5a03cebc37..210c88fee7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -16,7 +16,6 @@ import android.view.MenuItem import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.Toast -import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import network.loki.messenger.BuildConfig import network.loki.messenger.R @@ -40,6 +39,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.showSessionDialog import org.thoughtcrime.securesms.util.BitmapDecodingException import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities @@ -264,19 +264,15 @@ 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() + showSessionDialog { + title(R.string.activity_settings_set_display_picture) + view(R.layout.dialog_change_avatar) + button(R.string.activity_settings_upload) { startAvatarSelection() } + if (TextSecurePreferences.getProfileAvatarId(context) != 0) { + button(R.string.activity_settings_remove) { removeAvatar() } } - .setNegativeButton(R.string.cancel) { _, _ -> } - .apply { - if (TextSecurePreferences.getProfileAvatarId(context) != 0) { - setNeutralButton(R.string.activity_settings_remove) { _, _ -> removeAvatar() } - } - } - .show().apply { + cancelButton() + }.apply { val profilePic = findViewById(R.id.profile_picture_view) ?.also(::setupProfilePictureView) diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt index 1bd8373247..e3be429e3a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt @@ -1,17 +1,18 @@ package org.thoughtcrime.securesms.preferences +import android.app.Dialog import android.content.ContentResolver import android.content.ContentValues import android.content.Intent import android.media.MediaScannerConnection import android.net.Uri import android.os.Build +import android.os.Bundle import android.os.Environment import android.provider.MediaStore -import android.view.LayoutInflater import android.webkit.MimeTypeMap import android.widget.Toast -import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.Main @@ -20,11 +21,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.BuildConfig import network.loki.messenger.R -import network.loki.messenger.databinding.DialogShareLogsBinding import org.session.libsignal.utilities.ExternalStorageUtil import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ApplicationContext -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog +import org.thoughtcrime.securesms.createSessionDialog import org.thoughtcrime.securesms.util.FileProviderUtil import org.thoughtcrime.securesms.util.StreamUtil import java.io.File @@ -33,21 +33,15 @@ import java.io.IOException import java.util.Objects import java.util.concurrent.TimeUnit -class ShareLogsDialog : BaseDialog() { +class ShareLogsDialog : DialogFragment() { private var shareJob: Job? = null - override fun setContentView(builder: AlertDialog.Builder) { - val binding = DialogShareLogsBinding.inflate(LayoutInflater.from(requireContext())) - binding.cancelButton.setOnClickListener { - dismiss() - } - binding.shareButton.setOnClickListener { - // start the export and share - shareLogs() - } - builder.setView(binding.root) - builder.setCancelable(false) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { + title(R.string.dialog_share_logs_title) + text(R.string.dialog_share_logs_explanation) + button(R.string.share) { shareLogs() } + cancelButton { dismiss() } } private fun shareLogs() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt index d5b361ecd6..5ff823a15c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt @@ -7,10 +7,10 @@ import android.view.View import androidx.annotation.StyleRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.DialogFragment import network.loki.messenger.R import org.session.libsession.utilities.TextSecurePreferences import org.thoughtcrime.securesms.BaseActionBarActivity -import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) { val actionbar = supportActionBar!! @@ -66,7 +66,7 @@ interface ActivityDispatcher { fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher } fun dispatchIntent(body: (Context)->Intent?) - fun showDialog(baseDialog: BaseDialog, tag: String? = null) + fun showDialog(dialogFragment: DialogFragment, tag: String? = null) } fun TextSecurePreferences.themeState(): ThemeState { 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..8b219849a0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SaveAttachmentTask.kt @@ -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,12 +11,12 @@ 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 import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.mms.PartAuthority +import org.thoughtcrime.securesms.showSessionDialog import java.io.File import java.io.FileOutputStream import java.io.IOException @@ -30,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> { +class SaveAttachmentTask @JvmOverloads constructor(context: Context, count: Int = 1) : + ProgressDialogAsyncTask>( + 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 @@ -41,30 +45,25 @@ class SaveAttachmentTask : ProgressDialogAsyncTask Unit = {}) { + context.showSessionDialog { + title(R.string.ConversationFragment_save_to_sd_card) + iconAttribute(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() + button(R.string.yes) { onAcceptListener() } + button(R.string.no) + } } } private val contextReference: WeakReference - 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 { diff --git a/app/src/main/res/layout/dialog_blocked.xml b/app/src/main/res/layout/dialog_blocked.xml deleted file mode 100644 index 4ad01ec8ee..0000000000 --- a/app/src/main/res/layout/dialog_blocked.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - -