package org.thoughtcrime.securesms.logsubmit; import android.os.Bundle; import android.text.method.LinkMovementMethod; import android.text.util.Linkify; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; import androidx.core.app.ShareCompat; import androidx.core.text.util.LinkifyCompat; import androidx.lifecycle.ViewModelProviders; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.dd.CircularProgressButton; import org.thoughtcrime.securesms.BaseActionBarActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; import java.util.List; public class SubmitDebugLogActivity extends BaseActionBarActivity implements SubmitDebugLogAdapter.Listener { private RecyclerView lineList; private SubmitDebugLogAdapter adapter; private SubmitDebugLogViewModel viewModel; private View warningBanner; private View editBanner; private CircularProgressButton submitButton; private AlertDialog loadingDialog; private View scrollToBottomButton; private View scrollToTopButton; private MenuItem editMenuItem; private MenuItem doneMenuItem; private MenuItem searchMenuItem; private final DynamicTheme dynamicTheme = new DynamicTheme(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); dynamicTheme.onCreate(this); setContentView(R.layout.submit_debug_log_activity); getSupportActionBar().setDisplayHomeAsUpEnabled(true); initView(); initViewModel(); } @Override protected void onResume() { super.onResume(); dynamicTheme.onResume(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.submit_debug_log_normal, menu); this.editMenuItem = menu.findItem(R.id.menu_edit_log); this.doneMenuItem = menu.findItem(R.id.menu_done_editing_log); this.searchMenuItem = menu.findItem(R.id.menu_search); SearchView searchView = (SearchView) searchMenuItem.getActionView(); SearchView.OnQueryTextListener queryListener = new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { viewModel.onQueryUpdated(query); return true; } @Override public boolean onQueryTextChange(String query) { viewModel.onQueryUpdated(query); return true; } }; searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { searchView.setOnQueryTextListener(queryListener); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { searchView.setOnQueryTextListener(null); viewModel.onSearchClosed(); return true; } }); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch (item.getItemId()) { case android.R.id.home: finish(); return true; case R.id.menu_edit_log: viewModel.onEditButtonPressed(); break; case R.id.menu_done_editing_log: viewModel.onDoneEditingButtonPressed(); break; } return false; } @Override public void onBackPressed() { if (!viewModel.onBackPressed()) { super.onBackPressed(); } } @Override public void onLogDeleted(@NonNull LogLine logLine) { viewModel.onLogDeleted(logLine); } private void initView() { this.lineList = findViewById(R.id.debug_log_lines); this.warningBanner = findViewById(R.id.debug_log_warning_banner); this.editBanner = findViewById(R.id.debug_log_edit_banner); this.submitButton = findViewById(R.id.debug_log_submit_button); this.scrollToBottomButton = findViewById(R.id.debug_log_scroll_to_bottom); this.scrollToTopButton = findViewById(R.id.debug_log_scroll_to_top); this.adapter = new SubmitDebugLogAdapter(this); this.lineList.setLayoutManager(new LinearLayoutManager(this)); this.lineList.setAdapter(adapter); submitButton.setOnClickListener(v -> onSubmitClicked()); scrollToBottomButton.setOnClickListener(v -> lineList.scrollToPosition(adapter.getItemCount() - 1)); scrollToTopButton.setOnClickListener(v -> lineList.scrollToPosition(0)); lineList.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { if (((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition() < adapter.getItemCount() - 10) { scrollToBottomButton.setVisibility(View.VISIBLE); } else { scrollToBottomButton.setVisibility(View.GONE); } if (((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition() > 10) { scrollToTopButton.setVisibility(View.VISIBLE); } else { scrollToTopButton.setVisibility(View.GONE); } } }); this.loadingDialog = SimpleProgressDialog.show(this); } private void initViewModel() { this.viewModel = ViewModelProviders.of(this, new SubmitDebugLogViewModel.Factory()).get(SubmitDebugLogViewModel.class); viewModel.getLines().observe(this, this::presentLines); viewModel.getMode().observe(this, this::presentMode); } private void presentLines(@NonNull List<LogLine> lines) { if (loadingDialog != null) { loadingDialog.dismiss(); loadingDialog = null; warningBanner.setVisibility(View.VISIBLE); submitButton.setVisibility(View.VISIBLE); } adapter.setLines(lines); } private void presentMode(@NonNull SubmitDebugLogViewModel.Mode mode) { switch (mode) { case NORMAL: editBanner.setVisibility(View.GONE); adapter.setEditing(false); editMenuItem.setVisible(true); doneMenuItem.setVisible(false); searchMenuItem.setVisible(true); break; case SUBMITTING: editBanner.setVisibility(View.GONE); adapter.setEditing(false); editMenuItem.setVisible(false); doneMenuItem.setVisible(false); searchMenuItem.setVisible(false); break; case EDIT: editBanner.setVisibility(View.VISIBLE); adapter.setEditing(true); editMenuItem.setVisible(false); doneMenuItem.setVisible(true); searchMenuItem.setVisible(true); break; } } private void presentResultDialog(@NonNull String url) { AlertDialog.Builder builder = new AlertDialog.Builder(this) .setTitle(R.string.SubmitDebugLogActivity_success) .setCancelable(false) .setNeutralButton(R.string.SubmitDebugLogActivity_ok, (d, w) -> finish()) .setPositiveButton(R.string.SubmitDebugLogActivity_share, (d, w) -> { ShareCompat.IntentBuilder.from(this) .setText(url) .setType("text/plain") .setEmailTo(new String[] { "[email protected]" }) .startChooser(); }); TextView textView = new TextView(builder.getContext()); textView.setText(getResources().getString(R.string.SubmitDebugLogActivity_copy_this_url_and_add_it_to_your_issue, url)); textView.setMovementMethod(LinkMovementMethod.getInstance()); textView.setOnLongClickListener(v -> { Util.copyToClipboard(this, url); Toast.makeText(this, R.string.SubmitDebugLogActivity_copied_to_clipboard, Toast.LENGTH_SHORT).show(); return true; }); LinkifyCompat.addLinks(textView, Linkify.WEB_URLS); ViewUtil.setPadding(textView, (int) ThemeUtil.getThemedDimen(this, R.attr.dialogPreferredPadding)); builder.setView(textView); builder.show(); } private void onSubmitClicked() { submitButton.setClickable(false); submitButton.setIndeterminateProgressMode(true); submitButton.setProgress(50); viewModel.onSubmitClicked().observe(this, result -> { if (result.isPresent()) { presentResultDialog(result.get()); } else { Toast.makeText(this, R.string.SubmitDebugLogActivity_failed_to_submit_logs, Toast.LENGTH_LONG).show(); } submitButton.setClickable(true); submitButton.setIndeterminateProgressMode(false); submitButton.setProgress(0); }); } }