kopia lustrzana https://github.com/ryukoposting/Signal-Android
Ensure user leaves groups before deleting account.
rodzic
3242d97c75
commit
321c84583b
|
@ -958,6 +958,14 @@ private static final String[] GROUP_PROJECTION = {
|
|||
return getCurrent();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
if (cursor == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return cursor.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable GroupRecord getCurrent() {
|
||||
if (cursor == null || cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)) == null || cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID)) == 0) {
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package org.thoughtcrime.securesms.delete
|
||||
|
||||
/**
|
||||
* Account deletion event.
|
||||
*
|
||||
* @param type Specifies what type of event this is. Each type maps to a single class. This exists in order to facilitate
|
||||
* legacy Java switch statement.
|
||||
*/
|
||||
sealed class DeleteAccountEvent(val type: Type) {
|
||||
object NoCountryCode : DeleteAccountEvent(Type.NO_COUNTRY_CODE)
|
||||
|
||||
object NoNationalNumber : DeleteAccountEvent(Type.NO_NATIONAL_NUMBER)
|
||||
|
||||
object NotAMatch : DeleteAccountEvent(Type.NOT_A_MATCH)
|
||||
|
||||
object ConfirmDeletion : DeleteAccountEvent(Type.CONFIRM_DELETION)
|
||||
|
||||
object PinDeletionFailed : DeleteAccountEvent(Type.PIN_DELETION_FAILED)
|
||||
|
||||
object LeaveGroupsFailed : DeleteAccountEvent(Type.LEAVE_GROUPS_FAILED)
|
||||
|
||||
object ServerDeletionFailed : DeleteAccountEvent(Type.SERVER_DELETION_FAILED)
|
||||
|
||||
object LocalDataDeletionFailed : DeleteAccountEvent(Type.LOCAL_DATA_DELETION_FAILED)
|
||||
|
||||
object LeaveGroupsFinished : DeleteAccountEvent(Type.LEAVE_GROUPS_FINISHED)
|
||||
|
||||
/**
|
||||
* Progress update for leaving groups
|
||||
*
|
||||
* @param totalCount The total number of groups we are attempting to leave
|
||||
* @param leaveCount The number of groups we have left so far
|
||||
*/
|
||||
data class LeaveGroupsProgress(
|
||||
val totalCount: Int,
|
||||
val leaveCount: Int
|
||||
) : DeleteAccountEvent(Type.LEAVE_GROUPS_PROGRESS)
|
||||
|
||||
enum class Type {
|
||||
NO_COUNTRY_CODE,
|
||||
NO_NATIONAL_NUMBER,
|
||||
NOT_A_MATCH,
|
||||
CONFIRM_DELETION,
|
||||
LEAVE_GROUPS_FAILED,
|
||||
PIN_DELETION_FAILED,
|
||||
SERVER_DELETION_FAILED,
|
||||
LOCAL_DATA_DELETION_FAILED,
|
||||
LEAVE_GROUPS_PROGRESS,
|
||||
LEAVE_GROUPS_FINISHED
|
||||
}
|
||||
}
|
|
@ -25,8 +25,9 @@ import android.widget.TextView;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
|
@ -36,7 +37,6 @@ import org.thoughtcrime.securesms.components.LabeledEditText;
|
|||
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
||||
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
public class DeleteAccountFragment extends Fragment {
|
||||
|
@ -47,7 +47,7 @@ public class DeleteAccountFragment extends Fragment {
|
|||
private LabeledEditText number;
|
||||
private AsYouTypeFormatter countryFormatter;
|
||||
private DeleteAccountViewModel viewModel;
|
||||
private DialogInterface deletionProgressDialog;
|
||||
private DeleteAccountProgressDialog deletionProgressDialog;
|
||||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
@ -63,8 +63,7 @@ public class DeleteAccountFragment extends Fragment {
|
|||
countryCode = view.findViewById(R.id.delete_account_fragment_country_code);
|
||||
number = view.findViewById(R.id.delete_account_fragment_number);
|
||||
|
||||
viewModel = ViewModelProviders.of(requireActivity(), new DeleteAccountViewModel.Factory(new DeleteAccountRepository()))
|
||||
.get(DeleteAccountViewModel.class);
|
||||
viewModel = new ViewModelProvider(requireActivity(), new DeleteAccountViewModel.Factory(new DeleteAccountRepository())).get(DeleteAccountViewModel.class);
|
||||
viewModel.getCountryDisplayName().observe(getViewLifecycleOwner(), this::setCountryDisplay);
|
||||
viewModel.getRegionCode().observe(getViewLifecycleOwner(), this::handleRegionUpdated);
|
||||
viewModel.getEvents().observe(getViewLifecycleOwner(), this::handleEvent);
|
||||
|
@ -220,8 +219,8 @@ public class DeleteAccountFragment extends Fragment {
|
|||
viewModel.setNationalNumber(number);
|
||||
}
|
||||
|
||||
private void handleEvent(@NonNull DeleteAccountViewModel.EventType eventType) {
|
||||
switch (eventType) {
|
||||
private void handleEvent(@NonNull DeleteAccountEvent deleteAccountEvent) {
|
||||
switch (deleteAccountEvent.getType()) {
|
||||
case NO_COUNTRY_CODE:
|
||||
Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_country_code, Snackbar.LENGTH_SHORT).setTextColor(Color.WHITE).show();
|
||||
break;
|
||||
|
@ -240,14 +239,11 @@ public class DeleteAccountFragment extends Fragment {
|
|||
.setTitle(R.string.DeleteAccountFragment__are_you_sure)
|
||||
.setMessage(R.string.DeleteAccountFragment__this_will_delete_your_signal_account)
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
|
||||
.setPositiveButton(R.string.DeleteAccountFragment__delete_account, (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
deletionProgressDialog = SimpleProgressDialog.show(requireContext());
|
||||
viewModel.deleteAccount();
|
||||
})
|
||||
.setPositiveButton(R.string.DeleteAccountFragment__delete_account, this::handleDeleteAccountConfirmation)
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
break;
|
||||
case LEAVE_GROUPS_FAILED:
|
||||
case PIN_DELETION_FAILED:
|
||||
case SERVER_DELETION_FAILED:
|
||||
dismissDeletionProgressDialog();
|
||||
|
@ -257,8 +253,16 @@ public class DeleteAccountFragment extends Fragment {
|
|||
dismissDeletionProgressDialog();
|
||||
showLocalDataDeletionFailedDialog();
|
||||
break;
|
||||
case LEAVE_GROUPS_PROGRESS:
|
||||
ensureDeletionProgressDialog();
|
||||
deletionProgressDialog.presentLeavingGroups((DeleteAccountEvent.LeaveGroupsProgress) deleteAccountEvent);
|
||||
break;
|
||||
case LEAVE_GROUPS_FINISHED:
|
||||
ensureDeletionProgressDialog();
|
||||
deletionProgressDialog.presentDeletingAccount();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown error type: " + eventType);
|
||||
throw new IllegalStateException("Unknown error type: " + deleteAccountEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,11 +274,12 @@ public class DeleteAccountFragment extends Fragment {
|
|||
}
|
||||
|
||||
private void showNetworkDeletionFailedDialog() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setMessage(R.string.DeleteAccountFragment__failed_to_delete_account)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss())
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.DeleteAccountFragment__account_not_deleted)
|
||||
.setMessage(R.string.DeleteAccountFragment__there_was_a_problem)
|
||||
.setPositiveButton(android.R.string.ok, this::handleDeleteAccountConfirmation)
|
||||
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showLocalDataDeletionFailedDialog() {
|
||||
|
@ -288,4 +293,16 @@ public class DeleteAccountFragment extends Fragment {
|
|||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void handleDeleteAccountConfirmation(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
ensureDeletionProgressDialog();
|
||||
viewModel.deleteAccount();
|
||||
}
|
||||
|
||||
private void ensureDeletionProgressDialog() {
|
||||
if (deletionProgressDialog != null) {
|
||||
deletionProgressDialog = DeleteAccountProgressDialog.show(requireContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package org.thoughtcrime.securesms.delete
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
||||
/**
|
||||
* Dialog which shows one of two states:
|
||||
*
|
||||
* 1. A "Leaving Groups" state with a determinate progress bar which updates as we leave groups
|
||||
* 1. A "Deleting Account" state with an indeterminate progress bar
|
||||
*/
|
||||
class DeleteAccountProgressDialog private constructor(private val alertDialog: AlertDialog) {
|
||||
|
||||
val title: TextView = alertDialog.findViewById(R.id.delete_account_progress_dialog_title)!!
|
||||
val message: TextView = alertDialog.findViewById(R.id.delete_account_progress_dialog_message)!!
|
||||
val progressBar: ProgressBar = alertDialog.findViewById(R.id.delete_account_progress_dialog_spinner)!!
|
||||
|
||||
fun presentLeavingGroups(leaveGroupsProgress: DeleteAccountEvent.LeaveGroupsProgress) {
|
||||
title.setText(R.string.DeleteAccountFragment__leaving_groups)
|
||||
message.setText(R.string.DeleteAccountFragment__depending_on_the_number_of_groups)
|
||||
progressBar.max = leaveGroupsProgress.totalCount
|
||||
progressBar.progress = leaveGroupsProgress.leaveCount
|
||||
}
|
||||
|
||||
fun presentDeletingAccount() {
|
||||
title.setText(R.string.DeleteAccountFragment__deleting_account)
|
||||
message.setText(R.string.DeleteAccountFragment__deleting_all_user_data_and_resetting)
|
||||
progressBar.isIndeterminate = true
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun show(context: Context): DeleteAccountProgressDialog {
|
||||
return DeleteAccountProgressDialog(
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setView(R.layout.delete_account_progress_dialog)
|
||||
.show()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,17 @@
|
|||
package org.thoughtcrime.securesms.delete;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.pin.KbsEnclaves;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||
|
@ -36,18 +40,40 @@ class DeleteAccountRepository {
|
|||
return PhoneNumberUtil.getInstance().getCountryCodeForRegion(region);
|
||||
}
|
||||
|
||||
void deleteAccount(@NonNull Runnable onFailureToRemovePin,
|
||||
@NonNull Runnable onFailureToDeleteFromService,
|
||||
@NonNull Runnable onFailureToDeleteLocalData)
|
||||
{
|
||||
void deleteAccount(@NonNull Consumer<DeleteAccountEvent> onDeleteAccountEvent) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
Log.i(TAG, "deleteAccount: attempting to leave groups...");
|
||||
|
||||
int groupsLeft = 0;
|
||||
try (GroupDatabase.Reader groups = SignalDatabase.groups().getGroups()) {
|
||||
GroupDatabase.GroupRecord groupRecord = groups.getNext();
|
||||
onDeleteAccountEvent.accept(new DeleteAccountEvent.LeaveGroupsProgress(groups.getCount(), 0));
|
||||
Log.i(TAG, "deleteAccount: found " + groups.getCount() + " groups to leave.");
|
||||
|
||||
while (groupRecord != null) {
|
||||
if (groupRecord.getId().isPush() && groupRecord.isActive()) {
|
||||
GroupManager.leaveGroup(ApplicationDependencies.getApplication(), groupRecord.getId().requirePush());
|
||||
onDeleteAccountEvent.accept(new DeleteAccountEvent.LeaveGroupsProgress(groups.getCount(), ++groupsLeft));
|
||||
}
|
||||
|
||||
groupRecord = groups.getNext();
|
||||
}
|
||||
|
||||
onDeleteAccountEvent.accept(DeleteAccountEvent.LeaveGroupsFinished.INSTANCE);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "deleteAccount: failed to leave groups", e);
|
||||
onDeleteAccountEvent.accept(DeleteAccountEvent.LeaveGroupsFailed.INSTANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(TAG, "deleteAccount: successfully left all groups.");
|
||||
Log.i(TAG, "deleteAccount: attempting to remove pin...");
|
||||
|
||||
try {
|
||||
ApplicationDependencies.getKeyBackupService(KbsEnclaves.current()).newPinChangeSession().removePin();
|
||||
} catch (UnauthenticatedResponseException | IOException e) {
|
||||
Log.w(TAG, "deleteAccount: failed to remove PIN", e);
|
||||
onFailureToRemovePin.run();
|
||||
onDeleteAccountEvent.accept(DeleteAccountEvent.PinDeletionFailed.INSTANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -58,7 +84,7 @@ class DeleteAccountRepository {
|
|||
ApplicationDependencies.getSignalServiceAccountManager().deleteAccount();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "deleteAccount: failed to delete account from signal service", e);
|
||||
onFailureToDeleteFromService.run();
|
||||
onDeleteAccountEvent.accept(DeleteAccountEvent.ServerDeletionFailed.INSTANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -67,7 +93,7 @@ class DeleteAccountRepository {
|
|||
|
||||
if (!ServiceUtil.getActivityManager(ApplicationDependencies.getApplication()).clearApplicationUserData()) {
|
||||
Log.w(TAG, "deleteAccount: failed to delete user data");
|
||||
onFailureToDeleteLocalData.run();
|
||||
onDeleteAccountEvent.accept(DeleteAccountEvent.LocalDataDeletionFailed.INSTANCE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,9 +34,9 @@ public class DeleteAccountViewModel extends ViewModel {
|
|||
private final MutableLiveData<String> regionCode;
|
||||
private final LiveData<String> countryDisplayName;
|
||||
private final MutableLiveData<Long> nationalNumber;
|
||||
private final MutableLiveData<String> query;
|
||||
private final SingleLiveEvent<EventType> events;
|
||||
private final LiveData<Optional<String>> walletBalance;
|
||||
private final MutableLiveData<String> query;
|
||||
private final SingleLiveEvent<DeleteAccountEvent> events;
|
||||
private final LiveData<Optional<String>> walletBalance;
|
||||
|
||||
public DeleteAccountViewModel(@NonNull DeleteAccountRepository repository) {
|
||||
this.repository = repository;
|
||||
|
@ -67,7 +67,7 @@ public class DeleteAccountViewModel extends ViewModel {
|
|||
return Transformations.distinctUntilChanged(regionCode);
|
||||
}
|
||||
|
||||
@NonNull SingleLiveEvent<EventType> getEvents() {
|
||||
@NonNull SingleLiveEvent<DeleteAccountEvent> getEvents() {
|
||||
return events;
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,7 @@ public class DeleteAccountViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
void deleteAccount() {
|
||||
repository.deleteAccount(() -> events.postValue(EventType.PIN_DELETION_FAILED),
|
||||
() -> events.postValue(EventType.SERVER_DELETION_FAILED),
|
||||
() -> events.postValue(EventType.LOCAL_DATA_DELETION_FAILED));
|
||||
repository.deleteAccount(events::postValue);
|
||||
}
|
||||
|
||||
void submit() {
|
||||
|
@ -91,12 +89,12 @@ public class DeleteAccountViewModel extends ViewModel {
|
|||
Long nationalNumber = this.nationalNumber.getValue();
|
||||
|
||||
if (countryCode == null || countryCode == 0) {
|
||||
events.setValue(EventType.NO_COUNTRY_CODE);
|
||||
events.setValue(DeleteAccountEvent.NoCountryCode.INSTANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nationalNumber == null) {
|
||||
events.setValue(EventType.NO_NATIONAL_NUMBER);
|
||||
events.setValue(DeleteAccountEvent.NoNationalNumber.INSTANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,9 +103,9 @@ public class DeleteAccountViewModel extends ViewModel {
|
|||
number.setNationalNumber(nationalNumber);
|
||||
|
||||
if (PhoneNumberUtil.getInstance().isNumberMatch(number, Recipient.self().requireE164()) == PhoneNumberUtil.MatchType.EXACT_MATCH) {
|
||||
events.setValue(EventType.CONFIRM_DELETION);
|
||||
events.setValue(DeleteAccountEvent.ConfirmDeletion.INSTANCE);
|
||||
} else {
|
||||
events.setValue(EventType.NOT_A_MATCH);
|
||||
events.setValue(DeleteAccountEvent.NotAMatch.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,16 +153,6 @@ public class DeleteAccountViewModel extends ViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
enum EventType {
|
||||
NO_COUNTRY_CODE,
|
||||
NO_NATIONAL_NUMBER,
|
||||
NOT_A_MATCH,
|
||||
CONFIRM_DELETION,
|
||||
PIN_DELETION_FAILED,
|
||||
SERVER_DELETION_FAILED,
|
||||
LOCAL_DATA_DELETION_FAILED
|
||||
}
|
||||
|
||||
public static final class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
private final DeleteAccountRepository repository;
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/delete_account_progress_dialog_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:text="@string/DeleteAccountFragment__leaving_groups"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/delete_account_progress_dialog_spinner" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/delete_account_progress_dialog_message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:minLines="2"
|
||||
android:text="@string/DeleteAccountFragment__depending_on_the_number_of_groups"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||
android:textColor="@color/signal_text_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/delete_account_progress_dialog_title" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/delete_account_progress_dialog_spinner"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginTop="58dp"
|
||||
android:indeterminateBehavior="cycle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -3364,6 +3364,18 @@
|
|||
<string name="DeleteAccountFragment__failed_to_delete_account">Failed to delete account. Do you have a network connection?</string>
|
||||
<string name="DeleteAccountFragment__failed_to_delete_local_data">Failed to delete local data. You can manually clear it in the system application settings.</string>
|
||||
<string name="DeleteAccountFragment__launch_app_settings">Launch App Settings</string>
|
||||
<!-- Title of progress dialog shown when a user deletes their account and the process is leaving all groups -->
|
||||
<string name="DeleteAccountFragment__leaving_groups">Leaving groups…</string>
|
||||
<!-- Title of progress dialog shown when a user deletes their account and the process has left all groups -->
|
||||
<string name="DeleteAccountFragment__deleting_account">Deleting account…</string>
|
||||
<!-- Message of progress dialog shown when a user deletes their account and the process is leaving groups -->
|
||||
<string name="DeleteAccountFragment__depending_on_the_number_of_groups">Depending on the number of groups you\'re in, this might take a few minutes</string>
|
||||
<!-- Message of progress dialog shown when a user deletes their account and the process has left all groups -->
|
||||
<string name="DeleteAccountFragment__deleting_all_user_data_and_resetting">Deleting user data and resetting the app</string>
|
||||
<!-- Title of error dialog shown when a network error occurs during account deletion -->
|
||||
<string name="DeleteAccountFragment__account_not_deleted">Account Not Deleted</string>
|
||||
<!-- Message of error dialog shown when a network error occurs during account deletion -->
|
||||
<string name="DeleteAccountFragment__there_was_a_problem">There was a problem completing the deletion process. Check your network connection and try again.</string>
|
||||
|
||||
<!-- DeleteAccountCountryPickerFragment -->
|
||||
<string name="DeleteAccountCountryPickerFragment__search_countries">Search Countries</string>
|
||||
|
|
Ładowanie…
Reference in New Issue