package ru.ltst.u2020mvp.data;
import android.app.Application;
import android.os.AsyncTask;
import android.util.Log;
import org.threeten.bp.LocalDateTime;
import java.io.File;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import javax.inject.Inject;
import okio.BufferedSink;
import okio.Okio;
import ru.ltst.u2020mvp.ApplicationScope;
import rx.Observable;
import rx.Subscriber;
import rx.subjects.PublishSubject;
import timber.log.Timber;
import static org.threeten.bp.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
@ApplicationScope
public final class LumberYard {
private static final int BUFFER_SIZE = 200;
private final Application app;
private final Deque<Entry> entries = new ArrayDeque<>(BUFFER_SIZE + 1);
private final PublishSubject<Entry> entrySubject = PublishSubject.create();
@Inject public LumberYard(Application app) {
this.app = app;
}
public Timber.Tree tree() {
return new Timber.DebugTree() {
@Override protected void log(int priority, String tag, String message, Throwable t) {
addEntry(new Entry(priority, tag, message));
}
};
}
private synchronized void addEntry(Entry entry) {
entries.addLast(entry);
if (entries.size() > BUFFER_SIZE) {
entries.removeFirst();
}
entrySubject.onNext(entry);
}
public List<Entry> bufferedLogs() {
return new ArrayList<>(entries);
}
public Observable<Entry> logs() {
return entrySubject;
}
/** Save the current logs to disk. */
public Observable<File> save() {
return Observable.create(new Observable.OnSubscribe<File>() {
@Override public void call(Subscriber<? super File> subscriber) {
File folder = app.getExternalFilesDir(null);
if (folder == null) {
subscriber.onError(new IOException("External storage is not mounted."));
return;
}
String fileName = ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
File output = new File(folder, fileName);
BufferedSink sink = null;
try {
sink = Okio.buffer(Okio.sink(output));
List<Entry> entries = bufferedLogs();
for (Entry entry : entries) {
sink.writeUtf8(entry.prettyPrint()).writeByte('\n');
}
subscriber.onNext(output);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
} finally {
if (sink != null) {
try {
sink.close();
} catch (IOException e) {
subscriber.onError(e);
}
}
}
}
});
}
/**
* Delete all of the log files saved to disk. Be careful not to call this before any intents have
* finished using the file reference.
*/
public void cleanUp() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... folders) {
File folder = app.getExternalFilesDir(null);
if (folder != null) {
for (File file : folder.listFiles()) {
if (file.getName().endsWith(".log")) {
file.delete();
}
}
}
return null;
}
}.execute();
}
public static final class Entry {
public final int level;
public final String tag;
public final String message;
public Entry(int level, String tag, String message) {
this.level = level;
this.tag = tag;
this.message = message;
}
public String prettyPrint() {
return String.format("%22s %s %s", tag, displayLevel(),
// Indent newlines to match the original indentation.
message.replaceAll("\\n", "\n "));
}
public String displayLevel() {
switch (level) {
case Log.VERBOSE:
return "V";
case Log.DEBUG:
return "D";
case Log.INFO:
return "I";
case Log.WARN:
return "W";
case Log.ERROR:
return "E";
case Log.ASSERT:
return "A";
default:
return "?";
}
}
}
}