/* (C) 2012 Pragmatic Software This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/ */ package com.googlecode.networklog; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.widget.CheckBox; import android.widget.EditText; import android.view.View; import android.view.LayoutInflater; import android.os.Build; import android.os.Environment; import android.util.Log; import java.io.File; public class FeedbackDialog { public EditText message; public CheckBox attachLogcat; public AlertDialog dialog; private Context context; public FeedbackDialog(final Context context) { this.context = context; Resources res = context.getResources(); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.feedbackdialog, null); message = (EditText) view.findViewById(R.id.feedbackMessage); attachLogcat = (CheckBox) view.findViewById(R.id.attachLogcat); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(res.getString(R.string.feedback_title)) .setView(view) .setCancelable(true) .setPositiveButton(res.getString(R.string.feedback_send), new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int id) { // see show() method for implementation -- avoids dismiss() unless validation passes } }) .setNegativeButton(res.getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int id) { dialog.cancel(); dialog = null; } }); dialog = builder.create(); } public void setMessage(CharSequence text) { if(message != null) { message.setText(text); } } public void setCursorPosition(int position) { if(message != null) { message.setSelection(position); } } public void setAttachLogcat(boolean value) { if(attachLogcat != null) { attachLogcat.setChecked(value); } } public void show() { if(dialog != null) { dialog.show(); dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Resources res = context.getResources(); String msg = message.getText().toString().trim(); File logcat = null; if(msg.length() == 0) { SysUtils.showError(context, res.getString(R.string.feedback_error_no_message_title), res.getString(R.string.feedback_error_no_message_text)); return; } if(attachLogcat.isChecked()) { try { logcat = generateLogcat(); } catch(Exception e) { SysUtils.showError(context, res.getString(R.string.feedback_error_getting_debug_log), e.toString()); return; } } dialog.dismiss(); dialog = null; sendFeedback(msg, logcat); } }); } } public void dismiss() { if(dialog != null) { dialog.dismiss(); dialog = null; } } public File generateLogcat() throws Exception { File logcat = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "netlog_logcat.txt"); String path = logcat.getAbsolutePath(); String iptablesBinary = SysUtils.getIptablesBinary(context); if(iptablesBinary == null) { throw new Exception(String.format(context.getResources().getString(R.string.error_unsupported_system_text), Build.CPU_ABI)); } boolean hasRoot = SysUtils.checkRoot(context); InteractiveShell shell; if(hasRoot) { shell = NetworkLog.shell; } else { Log.d("NetworkLog", "No root shell, creating standard shell"); shell = new InteractiveShell("sh", "GenerateLogcat"); shell.start(); // FIXME: devise better method to check for exit that doesn't involve arbitrary sleeping try { Thread.sleep(500); } catch (Exception e) {} if(shell.hasError()) { String error = shell.getError(true); Log.e("NetworkLog", "Error creating standard shell: " + error); throw new Exception("Error creating shell: " + error); } if(shell.checkForExit()) { Log.e("NetworkLog", "Error creating standard shell: shell exited with code " + shell.exitval); throw new Exception("Error creating shell: exited with code " + shell.exitval); } } shell.sendCommand("logcat -d -v time > " + path, InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("echo === uname: >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("uname -a >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); if(hasRoot) { shell.sendCommand("echo === ip_tables_matches: >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("cat /proc/net/ip_tables_matches >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("echo === ip_tables_names: >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("cat /proc/net/ip_tables_names >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("echo === ip_tables_targets: >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("cat /proc/net/ip_tables_targets >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand("echo === iptables: >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.sendCommand(iptablesBinary + " -L -v >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); } else { shell.sendCommand("echo === not rooted >> " + path + " 2>&1", InteractiveShell.IGNORE_OUTPUT); shell.close(); } return logcat; } public void sendFeedback(String message, File logcat) { StringBuilder msg = new StringBuilder(message.length() + 512); msg.append(message); try { msg.append("\n\nNetworkLog " + context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName); } catch (android.content.pm.PackageManager.NameNotFoundException e) { msg.append("\n\nNetworkLog unknown version"); } msg.append("\nAndroid " + Build.VERSION.RELEASE + (SysUtils.checkRoot(context) ? " rooted" : "")); msg.append("\nDevice " + Build.MANUFACTURER + " " + Build.MODEL + " " + Build.PRODUCT + " " + Build.BRAND); msg.append("\nKernel " + System.getProperty("os.version")); msg.append("\n" + Build.DISPLAY); msg.append("\nCPU " + Build.CPU_ABI + " " + Build.CPU_ABI2); Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL, new String[]{ "pragma78@gmail.com" }); intent.putExtra(Intent.EXTRA_SUBJECT, "[NetworkLog] Bug report/feedback"); intent.putExtra(Intent.EXTRA_TEXT, msg.toString()); intent.setType("message/rfc822"); if(logcat != null) { intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(logcat)); } context.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.feedback_chooser_title))); } }