That fuck shit the fascists are using
at master 314 lines 12 kB view raw
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}