diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8a03bb61c5..87ed242c51 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -183,6 +183,11 @@
+
+
diff --git a/res/layout/log_submit_activity.xml b/res/layout/log_submit_activity.xml
new file mode 100644
index 0000000000..7c0cf5df79
--- /dev/null
+++ b/res/layout/log_submit_activity.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/log_submit.xml b/res/menu/log_submit.xml
new file mode 100644
index 0000000000..da663ffc16
--- /dev/null
+++ b/res/menu/log_submit.xml
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b04e606f76..92da7234e1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -441,7 +441,19 @@
REPEAT:
Continue
GENERATING SECRETS
-
+
+
+ This log will be posted publicly online for TextSecure contributors to view. Feel free to examine or edit the logs below before hitting submit.
+ Don\'t submit
+ Submit
+ Could not grab logs from your device. You can still use ADB to get debug logs instead.
+ Success!
+ Got it
+ Please copy this URL and add it to your issue (long press to put in clipboard):\n\n%1$s
+ Copied to clipboard
+ Loading logcat…
+ Thanks for your help!
+
Would you like to import your existing text messages into TextSecure\'s encrypted database?
The default system database will not be modified or altered in any way.
@@ -608,6 +620,7 @@
Verify Identity
Manage Identity Keys
Complete Key Exchange
+ Submit Debug Logs
Import / Export
@@ -770,6 +783,7 @@
Settings
Lock
Mark All Read
+ Submit debug log
Verified
diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 7cc4b7a366..5dc674df29 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -109,6 +109,7 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
menu.clear();
inflater.inflate(R.menu.text_secure_normal, menu);
+ inflater.inflate(R.menu.log_submit, menu);
menu.findItem(R.id.menu_clear_passphrase).setVisible(!TextSecurePreferences.isPasswordDisabled(this));
@@ -147,12 +148,13 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
int defaultType = ThreadDatabase.DistributionTypes.DEFAULT;
switch (item.getItemId()) {
- case R.id.menu_new_message: openSingleContactSelection(); return true;
- case R.id.menu_new_group: createGroup(); return true;
- case R.id.menu_settings: handleDisplaySettings(); return true;
- case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
- case R.id.menu_mark_all_read: handleMarkAllRead(); return true;
- case android.R.id.home: handleNavigationDrawerToggle(); return true;
+ case R.id.menu_new_message: openSingleContactSelection(); return true;
+ case R.id.menu_new_group: createGroup(); return true;
+ case R.id.menu_settings: handleDisplaySettings(); return true;
+ case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
+ case R.id.menu_mark_all_read: handleMarkAllRead(); return true;
+ case R.id.menu_submit_debug_logs: handleLogSubmit(); return true;
+ case android.R.id.home: handleNavigationDrawerToggle(); return true;
}
return false;
@@ -185,6 +187,11 @@ public class ConversationListActivity extends PassphraseRequiredSherlockFragment
startActivity(intent);
}
+ private void handleLogSubmit() {
+ Intent intent = new Intent(this, LogSubmitActivity.class);
+ startActivity(intent);
+ }
+
private void handleNavigationDrawerToggle() {
if (drawerLayout.isDrawerOpen(drawerList)) {
drawerLayout.closeDrawer(drawerList);
diff --git a/src/org/thoughtcrime/securesms/LogSubmitActivity.java b/src/org/thoughtcrime/securesms/LogSubmitActivity.java
new file mode 100644
index 0000000000..0c907b50b1
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/LogSubmitActivity.java
@@ -0,0 +1,266 @@
+package org.thoughtcrime.securesms;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.text.ClipboardManager;
+import android.content.DialogInterface;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.google.thoughtcrimegson.Gson;
+import com.google.thoughtcrimegson.JsonIOException;
+import com.google.thoughtcrimegson.JsonParseException;
+import com.google.thoughtcrimegson.JsonSyntaxException;
+import com.google.thoughtcrimegson.reflect.TypeToken;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.thoughtcrime.securesms.util.ActionBarUtil;
+import org.thoughtcrime.securesms.util.Util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Activity for submitting logcat logs to a pastebin service.
+ */
+public class LogSubmitActivity extends SherlockActivity {
+ private static final String TAG = LogSubmitActivity.class.getSimpleName();
+
+ private static final String HASTEBIN_ENDPOINT = "http://hastebin.com/documents";
+ private static final String HASTEBIN_PREFIX = "http://hastebin.com/";
+
+ private EditText logPreview;
+ private Button okButton;
+ private Button cancelButton;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.log_submit_activity);
+ ActionBarUtil.initializeDefaultActionBar(this, getSupportActionBar());
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ initializeResources();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ new PopulateLogcatAsyncTask().execute();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ super.onOptionsItemSelected(item);
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ }
+
+ return false;
+ }
+
+ private void initializeResources() {
+ logPreview = (EditText) findViewById(R.id.log_preview);
+ okButton = (Button) findViewById(R.id.ok);
+ cancelButton = (Button) findViewById(R.id.cancel);
+
+ okButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ new SubmitToPastebinAsyncTask(logPreview.getText().toString()).execute();
+ }
+ });
+
+ cancelButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ finish();
+ }
+ });
+ }
+
+ private static String grabLogcat() {
+ try {
+ Process process = Runtime.getRuntime().exec("logcat -d");
+ BufferedReader bufferedReader = new BufferedReader(
+ new InputStreamReader(process.getInputStream()));
+
+ StringBuilder log = new StringBuilder();
+ String separator = System.getProperty("line.separator");
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ log.append(line);
+ log.append(separator);
+ }
+ return log.toString();
+ } catch (IOException ioe) {
+ Log.w(TAG, "IOException when trying to read logcat.", ioe);
+ return null;
+ }
+ }
+
+ private class PopulateLogcatAsyncTask extends AsyncTask {
+
+ @Override
+ protected String doInBackground(Void... voids) {
+ return grabLogcat();
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ logPreview.setText(R.string.log_submit_activity__loading_logcat);
+ okButton.setEnabled(false);
+ }
+
+ @Override
+ protected void onPostExecute(String logcat) {
+ super.onPostExecute(logcat);
+ if (TextUtils.isEmpty(logcat)) {
+ Toast.makeText(getApplicationContext(), R.string.log_submit_activity__log_fetch_failed, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+ logPreview.setText(logcat);
+ okButton.setEnabled(true);
+ }
+ }
+
+ private class SubmitToPastebinAsyncTask extends AsyncTask {
+ private ProgressDialog progressDialog;
+ private final String paste;
+
+ public SubmitToPastebinAsyncTask(String paste) {
+ this.paste = paste;
+ }
+
+ @Override
+ protected String doInBackground(Void... voids) {
+ HttpURLConnection urlConnection = null;
+ try {
+ URL url = new URL(HASTEBIN_ENDPOINT);
+ urlConnection = (HttpURLConnection) url.openConnection();
+ urlConnection.setDoOutput(true);
+ urlConnection.setFixedLengthStreamingMode(paste.length());
+ urlConnection.setReadTimeout(10000);
+ urlConnection.connect();
+
+ OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
+ out.write(paste.getBytes());
+ out.flush();
+ InputStream in = new BufferedInputStream(urlConnection.getInputStream());
+
+ Type type = new TypeToken