mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-25 19:12:19 +00:00
Add ability to delete your Signal account from within the app.
This commit is contained in:
@@ -27,8 +27,7 @@ public abstract class FullScreenDialogFragment extends DialogFragment {
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setStyle(STYLE_NO_FRAME, ThemeUtil.isDarkTheme(requireActivity()) ? R.style.TextSecure_DarkTheme_FullScreenDialog
|
setStyle(STYLE_NO_FRAME, R.style.Signal_DayNight_Dialog_FullScreen);
|
||||||
: R.style.TextSecure_LightTheme_FullScreenDialog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,43 @@
|
|||||||
|
package org.thoughtcrime.securesms.delete;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
final class Country {
|
||||||
|
private final String displayName;
|
||||||
|
private final int code;
|
||||||
|
private final String normalized;
|
||||||
|
|
||||||
|
Country(@NonNull String displayName, int code) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.code = code;
|
||||||
|
this.normalized = displayName.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNormalizedDisplayName() {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Country country = (Country) o;
|
||||||
|
return displayName.equals(country.displayName) &&
|
||||||
|
code == country.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(displayName, code);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
package org.thoughtcrime.securesms.delete;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.util.Consumer;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
class DeleteAccountCountryPickerAdapter extends ListAdapter<Country, DeleteAccountCountryPickerAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final Callback callback;
|
||||||
|
|
||||||
|
protected DeleteAccountCountryPickerAdapter(@NonNull Callback callback) {
|
||||||
|
super(new CountryDiffCallback());
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.delete_account_country_adapter_item, parent, false);
|
||||||
|
|
||||||
|
return new ViewHolder(view, position -> callback.onItemSelected(getItem(position)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
holder.textView.setText(getItem(position).getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final TextView textView;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView, @NonNull Consumer<Integer> onItemClickedConsumer) {
|
||||||
|
super(itemView);
|
||||||
|
textView = itemView.findViewById(android.R.id.text1);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(unused -> {
|
||||||
|
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||||
|
onItemClickedConsumer.accept(getAdapterPosition());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CountryDiffCallback extends DiffUtil.ItemCallback<Country> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(@NonNull Country oldItem, @NonNull Country newItem) {
|
||||||
|
return Objects.equals(oldItem.getCode(), newItem.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(@NonNull Country oldItem, @NonNull Country newItem) {
|
||||||
|
return Objects.equals(oldItem, newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
void onItemSelected(@NonNull Country country);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
package org.thoughtcrime.securesms.delete;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
||||||
|
|
||||||
|
public class DeleteAccountCountryPickerFragment extends DialogFragment {
|
||||||
|
|
||||||
|
private DeleteAccountViewModel viewModel;
|
||||||
|
|
||||||
|
public static void show(@NonNull FragmentManager fragmentManager) {
|
||||||
|
new DeleteAccountCountryPickerFragment().show(fragmentManager, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setStyle(STYLE_NO_FRAME, R.style.Signal_DayNight_Dialog_FullScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.delete_account_country_picker, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
Toolbar toolbar = view.findViewById(R.id.delete_account_country_picker_toolbar);
|
||||||
|
EditText searchFilter = view.findViewById(R.id.delete_account_country_picker_filter);
|
||||||
|
RecyclerView recycler = view.findViewById(R.id.delete_account_country_picker_recycler);
|
||||||
|
DeleteAccountCountryPickerAdapter adapter = new DeleteAccountCountryPickerAdapter(this::onCountryPicked);
|
||||||
|
|
||||||
|
recycler.setAdapter(adapter);
|
||||||
|
|
||||||
|
toolbar.setNavigationOnClickListener(unused -> dismiss());
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(requireActivity()).get(DeleteAccountViewModel.class);
|
||||||
|
viewModel.getFilteredCountries().observe(getViewLifecycleOwner(), adapter::submitList);
|
||||||
|
|
||||||
|
searchFilter.addTextChangedListener(new AfterTextChanged(this::onQueryChanged));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onQueryChanged(@NonNull Editable e) {
|
||||||
|
viewModel.onQueryChanged(e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCountryPicked(@NonNull Country country) {
|
||||||
|
viewModel.onCountrySelected(country.getDisplayName(), country.getCode());
|
||||||
|
dismissAllowingStateLoss();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,288 @@
|
|||||||
|
package org.thoughtcrime.securesms.delete;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.i18n.phonenumbers.AsYouTypeFormatter;
|
||||||
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||||
|
import org.thoughtcrime.securesms.R;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class DeleteAccountFragment extends Fragment {
|
||||||
|
|
||||||
|
private ArrayAdapter<String> countrySpinnerAdapter;
|
||||||
|
private LabeledEditText countryCode;
|
||||||
|
private LabeledEditText number;
|
||||||
|
private AsYouTypeFormatter countryFormatter;
|
||||||
|
private DeleteAccountViewModel viewModel;
|
||||||
|
private DialogInterface deletionProgressDialog;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.delete_account_fragment, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
TextView bullets = view.findViewById(R.id.delete_account_fragment_bullets);
|
||||||
|
Spinner countrySpinner = view.findViewById(R.id.delete_account_fragment_country_spinner);
|
||||||
|
View confirm = view.findViewById(R.id.delete_account_fragment_delete);
|
||||||
|
|
||||||
|
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.getCountryDisplayName().observe(getViewLifecycleOwner(), this::setCountryDisplay);
|
||||||
|
viewModel.getRegionCode().observe(getViewLifecycleOwner(), this::setCountryFormatter);
|
||||||
|
viewModel.getCountryCode().observe(getViewLifecycleOwner(), this::setCountryCode);
|
||||||
|
viewModel.getEvents().observe(getViewLifecycleOwner(), this::handleEvent);
|
||||||
|
|
||||||
|
initializeNumberInput();
|
||||||
|
|
||||||
|
countryCode.getInput().addTextChangedListener(new AfterTextChanged(this::afterCountryCodeChanged));
|
||||||
|
countryCode.getInput().setImeOptions(EditorInfo.IME_ACTION_NEXT);
|
||||||
|
confirm.setOnClickListener(unused -> viewModel.submit());
|
||||||
|
|
||||||
|
bullets.setText(buildBulletsText());
|
||||||
|
initializeSpinner(countrySpinner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__delete_account);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull CharSequence buildBulletsText() {
|
||||||
|
return new SpannableStringBuilder().append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_your_account_info_and_profile_photo)))
|
||||||
|
.append("\n")
|
||||||
|
.append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_all_your_messages)))
|
||||||
|
.append("\n")
|
||||||
|
.append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__remove_you_from_all_signal_groups)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private void initializeSpinner(@NonNull Spinner countrySpinner) {
|
||||||
|
countrySpinnerAdapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item);
|
||||||
|
countrySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
|
||||||
|
countrySpinner.setAdapter(countrySpinnerAdapter);
|
||||||
|
countrySpinner.setOnTouchListener((view, event) -> {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
pickCountry();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
countrySpinner.setOnKeyListener((view, keyCode, event) -> {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && event.getAction() == KeyEvent.ACTION_UP) {
|
||||||
|
pickCountry();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pickCountry() {
|
||||||
|
DeleteAccountCountryPickerFragment.show(requireFragmentManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCountryCode(int countryCode) {
|
||||||
|
this.countryCode.setText(String.valueOf(countryCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCountryDisplay(@NonNull String regionDisplayName) {
|
||||||
|
countrySpinnerAdapter.clear();
|
||||||
|
if (TextUtils.isEmpty(regionDisplayName)) {
|
||||||
|
countrySpinnerAdapter.add(requireContext().getString(R.string.RegistrationActivity_select_your_country));
|
||||||
|
} else {
|
||||||
|
countrySpinnerAdapter.add(regionDisplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCountryFormatter(@Nullable String regionCode) {
|
||||||
|
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
|
||||||
|
|
||||||
|
countryFormatter = regionCode != null ? util.getAsYouTypeFormatter(regionCode) : null;
|
||||||
|
|
||||||
|
reformatText(number.getText());
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(regionCode) && !regionCode.equals("ZZ")) {
|
||||||
|
number.requestFocus();
|
||||||
|
|
||||||
|
int numberLength = number.getText().length();
|
||||||
|
number.getInput().setSelection(numberLength, numberLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long reformatText(Editable s) {
|
||||||
|
if (countryFormatter == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(s)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
countryFormatter.clear();
|
||||||
|
|
||||||
|
String formattedNumber = null;
|
||||||
|
StringBuilder justDigits = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < s.length(); i++) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (Character.isDigit(c)) {
|
||||||
|
formattedNumber = countryFormatter.inputDigit(c);
|
||||||
|
justDigits.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formattedNumber != null && !s.toString().equals(formattedNumber)) {
|
||||||
|
s.replace(0, s.length(), formattedNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (justDigits.length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Long.parseLong(justDigits.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeNumberInput() {
|
||||||
|
EditText numberInput = number.getInput();
|
||||||
|
Long nationalNumber = viewModel.getNationalNumber();
|
||||||
|
|
||||||
|
if (nationalNumber != null) {
|
||||||
|
number.setText(String.valueOf(nationalNumber));
|
||||||
|
} else {
|
||||||
|
number.setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
numberInput.addTextChangedListener(new AfterTextChanged(this::afterNumberChanged));
|
||||||
|
numberInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
|
numberInput.setOnEditorActionListener((v, actionId, event) -> {
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
ViewUtil.hideKeyboard(requireContext(), v);
|
||||||
|
viewModel.submit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void afterCountryCodeChanged(@Nullable Editable s) {
|
||||||
|
if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) {
|
||||||
|
viewModel.onCountrySelected(null, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.onCountrySelected(null, Integer.parseInt(s.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void afterNumberChanged(@Nullable Editable s) {
|
||||||
|
Long number = reformatText(s);
|
||||||
|
|
||||||
|
if (number == null) return;
|
||||||
|
|
||||||
|
viewModel.setNationalNumber(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEvent(@NonNull DeleteAccountViewModel.EventType eventType) {
|
||||||
|
switch (eventType) {
|
||||||
|
case NO_COUNTRY_CODE:
|
||||||
|
Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_country_code, Snackbar.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case NO_NATIONAL_NUMBER:
|
||||||
|
Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_number, Snackbar.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case NOT_A_MATCH:
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setMessage(R.string.DeleteAccountFragment__the_phone_number)
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss())
|
||||||
|
.setCancelable(true)
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
|
case CONFIRM_DELETION:
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.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();
|
||||||
|
})
|
||||||
|
.setCancelable(true)
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
|
case PIN_DELETION_FAILED:
|
||||||
|
case SERVER_DELETION_FAILED:
|
||||||
|
dismissDeletionProgressDialog();
|
||||||
|
showNetworkDeletionFailedDialog();
|
||||||
|
break;
|
||||||
|
case LOCAL_DATA_DELETION_FAILED:
|
||||||
|
dismissDeletionProgressDialog();
|
||||||
|
showLocalDataDeletionFailedDialog();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown error type: " + eventType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismissDeletionProgressDialog() {
|
||||||
|
if (deletionProgressDialog != null) {
|
||||||
|
deletionProgressDialog.dismiss();
|
||||||
|
deletionProgressDialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showLocalDataDeletionFailedDialog() {
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setMessage(R.string.DeleteAccountFragment__failed_to_delete_local_data)
|
||||||
|
.setPositiveButton(R.string.DeleteAccountFragment__launch_app_settings, (dialog, which) -> {
|
||||||
|
Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
|
settingsIntent.setData(Uri.fromParts("package", requireActivity().getPackageName(), null));
|
||||||
|
startActivity(settingsIntent);
|
||||||
|
})
|
||||||
|
.setCancelable(false)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
package org.thoughtcrime.securesms.delete;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
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.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.pin.KbsEnclaves;
|
||||||
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
|
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||||
|
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class DeleteAccountRepository {
|
||||||
|
private static final String TAG = Log.tag(DeleteAccountRepository.class);
|
||||||
|
|
||||||
|
@NonNull List<Country> getAllCountries() {
|
||||||
|
return Stream.of(PhoneNumberUtil.getInstance().getSupportedRegions())
|
||||||
|
.map(DeleteAccountRepository::getCountryForRegion)
|
||||||
|
.sorted(new RegionComparator())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteAccount(@NonNull Runnable onFailureToRemovePin,
|
||||||
|
@NonNull Runnable onFailureToDeleteFromService,
|
||||||
|
@NonNull Runnable onFailureToDeleteLocalData)
|
||||||
|
{
|
||||||
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
|
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();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "deleteAccount: successfully removed pin.");
|
||||||
|
Log.i(TAG, "deleteAccount: attempting to delete account from server...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
ApplicationDependencies.getSignalServiceAccountManager().deleteAccount();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "deleteAccount: failed to delete account from signal service", e);
|
||||||
|
onFailureToDeleteFromService.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "deleteAccount: successfully removed account from server");
|
||||||
|
Log.i(TAG, "deleteAccount: attempting to delete user data and close process...");
|
||||||
|
|
||||||
|
if (!ServiceUtil.getActivityManager(ApplicationDependencies.getApplication()).clearApplicationUserData()) {
|
||||||
|
Log.w(TAG, "deleteAccount: failed to delete user data");
|
||||||
|
onFailureToDeleteLocalData.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull Country getCountryForRegion(@NonNull String region) {
|
||||||
|
return new Country(PhoneNumberFormatter.getRegionDisplayName(region),
|
||||||
|
PhoneNumberUtil.getInstance().getCountryCodeForRegion(region));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RegionComparator implements Comparator<Country> {
|
||||||
|
|
||||||
|
private final Collator collator;
|
||||||
|
|
||||||
|
RegionComparator() {
|
||||||
|
collator = Collator.getInstance();
|
||||||
|
collator.setStrength(Collator.PRIMARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Country lhs, Country rhs) {
|
||||||
|
return collator.compare(lhs.getDisplayName(), rhs.getDisplayName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,175 @@
|
|||||||
|
package org.thoughtcrime.securesms.delete;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
import androidx.lifecycle.Transformations;
|
||||||
|
import androidx.lifecycle.ViewModel;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.registration.viewmodel.NumberViewState;
|
||||||
|
import org.thoughtcrime.securesms.util.DefaultValueLiveData;
|
||||||
|
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||||
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DeleteAccountViewModel extends ViewModel {
|
||||||
|
|
||||||
|
private final DeleteAccountRepository repository;
|
||||||
|
private final List<Country> allCountries;
|
||||||
|
private final LiveData<List<Country>> filteredCountries;
|
||||||
|
private final LiveData<String> regionCode;
|
||||||
|
private final MutableLiveData<Integer> countryCode;
|
||||||
|
private final MutableLiveData<String> countryDisplayName;
|
||||||
|
private final MutableLiveData<Long> nationalNumber;
|
||||||
|
private final MutableLiveData<String> query;
|
||||||
|
private final SingleLiveEvent<EventType> events;
|
||||||
|
private final LiveData<NumberViewState> numberViewState;
|
||||||
|
|
||||||
|
public DeleteAccountViewModel(@NonNull DeleteAccountRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.allCountries = repository.getAllCountries();
|
||||||
|
this.countryCode = new DefaultValueLiveData<>(NumberViewState.INITIAL.getCountryCode());
|
||||||
|
this.nationalNumber = new DefaultValueLiveData<>(NumberViewState.INITIAL.getNationalNumber());
|
||||||
|
this.countryDisplayName = new DefaultValueLiveData<>(NumberViewState.INITIAL.getCountryDisplayName());
|
||||||
|
this.query = new DefaultValueLiveData<>("");
|
||||||
|
this.regionCode = Transformations.map(countryCode, this::mapCountryCodeToRegionCode);
|
||||||
|
this.filteredCountries = Transformations.map(query, q -> Stream.of(allCountries).filter(country -> isMatch(q, country)).toList());
|
||||||
|
|
||||||
|
LiveData<NumberViewState> partialViewState = LiveDataUtil.combineLatest(countryCode,
|
||||||
|
countryDisplayName,
|
||||||
|
DeleteAccountViewModel::getPartialNumberViewState);
|
||||||
|
|
||||||
|
this.numberViewState = LiveDataUtil.combineLatest(partialViewState, nationalNumber, DeleteAccountViewModel::getCompleteNumberViewState);
|
||||||
|
this.events = new SingleLiveEvent<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull LiveData<List<Country>> getFilteredCountries() {
|
||||||
|
return filteredCountries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull LiveData<String> getCountryDisplayName() {
|
||||||
|
return Transformations.distinctUntilChanged(Transformations.map(numberViewState, NumberViewState::getCountryDisplayName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull LiveData<String> getRegionCode() {
|
||||||
|
return Transformations.distinctUntilChanged(regionCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull LiveData<Integer> getCountryCode() {
|
||||||
|
return Transformations.distinctUntilChanged(Transformations.map(numberViewState, NumberViewState::getCountryCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull SingleLiveEvent<EventType> getEvents() {
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable Long getNationalNumber() {
|
||||||
|
Long number = nationalNumber.getValue();
|
||||||
|
if (number == null || number == NumberViewState.INITIAL.getNationalNumber()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onQueryChanged(@NonNull String query) {
|
||||||
|
this.query.setValue(query.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteAccount() {
|
||||||
|
repository.deleteAccount(() -> events.postValue(EventType.PIN_DELETION_FAILED),
|
||||||
|
() -> events.postValue(EventType.SERVER_DELETION_FAILED),
|
||||||
|
() -> events.postValue(EventType.LOCAL_DATA_DELETION_FAILED));
|
||||||
|
}
|
||||||
|
|
||||||
|
void submit() {
|
||||||
|
Integer countryCode = this.countryCode.getValue();
|
||||||
|
Long nationalNumber = this.nationalNumber.getValue();
|
||||||
|
|
||||||
|
if (countryCode == null || countryCode == 0) {
|
||||||
|
events.setValue(EventType.NO_COUNTRY_CODE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nationalNumber == null) {
|
||||||
|
events.setValue(EventType.NO_NATIONAL_NUMBER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Phonenumber.PhoneNumber number = new Phonenumber.PhoneNumber();
|
||||||
|
number.setCountryCode(countryCode);
|
||||||
|
number.setNationalNumber(nationalNumber);
|
||||||
|
|
||||||
|
if (PhoneNumberUtil.getInstance().isNumberMatch(number, Recipient.self().requireE164()) == PhoneNumberUtil.MatchType.EXACT_MATCH) {
|
||||||
|
events.setValue(EventType.CONFIRM_DELETION);
|
||||||
|
} else {
|
||||||
|
events.setValue(EventType.NOT_A_MATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onCountrySelected(@Nullable String countryDisplayName, int countryCode) {
|
||||||
|
if (countryDisplayName != null) {
|
||||||
|
this.countryDisplayName.setValue(countryDisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.countryCode.setValue(countryCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNationalNumber(long nationalNumber) {
|
||||||
|
this.nationalNumber.setValue(nationalNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull String mapCountryCodeToRegionCode(int countryCode) {
|
||||||
|
return PhoneNumberUtil.getInstance().getRegionCodeForCountryCode(countryCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull NumberViewState getPartialNumberViewState(int countryCode, @Nullable String countryDisplayName) {
|
||||||
|
return new NumberViewState.Builder().countryCode(countryCode).selectedCountryDisplayName(countryDisplayName).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull NumberViewState getCompleteNumberViewState(@NonNull NumberViewState partial, long nationalNumber) {
|
||||||
|
return partial.toBuilder().nationalNumber(nationalNumber).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMatch(@NonNull String query, @NonNull Country country) {
|
||||||
|
if (TextUtils.isEmpty(query)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return country.getNormalizedDisplayName().contains(query.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public Factory(DeleteAccountRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||||
|
return modelClass.cast(new DeleteAccountViewModel(repository));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.preferences;
|
package org.thoughtcrime.securesms.preferences;
|
||||||
|
|
||||||
|
import android.app.ActionBar;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@@ -8,11 +9,14 @@ import android.net.Uri;
|
|||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.preference.CheckBoxPreference;
|
import androidx.preference.CheckBoxPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
@@ -24,11 +28,13 @@ import org.thoughtcrime.securesms.BuildConfig;
|
|||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
|
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
|
||||||
|
import org.thoughtcrime.securesms.delete.DeleteAccountFragment;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
|
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity;
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||||
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
||||||
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||||
@@ -45,6 +51,7 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
|
|||||||
private static final String SUBMIT_DEBUG_LOG_PREF = "pref_submit_debug_logs";
|
private static final String SUBMIT_DEBUG_LOG_PREF = "pref_submit_debug_logs";
|
||||||
private static final String INTERNAL_PREF = "pref_internal";
|
private static final String INTERNAL_PREF = "pref_internal";
|
||||||
private static final String ADVANCED_PIN_PREF = "pref_advanced_pin_settings";
|
private static final String ADVANCED_PIN_PREF = "pref_advanced_pin_settings";
|
||||||
|
private static final String DELETE_ACCOUNT = "pref_delete_account";
|
||||||
|
|
||||||
private static final int PICK_IDENTITY_CONTACT = 1;
|
private static final int PICK_IDENTITY_CONTACT = 1;
|
||||||
|
|
||||||
@@ -60,12 +67,7 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
|
|||||||
|
|
||||||
Preference pinSettings = this.findPreference(ADVANCED_PIN_PREF);
|
Preference pinSettings = this.findPreference(ADVANCED_PIN_PREF);
|
||||||
pinSettings.setOnPreferenceClickListener(preference -> {
|
pinSettings.setOnPreferenceClickListener(preference -> {
|
||||||
requireActivity().getSupportFragmentManager()
|
getApplicationPreferencesActivity().pushFragment(new AdvancedPreferenceFragment());
|
||||||
.beginTransaction()
|
|
||||||
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
|
|
||||||
.replace(android.R.id.content, new AdvancedPinPreferenceFragment())
|
|
||||||
.addToBackStack(null)
|
|
||||||
.commit();
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -73,17 +75,32 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
|
|||||||
internalPreference.setVisible(FeatureFlags.internalUser());
|
internalPreference.setVisible(FeatureFlags.internalUser());
|
||||||
internalPreference.setOnPreferenceClickListener(preference -> {
|
internalPreference.setOnPreferenceClickListener(preference -> {
|
||||||
if (FeatureFlags.internalUser()) {
|
if (FeatureFlags.internalUser()) {
|
||||||
requireActivity().getSupportFragmentManager()
|
getApplicationPreferencesActivity().pushFragment(new InternalOptionsPreferenceFragment());
|
||||||
.beginTransaction()
|
|
||||||
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
|
|
||||||
.replace(android.R.id.content, new InternalOptionsPreferenceFragment())
|
|
||||||
.addToBackStack(null)
|
|
||||||
.commit();
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Preference deleteAccount = this.findPreference(DELETE_ACCOUNT);
|
||||||
|
deleteAccount.setOnPreferenceClickListener(preference -> {
|
||||||
|
getApplicationPreferencesActivity().pushFragment(new DeleteAccountFragment());
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
view.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.signal_background_tertiary));
|
||||||
|
|
||||||
|
View list = view.findViewById(R.id.recycler_view);
|
||||||
|
ViewGroup.LayoutParams params = list.getLayoutParams();
|
||||||
|
|
||||||
|
params.height = ActionBar.LayoutParams.WRAP_CONTENT;
|
||||||
|
list.setLayoutParams(params);
|
||||||
|
list.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.signal_background_primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -94,7 +111,7 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__advanced);
|
getApplicationPreferencesActivity().getSupportActionBar().setTitle(R.string.preferences__advanced);
|
||||||
|
|
||||||
initializePushMessagingToggle();
|
initializePushMessagingToggle();
|
||||||
}
|
}
|
||||||
@@ -109,6 +126,10 @@ public class AdvancedPreferenceFragment extends CorrectedPreferenceFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @NonNull ApplicationPreferencesActivity getApplicationPreferencesActivity() {
|
||||||
|
return (ApplicationPreferencesActivity) requireActivity();
|
||||||
|
}
|
||||||
|
|
||||||
private void initializePushMessagingToggle() {
|
private void initializePushMessagingToggle() {
|
||||||
CheckBoxPreference preference = (CheckBoxPreference)this.findPreference(PUSH_MESSAGING_PREF);
|
CheckBoxPreference preference = (CheckBoxPreference)this.findPreference(PUSH_MESSAGING_PREF);
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import android.os.Build;
|
|||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.style.AbsoluteSizeSpan;
|
import android.text.style.AbsoluteSizeSpan;
|
||||||
|
import android.text.style.BulletSpan;
|
||||||
import android.text.style.DynamicDrawableSpan;
|
import android.text.style.DynamicDrawableSpan;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
@@ -50,6 +51,12 @@ public class SpanUtil {
|
|||||||
return spannable;
|
return spannable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @NonNull CharSequence bullet(@NonNull CharSequence sequence) {
|
||||||
|
SpannableString spannable = new SpannableString(sequence);
|
||||||
|
spannable.setSpan(new BulletSpan(), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
return spannable;
|
||||||
|
}
|
||||||
|
|
||||||
public static CharSequence buildImageSpan(@NonNull Drawable drawable) {
|
public static CharSequence buildImageSpan(@NonNull Drawable drawable) {
|
||||||
SpannableString imageSpan = new SpannableString(" ");
|
SpannableString imageSpan = new SpannableString(" ");
|
||||||
|
|
||||||
|
9
app/src/main/res/drawable-night/ic_warning_40.xml
Normal file
9
app/src/main/res/drawable-night/ic_warning_40.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18.75,5.44a1.32,1.32 0,0 1,2.5 0l16.5,28.49c0.69,1.19 0.13,2.16 -1.25,2.16H3.5c-1.38,0 -1.94,-1 -1.25,-2.16ZM19,25.5h2l0.5,-10.58h-3ZM22,30a2,2 0,1 0,-2 2A2,2 0,0 0,22 30Z"/>
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_warning_40.xml
Normal file
9
app/src/main/res/drawable/ic_warning_40.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18.5,14.92h3L21,25.5H19ZM22,30a2,2 0,1 0,-2 2A2,2 0,0 0,22 30ZM20,6.27 L3.6,34.59H36.4L20,6.27m0,-1.73a1.52,1.52 0,0 1,1.25 0.9l16.5,28.49c0.69,1.19 0.13,2.16 -1.25,2.16H3.5c-1.38,0 -1.94,-1 -1.25,-2.16L18.75,5.44A1.52,1.52 0,0 1,20 4.54Z"/>
|
||||||
|
</vector>
|
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/text1"
|
||||||
|
style="?attr/spinnerDropDownItemStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/listPreferredItemHeight"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:singleLine="true" />
|
33
app/src/main/res/layout/delete_account_country_picker.xml
Normal file
33
app/src/main/res/layout/delete_account_country_picker.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/delete_account_country_picker_toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:contentInsetEnd="16dp"
|
||||||
|
app:contentInsetStartWithNavigation="0dp"
|
||||||
|
app:navigationIcon="@drawable/ic_arrow_left_24">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/delete_account_country_picker_filter"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/DeleteAccountCountryPickerFragment__search_countries"
|
||||||
|
android:inputType="textCapWords" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/delete_account_country_picker_recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/support_simple_spinner_dropdown_item" />
|
||||||
|
</LinearLayout>
|
123
app/src/main/res/layout/delete_account_fragment.xml
Normal file
123
app/src/main/res/layout/delete_account_fragment.xml
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/delete_account_fragment_warning"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_warning_40"
|
||||||
|
app:tint="@color/signal_alert_primary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/delete_account_fragment_notice"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:text="@string/DeleteAccountFragment__deleting_your_account_will"
|
||||||
|
android:textAppearance="@style/Signal.Text.Body"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_warning" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/delete_account_fragment_bullets"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body2"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_notice"
|
||||||
|
tools:text="Some\nbullets\nhere" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/delete_account_fragment_enter_phone_number"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:text="@string/DeleteAccountFragment__enter_your_phone_number"
|
||||||
|
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_bullets" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/delete_account_fragment_country_spinner_frame"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:background="@drawable/labeled_edit_text_background_inactive"
|
||||||
|
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/delete_account_fragment_enter_phone_number">
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/delete_account_fragment_country_spinner"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/delete_account_fragment_linearLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:layoutDirection="ltr"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/delete_account_fragment_country_spinner_frame">
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.LabeledEditText
|
||||||
|
android:id="@+id/delete_account_fragment_country_code"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
app:labeledEditText_background="@color/white"
|
||||||
|
app:labeledEditText_textLayout="@layout/country_code_text" />
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.LabeledEditText
|
||||||
|
android:id="@+id/delete_account_fragment_number"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="3"
|
||||||
|
app:labeledEditText_background="?android:windowBackground"
|
||||||
|
app:labeledEditText_label="@string/RegistrationActivity_phone_number_description"
|
||||||
|
app:labeledEditText_textLayout="@layout/phone_text" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
style="@style/Signal.Widget.Button.Large.Danger"
|
||||||
|
android:id="@+id/delete_account_fragment_delete"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:text="@string/DeleteAccountFragment__delete_account"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/delete_account_fragment_linearLayout" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
19
app/src/main/res/layout/preference_danger.xml
Normal file
19
app/src/main/res/layout/preference_danger.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="15dp"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingEnd="15dp"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:fadingEdge="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/Signal.Text.Body"
|
||||||
|
android:textColor="@color/signal_alert_primary"
|
||||||
|
tools:text="Danger, danger! High voltage!" />
|
@@ -17,6 +17,10 @@
|
|||||||
<item name="android:minHeight">48dp</item>
|
<item name="android:minHeight">48dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Signal.Widget.Button.Large.Danger" parent="@style/Signal.Widget.Button.Large.Primary">
|
||||||
|
<item name="backgroundTint">@color/signal_alert_primary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="Signal.Widget.Button.Medium.Primary" parent="@style/Signal.Widget.Button.Base.Primary">
|
<style name="Signal.Widget.Button.Medium.Primary" parent="@style/Signal.Widget.Button.Base.Primary">
|
||||||
<item name="android:minHeight">36dp</item>
|
<item name="android:minHeight">36dp</item>
|
||||||
<item name="cornerRadius">8dp</item>
|
<item name="cornerRadius">8dp</item>
|
||||||
@@ -70,4 +74,8 @@
|
|||||||
<style name="Signal.DayNight.Toolbar.Overflow" parent="@style/Signal.Toolbar.Overflow.Light" />
|
<style name="Signal.DayNight.Toolbar.Overflow" parent="@style/Signal.Toolbar.Overflow.Light" />
|
||||||
|
|
||||||
<style name="Signal.DayNight.TitleTextStyle" parent="@style/TextSecure.TitleTextStyle" />
|
<style name="Signal.DayNight.TitleTextStyle" parent="@style/TextSecure.TitleTextStyle" />
|
||||||
|
|
||||||
|
<style name="Signal.DayNight.Dialog.FullScreen" parent="Signal.DayNight">
|
||||||
|
<item name="android:windowAnimationStyle">@style/TextSecure.Animation.FullScreenDialog</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -2357,6 +2357,7 @@
|
|||||||
<string name="preferences__advanced_pin_settings">Advanced PIN settings</string>
|
<string name="preferences__advanced_pin_settings">Advanced PIN settings</string>
|
||||||
<string name="preferences__free_private_messages_and_calls">Free private messages and calls to Signal users</string>
|
<string name="preferences__free_private_messages_and_calls">Free private messages and calls to Signal users</string>
|
||||||
<string name="preferences__submit_debug_log">Submit debug log</string>
|
<string name="preferences__submit_debug_log">Submit debug log</string>
|
||||||
|
<string name="preferences__delete_account">Delete account</string>
|
||||||
<string name="preferences__support_wifi_calling">\'WiFi Calling\' compatibility mode</string>
|
<string name="preferences__support_wifi_calling">\'WiFi Calling\' compatibility mode</string>
|
||||||
<string name="preferences__enable_if_your_device_supports_sms_mms_delivery_over_wifi">Enable if your device uses SMS/MMS delivery over WiFi (only enable when \'WiFi Calling\' is enabled on your device)</string>
|
<string name="preferences__enable_if_your_device_supports_sms_mms_delivery_over_wifi">Enable if your device uses SMS/MMS delivery over WiFi (only enable when \'WiFi Calling\' is enabled on your device)</string>
|
||||||
<string name="preferences__incognito_keyboard">Incognito keyboard</string>
|
<string name="preferences__incognito_keyboard">Incognito keyboard</string>
|
||||||
@@ -2987,6 +2988,25 @@
|
|||||||
<string name="CallParticipantsListUpdatePopupWindow__you_on_another_device">You (on another device)</string>
|
<string name="CallParticipantsListUpdatePopupWindow__you_on_another_device">You (on another device)</string>
|
||||||
<string name="CallParticipantsListUpdatePopupWindow__s_on_another_device">%1$s (on another device)</string>
|
<string name="CallParticipantsListUpdatePopupWindow__s_on_another_device">%1$s (on another device)</string>
|
||||||
|
|
||||||
|
<!-- DeleteAccountFragment -->
|
||||||
|
<string name="DeleteAccountFragment__deleting_your_account_will">Deleting your account will:</string>
|
||||||
|
<string name="DeleteAccountFragment__enter_your_phone_number">Enter your phone number</string>
|
||||||
|
<string name="DeleteAccountFragment__delete_account">Delete account</string>
|
||||||
|
<string name="DeleteAccountFragment__delete_your_account_info_and_profile_photo">Delete your account info and profile photo</string>
|
||||||
|
<string name="DeleteAccountFragment__delete_all_your_messages">Delete all your messages</string>
|
||||||
|
<string name="DeleteAccountFragment__remove_you_from_all_signal_groups">Remove you from all Signal groups</string>
|
||||||
|
<string name="DeleteAccountFragment__no_country_code">No country code specified</string>
|
||||||
|
<string name="DeleteAccountFragment__no_number">No number specified</string>
|
||||||
|
<string name="DeleteAccountFragment__the_phone_number">The phone number you entered doesn\'t match your account\'s.</string>
|
||||||
|
<string name="DeleteAccountFragment__are_you_sure">Are you sure you want to delete your account?</string>
|
||||||
|
<string name="DeleteAccountFragment__this_will_delete_your_signal_account">This will delete your Signal account and reset the application. The app will close after the process is complete.</string>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- DeleteAccountCountryPickerFragment -->
|
||||||
|
<string name="DeleteAccountCountryPickerFragment__search_countries">Search Countries</string>
|
||||||
|
|
||||||
<!-- EOF -->
|
<!-- EOF -->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -39,14 +39,6 @@
|
|||||||
<item name="android:windowBackground">@drawable/dialog_background</item>
|
<item name="android:windowBackground">@drawable/dialog_background</item>
|
||||||
<item name="android:textColor">@null</item>
|
<item name="android:textColor">@null</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TextSecure.DarkTheme.FullScreenDialog">
|
|
||||||
<item name="android:windowAnimationStyle">@style/TextSecure.Animation.FullScreenDialog</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextSecure.LightTheme.FullScreenDialog">
|
|
||||||
<item name="android:windowAnimationStyle">@style/TextSecure.Animation.FullScreenDialog</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TextSecure.Animation.FullScreenDialog" parent="@android:style/Animation.Activity">
|
<style name="TextSecure.Animation.FullScreenDialog" parent="@android:style/Animation.Activity">
|
||||||
<item name="android:windowEnterAnimation">@anim/fade_scale_in</item>
|
<item name="android:windowEnterAnimation">@anim/fade_scale_in</item>
|
||||||
|
@@ -27,4 +27,11 @@
|
|||||||
android:key="pref_submit_debug_logs"
|
android:key="pref_submit_debug_logs"
|
||||||
android:title="@string/preferences__submit_debug_log"/>
|
android:title="@string/preferences__submit_debug_log"/>
|
||||||
|
|
||||||
|
<PreferenceCategory android:layout="@layout/preference_divider"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:layout="@layout/preference_danger"
|
||||||
|
android:key="pref_delete_account"
|
||||||
|
android:title="@string/preferences__delete_account" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@@ -682,6 +682,10 @@ public class SignalServiceAccountManager {
|
|||||||
this.pushServiceSocket.deleteUsername();
|
this.pushServiceSocket.deleteUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteAccount() throws IOException {
|
||||||
|
this.pushServiceSocket.deleteAccount();
|
||||||
|
}
|
||||||
|
|
||||||
public void setSoTimeoutMillis(long soTimeoutMillis) {
|
public void setSoTimeoutMillis(long soTimeoutMillis) {
|
||||||
this.pushServiceSocket.setSoTimeoutMillis(soTimeoutMillis);
|
this.pushServiceSocket.setSoTimeoutMillis(soTimeoutMillis);
|
||||||
}
|
}
|
||||||
|
@@ -164,6 +164,7 @@ public class PushServiceSocket {
|
|||||||
private static final String WHO_AM_I = "/v1/accounts/whoami";
|
private static final String WHO_AM_I = "/v1/accounts/whoami";
|
||||||
private static final String SET_USERNAME_PATH = "/v1/accounts/username/%s";
|
private static final String SET_USERNAME_PATH = "/v1/accounts/username/%s";
|
||||||
private static final String DELETE_USERNAME_PATH = "/v1/accounts/username";
|
private static final String DELETE_USERNAME_PATH = "/v1/accounts/username";
|
||||||
|
private static final String DELETE_ACCOUNT_PATH = "/v1/accounts/me";
|
||||||
|
|
||||||
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
|
private static final String PREKEY_METADATA_PATH = "/v2/keys/";
|
||||||
private static final String PREKEY_PATH = "/v2/keys/%s";
|
private static final String PREKEY_PATH = "/v2/keys/%s";
|
||||||
@@ -749,6 +750,10 @@ public class PushServiceSocket {
|
|||||||
makeServiceRequest(DELETE_USERNAME_PATH, "DELETE", null);
|
makeServiceRequest(DELETE_USERNAME_PATH, "DELETE", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteAccount() throws IOException {
|
||||||
|
makeServiceRequest(DELETE_ACCOUNT_PATH, "DELETE", null);
|
||||||
|
}
|
||||||
|
|
||||||
public List<ContactTokenDetails> retrieveDirectory(Set<String> contactTokens)
|
public List<ContactTokenDetails> retrieveDirectory(Set<String> contactTokens)
|
||||||
throws NonSuccessfulResponseCodeException, PushNetworkException
|
throws NonSuccessfulResponseCodeException, PushNetworkException
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user