That fuck shit the fascists are using
at master 263 lines 9.9 kB view raw
1package org.tm.archive.help; 2 3import android.content.Intent; 4import android.net.Uri; 5import android.os.Bundle; 6import android.view.LayoutInflater; 7import android.view.View; 8import android.view.ViewGroup; 9import android.view.animation.AnimationUtils; 10import android.widget.AdapterView; 11import android.widget.ArrayAdapter; 12import android.widget.CheckBox; 13import android.widget.EditText; 14import android.widget.Spinner; 15import android.widget.Toast; 16 17import androidx.annotation.IdRes; 18import androidx.annotation.NonNull; 19import androidx.annotation.Nullable; 20import androidx.annotation.StringRes; 21import androidx.lifecycle.ViewModelProvider; 22 23import com.annimon.stream.Stream; 24 25import org.signal.core.util.ResourceUtil; 26import org.tm.archive.LoggingFragment; 27import org.tm.archive.R; 28import org.tm.archive.components.emoji.EmojiImageView; 29import org.tm.archive.util.CommunicationActions; 30import org.tm.archive.util.SupportEmailUtil; 31import org.tm.archive.util.Util; 32import org.tm.archive.util.text.AfterTextChanged; 33import org.tm.archive.util.views.CircularProgressMaterialButton; 34 35import java.util.ArrayList; 36import java.util.List; 37 38public class HelpFragment extends LoggingFragment { 39 40 public static final String START_CATEGORY_INDEX = "start_category_index"; 41 public static final int PAYMENT_INDEX = 6; 42 public static final int DONATION_INDEX = 7; 43 public static final int SMS_EXPORT_INDEX = 8; 44 45 private EditText problem; 46 private CheckBox includeDebugLogs; 47 private View debugLogInfo; 48 private View faq; 49 private CircularProgressMaterialButton next; 50 private View toaster; 51 private List<EmojiImageView> emoji; 52 private HelpViewModel helpViewModel; 53 private Spinner categorySpinner; 54 private ArrayAdapter<CharSequence> categoryAdapter; 55 56 @Override 57 public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 58 return inflater.inflate(R.layout.help_fragment, container, false); 59 } 60 61 @Override 62 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 63 initializeViewModels(); 64 initializeViews(view); 65 initializeListeners(); 66 initializeObservers(); 67 } 68 69 @Override 70 public void onResume() { 71 super.onResume(); 72 73 next.cancelSpinning(); 74 problem.setEnabled(true); 75 } 76 77 private void initializeViewModels() { 78 helpViewModel = new ViewModelProvider(this).get(HelpViewModel.class); 79 } 80 81 private void initializeViews(@NonNull View view) { 82 problem = view.findViewById(R.id.help_fragment_problem); 83 includeDebugLogs = view.findViewById(R.id.help_fragment_debug); 84 debugLogInfo = view.findViewById(R.id.help_fragment_debug_info); 85 faq = view.findViewById(R.id.help_fragment_faq); 86 next = view.findViewById(R.id.help_fragment_next); 87 toaster = view.findViewById(R.id.help_fragment_next_toaster); 88 categorySpinner = view.findViewById(R.id.help_fragment_category); 89 emoji = new ArrayList<>(Feeling.values().length); 90 91 for (Feeling feeling : Feeling.values()) { 92 EmojiImageView emojiView = view.findViewById(feeling.getViewId()); 93 emojiView.setImageEmoji(feeling.getEmojiCode()); 94 emoji.add(view.findViewById(feeling.getViewId())); 95 } 96 97 categoryAdapter = ArrayAdapter.createFromResource(requireContext(), R.array.HelpFragment__categories_5, android.R.layout.simple_spinner_item); 98 categoryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 99 100 categorySpinner.setAdapter(categoryAdapter); 101 102 Bundle args = getArguments(); 103 if (args != null) { 104 categorySpinner.setSelection(Util.clamp(args.getInt(START_CATEGORY_INDEX, 0), 0, categorySpinner.getCount() - 1)); 105 } 106 } 107 108 private void initializeListeners() { 109 problem.addTextChangedListener(new AfterTextChanged(e -> helpViewModel.onProblemChanged(e.toString()))); 110 Stream.of(emoji).forEach(view -> view.setOnClickListener(this::handleEmojiClicked)); 111 faq.setOnClickListener(v -> launchFaq()); 112 debugLogInfo.setOnClickListener(v -> launchDebugLogInfo()); 113 next.setOnClickListener(v -> submitForm()); 114 toaster.setOnClickListener(v -> { 115 if (helpViewModel.getCategoryIndex() == 0) { 116 categorySpinner.startAnimation(AnimationUtils.loadAnimation(requireContext(), R.anim.shake_horizontal)); 117 } 118 119 Toast.makeText(requireContext(), R.string.HelpFragment__please_be_as_descriptive_as_possible, Toast.LENGTH_LONG).show(); 120 }); 121 categorySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 122 @Override 123 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 124 helpViewModel.onCategorySelected(position); 125 } 126 127 @Override 128 public void onNothingSelected(AdapterView<?> parent) { 129 } 130 }); 131 } 132 133 private void initializeObservers() { 134 //noinspection CodeBlock2Expr 135 helpViewModel.isFormValid().observe(getViewLifecycleOwner(), isValid -> { 136 next.setEnabled(isValid); 137 toaster.setVisibility(isValid ? View.GONE : View.VISIBLE); 138 }); 139 } 140 141 private void handleEmojiClicked(@NonNull View clicked) { 142 if (clicked.isSelected()) { 143 clicked.setSelected(false); 144 } else { 145 Stream.of(emoji).forEach(view -> view.setSelected(false)); 146 clicked.setSelected(true); 147 } 148 } 149 150 private void launchFaq() { 151 Uri data = Uri.parse(getString(R.string.HelpFragment__link__faq)); 152 Intent intent = new Intent(Intent.ACTION_VIEW, data); 153 154 startActivity(intent); 155 } 156 157 private void launchDebugLogInfo() { 158 Uri data = Uri.parse(getString(R.string.HelpFragment__link__debug_info)); 159 Intent intent = new Intent(Intent.ACTION_VIEW, data); 160 161 startActivity(intent); 162 } 163 164 private void submitForm() { 165 next.setSpinning(); 166 problem.setEnabled(false); 167 168 helpViewModel.onSubmitClicked(includeDebugLogs.isChecked()).observe(getViewLifecycleOwner(), result -> { 169 if (result.getDebugLogUrl().isPresent()) { 170 submitFormWithDebugLog(result.getDebugLogUrl().get()); 171 } else if (result.isError()) { 172 submitFormWithDebugLog(getString(R.string.HelpFragment__could_not_upload_logs)); 173 } else { 174 submitFormWithDebugLog(null); 175 } 176 }); 177 } 178 179 private void submitFormWithDebugLog(@Nullable String debugLog) { 180 Feeling feeling = Stream.of(emoji) 181 .filter(View::isSelected) 182 .map(view -> Feeling.getByViewId(view.getId())) 183 .findFirst().orElse(null); 184 185 186 CommunicationActions.openEmail(requireContext(), 187 SupportEmailUtil.getSupportEmailAddress(requireContext()), 188 getEmailSubject(), 189 getEmailBody(debugLog, feeling)); 190 } 191 192 private String getEmailSubject() { 193 return getString(R.string.HelpFragment__signal_android_support_request); 194 } 195 196 private String getEmailBody(@Nullable String debugLog, @Nullable Feeling feeling) { 197 StringBuilder suffix = new StringBuilder(); 198 199 if (debugLog != null) { 200 suffix.append("\n"); 201 suffix.append(getString(R.string.HelpFragment__debug_log)); 202 suffix.append(" "); 203 suffix.append(debugLog); 204 } 205 206 if (feeling != null) { 207 suffix.append("\n\n"); 208 suffix.append(feeling.getEmojiCode()); 209 suffix.append("\n"); 210 suffix.append(getString(feeling.getStringId())); 211 } 212 213 String[] englishCategories = ResourceUtil.getEnglishResources(requireContext()).getStringArray(R.array.HelpFragment__categories_5); 214 String category = (helpViewModel.getCategoryIndex() >= 0 && helpViewModel.getCategoryIndex() < englishCategories.length) ? englishCategories[helpViewModel.getCategoryIndex()] 215 : categoryAdapter.getItem(helpViewModel.getCategoryIndex()).toString(); 216 217 return SupportEmailUtil.generateSupportEmailBody(requireContext(), 218 R.string.HelpFragment__signal_android_support_request, 219 " - " + category, 220 problem.getText().toString() + "\n\n", 221 suffix.toString()); 222 } 223 224 private enum Feeling { 225 ECSTATIC(R.id.help_fragment_emoji_5, R.string.HelpFragment__emoji_5, "\ud83d\ude00"), 226 HAPPY(R.id.help_fragment_emoji_4, R.string.HelpFragment__emoji_4, "\ud83d\ude42"), 227 AMBIVALENT(R.id.help_fragment_emoji_3, R.string.HelpFragment__emoji_3, "\ud83d\ude10"), 228 UNHAPPY(R.id.help_fragment_emoji_2, R.string.HelpFragment__emoji_2, "\ud83d\ude41"), 229 ANGRY(R.id.help_fragment_emoji_1, R.string.HelpFragment__emoji_1, "\ud83d\ude20"); 230 231 private final @IdRes int viewId; 232 private final @StringRes int stringId; 233 private final CharSequence emojiCode; 234 235 Feeling(@IdRes int viewId, @StringRes int stringId, @NonNull CharSequence emojiCode) { 236 this.viewId = viewId; 237 this.stringId = stringId; 238 this.emojiCode = emojiCode; 239 } 240 241 public @IdRes int getViewId() { 242 return viewId; 243 } 244 245 public @StringRes int getStringId() { 246 return stringId; 247 } 248 249 public @NonNull CharSequence getEmojiCode() { 250 return emojiCode; 251 } 252 253 static Feeling getByViewId(@IdRes int viewId) { 254 for (Feeling feeling : values()) { 255 if (feeling.viewId == viewId) { 256 return feeling; 257 } 258 } 259 260 throw new AssertionError(); 261 } 262 } 263}