diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 2be7b2583..3d4c53d54 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -312,6 +312,9 @@ android:configChanges="orientation" android:exported="false"/> + + collectAndSendLog()) + .setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> finish()) + .show(); + } + else{ + collectAndSendLog(); + } */ + + collectAndSendLog(); + } + + @SuppressWarnings("unchecked") + void collectAndSendLog(){ + /*Usage: logcat [options] [filterspecs] + options include: + -s Set default filter to silent. + Like specifying filterspec '*:s' + -f Log to file. Default to stdout + -r [] Rotate log every kbytes. (16 if unspecified). Requires -f + -n Sets max number of rotated logs to , default 4 + -v Sets the log print format, where is one of: + + brief process tag thread raw time threadtime long + + -c clear (flush) the entire log and exit + -d dump the log and then exit (don't block) + -g get the size of the log's ring buffer and exit + -b request alternate ring buffer + ('main' (default), 'radio', 'events') + -B output the log in binary + filterspecs are a series of + [:priority] + + where is a log component tag (or * for all) and priority is: + V Verbose + D Debug + I Info + W Warn + E Error + F Fatal + S Silent (supress all output) + + '*' means '*:d' and by itself means :v + + If not specified on the commandline, filterspec is set from ANDROID_LOG_TAGS. + If no filterspec is found, filter defaults to '*:I' + + If not specified with -v, format is set from ANDROID_PRINTF_LOG + or defaults to "brief"*/ + + ArrayList list = new ArrayList(); + + if (mFormat != null){ + list.add("-v"); + list.add(mFormat); + } + + if (mBuffer != null){ + list.add("-b"); + list.add(mBuffer); + } + + if (mFilterSpecs != null){ + for (String filterSpec : mFilterSpecs){ + list.add(filterSpec); + } + } + + mCollectLogTask = (CollectLogTask) new CollectLogTask().execute(list); + } + + private class CollectLogTask extends AsyncTask, Void, File>{ + @Override + protected void onPreExecute(){ + showProgressDialog(getString(R.string.acquiring_log_progress_dialog_message)); + } + + @Override + protected File doInBackground(ArrayList... params){ + final StringBuilder log = new StringBuilder(); + File logFile = new File(getCacheDir(),"logs/logcat.txt"); + + logFile.delete(); + logFile.getParentFile().mkdirs(); + + try{ + ArrayList commandLine = new ArrayList(); + commandLine.add("logcat"); + commandLine.add("-d"); + ArrayList arguments = ((params != null) && (params.length > 0)) ? params[0] : null; + if (null != arguments){ + commandLine.addAll(arguments); + } + + Process process = Runtime.getRuntime().exec(commandLine.toArray(new String[0])); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + while ((line = bufferedReader.readLine()) != null){ + log.append(line); + log.append(LINE_SEPARATOR); + } + + FileWriter fr = new FileWriter(logFile); + BufferedWriter writer = new BufferedWriter(fr); + + if (mAdditonalInfo != null){ + log.insert(0, LINE_SEPARATOR); + log.insert(0, mAdditonalInfo); + } + + writer.write(log.toString()); + } + catch (IOException e){ + Log.e(Config.LOGTAG, "CollectLogTask.doInBackground failed", e); + } + + return logFile; + } + + @Override + protected void onPostExecute(File logFile){ + if (null != logFile){ + Uri uri = FileProvider.getUriForFile(SendLogActivity.this, getPackageName() + ".files", logFile); + + new ShareCompat + .IntentBuilder(SendLogActivity.this) + .setType("text/*") + .addStream(uri) + .setChooserTitle(getString(R.string.chooser_title)) + .startChooser(); + + /* Intent sharingIntent = new Intent(Intent.ACTION_SEND); + sharingIntent.setType("text/*"); + sharingIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); + startActivity(Intent.createChooser(sharingIntent, getString(R.string.chooser_title)));*/ + dismissProgressDialog(); + dismissMainDialog(); + finish(); + } + else{ + dismissProgressDialog(); + showErrorDialog(getString(R.string.failed_to_get_log_message)); + } + } + } + + void showErrorDialog(String errorMessage){ + new AlertDialog.Builder(this) + .setTitle(getString(R.string.app_name)) + .setMessage(errorMessage) + .setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ + public void onClick(DialogInterface dialog, int whichButton){ + finish(); + } + }) + .show(); + } + + void dismissMainDialog(){ + if (null != mMainDialog && mMainDialog.isShowing()){ + mMainDialog.dismiss(); + mMainDialog = null; + } + } + + void showProgressDialog(String message){ + mProgressDialog = new ProgressDialog(this); + mProgressDialog.setIndeterminate(true); + mProgressDialog.setMessage(message); + mProgressDialog.setCancelable(true); + mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){ + public void onCancel(DialogInterface dialog){ + cancellCollectTask(); + finish(); + } + }); + mProgressDialog.show(); + } + + private void dismissProgressDialog(){ + if (null != mProgressDialog && mProgressDialog.isShowing()) + { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + + void cancellCollectTask(){ + if (mCollectLogTask != null && mCollectLogTask.getStatus() == AsyncTask.Status.RUNNING) + { + mCollectLogTask.cancel(true); + mCollectLogTask = null; + } + } + + @Override + protected void onPause(){ + cancellCollectTask(); + dismissProgressDialog(); + dismissMainDialog(); + + super.onPause(); + } + + private static String getVersionNumber(Context context) + { + String version = "?"; + try + { + PackageInfo packagInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + version = packagInfo.versionName; + } + catch (PackageManager.NameNotFoundException e){}; + + return version; + } + + private String getFormattedKernelVersion() + { + String procVersionStr; + + try { + BufferedReader reader = new BufferedReader(new FileReader("/proc/version"), 256); + try { + procVersionStr = reader.readLine(); + } finally { + reader.close(); + } + + final String PROC_VERSION_REGEX = + "\\w+\\s+" + /* ignore: Linux */ + "\\w+\\s+" + /* ignore: version */ + "([^\\s]+)\\s+" + /* group 1: 2.6.22-omap1 */ + "\\(([^\\s@]+(?:@[^\\s.]+)?)[^)]*\\)\\s+" + /* group 2: (xxxxxx@xxxxx.constant) */ + "\\([^)]+\\)\\s+" + /* ignore: (gcc ..) */ + "([^\\s]+)\\s+" + /* group 3: #26 */ + "(?:PREEMPT\\s+)?" + /* ignore: PREEMPT (optional) */ + "(.+)"; /* group 4: date */ + + Pattern p = Pattern.compile(PROC_VERSION_REGEX); + Matcher m = p.matcher(procVersionStr); + + if (!m.matches()) { + Log.e(TAG, "Regex did not match on /proc/version: " + procVersionStr); + return "Unavailable"; + } else if (m.groupCount() < 4) { + Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount() + + " groups"); + return "Unavailable"; + } else { + return (new StringBuilder(m.group(1)).append("\n").append( + m.group(2)).append(" ").append(m.group(3)).append("\n") + .append(m.group(4))).toString(); + } + } catch (IOException e) { + Log.e(TAG, + "IO Exception when getting kernel version for Device Info screen", + e); + + return "Unavailable"; + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index 021fc5fad..605851c7b 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.ui; import android.app.FragmentManager; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -28,6 +29,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.io.File; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.security.KeyStoreException; @@ -319,6 +321,16 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference }); } + final Preference sendLogsPreference = mSettingsFragment.findPreference("send_logs"); + if (sendLogsPreference != null) { + sendLogsPreference.setOnPreferenceClickListener( + preference -> { + final Intent intent = new Intent(this, SendLogActivity.class); + startActivity(intent); + return true; + }); + } + if (Config.ONLY_INTERNAL_STORAGE) { final Preference cleanCachePreference = mSettingsFragment.findPreference("clean_cache"); if (cleanCachePreference != null) { diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 25cf99588..ba780b3da 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -330,6 +330,8 @@ Prevents the operating system from killing your connection Create backup Backup files will be stored in %s + Send report + It can help us to fix your problem Creating backup files Your backup has been created The backup files have been stored in %s diff --git a/src/main/res/values/strings_logs_reporter.xml b/src/main/res/values/strings_logs_reporter.xml new file mode 100644 index 000000000..b60f785a4 --- /dev/null +++ b/src/main/res/values/strings_logs_reporter.xml @@ -0,0 +1,18 @@ + + + Android device log + Acquiring log from the system... + "Select an application to send the log" + "Failed to get the log from the system." + "This application will attempt to collect the device log. +The collected log will be sent using an application of your choice. +You also will have an opportunity to see and modify the data being sent." + + +Log Collector version: %1$s\n +Device model: %2$s\n +Firmware version: %3$s\n +Kernel version: %4$s\n +Build number: %5$s\n + + \ No newline at end of file diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 11a671c54..08d23429a 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -390,6 +390,15 @@ @drawable/background + +