/*
* Copyright (c) 2013, Psiphon Inc.
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ca.psiphon.ploggy;
import java.util.ArrayList;
import java.util.Date;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
/**
* Logging facility.
*
* TODO: consider using Log4J or Logback (http://tony19.github.io/logback-android/)
*
* Maintains a fixed-size queue of recent log entries. Posts events to observers of
* recent entries using the main UI thread for compatibility with ListView Adapters.
*/
public class Log {
private static final String LOG_TAG = "Log";
public static class Entry {
public final Date mTimestamp;
public final String mTag;
public final String mMessage;
public Entry(String tag, String message) {
mTimestamp = new Date();
mTag = tag;
mMessage = message;
}
}
public interface Observer {
void onUpdatedRecentEntries();
}
private static final int MAX_RECENT_ENTRIES = 500;
private static ArrayList<Entry> mRecentEntries;
private static ArrayList<Observer> mObservers;
private static Handler mHandler;
// TODO: explicit singleton?
public synchronized static void initialize() {
mRecentEntries = new ArrayList<Entry>();
mObservers = new ArrayList<Observer>();
mHandler = new Handler();
}
public synchronized static void addEntry(String tag, String message) {
if (message == null) {
message = "(null)";
}
Entry entry = new Entry(tag, message);
// Update the in-memory entry list on the UI thread (also
// notifies any ListView adapters subscribed to that list)
postAddEntry(entry);
// Temporary
android.util.Log.e("Ploggy", tag + " " + message);
}
public synchronized static int getRecentEntryCount() {
return mRecentEntries.size();
}
public synchronized static Entry getRecentEntry(int index) {
return mRecentEntries.get(index);
}
public synchronized static void registerObserver(Observer observer) {
if (!mObservers.contains(observer)) {
mObservers.add(observer);
}
}
public synchronized static void unregisterObserver(Observer observer) {
mObservers.remove(observer);
}
public synchronized static void composeEmail(Context context) {
// TODO: temporary feature for debugging prototype -- will compromise unlinkability
try {
StringBuilder body = new StringBuilder();
for (Entry entry : mRecentEntries) {
body.append(entry.mTimestamp);
body.append(" ");
body.append(entry.mTag);
body.append(": ");
body.append(entry.mMessage);
body.append("\n");
}
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("message/rfc822");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"feedback+ploggy@psiphon.ca"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Ploggy Logs");
intent.putExtra(Intent.EXTRA_TEXT, body.toString());
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.addEntry(LOG_TAG, e.getMessage());
Log.addEntry(LOG_TAG, "compose log email failed");
}
}
private static void postAddEntry(Entry entry) {
final Entry finalEntry = entry;
mHandler.post(
new Runnable() {
@Override
public void run() {
mRecentEntries.add(finalEntry);
while (mRecentEntries.size() > MAX_RECENT_ENTRIES) {
mRecentEntries.remove(0);
}
for (Observer observer : mObservers) {
observer.onUpdatedRecentEntries();
}
}
});
}
}