That fuck shit the fascists are using
1package org.tm.archive.delete;
2
3import android.annotation.SuppressLint;
4import android.app.AlertDialog;
5import android.content.DialogInterface;
6import android.content.Intent;
7import android.graphics.Color;
8import android.net.Uri;
9import android.os.Bundle;
10import android.provider.Settings;
11import android.text.Editable;
12import android.text.SpannableStringBuilder;
13import android.text.TextUtils;
14import android.view.KeyEvent;
15import android.view.LayoutInflater;
16import android.view.MotionEvent;
17import android.view.View;
18import android.view.ViewGroup;
19import android.view.inputmethod.EditorInfo;
20import android.widget.ArrayAdapter;
21import android.widget.EditText;
22import android.widget.Spinner;
23import android.widget.TextView;
24
25import androidx.annotation.NonNull;
26import androidx.annotation.Nullable;
27import androidx.fragment.app.Fragment;
28import androidx.lifecycle.ViewModelProvider;
29
30import com.google.android.material.dialog.MaterialAlertDialogBuilder;
31import com.google.android.material.snackbar.Snackbar;
32import com.google.i18n.phonenumbers.AsYouTypeFormatter;
33import com.google.i18n.phonenumbers.PhoneNumberUtil;
34
35import org.tm.archive.R;
36import org.tm.archive.components.LabeledEditText;
37import org.tm.archive.util.SpanUtil;
38import org.tm.archive.util.ViewUtil;
39import org.tm.archive.util.text.AfterTextChanged;
40
41import java.util.Optional;
42
43
44public class DeleteAccountFragment extends Fragment {
45
46 private ArrayAdapter<String> countrySpinnerAdapter;
47 private TextView bullets;
48 private LabeledEditText countryCode;
49 private LabeledEditText number;
50 private AsYouTypeFormatter countryFormatter;
51 private DeleteAccountViewModel viewModel;
52 private DeleteAccountProgressDialog deletionProgressDialog;
53
54 @Override
55 public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
56 return inflater.inflate(R.layout.delete_account_fragment, container, false);
57 }
58
59 @Override
60 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
61 Spinner countrySpinner = view.findViewById(R.id.delete_account_fragment_country_spinner);
62 View confirm = view.findViewById(R.id.delete_account_fragment_delete);
63
64 bullets = view.findViewById(R.id.delete_account_fragment_bullets);
65 countryCode = view.findViewById(R.id.delete_account_fragment_country_code);
66 number = view.findViewById(R.id.delete_account_fragment_number);
67
68 viewModel = new ViewModelProvider(requireActivity(), new DeleteAccountViewModel.Factory(new DeleteAccountRepository())).get(DeleteAccountViewModel.class);
69 viewModel.getCountryDisplayName().observe(getViewLifecycleOwner(), this::setCountryDisplay);
70 viewModel.getRegionCode().observe(getViewLifecycleOwner(), this::handleRegionUpdated);
71 viewModel.getEvents().observe(getViewLifecycleOwner(), this::handleEvent);
72 viewModel.getWalletBalance().observe(getViewLifecycleOwner(), this::updateBullets);
73
74 initializeNumberInput();
75
76 countryCode.getInput().addTextChangedListener(new AfterTextChanged(this::afterCountryCodeChanged));
77 countryCode.getInput().setImeOptions(EditorInfo.IME_ACTION_NEXT);
78 confirm.setOnClickListener(unused -> viewModel.submit());
79
80 initializeSpinner(countrySpinner);
81 }
82
83 private void updateBullets(@NonNull Optional<String> formattedBalance) {
84 bullets.setText(buildBulletsText(formattedBalance));
85 }
86
87 private @NonNull CharSequence buildBulletsText(@NonNull Optional<String> formattedBalance) {
88 SpannableStringBuilder builder = new SpannableStringBuilder().append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_your_account_info_and_profile_photo),8))
89 .append("\n")
90 .append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_all_your_messages),8));
91
92 if (formattedBalance.isPresent()) {
93 builder.append("\n");
94 builder.append(SpanUtil.bullet(getString(R.string.DeleteAccountFragment__delete_s_in_your_payments_account, formattedBalance.get()),8));
95 }
96
97 return builder;
98 }
99
100 @SuppressLint("ClickableViewAccessibility")
101 private void initializeSpinner(@NonNull Spinner countrySpinner) {
102 countrySpinnerAdapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item);
103 countrySpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
104
105 countrySpinner.setAdapter(countrySpinnerAdapter);
106 countrySpinner.setOnTouchListener((view, event) -> {
107 if (event.getAction() == MotionEvent.ACTION_UP) {
108 pickCountry();
109 }
110 return true;
111 });
112 countrySpinner.setOnKeyListener((view, keyCode, event) -> {
113 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && event.getAction() == KeyEvent.ACTION_UP) {
114 pickCountry();
115 return true;
116 }
117 return false;
118 });
119 }
120
121 private void pickCountry() {
122 countryCode.clearFocus();
123 DeleteAccountCountryPickerFragment.show(requireFragmentManager());
124 }
125
126 private void setCountryDisplay(@NonNull String regionDisplayName) {
127 countrySpinnerAdapter.clear();
128 if (TextUtils.isEmpty(regionDisplayName)) {
129 countrySpinnerAdapter.add(requireContext().getString(R.string.RegistrationActivity_select_your_country));
130 } else {
131 countrySpinnerAdapter.add(regionDisplayName);
132 }
133 }
134
135 private void handleRegionUpdated(@Nullable String regionCode) {
136 PhoneNumberUtil util = PhoneNumberUtil.getInstance();
137
138 countryFormatter = regionCode != null ? util.getAsYouTypeFormatter(regionCode) : null;
139
140 reformatText(number.getText());
141
142 if (!TextUtils.isEmpty(regionCode) && !"ZZ".equals(regionCode)) {
143 number.requestFocus();
144
145 int numberLength = number.getText().length();
146 number.getInput().setSelection(numberLength, numberLength);
147
148 countryCode.setText(String.valueOf(util.getCountryCodeForRegion(regionCode)));
149 }
150 }
151
152 private Long reformatText(Editable s) {
153 if (countryFormatter == null) {
154 return null;
155 }
156
157 if (TextUtils.isEmpty(s)) {
158 return null;
159 }
160
161 countryFormatter.clear();
162
163 String formattedNumber = null;
164 StringBuilder justDigits = new StringBuilder();
165
166 for (int i = 0; i < s.length(); i++) {
167 char c = s.charAt(i);
168 if (Character.isDigit(c)) {
169 formattedNumber = countryFormatter.inputDigit(c);
170 justDigits.append(c);
171 }
172 }
173
174 if (formattedNumber != null && !s.toString().equals(formattedNumber)) {
175 s.replace(0, s.length(), formattedNumber);
176 }
177
178 if (justDigits.length() == 0) {
179 return null;
180 }
181
182 return Long.parseLong(justDigits.toString());
183 }
184
185 private void initializeNumberInput() {
186 EditText numberInput = number.getInput();
187 Long nationalNumber = viewModel.getNationalNumber();
188
189 if (nationalNumber != null) {
190 number.setText(String.valueOf(nationalNumber));
191 } else {
192 number.setText("");
193 }
194
195 numberInput.addTextChangedListener(new AfterTextChanged(this::afterNumberChanged));
196 numberInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
197 numberInput.setOnEditorActionListener((v, actionId, event) -> {
198 if (actionId == EditorInfo.IME_ACTION_DONE) {
199 ViewUtil.hideKeyboard(requireContext(), v);
200 viewModel.submit();
201 return true;
202 }
203 return false;
204 });
205 }
206
207 private void afterCountryCodeChanged(@Nullable Editable s) {
208 if (TextUtils.isEmpty(s) || !TextUtils.isDigitsOnly(s)) {
209 viewModel.onCountrySelected(0);
210 return;
211 }
212
213 viewModel.onCountrySelected(Integer.parseInt(s.toString()));
214 }
215
216 private void afterNumberChanged(@Nullable Editable s) {
217 Long number = reformatText(s);
218
219 if (number == null) return;
220
221 viewModel.setNationalNumber(number);
222 }
223
224 private void handleEvent(@NonNull DeleteAccountEvent deleteAccountEvent) {
225 switch (deleteAccountEvent.getType()) {
226 case NO_COUNTRY_CODE:
227 Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_country_code, Snackbar.LENGTH_SHORT).show();
228 break;
229 case NO_NATIONAL_NUMBER:
230 Snackbar.make(requireView(), R.string.DeleteAccountFragment__no_number, Snackbar.LENGTH_SHORT).show();
231 break;
232 case NOT_A_MATCH:
233 new MaterialAlertDialogBuilder(requireContext())
234 .setMessage(R.string.DeleteAccountFragment__the_phone_number)
235 .setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss())
236 .setCancelable(true)
237 .show();
238 break;
239 case CONFIRM_DELETION:
240 new MaterialAlertDialogBuilder(requireContext())
241 .setTitle(R.string.DeleteAccountFragment__are_you_sure)
242 .setMessage(R.string.DeleteAccountFragment__this_will_delete_your_signal_account)
243 .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
244 .setPositiveButton(R.string.DeleteAccountFragment__delete_account, this::handleDeleteAccountConfirmation)
245 .setCancelable(true)
246 .show();
247 break;
248 case LEAVE_GROUPS_FAILED:
249 case PIN_DELETION_FAILED:
250 case SERVER_DELETION_FAILED:
251 case CANCEL_SUBSCRIPTION_FAILED:
252 dismissDeletionProgressDialog();
253 showNetworkDeletionFailedDialog();
254 break;
255 case LOCAL_DATA_DELETION_FAILED:
256 dismissDeletionProgressDialog();
257 showLocalDataDeletionFailedDialog();
258 break;
259 case LEAVE_GROUPS_PROGRESS:
260 ensureDeletionProgressDialog();
261 deletionProgressDialog.presentLeavingGroups((DeleteAccountEvent.LeaveGroupsProgress) deleteAccountEvent);
262 break;
263 case LEAVE_GROUPS_FINISHED:
264 ensureDeletionProgressDialog();
265 deletionProgressDialog.presentDeletingAccount();
266 case CANCELING_SUBSCRIPTION:
267 ensureDeletionProgressDialog();
268 deletionProgressDialog.presentCancelingSubscription();
269 break;
270 default:
271 throw new IllegalStateException("Unknown error type: " + deleteAccountEvent);
272 }
273 }
274
275 private void dismissDeletionProgressDialog() {
276 if (deletionProgressDialog != null) {
277 deletionProgressDialog.dismiss();
278 deletionProgressDialog = null;
279 }
280 }
281
282 private void showNetworkDeletionFailedDialog() {
283 new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.DeleteAccountFragment__account_not_deleted)
284 .setMessage(R.string.DeleteAccountFragment__there_was_a_problem)
285 .setPositiveButton(android.R.string.ok, this::handleDeleteAccountConfirmation)
286 .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
287 .setCancelable(true)
288 .show();
289 }
290
291 private void showLocalDataDeletionFailedDialog() {
292 new MaterialAlertDialogBuilder(requireContext())
293 .setMessage(R.string.DeleteAccountFragment__failed_to_delete_local_data)
294 .setPositiveButton(R.string.DeleteAccountFragment__launch_app_settings, (dialog, which) -> {
295 Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
296 settingsIntent.setData(Uri.fromParts("package", requireActivity().getPackageName(), null));
297 startActivity(settingsIntent);
298 })
299 .setCancelable(false)
300 .show();
301 }
302
303 private void handleDeleteAccountConfirmation(DialogInterface dialog, int which) {
304 dialog.dismiss();
305 ensureDeletionProgressDialog();
306 viewModel.deleteAccount();
307 }
308
309 private void ensureDeletionProgressDialog() {
310 if (deletionProgressDialog == null) {
311 deletionProgressDialog = DeleteAccountProgressDialog.show(requireContext());
312 }
313 }
314}