Java tutorial
/** * Wi-Fi (pw.thedrhax.mosmetro, Moscow Wi-Fi autologin) * Copyright 2015 Dmitry Karikh <the.dr.hax@gmail.com> * * 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 pw.thedrhax.util; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.support.v4.content.FileProvider; import android.util.Log; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; import java.util.Map; import pw.thedrhax.mosmetro.R; public class Logger { public enum LEVEL { INFO, DEBUG } private static final Map<LEVEL, LinkedList<String>> logs = new HashMap<LEVEL, LinkedList<String>>() { { for (LEVEL level : LEVEL.values()) { put(level, new LinkedList<String>()); } } }; private static long last_timestamp = 0; private static String timestamp() { long diff = System.currentTimeMillis() - last_timestamp; last_timestamp = System.currentTimeMillis(); return diff > 9999 ? "[+>10s]" : String.format(Locale.ENGLISH, "[+%04d]", diff); } /* * Configuration */ private static boolean logcat = false; public static void configure(Context context) { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); logcat = settings.getBoolean("pref_debug_logcat", false); } /* * Inputs */ public static void log(LEVEL level, String message) { if (level == LEVEL.DEBUG) { message = timestamp() + " " + message; } synchronized (logs) { if (logcat && level == LEVEL.DEBUG) { Log.d("pw.thedrhax.mosmetro", message); } logs.get(level).add(message); } for (Callback callback : callbacks.values()) { callback.call(level, message); } } public static void log(LEVEL level, Throwable ex) { log(level, Log.getStackTraceString(ex)); } public static void log(String message) { for (LEVEL level : LEVEL.values()) { log(level, message); } } public static void log(Object obj, String message) { log(LEVEL.DEBUG, String.format(Locale.ENGLISH, "%s (%d) | %s", obj.getClass().getSimpleName(), System.identityHashCode(obj), message)); } /* * Logger Utils */ public static void date(String prefix) { log(prefix + DateFormat.getDateTimeInstance().format(new Date())); } public static void wipe() { synchronized (logs) { for (LEVEL level : LEVEL.values()) { logs.get(level).clear(); } } } /* * Outputs */ public static LinkedList<String> read(LEVEL level) { synchronized (logs) { return new LinkedList<>(logs.get(level)); } } public static String toString(LEVEL level) { StringBuilder result = new StringBuilder(); for (String message : read(level)) { result.append(message).append("\n"); } return result.toString(); } /** * Log sharing routines */ public static Uri writeToFile(Context context) throws IOException { File log_file = new File(context.getFilesDir(), "pw.thedrhax.mosmetro.txt"); FileWriter writer = new FileWriter(log_file); writer.write(toString(Logger.LEVEL.DEBUG)); writer.flush(); writer.close(); return FileProvider.getUriForFile(context, "pw.thedrhax.mosmetro.provider", log_file); } public static void share(Context context) { Intent share = new Intent(Intent.ACTION_SEND).setType("text/plain") .putExtra(Intent.EXTRA_EMAIL, new String[] { context.getString(R.string.report_email_address) }) .putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.report_email_subject, Version.getFormattedVersion())); try { share.putExtra(Intent.EXTRA_STREAM, writeToFile(context)); } catch (IOException ex) { Logger.log(Logger.LEVEL.DEBUG, ex); Logger.log(context.getString(R.string.error, context.getString(R.string.error_log_file))); share.putExtra(Intent.EXTRA_TEXT, read(Logger.LEVEL.DEBUG)); } context.startActivity(Intent.createChooser(share, context.getString(R.string.report_choose_client))); } /** * Callback interface * * This abstract class will receive messages from another thread * and forward them safely to the current thread, so it can be * used with the Android UI. */ public static abstract class Callback { /** * Handler object used to forward messages between threads */ private Handler handler; protected Callback() { handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { log(LEVEL.valueOf(msg.getData().getString("level")), msg.getData().getString("message")); return true; } }); } /** * This method is being called from another thread * @param level One of values stored in LEVEL enum * @param message Text of the message being forwarded */ void call(LEVEL level, String message) { Bundle data = new Bundle(); data.putString("level", level.name()); data.putString("message", message); Message msg = new Message(); msg.setData(data); handler.sendMessage(msg); } /** * This method must be implemented by all sub classes * to be able to receive the forwarded messages. * @param level One of values stored in LEVEL enum * @param message Text of the message being forwarded */ public abstract void log(LEVEL level, String message); } /** * Map of registered Callback objects */ private static Map<Object, Callback> callbacks = new HashMap<>(); /** * Register the Callback object in the Logger * @param key Any object used to identify the Callback * @param callback Callback object to be registered */ public static void registerCallback(Object key, Callback callback) { callbacks.put(key, callback); } /** * Unregister the Callback object * @param key Any object used to identify the Callback */ public static void unregisterCallback(Object key) { callbacks.remove(key); } /** * Get the registered Callback by it's key object * @param key Any object used to identify the Callback * @return Callback object identified by this key */ public static Callback getCallback(Object key) { return callbacks.get(key); } }