/* * Copyright 2014 Sebastiano Poggi and Francesco Pontillo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.frakbot.util.log; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import net.frakbot.global.Const; import java.util.concurrent.atomic.AtomicBoolean; /** * Log class for handling custom logic. * * @author Francesco Pontillo, Sebastiano Poggi */ @SuppressWarnings("UnusedDeclaration") public class FLog { /** Log tag max length, as defined by Android */ public static final int TAG_MAX_LENGTH = 23; private static final String SCRIPT_SETPROP = "setprop %1$s %2$s"; private static String TAG_PREFIX = null; private static LogLevel sForcedLevel = null; /* * You can change the default level by setting a system property: * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. * SUPPRESS will turn off all logging for your tag. */ private static boolean DEBUG = false; private static boolean VERBOSE = false; /** * Return code that indicates that the FLog class hasn't been * initialized yet. */ public static final int ERROR_NOT_INITIALIZED = Integer.MIN_VALUE; /** * Return code that indicates that the log has been filtered out * because of the current log level preferences. */ public static final int ERROR_FILTERED = Integer.MIN_VALUE + 1; private static final AtomicBoolean sInitialized = new AtomicBoolean(false); private static final Object sLock = new Object(); @SuppressWarnings("FieldCanBeLocal") private static SharedPreferences.OnSharedPreferenceChangeListener sDebugChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (Const.Preferences.DEBUG.equals(key)) { FLog.d("FLogPrefWatch", "Debug mode preference change detected."); boolean forceDebug = sharedPreferences.getBoolean(key, false); FLog.v("FLogPrefWatch", "New DEBUG value: " + forceDebug); setLogLevel(forceDebug ? LogLevel.DEBUG : null); } } }; private FLog() { } /** * Initialize the logging system. * * @param context The context used to initialize FLog */ public static void initLog(Context context) { if (sInitialized.get()) { FLog.d("FLog", "Trying to re-initialize FLog, ignoring"); return; } synchronized (sLock) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); prefs.registerOnSharedPreferenceChangeListener(sDebugChangeListener); // Initialize the log tag (we could remove all Context-requesting log methods...) getTag(context, "FLogInit"); sInitialized.set(true); } } /** * Uninitialize the logging system, disposing all unneeded resources. * * @param context The context used to uninitialize FLog */ public static void uninitLog(Context context) { if (!sInitialized.get()) { FLog.d("FLog", "Trying to uninitialize an uninitialized FLog, ignoring"); return; } synchronized (sLock) { if (sDebugChangeListener != null) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); prefs.unregisterOnSharedPreferenceChangeListener(sDebugChangeListener); } TAG_PREFIX = null; sInitialized.set(false); } } /** * Send a {@link android.util.Log#VERBOSE} log message. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int v(Context context, String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return VERBOSE ? Log.v(getTag(context, tag), msg) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#VERBOSE} log message. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int v(String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return VERBOSE ? Log.v(getTag(null, tag), msg) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#VERBOSE} log message and log the exception. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int v(Context context, String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return VERBOSE ? Log.v(getTag(context, tag), msg, tr) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#VERBOSE} log message and log the exception. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int v(String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return VERBOSE ? Log.v(getTag(null, tag), msg, tr) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#DEBUG} log message. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int d(Context context, String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return DEBUG ? Log.d(getTag(context, tag), msg) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#DEBUG} log message. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int d(String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return DEBUG ? Log.d(getTag(null, tag), msg) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#DEBUG} log message and log the exception. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int d(Context context, String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return DEBUG ? Log.d(getTag(context, tag), msg, tr) : ERROR_FILTERED; } /** * Send a {@link android.util.Log#DEBUG} log message and log the exception. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int d(String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return DEBUG ? Log.d(getTag(null, tag), msg, tr) : ERROR_FILTERED; } /** * Send an {@link android.util.Log#INFO} log message. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int i(Context context, String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.i(getTag(context, tag), msg); } /** * Send an {@link android.util.Log#INFO} log message. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int i(String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.i(getTag(null, tag), msg); } /** * Send a {@link android.util.Log#INFO} log message and log the exception. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int i(Context context, String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.i(getTag(context, tag), msg, tr); } /** * Send a {@link android.util.Log#INFO} log message and log the exception. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int i(String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.i(getTag(null, tag), msg, tr); } /** * Send a {@link android.util.Log#WARN} log message. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int w(Context context, String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.w(getTag(context, tag), msg); } /** * Send a {@link android.util.Log#WARN} log message. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int w(String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.w(getTag(null, tag), msg); } /** * Send a {@link android.util.Log#WARN} log message and log the exception. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int w(Context context, String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.w(getTag(context, tag), msg, tr); } /** * Send a {@link android.util.Log#WARN} log message and log the exception. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int w(String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.w(getTag(null, tag), msg, tr); } /** * Send a {@link android.util.Log#WARN} log message and log the exception. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param tr An exception to log */ public static int w(Context context, String tag, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.w(getTag(context, tag), tr); } /** * Send a {@link android.util.Log#WARN} log message and log the exception. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param tr An exception to log */ public static int w(String tag, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.w(getTag(null, tag), tr); } /** * Send an {@link android.util.Log#ERROR} log message. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int e(Context context, String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.e(getTag(context, tag), msg); } /** * Send an {@link android.util.Log#ERROR} log message. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int e(String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.e(getTag(null, tag), msg); } /** * Send a {@link android.util.Log#ERROR} log message and log the exception. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int e(Context context, String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.e(getTag(context, tag), msg, tr); } /** * Send a {@link android.util.Log#ERROR} log message and log the exception. * * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int e(String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.e(getTag(null, tag), msg, tr); } /** * What a Terrible Failure: Report a condition that should never happen. * The error will always be logged at level ASSERT with the call stack. * Depending on system configuration, a report may be added to the * {@link android.os.DropBoxManager} and/or the process may be terminated * immediately with an error dialog. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. * @param msg The message you would like logged. */ public static int wtf(Context context, String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.wtf(getTag(context, tag), msg); } /** * What a Terrible Failure: Report a condition that should never happen. * The error will always be logged at level ASSERT with the call stack. * Depending on system configuration, a report may be added to the * {@link android.os.DropBoxManager} and/or the process may be terminated * immediately with an error dialog. * * @param tag Used to identify the source of a log message. * @param msg The message you would like logged. */ public static int wtf(String tag, String msg) { if (!sInitialized.get()) return printNotInitializedError(); return Log.wtf(getTag(null, tag), msg); } /** * What a Terrible Failure: Report an exception that should never happen. * Similar to {@link android.util.Log#wtf(String, String)}, with an exception to log. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. * @param tr An exception to log. */ public static int wtf(Context context, String tag, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.wtf(getTag(context, tag), tr); } /** * What a Terrible Failure: Report an exception that should never happen. * Similar to {@link android.util.Log#wtf(String, String)}, with an exception to log. * * @param tag Used to identify the source of a log message. * @param tr An exception to log. */ public static int wtf(String tag, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.wtf(getTag(null, tag), tr); } /** * What a Terrible Failure: Report an exception that should never happen. * Similar to {@link android.util.Log#wtf(String, Throwable)}, with a message as well. * * @param context Used to retrieve the version number. * @param tag Used to identify the source of a log message. * @param msg The message you would like logged. * @param tr An exception to log. May be null. */ public static int wtf(Context context, String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.wtf(getTag(context, tag), msg, tr); } /** * What a Terrible Failure: Report an exception that should never happen. * Similar to {@link android.util.Log#wtf(String, Throwable)}, with a message as well. * * @param tag Used to identify the source of a log message. * @param msg The message you would like logged. * @param tr An exception to log. May be null. */ public static int wtf(String tag, String msg, Throwable tr) { if (!sInitialized.get()) return printNotInitializedError(); return Log.wtf(getTag(null, tag), msg, tr); } /** Updates the logging levels, by checking the system properties. */ @SuppressWarnings({"ConstantConditions", "PointlessBooleanExpression"}) public static void recheckLogLevels() { String tmpTag = Const.APP_NAME; if (tmpTag.length() > TAG_MAX_LENGTH) { tmpTag = tmpTag.substring(0, TAG_MAX_LENGTH); Log.w("FLog", "(recheck) The app name is too long, it's been trimmed: " + tmpTag); } VERBOSE = sForcedLevel == null ? Log.isLoggable(tmpTag, Log.VERBOSE) : sForcedLevel.toInt() == LogLevel.VERBOSE.toInt(); DEBUG = sForcedLevel == null ? Log.isLoggable(tmpTag, Log.DEBUG) : sForcedLevel.toInt() <= LogLevel.DEBUG.toInt(); } /** * Changes the log level for the app. * * @param level The forced logging level, or null to disable forcing. * * @return Returns true if the logging level has been changed, * false otherwise. */ public static boolean setLogLevel(LogLevel level) { synchronized (sLock) { sForcedLevel = level; recheckLogLevels(); } //noinspection ConstantConditions FLog.i("FLog", "Log level now forced to " + (level != null ? level.toString() : "[not forced]")); return true; } /** * Gets the log tag using the base log tag (the app name and version), * plus the specified log tag. * <p/> * The base log tag is prepared using the provided Context, if it's still * uninitialized. If the Context is null, we use the hardcoded * {@link Const#APP_NAME} constant, and postpone the log tag initialization. * * @param context The Context used if the base log tag has to be initialized * @param tagSuffix The tag suffix (specific log tag) * * @return Returns the full log tag, if possible, or some hacked together * version of it if the base log tag has not been initialized yet */ private static String getTag(Context context, String tagSuffix) { synchronized (sLock) { if (TAG_PREFIX != null) { // The TAG_PREFIX has been initialized, we're good to go! return TAG_PREFIX + (!TextUtils.isEmpty(tagSuffix) ? "-" + tagSuffix : ""); } if (context == null) { // This is the case where we still haven't inizialized TAG_PREFIX, and // we're still not able to. Fall back onto the hardcoded APP_NAME (no version). return Const.APP_NAME + (!TextUtils.isEmpty(tagSuffix) ? "-" + tagSuffix : ""); } // Get the app name and version PackageManager pm; String packageName; int versionCode; pm = context.getPackageManager(); packageName = context.getPackageName(); if (pm != null) { try { PackageInfo info = pm.getPackageInfo(packageName, 0); versionCode = info.versionCode; TAG_PREFIX = String.format("%s-v%d", Const.APP_NAME, versionCode); } catch (PackageManager.NameNotFoundException e) { // Unable to retrieve app info, solely rely on the hardcoded app name return Const.APP_NAME + (!TextUtils.isEmpty(tagSuffix) ? "-" + tagSuffix : ""); } } return TAG_PREFIX + (!TextUtils.isEmpty(tagSuffix) ? "-" + tagSuffix : ""); } } /** * Prints an error message in the logcat stating that FLog * hasn't yet been initialized. * * @return Always returns {@link #ERROR_NOT_INITIALIZED} */ private static int printNotInitializedError() { Log.w(getTag(null, "FLog"), "FLog not initialized yet! Get you shit together, man"); return ERROR_NOT_INITIALIZED; } }