Use consistent dialog style

This commit is contained in:
Andrew 2023-07-13 15:39:01 +09:30 committed by GitHub
commit 96ec733517
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 920 additions and 1414 deletions

View File

@ -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()
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -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.length;i++) {
expirationDisplayValues[i] = ExpirationUtil.getExpirationDisplayValue(context, expirationTimes[i]);
if ((currentExpiration >= 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);
}
}

View File

@ -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<NumberPickerView>(R.id.expiration_number_picker)
fun updateText(index: Int) {
view.findViewById<TextView>(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()
}
}

View File

@ -76,6 +76,7 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import kotlin.Unit;
import network.loki.messenger.R; import network.loki.messenger.R;
/** /**
@ -318,9 +319,9 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@SuppressWarnings("CodeBlock2Expr") @SuppressWarnings("CodeBlock2Expr")
@SuppressLint({"InlinedApi", "StaticFieldLeak"}) @SuppressLint({"InlinedApi", "StaticFieldLeak"})
private void handleSaveMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) { private void handleSaveMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) {
final Context context = getContext(); final Context context = requireContext();
SaveAttachmentTask.showWarningDialog(context, (dialogInterface, which) -> { SaveAttachmentTask.showWarningDialog(context, mediaRecords.size(), () -> {
Permissions.with(this) Permissions.with(this)
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P) .maxSdkVersion(Build.VERSION_CODES.P)
@ -362,7 +363,8 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
}.execute(); }.execute();
}) })
.execute(); .execute();
}, mediaRecords.size()); return Unit.INSTANCE;
});
} }
private void sendMediaSavedNotificationIfNeeded() { private void sendMediaSavedNotificationIfNeeded() {
@ -374,41 +376,26 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) { private void handleDeleteMedia(@NonNull Collection<MediaDatabase.MediaRecord> mediaRecords) {
int recordCount = mediaRecords.size(); 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()); DeleteMediaDialog.show(
builder.setIconAttribute(R.attr.dialog_alert_icon); requireContext(),
builder.setTitle(confirmTitle); recordCount,
builder.setMessage(confirmMessage); () -> new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(
builder.setCancelable(true); requireContext(),
R.string.MediaOverviewActivity_Media_delete_progress_title,
builder.setPositiveButton(R.string.delete, (dialogInterface, i) -> { R.string.MediaOverviewActivity_Media_delete_progress_message) {
new ProgressDialogAsyncTask<MediaDatabase.MediaRecord, Void, Void>(getContext(), @Override
R.string.MediaOverviewActivity_Media_delete_progress_title, protected Void doInBackground(MediaDatabase.MediaRecord... records) {
R.string.MediaOverviewActivity_Media_delete_progress_message) if (records == null || records.length == 0) {
{
@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());
}
return null; return null;
} }
}.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()])); for (MediaDatabase.MediaRecord record : records) {
}); AttachmentUtil.deleteAttachment(getContext(), record.getAttachment());
builder.setNegativeButton(android.R.string.cancel, null); }
builder.show(); return null;
}
}.execute(mediaRecords.toArray(new MediaDatabase.MediaRecord[mediaRecords.size()])));
} }
private void handleSelectAllMedia() { private void handleSelectAllMedia() {

View File

@ -85,6 +85,7 @@ import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import kotlin.Unit;
import network.loki.messenger.R; import network.loki.messenger.R;
/** /**
@ -416,7 +417,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
MediaItem mediaItem = getCurrentMediaItem(); MediaItem mediaItem = getCurrentMediaItem();
if (mediaItem == null) return; if (mediaItem == null) return;
SaveAttachmentTask.showWarningDialog(this, (dialogInterface, i) -> { SaveAttachmentTask.showWarningDialog(this, 1, () -> {
Permissions.with(this) Permissions.with(this)
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) .request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P) .maxSdkVersion(Build.VERSION_CODES.P)
@ -433,6 +434,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
} }
}) })
.execute(); .execute();
return Unit.INSTANCE;
}); });
} }
@ -449,29 +451,20 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
return; return;
} }
AlertDialog.Builder builder = new AlertDialog.Builder(this); DeleteMediaPreviewDialog.show(this, () -> {
builder.setIconAttribute(R.attr.dialog_alert_icon); new AsyncTask<Void, Void, Void>() {
builder.setTitle(R.string.MediaPreviewActivity_media_delete_confirmation_title); @Override
builder.setMessage(R.string.MediaPreviewActivity_media_delete_confirmation_message); protected Void doInBackground(Void... voids) {
builder.setCancelable(true); DatabaseAttachment attachment = mediaItem.attachment;
if (attachment != null) {
builder.setPositiveButton(R.string.delete, (dialogInterface, which) -> { AttachmentUtil.deleteAttachment(getApplicationContext(), attachment);
new AsyncTask<Void, Void, Void>() { }
@Override return null;
protected Void doInBackground(Void... voids) { }
if (mediaItem.attachment == null) { }.execute();
return null;
}
AttachmentUtil.deleteAttachment(MediaPreviewActivity.this.getApplicationContext(),
mediaItem.attachment);
return null;
}
}.execute();
finish(); finish();
}); });
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
} }
@Override @Override

View File

@ -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);
}
}

View File

@ -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 })
}

View File

@ -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<String>,
currentSelected: Int = 0,
onSelect: (Int) -> Unit
) = singleChoiceItems(options.toTypedArray(), currentSelected, onSelect)
fun singleChoiceItems(
options: Array<String>,
currentSelected: Int = 0,
onSelect: (Int) -> Unit
): AlertDialog.Builder = dialogBuilder.setSingleChoiceItems(
options,
currentSelected
) { dialog, it -> onSelect(it); dialog.dismiss() }
fun items(
options: Array<String>,
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()

View File

@ -20,8 +20,8 @@ import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.annotation.DimenRes import androidx.annotation.DimenRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope 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.guava.Optional
import org.session.libsignal.utilities.hexEncodedPrivateKey import org.session.libsignal.utilities.hexEncodedPrivateKey
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.ExpirationDialog
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.attachments.ScreenshotObserver import org.thoughtcrime.securesms.attachments.ScreenshotObserver
import org.thoughtcrime.securesms.audio.AudioRecorder 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.permissions.Permissions
import org.thoughtcrime.securesms.reactions.ReactionsDialogFragment import org.thoughtcrime.securesms.reactions.ReactionsDialogFragment
import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment
import org.thoughtcrime.securesms.showExpirationDialog
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.util.* import org.thoughtcrime.securesms.util.*
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* import java.util.*
@ -406,8 +407,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
push(intent, false) push(intent, false)
} }
override fun showDialog(baseDialog: BaseDialog, tag: String?) { override fun showDialog(dialogFragment: DialogFragment, tag: String?) {
baseDialog.show(supportFragmentManager, tag) dialogFragment.show(supportFragmentManager, tag)
} }
override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<Cursor> { override fun onCreateLoader(id: Int, bundle: Bundle?): Loader<Cursor> {
@ -965,21 +966,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
override fun block(deleteThread: Boolean) { override fun block(deleteThread: Boolean) {
val title = R.string.RecipientPreferenceActivity_block_this_contact_question showSessionDialog {
val message = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact title(R.string.RecipientPreferenceActivity_block_this_contact_question)
val dialog = AlertDialog.Builder(this) text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
.setTitle(title) destructiveButton(R.string.RecipientPreferenceActivity_block, R.string.AccessibilityId_block_confirm) {
.setMessage(message)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { _, _ ->
viewModel.block(this@ConversationActivityV2) viewModel.block(this@ConversationActivityV2)
if (deleteThread) { if (deleteThread) {
viewModel.deleteThread() viewModel.deleteThread()
finish() finish()
} }
}.show() }
val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE) cancelButton()
button.setContentDescription("Confirm block") }
} }
override fun copySessionID(sessionId: String) { override fun copySessionID(sessionId: String) {
@ -1006,28 +1004,27 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val group = groupDb.getGroup(thread.address.toGroupString()).orNull() val group = groupDb.getGroup(thread.address.toGroupString()).orNull()
if (group?.isActive == false) { return } if (group?.isActive == false) { return }
} }
ExpirationDialog.show(this, thread.expireMessages) { expirationTime: Int -> showExpirationDialog(thread.expireMessages) { expirationTime ->
recipientDb.setExpireMessages(thread, expirationTime) recipientDb.setExpireMessages(thread, expirationTime)
val message = ExpirationTimerUpdate(expirationTime) val message = ExpirationTimerUpdate(expirationTime)
message.recipient = thread.address.serialize() message.recipient = thread.address.serialize()
message.sentTimestamp = SnodeAPI.nowWithOffset message.sentTimestamp = SnodeAPI.nowWithOffset
val expiringMessageManager = ApplicationContext.getInstance(this).expiringMessageManager ApplicationContext.getInstance(this).expiringMessageManager.setExpirationTimer(message)
expiringMessageManager.setExpirationTimer(message)
MessageSender.send(message, thread.address) MessageSender.send(message, thread.address)
invalidateOptionsMenu() invalidateOptionsMenu()
} }
} }
override fun unblock() { override fun unblock() {
val title = R.string.ConversationActivity_unblock_this_contact_question showSessionDialog {
val message = R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact title(R.string.ConversationActivity_unblock_this_contact_question)
AlertDialog.Builder(this) text(R.string.ConversationActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
.setTitle(title) destructiveButton(
.setMessage(message) R.string.ConversationActivity_unblock,
.setNegativeButton(android.R.string.cancel, null) R.string.AccessibilityId_block_confirm
.setPositiveButton(R.string.ConversationActivity_unblock) { _, _ -> ) { viewModel.unblock(this@ConversationActivityV2) }
viewModel.unblock(this@ConversationActivityV2) cancelButton()
}.show() }
} }
// `position` is the adapter position; not the visual position // `position` is the adapter position; not the visual position
@ -1468,23 +1465,22 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun showGIFPicker() { private fun showGIFPicker() {
val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning() val hasSeenGIFMetaDataWarning: Boolean = textSecurePreferences.hasSeenGIFMetaDataWarning()
if (!hasSeenGIFMetaDataWarning) { if (!hasSeenGIFMetaDataWarning) {
val builder = AlertDialog.Builder(this) showSessionDialog {
builder.setTitle(R.string.giphy_permission_title) title(R.string.giphy_permission_title)
builder.setMessage(R.string.giphy_permission_message) text(R.string.giphy_permission_message)
builder.setPositiveButton(R.string.continue_2) { dialog: DialogInterface, _: Int -> button(R.string.continue_2) {
textSecurePreferences.setHasSeenGIFMetaDataWarning() textSecurePreferences.setHasSeenGIFMetaDataWarning()
AttachmentManager.selectGif(this, PICK_GIF) selectGif()
dialog.dismiss() }
cancelButton()
} }
builder.setNegativeButton(R.string.cancel) { dialog: DialogInterface, _: Int ->
dialog.dismiss()
}
builder.create().show()
} else { } else {
AttachmentManager.selectGif(this, PICK_GIF) selectGif()
} }
} }
private fun selectGif() = AttachmentManager.selectGif(this, PICK_GIF)
private fun showDocumentPicker() { private fun showDocumentPicker() {
AttachmentManager.selectDocument(this, PICK_DOCUMENT) AttachmentManager.selectDocument(this, PICK_DOCUMENT)
} }
@ -1631,35 +1627,23 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id) != null } val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id) != null }
if (recipient.isOpenGroupRecipient) { if (recipient.isOpenGroupRecipient) {
val messageCount = 1 val messageCount = 1
val builder = AlertDialog.Builder(this)
builder.setTitle(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount)) showSessionDialog {
builder.setMessage(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount)) title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
builder.setCancelable(true) text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
builder.setPositiveButton(R.string.delete) { _, _ -> button(R.string.delete) { messages.forEach(viewModel::deleteForEveryone); endActionMode() }
for (message in messages) { cancelButton { endActionMode() }
viewModel.deleteForEveryone(message)
}
endActionMode()
} }
builder.setNegativeButton(android.R.string.cancel) { dialog, _ ->
dialog.dismiss()
endActionMode()
}
builder.show()
} else if (allSentByCurrentUser && allHasHash) { } else if (allSentByCurrentUser && allHasHash) {
val bottomSheet = DeleteOptionsBottomSheet() val bottomSheet = DeleteOptionsBottomSheet()
bottomSheet.recipient = recipient bottomSheet.recipient = recipient
bottomSheet.onDeleteForMeTapped = { bottomSheet.onDeleteForMeTapped = {
for (message in messages) { messages.forEach(viewModel::deleteLocally)
viewModel.deleteLocally(message)
}
bottomSheet.dismiss() bottomSheet.dismiss()
endActionMode() endActionMode()
} }
bottomSheet.onDeleteForEveryoneTapped = { bottomSheet.onDeleteForEveryoneTapped = {
for (message in messages) { messages.forEach(viewModel::deleteForEveryone)
viewModel.deleteForEveryone(message)
}
bottomSheet.dismiss() bottomSheet.dismiss()
endActionMode() endActionMode()
} }
@ -1670,54 +1654,32 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
bottomSheet.show(supportFragmentManager, bottomSheet.tag) bottomSheet.show(supportFragmentManager, bottomSheet.tag)
} else { } else {
val messageCount = 1 val messageCount = 1
val builder = AlertDialog.Builder(this)
builder.setTitle(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount)) showSessionDialog {
builder.setMessage(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount)) title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount))
builder.setCancelable(true) text(resources.getQuantityString(R.plurals.ConversationFragment_this_will_permanently_delete_all_n_selected_messages, messageCount, messageCount))
builder.setPositiveButton(R.string.delete) { _, _ -> button(R.string.delete) { messages.forEach(viewModel::deleteLocally); endActionMode() }
for (message in messages) { cancelButton(::endActionMode)
viewModel.deleteLocally(message)
}
endActionMode()
} }
builder.setNegativeButton(android.R.string.cancel) { dialog, _ ->
dialog.dismiss()
endActionMode()
}
builder.show()
} }
} }
override fun banUser(messages: Set<MessageRecord>) { override fun banUser(messages: Set<MessageRecord>) {
val builder = AlertDialog.Builder(this) showSessionDialog {
builder.setTitle(R.string.ConversationFragment_ban_selected_user) title(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.") text("This will ban the selected user from this room. It won't ban them from other rooms.")
builder.setCancelable(true) button(R.string.ban) { viewModel.banUser(messages.first().individualRecipient); endActionMode() }
builder.setPositiveButton(R.string.ban) { _, _ -> cancelButton(::endActionMode)
viewModel.banUser(messages.first().individualRecipient)
endActionMode()
} }
builder.setNegativeButton(android.R.string.cancel) { dialog, _ ->
dialog.dismiss()
endActionMode()
}
builder.show()
} }
override fun banAndDeleteAll(messages: Set<MessageRecord>) { override fun banAndDeleteAll(messages: Set<MessageRecord>) {
val builder = AlertDialog.Builder(this) showSessionDialog {
builder.setTitle(R.string.ConversationFragment_ban_selected_user) title(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.") 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.")
builder.setCancelable(true) button(R.string.ban) { viewModel.banAndDeleteAll(messages.first().individualRecipient); endActionMode() }
builder.setPositiveButton(R.string.ban) { _, _ -> cancelButton(::endActionMode)
viewModel.banAndDeleteAll(messages.first().individualRecipient)
endActionMode()
} }
builder.setNegativeButton(android.R.string.cancel) { dialog, _ ->
dialog.dismiss()
endActionMode()
}
builder.show()
} }
override fun copyMessages(messages: Set<MessageRecord>) { override fun copyMessages(messages: Set<MessageRecord>) {
@ -1781,7 +1743,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
override fun saveAttachment(messages: Set<MessageRecord>) { override fun saveAttachment(messages: Set<MessageRecord>) {
val message = messages.first() as MmsMessageRecord val message = messages.first() as MmsMessageRecord
SaveAttachmentTask.showWarningDialog(this, { _, _ -> SaveAttachmentTask.showWarningDialog(this) {
Permissions.with(this) Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE) .request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P) .maxSdkVersion(Build.VERSION_CODES.P)
@ -1809,7 +1771,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
Toast.LENGTH_LONG).show() Toast.LENGTH_LONG).show()
} }
.execute() .execute()
}) }
} }
override fun reply(messages: Set<MessageRecord>) { override fun reply(messages: Set<MessageRecord>) {

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.conversation.v2 package org.thoughtcrime.securesms.conversation.v2
import android.app.AlertDialog
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.database.Cursor 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.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
import org.thoughtcrime.securesms.showSessionDialog
class ConversationAdapter( class ConversationAdapter(
context: Context, context: Context,
@ -146,17 +146,15 @@ class ConversationAdapter(
viewHolder.view.bind(message, messageBefore) viewHolder.view.bind(message, messageBefore)
if (message.isCallLog && message.isFirstMissedCall) { if (message.isCallLog && message.isFirstMissedCall) {
viewHolder.view.setOnClickListener { viewHolder.view.setOnClickListener {
AlertDialog.Builder(context) context.showSessionDialog {
.setTitle(R.string.CallNotificationBuilder_first_call_title) title(R.string.CallNotificationBuilder_first_call_title)
.setMessage(R.string.CallNotificationBuilder_first_call_message) text(R.string.CallNotificationBuilder_first_call_message)
.setPositiveButton(R.string.activity_settings_title) { _, _ -> button(R.string.activity_settings_title) {
val intent = Intent(context, PrivacySettingsActivity::class.java) Intent(context, PrivacySettingsActivity::class.java)
context.startActivity(intent) .let(context::startActivity)
} }
.setNeutralButton(R.string.cancel) { d, _ -> cancelButton()
d.dismiss() }
}
.show()
} }
} else { } else {
viewHolder.view.setOnClickListener(null) viewHolder.view.setOnClickListener(null)

View File

@ -1,42 +1,41 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.content.Context import android.content.Context
import android.app.Dialog
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Bundle
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.StyleSpan import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.fragment.app.DialogFragment
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.DialogBlockedBinding
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.recipients.Recipient 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.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
/** Shown upon sending a message to a user that's blocked. */ /** 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) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogBlockedBinding.inflate(LayoutInflater.from(requireContext()))
val contactDB = DatabaseComponent.get(requireContext()).sessionContactDatabase() val contactDB = DatabaseComponent.get(requireContext()).sessionContactDatabase()
val sessionID = recipient.address.toString() val sessionID = recipient.address.toString()
val contact = contactDB.getContactWithSessionID(sessionID) val contact = contactDB.getContactWithSessionID(sessionID)
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: 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 explanation = resources.getString(R.string.dialog_blocked_explanation, name)
val spannable = SpannableStringBuilder(explanation) val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name) val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.blockedExplanationTextView.text = spannable
binding.cancelButton.setOnClickListener { dismiss() } title(resources.getString(R.string.dialog_blocked_title, name))
binding.unblockButton.setOnClickListener { unblock() } text(spannable)
builder.setView(binding.root) button(R.string.ConversationActivity_unblock) { unblock() }
cancelButton { dismiss() }
} }
private fun unblock() { private fun unblock() {

View File

@ -1,19 +1,19 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.app.Dialog
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Bundle
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.StyleSpan import android.text.style.StyleSpan
import android.view.LayoutInflater import androidx.fragment.app.DialogFragment
import androidx.appcompat.app.AlertDialog
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.DialogDownloadBinding
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.AttachmentDownloadJob import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.utilities.recipients.Recipient 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.database.SessionContactDatabase
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import javax.inject.Inject 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 /** 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. */ * they are to be trusted and files sent by them are to be downloaded. */
@AndroidEntryPoint @AndroidEntryPoint
class DownloadDialog(private val recipient: Recipient) : BaseDialog() { class DownloadDialog(private val recipient: Recipient) : DialogFragment() {
@Inject lateinit var contactDB: SessionContactDatabase @Inject lateinit var contactDB: SessionContactDatabase
override fun setContentView(builder: AlertDialog.Builder) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogDownloadBinding.inflate(LayoutInflater.from(requireContext()))
val sessionID = recipient.address.toString() val sessionID = recipient.address.toString()
val contact = contactDB.getContactWithSessionID(sessionID) val contact = contactDB.getContactWithSessionID(sessionID)
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID
val title = resources.getString(R.string.dialog_download_title, name) title(resources.getString(R.string.dialog_download_title, name))
binding.downloadTitleTextView.text = title
val explanation = resources.getString(R.string.dialog_download_explanation, name) val explanation = resources.getString(R.string.dialog_download_explanation, name)
val spannable = SpannableStringBuilder(explanation) val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name) val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.downloadExplanationTextView.text = spannable text(spannable)
binding.cancelButton.setOnClickListener { dismiss() }
binding.downloadButton.setOnClickListener { trust() } button(R.string.dialog_download_button_title, R.string.AccessibilityId_download_media) { trust() }
builder.setView(binding.root) cancelButton { dismiss() }
} }
private fun trust() { private fun trust() {

View File

@ -1,45 +1,41 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.app.Dialog
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Bundle
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.StyleSpan import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment
import androidx.appcompat.app.AppCompatActivity
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.DialogJoinOpenGroupBinding
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.utilities.OpenGroupUrlParser import org.session.libsession.utilities.OpenGroupUrlParser
import org.session.libsignal.utilities.ThreadUtils 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.groups.OpenGroupManager
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
/** Shown upon tapping an open group invitation. */ /** 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) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogJoinOpenGroupBinding.inflate(LayoutInflater.from(requireContext())) title(resources.getString(R.string.dialog_join_open_group_title, name))
val title = resources.getString(R.string.dialog_join_open_group_title, name)
binding.joinOpenGroupTitleTextView.text = title
val explanation = resources.getString(R.string.dialog_join_open_group_explanation, name) val explanation = resources.getString(R.string.dialog_join_open_group_explanation, name)
val spannable = SpannableStringBuilder(explanation) val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name) val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.joinOpenGroupExplanationTextView.text = spannable text(spannable)
binding.cancelButton.setOnClickListener { dismiss() } cancelButton { dismiss() }
binding.joinButton.setOnClickListener { join() } button(R.string.open_group_invitation_view__join_accessibility_description) { join() }
builder.setView(binding.root)
} }
private fun join() { private fun join() {
val openGroup = OpenGroupUrlParser.parseUrl(url) val openGroup = OpenGroupUrlParser.parseUrl(url)
val activity = requireContext() as AppCompatActivity val activity = requireActivity()
ThreadUtils.queue { ThreadUtils.queue {
try { try {
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity) openGroup.apply { OpenGroupManager.add(server, room, serverPublicKey, activity) }
MessagingModuleConfiguration.shared.storage.onOpenGroupAdded(openGroup.server) MessagingModuleConfiguration.shared.storage.onOpenGroupAdded(openGroup.server)
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(activity)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -1,20 +1,21 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.view.LayoutInflater import android.app.Dialog
import androidx.appcompat.app.AlertDialog import android.os.Bundle
import network.loki.messenger.databinding.DialogLinkPreviewBinding import androidx.fragment.app.DialogFragment
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences 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 /** 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. */ * 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) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogLinkPreviewBinding.inflate(LayoutInflater.from(requireContext())) title(R.string.dialog_link_preview_title)
binding.cancelButton.setOnClickListener { dismiss() } text(R.string.dialog_link_preview_explanation)
binding.enableLinkPreviewsButton.setOnClickListener { enable() } button(R.string.dialog_link_preview_enable_button_title) { enable() }
builder.setView(binding.root) cancelButton { dismiss() }
} }
private fun enable() { private fun enable() {

View File

@ -1,18 +1,19 @@
package org.thoughtcrime.securesms.conversation.v2.dialogs package org.thoughtcrime.securesms.conversation.v2.dialogs
import android.view.LayoutInflater import android.app.Dialog
import androidx.appcompat.app.AlertDialog import android.os.Bundle
import network.loki.messenger.databinding.DialogSendSeedBinding import androidx.fragment.app.DialogFragment
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog import network.loki.messenger.R
import org.thoughtcrime.securesms.createSessionDialog
/** Shown if the user is about to send their recovery phrase to someone. */ /** 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) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogSendSeedBinding.inflate(LayoutInflater.from(requireContext())) title(R.string.dialog_send_seed_title)
binding.cancelButton.setOnClickListener { dismiss() } text(R.string.dialog_send_seed_explanation)
binding.sendSeedButton.setOnClickListener { send() } button(R.string.dialog_send_seed_send_button_title) { send() }
builder.setView(binding.root) cancelButton()
} }
private fun send() { private fun send() {

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.conversation.v2.menus
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.PorterDuff import android.graphics.PorterDuff
@ -15,7 +14,6 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.appcompat.widget.SearchView 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.guava.Optional
import org.session.libsignal.utilities.toHexString import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.MediaOverviewActivity import org.thoughtcrime.securesms.MediaOverviewActivity
import org.thoughtcrime.securesms.MuteDialog
import org.thoughtcrime.securesms.ShortcutLauncherActivity import org.thoughtcrime.securesms.ShortcutLauncherActivity
import org.thoughtcrime.securesms.calls.WebRtcCallActivity import org.thoughtcrime.securesms.calls.WebRtcCallActivity
import org.thoughtcrime.securesms.contacts.SelectContactsActivity 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.groups.EditClosedGroupActivity.Companion.groupIDKey
import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity import org.thoughtcrime.securesms.preferences.PrivacySettingsActivity
import org.thoughtcrime.securesms.service.WebRtcCallService import org.thoughtcrime.securesms.service.WebRtcCallService
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.showMuteDialog
import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.BitmapUtil
import java.io.IOException import java.io.IOException
@ -186,29 +185,23 @@ object ConversationMenuHelper {
private fun call(context: Context, thread: Recipient) { private fun call(context: Context, thread: Recipient) {
if (!TextSecurePreferences.isCallNotificationsEnabled(context)) { if (!TextSecurePreferences.isCallNotificationsEnabled(context)) {
val dialog = AlertDialog.Builder(context) context.showSessionDialog {
.setTitle(R.string.ConversationActivity_call_title) title(R.string.ConversationActivity_call_title)
.setMessage(R.string.ConversationActivity_call_prompt) text(R.string.ConversationActivity_call_prompt)
.setPositiveButton(R.string.activity_settings_title) { _, _ -> button(R.string.activity_settings_title, R.string.AccessibilityId_settings) {
val intent = Intent(context, PrivacySettingsActivity::class.java) Intent(context, PrivacySettingsActivity::class.java).let(context::startActivity)
context.startActivity(intent)
} }
.setNeutralButton(R.string.cancel) { d, _ -> cancelButton()
d.dismiss() }
}.create()
dialog.getButton(DialogInterface.BUTTON_POSITIVE)?.contentDescription = context.getString(R.string.AccessibilityId_settings)
dialog.getButton(DialogInterface.BUTTON_NEGATIVE)?.contentDescription = context.getString(R.string.AccessibilityId_cancel_button)
dialog.show()
return return
} }
val service = WebRtcCallService.createCall(context, thread) WebRtcCallService.createCall(context, thread)
context.startService(service) .let(context::startService)
val activity = Intent(context, WebRtcCallActivity::class.java).apply { Intent(context, WebRtcCallActivity::class.java)
flags = Intent.FLAG_ACTIVITY_NEW_TASK .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
} .let(context::startActivity)
context.startActivity(activity)
} }
@ -295,9 +288,7 @@ object ConversationMenuHelper {
private fun leaveClosedGroup(context: Context, thread: Recipient) { private fun leaveClosedGroup(context: Context, thread: Recipient) {
if (!thread.isClosedGroupRecipient) { return } 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 group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull()
val admins = group.admins val admins = group.admins
val sessionID = TextSecurePreferences.getLocalNumber(context) val sessionID = TextSecurePreferences.getLocalNumber(context)
@ -307,29 +298,25 @@ object ConversationMenuHelper {
} else { } else {
context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group) context.resources.getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)
} }
builder.setMessage(message)
builder.setPositiveButton(R.string.yes) { _, _ -> fun onLeaveFailed() = Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
var groupPublicKey: String?
var isClosedGroup: Boolean context.showSessionDialog {
try { title(R.string.ConversationActivity_leave_group)
groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString() text(message)
isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey) button(R.string.yes) {
} catch (e: IOException) { try {
groupPublicKey = null val groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString()
isClosedGroup = false val isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey)
}
try { if (isClosedGroup) MessageSender.leave(groupPublicKey, true)
if (isClosedGroup) { else onLeaveFailed()
MessageSender.leave(groupPublicKey!!, true) } catch (e: Exception) {
} else { onLeaveFailed()
Toast.makeText(context, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show()
} }
} 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) { private fun inviteContacts(context: Context, thread: Recipient) {
@ -344,7 +331,7 @@ object ConversationMenuHelper {
} }
private fun mute(context: Context, thread: Recipient) { 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) DatabaseComponent.get(context).recipientDatabase().setMuted(thread, until)
} }
} }

View File

@ -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
}
}

View File

@ -1,21 +1,18 @@
package org.thoughtcrime.securesms.conversation.v2.utilities package org.thoughtcrime.securesms.conversation.v2.utilities
import android.content.Context import android.content.Context
import androidx.appcompat.app.AlertDialog
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.showSessionDialog
object NotificationUtils { object NotificationUtils {
fun showNotifyDialog(context: Context, thread: Recipient, notifyTypeHandler: (Int)->Unit) { fun showNotifyDialog(context: Context, thread: Recipient, notifyTypeHandler: (Int)->Unit) {
val notifyTypes = context.resources.getStringArray(R.array.notify_types) context.showSessionDialog {
val currentSelected = thread.notifyType title(R.string.RecipientPreferenceActivity_notification_settings)
singleChoiceItems(
AlertDialog.Builder(context) context.resources.getStringArray(R.array.notify_types),
.setSingleChoiceItems(notifyTypes,currentSelected) { d, newSelection -> thread.notifyType
notifyTypeHandler(newSelection) ) { notifyTypeHandler(it) }
d.dismiss() }
}
.setTitle(R.string.RecipientPreferenceActivity_notification_settings)
.show()
} }
} }

View File

@ -10,7 +10,6 @@ import android.os.Bundle
import android.text.SpannableString import android.text.SpannableString
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope 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.ThreadUtils
import org.session.libsignal.utilities.toHexString import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.MuteDialog
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.start.NewConversationFragment import org.thoughtcrime.securesms.conversation.start.NewConversationFragment
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 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.SeedActivity
import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate
import org.thoughtcrime.securesms.preferences.SettingsActivity 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.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.IP2Country import org.thoughtcrime.securesms.util.IP2Country
@ -488,39 +488,39 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} }
private fun blockConversation(thread: ThreadRecord) { private fun blockConversation(thread: ThreadRecord) {
AlertDialog.Builder(this) showSessionDialog {
.setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) title(R.string.RecipientPreferenceActivity_block_this_contact_question)
.setMessage(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact) text(R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact)
.setNegativeButton(android.R.string.cancel, null) button(R.string.RecipientPreferenceActivity_block) {
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ -> lifecycleScope.launch(Dispatchers.IO) {
lifecycleScope.launch(Dispatchers.IO) { recipientDatabase.setBlocked(thread.recipient, true)
recipientDatabase.setBlocked(thread.recipient, true) // TODO: Remove in UserConfig branch
// TODO: Remove in UserConfig branch ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity)
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity) withContext(Dispatchers.Main) {
withContext(Dispatchers.Main) { binding.recyclerView.adapter!!.notifyDataSetChanged()
binding.recyclerView.adapter!!.notifyDataSetChanged()
dialog.dismiss()
}
} }
}.show() }
}
cancelButton()
}
} }
private fun unblockConversation(thread: ThreadRecord) { private fun unblockConversation(thread: ThreadRecord) {
AlertDialog.Builder(this) showSessionDialog {
.setTitle(R.string.RecipientPreferenceActivity_unblock_this_contact_question) title(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) text(R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact)
.setNegativeButton(android.R.string.cancel, null) button(R.string.RecipientPreferenceActivity_unblock) {
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ -> lifecycleScope.launch(Dispatchers.IO) {
lifecycleScope.launch(Dispatchers.IO) { recipientDatabase.setBlocked(thread.recipient, false)
recipientDatabase.setBlocked(thread.recipient, false) // TODO: Remove in UserConfig branch
// TODO: Remove in UserConfig branch ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity)
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@HomeActivity) withContext(Dispatchers.Main) {
withContext(Dispatchers.Main) { binding.recyclerView.adapter!!.notifyDataSetChanged()
binding.recyclerView.adapter!!.notifyDataSetChanged()
dialog.dismiss()
}
} }
}.show() }
}
cancelButton()
}
} }
private fun setConversationMuted(thread: ThreadRecord, isMuted: Boolean) { private fun setConversationMuted(thread: ThreadRecord, isMuted: Boolean) {
@ -532,7 +532,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} }
} }
} else { } else {
MuteDialog.show(this) { until: Long -> showMuteDialog(this) { until ->
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
recipientDatabase.setMuted(thread.recipient, until) recipientDatabase.setMuted(thread.recipient, until)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@ -578,48 +578,41 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} else { } else {
resources.getString(R.string.activity_home_delete_conversation_dialog_message) resources.getString(R.string.activity_home_delete_conversation_dialog_message)
} }
val dialog = AlertDialog.Builder(this)
dialog.setMessage(message) showSessionDialog {
dialog.setPositiveButton(R.string.yes) { _, _ -> text(message)
lifecycleScope.launch(Dispatchers.Main) { button(R.string.yes) {
val context = this@HomeActivity as Context lifecycleScope.launch(Dispatchers.Main) {
// Cancel any outstanding jobs val context = this@HomeActivity
DatabaseComponent.get(context).sessionJobDatabase().cancelPendingMessageSendJobs(threadID) // Cancel any outstanding jobs
// Send a leave group message if this is an active closed group DatabaseComponent.get(context).sessionJobDatabase().cancelPendingMessageSendJobs(threadID)
if (recipient.address.isClosedGroup && DatabaseComponent.get(context).groupDatabase().isActive(recipient.address.toGroupString())) { // Send a leave group message if this is an active closed group
var isClosedGroup: Boolean if (recipient.address.isClosedGroup && DatabaseComponent.get(context).groupDatabase().isActive(recipient.address.toGroupString())) {
var groupPublicKey: String? try {
try { GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString()
groupPublicKey = GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString() .takeIf(DatabaseComponent.get(context).lokiAPIDatabase()::isClosedGroup)
isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey) ?.let { MessageSender.explicitLeave(it, false) }
} catch (e: IOException) { } catch (_: IOException) {
groupPublicKey = null }
isClosedGroup = false
} }
if (isClosedGroup) { // Delete the conversation
MessageSender.explicitLeave(groupPublicKey!!, false) 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() { private fun openSettings() {
@ -633,17 +626,15 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} }
private fun hideMessageRequests() { private fun hideMessageRequests() {
AlertDialog.Builder(this) showSessionDialog {
.setMessage("Hide message requests?") text("Hide message requests?")
.setPositiveButton(R.string.yes) { _, _ -> button(R.string.yes) {
textSecurePreferences.setHasHiddenMessageRequests() textSecurePreferences.setHasHiddenMessageRequests()
setupMessageRequestsBanner() setupMessageRequestsBanner()
homeViewModel.tryUpdateChannel() homeViewModel.tryUpdateChannel()
} }
.setNegativeButton(R.string.no) { _, _ -> button(R.string.no)
// Do nothing }
}
.create().show()
} }
private fun showNewConversation() { private fun showNewConversation() {

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.messagerequests package org.thoughtcrime.securesms.messagerequests
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.database.Cursor import android.database.Cursor
import android.os.Bundle 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.database.model.ThreadRecord
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.push import org.thoughtcrime.securesms.util.push
import javax.inject.Inject import javax.inject.Inject
@ -77,34 +77,34 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat
} }
override fun onBlockConversationClick(thread: ThreadRecord) { override fun onBlockConversationClick(thread: ThreadRecord) {
val dialog = AlertDialog.Builder(this) fun doBlock() {
dialog.setTitle(R.string.RecipientPreferenceActivity_block_this_contact_question) viewModel.blockMessageRequest(thread)
.setMessage(R.string.message_requests_block_message) LoaderManager.getInstance(this).restartLoader(0, null, this)
.setPositiveButton(R.string.recipient_preferences__block) { _, _ -> }
viewModel.blockMessageRequest(thread)
LoaderManager.getInstance(this).restartLoader(0, null, this) showSessionDialog {
} title(R.string.RecipientPreferenceActivity_block_this_contact_question)
.setNegativeButton(R.string.no) { _, _ -> text(R.string.message_requests_block_message)
// Do nothing button(R.string.recipient_preferences__block) { doBlock() }
} button(R.string.no)
dialog.create().show() }
} }
override fun onDeleteConversationClick(thread: ThreadRecord) { override fun onDeleteConversationClick(thread: ThreadRecord) {
val dialog = AlertDialog.Builder(this) fun doDecline() {
dialog.setTitle(R.string.decline) viewModel.deleteMessageRequest(thread)
.setMessage(resources.getString(R.string.message_requests_decline_message)) LoaderManager.getInstance(this).restartLoader(0, null, this)
.setPositiveButton(R.string.decline) { _,_ -> lifecycleScope.launch(Dispatchers.IO) {
viewModel.deleteMessageRequest(thread) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@MessageRequestsActivity)
LoaderManager.getInstance(this).restartLoader(0, null, this)
lifecycleScope.launch(Dispatchers.IO) {
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@MessageRequestsActivity)
}
} }
.setNegativeButton(R.string.no) { _, _ -> }
// Do nothing
} showSessionDialog {
dialog.create().show() 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() { private fun updateEmptyState() {
@ -114,18 +114,18 @@ class MessageRequestsActivity : PassphraseRequiredActionBarActivity(), Conversat
} }
private fun deleteAllAndBlock() { private fun deleteAllAndBlock() {
val dialog = AlertDialog.Builder(this) fun doDeleteAllAndBlock() {
dialog.setMessage(resources.getString(R.string.message_requests_clear_all_message))
dialog.setPositiveButton(R.string.yes) { _, _ ->
viewModel.clearAllMessageRequests() viewModel.clearAllMessageRequests()
LoaderManager.getInstance(this).restartLoader(0, null, this) LoaderManager.getInstance(this).restartLoader(0, null, this)
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(this@MessageRequestsActivity) 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()
} }
} }

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.onboarding
import android.animation.ArgbEvaluator import android.animation.ArgbEvaluator
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.graphics.drawable.TransitionDrawable import android.graphics.drawable.TransitionDrawable
import android.net.Uri import android.net.Uri
@ -20,6 +19,7 @@ import org.session.libsession.utilities.ThemeUtil
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.home.HomeActivity import org.thoughtcrime.securesms.home.HomeActivity
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.util.GlowViewUtilities import org.thoughtcrime.securesms.util.GlowViewUtilities
import org.thoughtcrime.securesms.util.PNModeView import org.thoughtcrime.securesms.util.PNModeView
import org.thoughtcrime.securesms.util.disableClipping import org.thoughtcrime.securesms.util.disableClipping
@ -151,12 +151,13 @@ class PNModeActivity : BaseActionBarActivity() {
private fun register() { private fun register() {
if (selectedOptionView == null) { if (selectedOptionView == null) {
val dialog = AlertDialog.Builder(this) showSessionDialog {
dialog.setTitle(R.string.activity_pn_mode_no_option_picked_dialog_title) title(R.string.activity_pn_mode_no_option_picked_dialog_title)
dialog.setPositiveButton(R.string.ok) { _, _ -> } button(R.string.ok)
dialog.create().show() }
return return
} }
TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == binding.fcmOptionView)) TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == binding.fcmOptionView))
val application = ApplicationContext.getInstance(this) val application = ApplicationContext.getInstance(this)
application.startPollingIfNeeded() application.startPollingIfNeeded()

View File

@ -162,15 +162,13 @@ public class Permissions {
request.onResult(requestedPermissions, grantResults, new boolean[requestedPermissions.length]); request.onResult(requestedPermissions, grantResults, new boolean[requestedPermissions.length]);
} }
@SuppressWarnings("ConstantConditions")
private void executePermissionsRequestWithRationale(PermissionsRequest request) { private void executePermissionsRequestWithRationale(PermissionsRequest request) {
AlertDialog dialog = RationaleDialog.createFor(permissionObject.getContext(), rationaleDialogMessage, rationalDialogHeader) RationaleDialog.show(
.setPositiveButton(R.string.Permissions_continue, (d, which) -> executePermissionsRequest(request)) permissionObject.getContext(),
.setNegativeButton(R.string.Permissions_not_now, (d, which) -> executeNoPermissionsRequest(request)) rationaleDialogMessage,
.show(); () -> executePermissionsRequest(request),
dialog.getWindow().setLayout((int)(permissionObject.getWindowWidth() * .75), ViewGroup.LayoutParams.WRAP_CONTENT); () -> executeNoPermissionsRequest(request),
Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); rationalDialogHeader);
positiveButton.setContentDescription("Continue");
} }
private void executePermissionsRequest(PermissionsRequest request) { private void executePermissionsRequest(PermissionsRequest request) {
@ -257,7 +255,7 @@ public class Permissions {
resultListener.onResult(permissions, grantResults, shouldShowRationaleDialog); resultListener.onResult(permissions, grantResults, shouldShowRationaleDialog);
} }
private static Intent getApplicationSettingsIntent(@NonNull Context context) { static Intent getApplicationSettingsIntent(@NonNull Context context) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", context.getPackageName(), null); Uri uri = Uri.fromParts("package", context.getPackageName(), null);
@ -354,20 +352,8 @@ public class Permissions {
@Override @Override
public void run() { public void run() {
Context context = this.context.get(); Context context = this.context.get();
if (context == null) return;
if (context != null) { SettingsDialog.show(context, message);
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();
}
} }
} }
} }

View File

@ -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<drawables.length;i++) {
ImageView imageView = new ImageView(context);
imageView.setImageDrawable(context.getResources().getDrawable(drawables[i]));
imageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
header.addView(imageView);
if (i != drawables.length - 1) {
TextView plus = new TextView(context);
plus.setText("+");
plus.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40);
plus.setTextColor(Color.WHITE);
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(ViewUtil.dpToPx(context, 20), 0, ViewUtil.dpToPx(context, 20), 0);
plus.setLayoutParams(layoutParams);
header.addView(plus);
}
}
text.setText(message);
return new AlertDialog.Builder(context, R.style.ThemeOverlay_Session_AlertDialog).setView(view);
}
}

View File

@ -0,0 +1,60 @@
package org.thoughtcrime.securesms.permissions
import android.content.Context
import android.graphics.Color
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AlertDialog
import androidx.core.content.res.ResourcesCompat
import network.loki.messenger.R
import org.session.libsession.utilities.ViewUtil
import org.thoughtcrime.securesms.showSessionDialog
object RationaleDialog {
@JvmStatic
fun show(
context: Context,
message: String,
onPositive: Runnable,
onNegative: Runnable,
@DrawableRes vararg drawables: Int
): AlertDialog {
val view = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null)
.apply { clipToOutline = true }
val header = view.findViewById<ViewGroup>(R.id.header_container)
view.findViewById<TextView>(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() }
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.app.AlertDialog
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -8,6 +7,7 @@ import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityBlockedContactsBinding import network.loki.messenger.databinding.ActivityBlockedContactsBinding
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.showSessionDialog
@AndroidEntryPoint @AndroidEntryPoint
class BlockedContactsActivity: PassphraseRequiredActionBarActivity() { class BlockedContactsActivity: PassphraseRequiredActionBarActivity() {
@ -19,17 +19,12 @@ class BlockedContactsActivity: PassphraseRequiredActionBarActivity() {
val adapter: BlockedContactsAdapter by lazy { BlockedContactsAdapter(viewModel) } val adapter: BlockedContactsAdapter by lazy { BlockedContactsAdapter(viewModel) }
fun unblock() { fun unblock() {
// show dialog showSessionDialog {
val title = viewModel.getTitle(this) title(viewModel.getTitle(this@BlockedContactsActivity))
text(viewModel.getMessage(this@BlockedContactsActivity))
val message = viewModel.getMessage(this) button(R.string.continue_2) { viewModel.unblock(this@BlockedContactsActivity) }
cancelButton()
AlertDialog.Builder(this) }
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.continue_2) { _, _ -> viewModel.unblock(this@BlockedContactsActivity) }
.setNegativeButton(R.string.cancel) { _, _ -> }
.show()
} }
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
@ -51,4 +46,3 @@ class BlockedContactsActivity: PassphraseRequiredActionBarActivity() {
} }
} }

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -1,9 +1,12 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import android.view.View
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -15,10 +18,10 @@ import network.loki.messenger.databinding.DialogClearAllDataBinding
import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeAPI
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog import org.thoughtcrime.securesms.createSessionDialog
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
class ClearAllDataDialog : BaseDialog() { class ClearAllDataDialog : DialogFragment() {
private lateinit var binding: DialogClearAllDataBinding private lateinit var binding: DialogClearAllDataBinding
enum class Steps { enum class Steps {
@ -35,7 +38,11 @@ class ClearAllDataDialog : BaseDialog() {
updateUI() 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())) binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext()))
val device = RadioOption("deviceOnly", requireContext().getString(R.string.dialog_clear_all_data_clear_device_only)) 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)) 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 */ } Steps.DELETING -> { /* do nothing intentionally */ }
} }
} }
builder.setView(binding.root) return binding.root
builder.setCancelable(false)
} }
private fun updateUI() { private fun updateUI() {

View File

@ -1,41 +1,24 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.content.Context import android.content.Context
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.preference.ListPreference import androidx.preference.ListPreference
import network.loki.messenger.databinding.DialogListPreferenceBinding import org.thoughtcrime.securesms.showSessionDialog
fun listPreferenceDialog( fun listPreferenceDialog(
context: Context, context: Context,
listPreference: ListPreference, listPreference: ListPreference,
dialogListener: () -> Unit onChange: () -> Unit
) : AlertDialog { ) : AlertDialog = listPreference.run {
context.showSessionDialog {
val index = entryValues.indexOf(value)
val options = entries.map(CharSequence::toString).toTypedArray()
val builder = AlertDialog.Builder(context) title(dialogTitle)
text(dialogMessage)
val binding = DialogListPreferenceBinding.inflate(LayoutInflater.from(context)) singleChoiceItems(options, index) {
binding.titleTextView.text = listPreference.dialogTitle listPreference.setValueIndex(it)
binding.messageTextView.text = listPreference.dialogMessage onChange()
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)
} }
.let { binding.recyclerView.adapter = it } }
binding.closeButton.setOnClickListener { dialog.dismiss() }
return dialog
} }

View File

@ -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<Boolean, Void> setCallback;
private CallToggleListener(Fragment context, Function1<Boolean,Void> 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;
}
}
}
}

View File

@ -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<Preference>(TextSecurePreferences.SCREEN_LOCK)!!
.onPreferenceChangeListener = ScreenLockListener()
findPreference<Preference>(TextSecurePreferences.TYPING_INDICATORS)!!
.onPreferenceChangeListener = TypingIndicatorsToggleListener()
findPreference<Preference>(TextSecurePreferences.CALL_NOTIFICATIONS_ENABLED)!!
.onPreferenceChangeListener = CallToggleListener(this) { setCall(it) }
initializeVisibility()
}
private fun setCall(isEnabled: Boolean) {
(findPreference<Preference>(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<String>,
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<SwitchPreferenceCompat>(TextSecurePreferences.SCREEN_LOCK)!!.isChecked = false
findPreference<Preference>(TextSecurePreferences.SCREEN_LOCK)!!.isEnabled = false
}
} else {
findPreference<Preference>(TextSecurePreferences.SCREEN_LOCK)!!.isVisible = false
findPreference<Preference>(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
}
}
}

View File

@ -1,38 +1,34 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.app.Dialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.DialogSeedBinding
import org.session.libsignal.crypto.MnemonicCodec import org.session.libsignal.crypto.MnemonicCodec
import org.session.libsignal.utilities.hexEncodedPrivateKey import org.session.libsignal.utilities.hexEncodedPrivateKey
import org.thoughtcrime.securesms.createSessionDialog
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.MnemonicUtilities 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 { private val seed by lazy {
var hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED) val hexEncodedSeed = IdentityKeyUtil.retrieve(requireContext(), IdentityKeyUtil.LOKI_SEED)
if (hexEncodedSeed == null) { ?: IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account
hexEncodedSeed = IdentityKeyUtil.getIdentityKeyPair(requireContext()).hexEncodedPrivateKey // Legacy account
} MnemonicCodec { fileName -> MnemonicUtilities.loadFileContents(requireContext(), fileName) }
val loadFileContents: (String) -> String = { fileName -> .encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.english)
MnemonicUtilities.loadFileContents(requireContext(), fileName)
}
MnemonicCodec(loadFileContents).encode(hexEncodedSeed!!, MnemonicCodec.Language.Configuration.english)
} }
override fun setContentView(builder: AlertDialog.Builder) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogSeedBinding.inflate(LayoutInflater.from(requireContext())) title(R.string.dialog_seed_title)
binding.seedTextView.text = seed text(R.string.dialog_seed_explanation)
binding.closeButton.setOnClickListener { dismiss() } text(seed, R.style.SessionIDTextView)
binding.copyButton.setOnClickListener { copySeed() } button(R.string.copy, R.string.AccessibilityId_copy_recovery_phrase) { copySeed() }
builder.setView(binding.root) button(R.string.close) { dismiss() }
} }
private fun copySeed() { private fun copySeed() {

View File

@ -16,7 +16,6 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible import androidx.core.view.isVisible
import network.loki.messenger.BuildConfig import network.loki.messenger.BuildConfig
import network.loki.messenger.R 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.permissions.Permissions
import org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity import org.thoughtcrime.securesms.preferences.appearance.AppearanceSettingsActivity
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.util.BitmapDecodingException import org.thoughtcrime.securesms.util.BitmapDecodingException
import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
@ -264,19 +264,15 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
} }
private fun showEditProfilePictureUI() { private fun showEditProfilePictureUI() {
AlertDialog.Builder(this) showSessionDialog {
.setTitle(R.string.activity_settings_set_display_picture) title(R.string.activity_settings_set_display_picture)
.setView(R.layout.dialog_change_avatar) view(R.layout.dialog_change_avatar)
.setPositiveButton(R.string.activity_settings_upload) { _, _ -> button(R.string.activity_settings_upload) { startAvatarSelection() }
startAvatarSelection() if (TextSecurePreferences.getProfileAvatarId(context) != 0) {
button(R.string.activity_settings_remove) { removeAvatar() }
} }
.setNegativeButton(R.string.cancel) { _, _ -> } cancelButton()
.apply { }.apply {
if (TextSecurePreferences.getProfileAvatarId(context) != 0) {
setNeutralButton(R.string.activity_settings_remove) { _, _ -> removeAvatar() }
}
}
.show().apply {
val profilePic = findViewById<ProfilePictureView>(R.id.profile_picture_view) val profilePic = findViewById<ProfilePictureView>(R.id.profile_picture_view)
?.also(::setupProfilePictureView) ?.also(::setupProfilePictureView)

View File

@ -1,17 +1,18 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.app.Dialog
import android.content.ContentResolver import android.content.ContentResolver
import android.content.ContentValues import android.content.ContentValues
import android.content.Intent import android.content.Intent
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import android.view.LayoutInflater
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
@ -20,11 +21,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.BuildConfig import network.loki.messenger.BuildConfig
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.DialogShareLogsBinding
import org.session.libsignal.utilities.ExternalStorageUtil import org.session.libsignal.utilities.ExternalStorageUtil
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.ApplicationContext 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.FileProviderUtil
import org.thoughtcrime.securesms.util.StreamUtil import org.thoughtcrime.securesms.util.StreamUtil
import java.io.File import java.io.File
@ -33,21 +33,15 @@ import java.io.IOException
import java.util.Objects import java.util.Objects
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class ShareLogsDialog : BaseDialog() { class ShareLogsDialog : DialogFragment() {
private var shareJob: Job? = null private var shareJob: Job? = null
override fun setContentView(builder: AlertDialog.Builder) { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val binding = DialogShareLogsBinding.inflate(LayoutInflater.from(requireContext())) title(R.string.dialog_share_logs_title)
binding.cancelButton.setOnClickListener { text(R.string.dialog_share_logs_explanation)
dismiss() button(R.string.share) { shareLogs() }
} cancelButton { dismiss() }
binding.shareButton.setOnClickListener {
// start the export and share
shareLogs()
}
builder.setView(binding.root)
builder.setCancelable(false)
} }
private fun shareLogs() { private fun shareLogs() {

View File

@ -7,10 +7,10 @@ import android.view.View
import androidx.annotation.StyleRes import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.DialogFragment
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.BaseActionBarActivity import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) { fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) {
val actionbar = supportActionBar!! val actionbar = supportActionBar!!
@ -66,7 +66,7 @@ interface ActivityDispatcher {
fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher
} }
fun dispatchIntent(body: (Context)->Intent?) fun dispatchIntent(body: (Context)->Intent?)
fun showDialog(baseDialog: BaseDialog, tag: String? = null) fun showDialog(dialogFragment: DialogFragment, tag: String? = null)
} }
fun TextSecurePreferences.themeState(): ThemeState { fun TextSecurePreferences.themeState(): ThemeState {

View File

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

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:id="@+id/blockedTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_blocked_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/blockedExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_blocked_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/unblockButton"
android:contentDescription="@string/AccessibilityId_block_confirm"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/ConversationActivity_unblock" />
</LinearLayout>
</LinearLayout>

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:id="@+id/downloadTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_download_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/downloadExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_download_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:contentDescription="@string/AccessibilityId_dont_download_media"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/downloadButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:contentDescription="@string/AccessibilityId_download_media"
android:text="@string/dialog_download_button_title" />
</LinearLayout>
</LinearLayout>

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:id="@+id/joinOpenGroupTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_join_open_group_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/joinOpenGroupExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_join_open_group_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/joinButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/open_group_invitation_view__join_accessibility_description" />
</LinearLayout>
</LinearLayout>

View File

@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:id="@+id/linkPreviewDialogTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_link_preview_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/linkPreviewDialogExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_link_preview_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel"
android:contentDescription="@string/AccessibilityId_cancel_link_preview_button"/>
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/enableLinkPreviewsButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/dialog_link_preview_enable_button_title"
android:contentDescription="@string/AccessibilityId_enable_link_preview_button"
/>
</LinearLayout>
</LinearLayout>

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:id="@+id/openURLTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_open_url_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/large_font_size" />
<TextView
android:id="@+id/openURLExplanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_open_url_explanation"
android:paddingHorizontal="@dimen/medium_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/openURLButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/open" />
</LinearLayout>
</LinearLayout>

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="@dimen/medium_spacing">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_seed_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textAlignment="center"
android:textSize="@dimen/medium_font_size" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_seed_explanation"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/medium_font_size"
android:textAlignment="center"
android:alpha="0.6" />
<TextView
style="@style/SessionIDTextView"
android:contentDescription="@string/AccessibilityId_recovery_phrase"
android:id="@+id/seedTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:padding="@dimen/small_spacing"
tools:text="habitat kiwi amply iceberg dog nerves spiderman soft match partial awakened maximum degrees"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:contentDescription="@string/AccessibilityId_cancel_button"
android:id="@+id/copyButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/copy" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:contentDescription="@string/AccessibilityId_copy_recovery_phrase"
android:id="@+id/closeButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/close" />
</LinearLayout>
</LinearLayout>

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_share_logs_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/medium_font_size" />
<TextView
android:id="@+id/dialogDescriptionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_share_logs_explanation"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancelButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/shareButton"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginStart="@dimen/medium_spacing"
android:text="@string/share" />
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Small.ThreeBounce"
android:id="@+id/progressBar"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
app:SpinKit_Color="?colorAccent"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View File

@ -169,14 +169,6 @@
<item>@string/arrays__use_custom</item> <item>@string/arrays__use_custom</item>
</string-array> </string-array>
<string-array name="mute_durations">
<item>@string/arrays__mute_for_one_hour</item>
<item>@string/arrays__mute_for_two_hours</item>
<item>@string/arrays__mute_for_one_day</item>
<item>@string/arrays__mute_for_seven_days</item>
<item>@string/arrays__mute_forever</item>
</string-array>
<string-array name="pref_notification_privacy_entries"> <string-array name="pref_notification_privacy_entries">
<item>@string/arrays__name_and_message</item> <item>@string/arrays__name_and_message</item>
<item>@string/arrays__name_only</item> <item>@string/arrays__name_only</item>

View File

@ -71,6 +71,7 @@
<!-- TODO These button styles require proper background selectors for up/down visual state. --> <!-- TODO These button styles require proper background selectors for up/down visual state. -->
<style name="Widget.Session.Button.Common" parent=""> <style name="Widget.Session.Button.Common" parent="">
<item name="android:gravity">center</item>
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
<item name="android:textSize">@dimen/medium_font_size</item> <item name="android:textSize">@dimen/medium_font_size</item>
<item name="android:fontFamily">sans-serif-medium</item> <item name="android:fontFamily">sans-serif-medium</item>
@ -110,6 +111,7 @@
</style> </style>
<style name="Widget.Session.Button.Dialog" parent=""> <style name="Widget.Session.Button.Dialog" parent="">
<item name="android:gravity">center</item>
<item name="android:textAllCaps">false</item> <item name="android:textAllCaps">false</item>
<item name="android:textSize">@dimen/small_font_size</item> <item name="android:textSize">@dimen/small_font_size</item>
<item name="android:textColor">?android:textColorPrimary</item> <item name="android:textColor">?android:textColorPrimary</item>