package net.sourceforge.filebot; import static com.dmurph.tracking.JGoogleAnalyticsTracker.GoogleAnalyticsVersion.*; import static net.sourceforge.filebot.Settings.*; import java.awt.DisplayMode; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.dmurph.tracking.AnalyticsConfigData; import com.dmurph.tracking.JGoogleAnalyticsTracker; import com.dmurph.tracking.VisitorData; import com.sun.jna.Platform; public class Analytics { private static JGoogleAnalyticsTracker tracker; private static VisitorData visitorData; private static String host = "filebot.sourceforge.net"; private static String currentView = null; private static boolean enabled = false; public static synchronized JGoogleAnalyticsTracker getTracker() { if (tracker != null) return tracker; // initialize tracker visitorData = restoreVisitorData(); tracker = new JGoogleAnalyticsTracker(getConfig(getApplicationProperty("analytics.WebPropertyID"), visitorData), V_4_7_2); // store session data on shutdown Runtime.getRuntime().addShutdownHook(new Thread("AnalyticsShutdownHook") { @Override public void run() { storeVisitorData(visitorData); JGoogleAnalyticsTracker.completeBackgroundTasks(2000); } }); return tracker; } public static void trackView(Class view, String title) { trackView(view.getName().replace('.', '/'), title); } public static synchronized void trackView(String view, String title) { if (!enabled) return; if (currentView == null) { // track application startup getTracker().trackPageViewFromSearch(view, title, host, getJavaRuntimeIdentifier(), getDeploymentMethod()); } else { // track application state change getTracker().trackPageViewFromReferrer(view, title, host, host, currentView); } currentView = view; } public static void trackEvent(String category, String action, String label) { trackEvent(category, action, label, null); } public static synchronized void trackEvent(String category, String action, String label, Integer value) { if (!enabled) return; getTracker().trackEvent(normalize(category), normalize(action), normalize(label), value); } private static String normalize(String label) { if (label == null) return null; // trim braces return label.replaceAll("[*()]", "").trim(); } public static synchronized void setEnabled(boolean b) { enabled = b; } private static String getDeploymentMethod() { return getApplicationDeployment() == null ? "fatjar" : getApplicationDeployment(); } private static AnalyticsConfigData getConfig(String webPropertyID, VisitorData visitorData) { AnalyticsConfigData config = new AnalyticsConfigData(webPropertyID, visitorData); config.setUserAgent(getUserAgent()); config.setEncoding(System.getProperty("file.encoding")); config.setUserLanguage(getUserLanguage()); try { if (GraphicsEnvironment.isHeadless()) { // headless environment config.setScreenResolution("80x25"); config.setColorDepth("1"); } else { // desktop environment GraphicsDevice[] display = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); config.setScreenResolution(getScreenResolution(display)); config.setColorDepth(getColorDepth(display)); } } catch (Throwable e) { // just in case Logger.getLogger(Analytics.class.getName()).log(Level.WARNING, e.getMessage(), e); } return config; } private static String getUserAgent() { String wm = null; String os = null; if (Platform.isWindows()) { wm = "Windows"; os = "Windows NT " + System.getProperty("os.version"); } else if (Platform.isX11()) { wm = "X11"; if (Platform.isLinux()) os = "Linux " + System.getProperty("os.arch"); else if (Platform.isSolaris()) os = "SunOS " + System.getProperty("os.version"); else if (Platform.isFreeBSD()) os = "FreeBSD"; else if (Platform.isOpenBSD()) os = "OpenBSD"; } else if (Platform.isMac()) { wm = "Macintosh"; os = System.getProperty("os.name"); } else { wm = System.getProperty("os.name"); os = System.getProperty("os.name") + " " + System.getProperty("os.version"); } return String.format("%s/%s (%s; U; %s; JRE %s)", getApplicationName(), getApplicationVersion(), wm, os, System.getProperty("java.version")); } private static String getUserLanguage() { String language = System.getProperty("user.language"); // user region or country String region = System.getProperty("user.region"); if (region == null) region = System.getProperty("user.country"); // return language string user language with or without user region if (region == null) return language; return language + "-" + region; } private static String getScreenResolution(GraphicsDevice[] display) { int screenHeight = 0; int screenWidth = 0; // get size of each screen for (int i = 0; i < display.length; i++) { DisplayMode dm = display[i].getDisplayMode(); screenWidth += dm.getWidth(); screenHeight += dm.getHeight(); } return screenWidth + "x" + screenHeight; } private static String getColorDepth(GraphicsDevice[] display) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < display.length; i++) { if (sb.length() > 0) { sb.append(", "); } sb.append(display[i].getDisplayMode().getBitDepth()); } return sb.toString(); } private static final String VISITOR_ID = "visitorId"; private static final String TIMESTAMP_FIRST = "timestampFirst"; private static final String TIMESTAMP_LAST = "timestampLast"; private static final String VISITS = "visits"; private synchronized static VisitorData restoreVisitorData() { try { // try to restore visitor Map data = getPersistentData(); int visitorId = Integer.parseInt(data.get(VISITOR_ID)); long timestampFirst = Long.parseLong(data.get(TIMESTAMP_FIRST)); long timestampLast = Long.parseLong(data.get(TIMESTAMP_LAST)); int visits = Integer.parseInt(data.get(VISITS)); return VisitorData.newSession(visitorId, timestampFirst, timestampLast, visits); } catch (Exception e) { // new visitor return VisitorData.newVisitor(); } } private synchronized static void storeVisitorData(VisitorData visitor) { Map data = getPersistentData(); data.put(VISITOR_ID, String.valueOf(visitor.getVisitorId())); data.put(TIMESTAMP_FIRST, String.valueOf(visitor.getTimestampFirst())); data.put(TIMESTAMP_LAST, String.valueOf(visitor.getTimestampPrevious())); data.put(VISITS, String.valueOf(visitor.getVisits())); } private static Map getPersistentData() { return Settings.forPackage(Analytics.class).node("analytics").asMap(); } /** * Dummy constructor to prevent instantiation */ private Analytics() { throw new UnsupportedOperationException(); } }