That fuck shit the fascists are using
at master 325 lines 12 kB view raw
1package org.tm.archive.logsubmit; 2 3import android.app.Activity; 4import android.content.Intent; 5import android.net.Uri; 6import android.os.Bundle; 7import android.text.SpannableString; 8import android.text.Spanned; 9import android.text.style.URLSpan; 10import android.text.util.Linkify; 11import android.view.Menu; 12import android.view.MenuItem; 13import android.view.View; 14import android.widget.TextView; 15import android.widget.Toast; 16 17import androidx.annotation.NonNull; 18import androidx.annotation.Nullable; 19import androidx.appcompat.app.AlertDialog; 20import androidx.appcompat.widget.SearchView; 21import androidx.core.app.ShareCompat; 22import androidx.core.text.util.LinkifyCompat; 23import androidx.lifecycle.ViewModelProvider; 24import androidx.recyclerview.widget.LinearLayoutManager; 25import androidx.recyclerview.widget.RecyclerView; 26 27import com.google.android.material.dialog.MaterialAlertDialogBuilder; 28 29import org.tm.archive.BaseActivity; 30import org.tm.archive.R; 31import org.tm.archive.components.ProgressCard; 32import org.tm.archive.util.DynamicTheme; 33import org.tm.archive.util.LongClickCopySpan; 34import org.tm.archive.util.LongClickMovementMethod; 35import org.tm.archive.util.ThemeUtil; 36import org.tm.archive.util.ViewUtil; 37import org.tm.archive.util.views.CircularProgressMaterialButton; 38 39import java.util.List; 40 41public class SubmitDebugLogActivity extends BaseActivity implements SubmitDebugLogAdapter.Listener { 42 43 private static final int CODE_SAVE = 24601; 44 45 private RecyclerView lineList; 46 private SubmitDebugLogAdapter adapter; 47 private SubmitDebugLogViewModel viewModel; 48 49 private View warningBanner; 50 private View editBanner; 51 private CircularProgressMaterialButton submitButton; 52 private View scrollToBottomButton; 53 private View scrollToTopButton; 54 private ProgressCard progressCard; 55 56 private MenuItem editMenuItem; 57 private MenuItem doneMenuItem; 58 private MenuItem searchMenuItem; 59 private MenuItem saveMenuItem; 60 61 private final DynamicTheme dynamicTheme = new DynamicTheme(); 62 63 @Override 64 protected void onCreate(Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 dynamicTheme.onCreate(this); 67 setContentView(R.layout.submit_debug_log_activity); 68 getSupportActionBar().setDisplayHomeAsUpEnabled(true); 69 getSupportActionBar().setTitle(R.string.HelpSettingsFragment__debug_log); 70 71 this.viewModel = new ViewModelProvider(this, new SubmitDebugLogViewModel.Factory()).get(SubmitDebugLogViewModel.class); 72 73 initView(); 74 initViewModel(); 75 } 76 77 @Override 78 protected void onResume() { 79 super.onResume(); 80 dynamicTheme.onResume(this); 81 } 82 83 @Override 84 public boolean onCreateOptionsMenu(Menu menu) { 85 getMenuInflater().inflate(R.menu.submit_debug_log_normal, menu); 86 87 this.editMenuItem = menu.findItem(R.id.menu_edit_log); 88 this.doneMenuItem = menu.findItem(R.id.menu_done_editing_log); 89 this.searchMenuItem = menu.findItem(R.id.menu_search); 90 this.saveMenuItem = menu.findItem(R.id.menu_save); 91 92 SearchView searchView = (SearchView) searchMenuItem.getActionView(); 93 SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() { 94 @Override 95 public boolean onQueryTextSubmit(String query) { 96 viewModel.onQueryUpdated(query); 97 return true; 98 } 99 100 @Override 101 public boolean onQueryTextChange(String query) { 102 viewModel.onQueryUpdated(query); 103 return true; 104 } 105 }; 106 107 searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { 108 @Override 109 public boolean onMenuItemActionExpand(MenuItem item) { 110 searchView.setOnQueryTextListener(queryListener); 111 return true; 112 } 113 114 @Override 115 public boolean onMenuItemActionCollapse(MenuItem item) { 116 searchView.setOnQueryTextListener(null); 117 viewModel.onSearchClosed(); 118 return true; 119 } 120 }); 121 122 return true; 123 } 124 125 @Override 126 public boolean onOptionsItemSelected(MenuItem item) { 127 super.onOptionsItemSelected(item); 128 129 if (item.getItemId() == android.R.id.home) { 130 finish(); 131 return true; 132 } else if (item.getItemId() == R.id.menu_edit_log) { 133 viewModel.onEditButtonPressed(); 134 } else if (item.getItemId() == R.id.menu_done_editing_log) { 135 viewModel.onDoneEditingButtonPressed(); 136 } else if (item.getItemId() == R.id.menu_save) { 137 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); 138 intent.addCategory(Intent.CATEGORY_OPENABLE); 139 intent.setType("application/zip"); 140 intent.putExtra(Intent.EXTRA_TITLE, "signal-log-" + System.currentTimeMillis() + ".zip"); 141 142 startActivityForResult(intent, CODE_SAVE); 143 } 144 145 return false; 146 } 147 148 @Override 149 public void onBackPressed() { 150 if (!viewModel.onBackPressed()) { 151 super.onBackPressed(); 152 } 153 } 154 155 @Override 156 protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 157 super.onActivityResult(requestCode, resultCode, data); 158 159 if (requestCode == CODE_SAVE && resultCode == Activity.RESULT_OK) { 160 Uri uri = data != null ? data.getData() : null; 161 viewModel.onDiskSaveLocationReady(uri); 162 if (progressCard != null) { 163 progressCard.setVisibility(View.VISIBLE); 164 } 165 } 166 } 167 168 @Override 169 public void onLogDeleted(@NonNull LogLine logLine) { 170 viewModel.onLogDeleted(logLine); 171 } 172 173 private void initView() { 174 this.lineList = findViewById(R.id.debug_log_lines); 175 this.warningBanner = findViewById(R.id.debug_log_warning_banner); 176 this.editBanner = findViewById(R.id.debug_log_edit_banner); 177 this.submitButton = findViewById(R.id.debug_log_submit_button); 178 this.scrollToBottomButton = findViewById(R.id.debug_log_scroll_to_bottom); 179 this.scrollToTopButton = findViewById(R.id.debug_log_scroll_to_top); 180 this.progressCard = findViewById(R.id.debug_log_progress_card); 181 182 this.adapter = new SubmitDebugLogAdapter(this, viewModel.getPagingController()); 183 184 this.lineList.setLayoutManager(new LinearLayoutManager(this)); 185 this.lineList.setAdapter(adapter); 186 this.lineList.setItemAnimator(null); 187 188 submitButton.setOnClickListener(v -> onSubmitClicked()); 189 190 scrollToBottomButton.setOnClickListener(v -> lineList.scrollToPosition(adapter.getItemCount() - 1)); 191 scrollToTopButton.setOnClickListener(v -> lineList.scrollToPosition(0)); 192 193 lineList.addOnScrollListener(new RecyclerView.OnScrollListener() { 194 @Override 195 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { 196 if (((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition() < adapter.getItemCount() - 10) { 197 scrollToBottomButton.setVisibility(View.VISIBLE); 198 } else { 199 scrollToBottomButton.setVisibility(View.GONE); 200 } 201 202 if (((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition() > 10) { 203 scrollToTopButton.setVisibility(View.VISIBLE); 204 } else { 205 scrollToTopButton.setVisibility(View.GONE); 206 } 207 } 208 }); 209 this.progressCard.setVisibility(View.VISIBLE); 210 211 } 212 213 private void initViewModel() { 214 viewModel.getLines().observe(this, this::presentLines); 215 viewModel.getMode().observe(this, this::presentMode); 216 viewModel.getEvents().observe(this, this::presentEvents); 217 } 218 219 private void presentLines(@NonNull List<LogLine> lines) { 220 if (progressCard != null && lines.size() > 0) { 221 progressCard.setVisibility(View.GONE); 222 223 warningBanner.setVisibility(View.VISIBLE); 224 submitButton.setVisibility(View.VISIBLE); 225 } 226 227 adapter.submitList(lines); 228 } 229 230 private void presentMode(@NonNull SubmitDebugLogViewModel.Mode mode) { 231 switch (mode) { 232 case NORMAL: 233 editBanner.setVisibility(View.GONE); 234 adapter.setEditing(false); 235 saveMenuItem.setVisible(true); 236 // TODO [greyson][log] Not yet implemented 237// editMenuItem.setVisible(true); 238// doneMenuItem.setVisible(false); 239// searchMenuItem.setVisible(true); 240 break; 241 case SUBMITTING: 242 editBanner.setVisibility(View.GONE); 243 adapter.setEditing(false); 244 editMenuItem.setVisible(false); 245 doneMenuItem.setVisible(false); 246 searchMenuItem.setVisible(false); 247 saveMenuItem.setVisible(false); 248 break; 249 case EDIT: 250 editBanner.setVisibility(View.VISIBLE); 251 adapter.setEditing(true); 252 editMenuItem.setVisible(false); 253 doneMenuItem.setVisible(true); 254 searchMenuItem.setVisible(true); 255 saveMenuItem.setVisible(false); 256 break; 257 } 258 } 259 260 private void presentEvents(@NonNull SubmitDebugLogViewModel.Event event) { 261 switch (event) { 262 case FILE_SAVE_SUCCESS: 263 Toast.makeText(this, R.string.SubmitDebugLogActivity_save_complete, Toast.LENGTH_SHORT).show(); 264 if (progressCard != null) { 265 progressCard.setVisibility(View.GONE); 266 } 267 break; 268 case FILE_SAVE_ERROR: 269 Toast.makeText(this, R.string.SubmitDebugLogActivity_failed_to_save, Toast.LENGTH_SHORT).show(); 270 break; 271 } 272 } 273 274 private void presentResultDialog(@NonNull String url) { 275 AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this) 276 .setTitle(R.string.SubmitDebugLogActivity_success) 277 .setCancelable(false) 278 .setNeutralButton(android.R.string.ok, (d, w) -> finish()) 279 .setPositiveButton(R.string.SubmitDebugLogActivity_share, (d, w) -> { 280 ShareCompat.IntentBuilder.from(this) 281 .setText(url) 282 .setType("text/plain") 283 .setEmailTo(new String[] { "support@signal.org" }) 284 .startChooser(); 285 }); 286 287 String dialogText = getResources().getString(R.string.SubmitDebugLogActivity_copy_this_url_and_add_it_to_your_issue, url); 288 SpannableString spannableDialogText = new SpannableString(dialogText); 289 TextView dialogView = new TextView(builder.getContext()); 290 LongClickCopySpan longClickUrl = new LongClickCopySpan(url); 291 292 293 LinkifyCompat.addLinks(spannableDialogText, Linkify.WEB_URLS); 294 295 URLSpan[] spans = spannableDialogText.getSpans(0, spannableDialogText.length(), URLSpan.class); 296 for (URLSpan span : spans) { 297 int start = spannableDialogText.getSpanStart(span); 298 int end = spannableDialogText.getSpanEnd(span); 299 300 spannableDialogText.setSpan(longClickUrl, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 301 } 302 303 dialogView.setText(spannableDialogText); 304 dialogView.setMovementMethod(LongClickMovementMethod.getInstance(this)); 305 306 ViewUtil.setPadding(dialogView, (int) ThemeUtil.getThemedDimen(this, R.attr.dialogPreferredPadding)); 307 308 builder.setView(dialogView); 309 builder.show(); 310 } 311 312 private void onSubmitClicked() { 313 submitButton.setSpinning(); 314 315 viewModel.onSubmitClicked().observe(this, result -> { 316 if (result.isPresent()) { 317 presentResultDialog(result.get()); 318 } else { 319 Toast.makeText(this, R.string.SubmitDebugLogActivity_failed_to_submit_logs, Toast.LENGTH_LONG).show(); 320 } 321 322 submitButton.cancelSpinning(); 323 }); 324 } 325}