forked from organicmaps/organicmaps
[android] Refactor sending of zipped logs
- make bugreport and general feedback features send to flavor-specific support email - improve freeing of resources in case of exceptions - simplify and delete unused code Signed-off-by: Konstantin Pastbin <konstantin.pastbin@gmail.com>
This commit is contained in:
parent
48a8d2aba5
commit
b07b3d3531
7 changed files with 78 additions and 101 deletions
|
@ -30,7 +30,7 @@ public class FaqFragment extends BaseMwmFragment
|
|||
|
||||
private void reportBug()
|
||||
{
|
||||
Utils.sendBugReport(requireActivity(), "Organic Maps Bugreport");
|
||||
Utils.sendBugReport(requireActivity(), "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -128,7 +128,7 @@ public class HelpFragment extends BaseMwmFragment implements View.OnClickListene
|
|||
else if (id == R.id.faq)
|
||||
((HelpActivity) getActivity()).stackFragment(FaqFragment.class, getString(R.string.faq), null);
|
||||
else if (id == R.id.report)
|
||||
Utils.sendFeedback(getActivity());
|
||||
Utils.sendBugReport(getActivity(), "");
|
||||
else if (id == R.id.support_us)
|
||||
openLink(Constants.Url.SUPPORT_US);
|
||||
else if (id == R.id.rate)
|
||||
|
|
|
@ -46,7 +46,6 @@ public final class Constants
|
|||
|
||||
public static class Email
|
||||
{
|
||||
public static final String FEEDBACK = "android@organicmaps.app";
|
||||
public static final String SUPPORT = BuildConfig.SUPPORT_MAIL;
|
||||
public static final String RATING = "rating@organicmaps.app";
|
||||
|
||||
|
|
|
@ -98,14 +98,6 @@ public class StorageUtils
|
|||
return externalDir + File.separator + LOGS_FOLDER;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getLogsZipPath(@NonNull Application application)
|
||||
{
|
||||
String zipFile = getExternalFilesDir(application) + File.separator + LOGS_FOLDER + ".zip";
|
||||
File file = new File(zipFile);
|
||||
return file.isFile() && file.exists() ? zipFile : null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getApkPath(@NonNull Application application)
|
||||
{
|
||||
|
|
|
@ -343,16 +343,23 @@ public class Utils
|
|||
}
|
||||
}
|
||||
|
||||
// subject is optional (could be an empty string).
|
||||
|
||||
/**
|
||||
* @param subject could be an empty string
|
||||
*/
|
||||
public static void sendBugReport(@NonNull Activity activity, @NonNull String subject)
|
||||
{
|
||||
subject = "Organic Maps Bugreport" + (TextUtils.isEmpty(subject) ? "" : ": " + subject);
|
||||
LoggerFactory.INSTANCE.zipLogs(new SupportInfoWithLogsCallback(activity, subject,
|
||||
Constants.Email.SUPPORT));
|
||||
}
|
||||
|
||||
// TODO: Don't send logs with general feedback, send system information only (version, device name, connectivity, etc.)
|
||||
public static void sendFeedback(@NonNull Activity activity)
|
||||
{
|
||||
LoggerFactory.INSTANCE.zipLogs(new SupportInfoWithLogsCallback(activity, "Organic Maps Feedback",
|
||||
Constants.Email.FEEDBACK));
|
||||
Constants.Email.SUPPORT));
|
||||
}
|
||||
|
||||
public static void navigateToParent(@Nullable Activity activity)
|
||||
|
@ -799,10 +806,11 @@ public class Utils
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onCompleted(final boolean success)
|
||||
public void onCompleted(final boolean success, @Nullable final String zipPath)
|
||||
{
|
||||
//TODO: delete zip file after its sent.
|
||||
UiThread.run(() -> {
|
||||
Activity activity = mActivityRef.get();
|
||||
final Activity activity = mActivityRef.get();
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
|
@ -810,21 +818,18 @@ public class Utils
|
|||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { mEmail });
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, "[" + BuildConfig.VERSION_NAME + "] " + mSubject);
|
||||
//TODO: if zipping logs failed send a short text attachment with system info and logs zipping error/stacktrace.
|
||||
if (success)
|
||||
{
|
||||
String logsZipFile = StorageUtils.getLogsZipPath(activity.getApplication());
|
||||
if (!TextUtils.isEmpty(logsZipFile))
|
||||
{
|
||||
Uri uri = StorageUtils.getUriForFilePath(activity, logsZipFile);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
// Properly set permissions for intent, see
|
||||
// https://developer.android.com/reference/androidx/core/content/FileProvider#include-the-permission-in-an-intent
|
||||
intent.setData(uri);
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
intent.setClipData(ClipData.newRawUri("", uri));
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
}
|
||||
final Uri uri = StorageUtils.getUriForFilePath(activity, zipPath);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
// Properly set permissions for intent, see
|
||||
// https://developer.android.com/reference/androidx/core/content/FileProvider#include-the-permission-in-an-intent
|
||||
intent.setData(uri);
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
intent.setClipData(ClipData.newRawUri("", uri));
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
// Do this so some email clients don't complain about empty body.
|
||||
|
@ -836,7 +841,7 @@ public class Utils
|
|||
}
|
||||
catch (ActivityNotFoundException e)
|
||||
{
|
||||
CrashlyticsUtils.INSTANCE.logException(e);
|
||||
LOGGER.w("No activities found which can handle sending a support message.", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,14 +35,8 @@ public class LoggerFactory
|
|||
|
||||
public interface OnZipCompletedListener
|
||||
{
|
||||
/**
|
||||
* Indicates about completion of zipping operation.
|
||||
* <p>
|
||||
* <b>NOTE:</b> called from the logger thread
|
||||
* </p>
|
||||
* @param success indicates about a status of zipping operation
|
||||
*/
|
||||
void onCompleted(boolean success);
|
||||
// Called from the logger thread.
|
||||
public void onCompleted(final boolean success, @Nullable final String zipPath);
|
||||
}
|
||||
|
||||
public final static LoggerFactory INSTANCE = new LoggerFactory();
|
||||
|
@ -92,7 +86,7 @@ public class LoggerFactory
|
|||
.putBoolean(mApplication.getString(R.string.pref_enable_logging), enabled)
|
||||
.apply();
|
||||
updateLoggers();
|
||||
getLogger(Type.MISC).i(TAG, "File logging " + (enabled ? " started to " + mLogsFolder : "stopped"));
|
||||
getLogger(Type.MISC).i(TAG, "File logging " + (enabled ? "started to " + mLogsFolder : "stopped"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,7 +131,7 @@ public class LoggerFactory
|
|||
if (TextUtils.isEmpty(mLogsFolder))
|
||||
{
|
||||
if (listener != null)
|
||||
listener.onCompleted(false);
|
||||
listener.onCompleted(false, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package com.mapswithme.util.log;
|
||||
|
||||
import android.app.Application;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.mapswithme.util.StorageUtils;
|
||||
import com.mapswithme.util.Utils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
|
@ -24,68 +22,50 @@ import java.util.zip.ZipOutputStream;
|
|||
class ZipLogsTask implements Runnable
|
||||
{
|
||||
private final static String TAG = ZipLogsTask.class.getSimpleName();
|
||||
private final static Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
|
||||
private final static int BUFFER_SIZE = 2048;
|
||||
@NonNull
|
||||
private final String mSourcePath;
|
||||
private final String mLogsPath;
|
||||
@NonNull
|
||||
private final String mDestPath;
|
||||
private final String mZipPath;
|
||||
@Nullable
|
||||
private final LoggerFactory.OnZipCompletedListener mOnCompletedListener;
|
||||
@NonNull
|
||||
private final Application mApplication;
|
||||
|
||||
ZipLogsTask(@NonNull Application application, @NonNull String sourcePath,
|
||||
@NonNull String destPath,
|
||||
ZipLogsTask(@NonNull Application application, @NonNull String logsPath,
|
||||
@NonNull String zipPath,
|
||||
@Nullable LoggerFactory.OnZipCompletedListener onCompletedListener)
|
||||
{
|
||||
mApplication = application;
|
||||
mSourcePath = sourcePath;
|
||||
mDestPath = destPath;
|
||||
mLogsPath = logsPath;
|
||||
mZipPath = zipPath;
|
||||
mOnCompletedListener = onCompletedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
saveSystemLogcat();
|
||||
boolean success = zipFileAtPath(mSourcePath, mDestPath);
|
||||
saveSystemLogcat(mLogsPath);
|
||||
final boolean success = zipFileAtPath(mLogsPath, mZipPath);
|
||||
if (mOnCompletedListener != null)
|
||||
mOnCompletedListener.onCompleted(success);
|
||||
mOnCompletedListener.onCompleted(success, mZipPath);
|
||||
}
|
||||
|
||||
private boolean zipFileAtPath(@NonNull String sourcePath, @NonNull String toLocation)
|
||||
{
|
||||
File sourceFile = new File(sourcePath);
|
||||
try(FileOutputStream dest = new FileOutputStream(toLocation, false);
|
||||
if (!sourceFile.isDirectory())
|
||||
return false;
|
||||
try (
|
||||
FileOutputStream dest = new FileOutputStream(toLocation, false);
|
||||
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)))
|
||||
{
|
||||
if (sourceFile.isDirectory())
|
||||
{
|
||||
zipSubFolder(out, sourceFile, sourceFile.getParent().length());
|
||||
}
|
||||
else
|
||||
{
|
||||
try(FileInputStream fi = new FileInputStream(sourcePath);
|
||||
BufferedInputStream origin = new BufferedInputStream(fi, BUFFER_SIZE))
|
||||
{
|
||||
ZipEntry entry = new ZipEntry(getLastPathComponent(sourcePath));
|
||||
out.putNextEntry(entry);
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
int count;
|
||||
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
out.write(data, 0, count);
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
Log.e(TAG, "Failed to read zip file entry '" + sourcePath +"' to location '"
|
||||
+ toLocation + "'", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
zipSubFolder(out, sourceFile, sourceFile.getParent().length());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e(TAG, "Failed to zip file '" + sourcePath +"' to location '" + toLocation + "'", e);
|
||||
Log.e(TAG, "Failed to zip file '" + sourcePath + "' to location '" + toLocation + "'", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -95,7 +75,9 @@ class ZipLogsTask implements Runnable
|
|||
int basePathLength) throws IOException
|
||||
{
|
||||
File[] fileList = folder.listFiles();
|
||||
BufferedInputStream origin;
|
||||
if (fileList == null)
|
||||
throw new IOException("Can't get files list of " + folder.getPath());
|
||||
|
||||
for (File file : fileList)
|
||||
{
|
||||
if (file.isDirectory())
|
||||
|
@ -106,18 +88,19 @@ class ZipLogsTask implements Runnable
|
|||
{
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
String unmodifiedFilePath = file.getPath();
|
||||
String relativePath = unmodifiedFilePath
|
||||
.substring(basePathLength);
|
||||
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
|
||||
origin = new BufferedInputStream(fi, BUFFER_SIZE);
|
||||
ZipEntry entry = new ZipEntry(relativePath);
|
||||
out.putNextEntry(entry);
|
||||
int count;
|
||||
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1)
|
||||
String relativePath = unmodifiedFilePath.substring(basePathLength);
|
||||
try (
|
||||
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
|
||||
BufferedInputStream origin = new BufferedInputStream(fi, BUFFER_SIZE))
|
||||
{
|
||||
out.write(data, 0, count);
|
||||
ZipEntry entry = new ZipEntry(relativePath);
|
||||
out.putNextEntry(entry);
|
||||
int count;
|
||||
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1)
|
||||
{
|
||||
out.write(data, 0, count);
|
||||
}
|
||||
}
|
||||
origin.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,19 +113,27 @@ class ZipLogsTask implements Runnable
|
|||
return segments[segments.length - 1];
|
||||
}
|
||||
|
||||
private void saveSystemLogcat()
|
||||
private void saveSystemLogcat(String path)
|
||||
{
|
||||
String fullName = StorageUtils.getLogsFolder(mApplication) + File.separator + "logcat.log";
|
||||
final File file = new File(fullName);
|
||||
InputStreamReader reader = null;
|
||||
FileWriter writer = null;
|
||||
final String cmd = "logcat -d -v time";
|
||||
Process process;
|
||||
try
|
||||
{
|
||||
writer = new FileWriter(file);
|
||||
process = Runtime.getRuntime().exec(cmd);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.e(TAG, "Failed to get system logcat", e);
|
||||
return;
|
||||
}
|
||||
|
||||
path += File.separator + "logcat.log";
|
||||
final File file = new File(path);
|
||||
try (
|
||||
InputStreamReader reader = new InputStreamReader(process.getInputStream());
|
||||
FileWriter writer = new FileWriter(file))
|
||||
{
|
||||
FileLoggerStrategy.WriteTask.writeSystemInformation(mApplication, writer);
|
||||
String cmd = "logcat -d -v time";
|
||||
Process process = Runtime.getRuntime().exec(cmd);
|
||||
reader = new InputStreamReader(process.getInputStream());
|
||||
char[] buffer = new char[10000];
|
||||
do
|
||||
{
|
||||
|
@ -154,12 +145,8 @@ class ZipLogsTask implements Runnable
|
|||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
Log.e(TAG, "Failed to save system logcat to file: " + file, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Utils.closeSafely(writer);
|
||||
Utils.closeSafely(reader);
|
||||
LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
|
||||
LOGGER.e(TAG, "Failed to save system logcat to " + path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue