private List<Map.Entry<String, String>> queryStringValues;
- private List<Map.Entry<String, Object>> jsonValues;
+ private List<Map.Entry<String, Object>> jsonObject;
+
+ private List<Event> events;
private List<Map.Entry<String, String>> cookieValues;
+ private static boolean ENABLED = false;
+
public GoogleAnalytics4()
{
this.reset();
}
+ private static void setEnabled(boolean b)
+ {
+ ENABLED = b;
+ }
+
public void sendAnalytics()
{
+ sendAnalytics(null);
+ }
+
+ public void sendAnalytics(String path)
+ {
+ if (!ENABLED)
+ {
+ Console.debug("GoogleAnalytics4 not enabled.");
+ return;
+ }
+ if (path != null)
+ {
+ addEvent("page_event", path);
+ }
+ addQueryStringValue("measurement_id", MEASUREMENT_ID);
+ addQueryStringValue("api_secret", API_SECRET);
+ addJsonValue("client_id", CLIENT_ID);
+ addJsonValues("events", Event.toObjectList(events));
+ // addJsonValue("events", )
StringBuilder sb = new StringBuilder();
sb.append(BASE_URL);
sb.append('?');
{
os.write(jsonBytes);
}
-
+ int responseCode = httpURLConnection.getResponseCode();
+ String responseMessage = httpURLConnection.getResponseMessage();
+ if (responseCode < 200 || responseCode > 299)
+ {
+ Console.warn("GoogleAnalytics4 connection failed: '" + responseCode
+ + " " + responseMessage + "'");
+ }
+ else
+ {
+ Console.debug("GoogleAnalytics4 connection succeeded: '"
+ + responseCode + " " + responseMessage + "'");
+ }
} catch (MalformedURLException e)
{
Console.debug(
"Couldn't cast URLConnection to HttpURLConnection in GoogleAnalytics4.",
e);
}
+ }
+ public void addEvent(String name, String... paramsStrings)
+ {
+ if (paramsStrings.length % 2 != 0)
+ {
+ Console.error(
+ "Cannot addEvent with odd number of paramsStrings. Ignoring.");
+ return;
+ }
+ Event event = new Event(name);
+ for (int i = 0; i < paramsStrings.length - 1; i += 2)
+ {
+ String key = paramsStrings[i];
+ String value = paramsStrings[i + 1];
+ event.addParam(key, value);
+ }
+ events.add(event);
}
- private void addToJson(String key, List<Object> values)
+ private void addJsonObject(String key,
+ List<Map.Entry<String, Object>> object)
{
- jsonValues.add(jsonEntry(key, values));
+ jsonObject.add(objectEntry(key, object));
}
- private void addToJson(String key, String value)
+ private void addJsonValues(String key, List<Object> values)
{
- jsonValues.add(jsonEntry(key, value));
+ jsonObject.add(objectEntry(key, values));
}
- private void addToJson(String key, int value)
+ private void addJsonValue(String key, String value)
{
- jsonValues.add(jsonEntry(key, Integer.valueOf(value)));
+ jsonObject.add(objectEntry(key, value));
}
- private void addToJson(String key, boolean value)
+ private void addJsonValue(String key, int value)
{
- jsonValues.add(jsonEntry(key, Boolean.valueOf(value)));
+ jsonObject.add(objectEntry(key, Integer.valueOf(value)));
+ }
+
+ private void addJsonValue(String key, boolean value)
+ {
+ jsonObject.add(objectEntry(key, Boolean.valueOf(value)));
}
private void addQueryStringValue(String key, String value)
{
- queryStringValues.add(qsEntry(key, value));
+ queryStringValues.add(stringEntry(key, value));
}
private void addCookieValue(String key, String value)
{
- cookieValues.add(qsEntry(key, value));
+ cookieValues.add(stringEntry(key, value));
}
public void reset()
{
- jsonValues = new ArrayList<>();
+ jsonObject = new ArrayList<>();
+ events = new ArrayList<Event>();
queryStringValues = new ArrayList<>();
cookieValues = new ArrayList<>();
}
private String buildJson()
{
StringBuilder sb = new StringBuilder();
- int indent = 0;
- sb.append("{");
- for (Map.Entry<String, Object> entry : jsonValues)
- {
-
- }
- sb.append("}");
+ addJsonObject(sb, 0, jsonObject);
return sb.toString();
}
private void addJsonObject(StringBuilder sb, int indent,
- Map.Entry<String, Object> entry)
+ List<Map.Entry<String, Object>> entries)
{
- String key = entry.getKey();
- Object value = entry.getValue();
indent(sb, indent);
- sb.append('"').append(key).append('"');
- sb.append(": ");
- if (List.class.equals(value.getClass()))
+ sb.append("{\n");
+ for (Map.Entry<String, Object> entry : entries)
{
- sb.append('\n');
+ String key = entry.getKey();
+ // TODO sensibly escape " characters in key
+ Object value = entry.getValue();
+ indent(sb, indent);
+ sb.append('"').append(key).append('"');
+ sb.append(": ");
+ if (List.class.equals(value.getClass()))
+ {
+ sb.append('\n');
+ }
+ addJsonValue(sb, indent + 1, value);
+ sb.append(",\n");
}
- addJsonValue(sb, indent, value);
- sb.append(",\n");
+ sb.append("}\n");
}
private void addJsonValue(StringBuilder sb, int indent, Object value)
Class<? extends Object> c = value.getClass();
if (Map.Entry.class.equals(c))
{
- Map.Entry<String, Object> object = (Map.Entry<String, Object>) value;
+ Map.Entry<String, Object> entry = (Map.Entry<String, Object>) value;
+ List<Map.Entry<String, Object>> object = new ArrayList<>();
+ object.add(entry);
addJsonObject(sb, indent + 1, object);
}
else if (List.class.equals(c))
{
- indent(sb, indent);
- sb.append("[\n");
- for (Object v : (List<Object>) value)
+ // list of Map.Entries or list of values?
+ List<Object> valueList = (List<Object>) value;
+ if (valueList.size() > 0
+ && Map.Entry.class.equals(valueList.get(0).getClass()))
+ {
+ // entries
+ indent(sb, indent);
+ List<Map.Entry<String, Object>> entryList = (List<Map.Entry<String, Object>>) value;
+ addJsonObject(sb, indent + 1, entryList);
+ }
+ else
{
- indent(sb, indent + 1);
- addJsonValue(sb, indent, v);
- sb.append(",\n");
+ // values
+ indent(sb, indent);
+ sb.append("[\n");
+ for (Object v : valueList)
+ {
+ indent(sb, indent + 1);
+ addJsonValue(sb, indent + 1, v);
+ sb.append(",\n");
+ }
+ indent(sb, indent);
+ sb.append("]");
}
- indent(sb, indent);
- sb.append("]");
}
else if (String.class.equals(c))
{
sb.append(" ".repeat(indent));
}
- private static Map.Entry<String, Object> jsonEntry(String s, Object o)
+ protected static Map.Entry<String, Object> objectEntry(String s, Object o)
{
return new AbstractMap.SimpleEntry<String, Object>(s, o);
}
- private static Map.Entry<String, String> qsEntry(String s, String v)
+ protected static Map.Entry<String, String> stringEntry(String s, String v)
{
return new AbstractMap.SimpleEntry<String, String>(s, v);
}
}
+
+class Event
+{
+ private String name;
+
+ private List<Map.Entry<String, String>> params;
+
+ @SafeVarargs
+ public Event(String name, Map.Entry<String, String>... paramEntries)
+ {
+ this.name = name;
+ this.params = new ArrayList<Map.Entry<String, String>>();
+ for (Map.Entry<String, String> paramEntry : paramEntries)
+ {
+ if (paramEntry == null)
+ {
+ continue;
+ }
+ params.add(paramEntry);
+ }
+ }
+
+ public void addParam(String param, String value)
+ {
+ params.add(GoogleAnalytics4.stringEntry(param, value));
+ }
+
+ protected List<Map.Entry<String, Object>> toObject()
+ {
+ List<Map.Entry<String, Object>> object = new ArrayList<>();
+ object.add(GoogleAnalytics4.objectEntry("name", (Object) name));
+ if (params.size() > 0)
+ {
+ object.add(GoogleAnalytics4.objectEntry("params", (Object) params));
+ }
+ return object;
+ }
+
+ protected static List<Object> toObjectList(List<Event> events)
+ {
+ List<Object> eventObjectList = new ArrayList<>();
+ for (Event event : events)
+ {
+ eventObjectList.add((Object) event.toObject());
+ }
+ return eventObjectList;
+ }
+}
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
+import jalview.analytics.GoogleAnalytics4;
import jalview.datamodel.PDBEntry;
import jalview.gui.Preferences;
import jalview.gui.UserDefinedColours;
protected static Class jgoogleanalyticstracker = null;
+ private static GoogleAnalytics4 gaTracker = null;
+
+ private static boolean useGA4 = true;
+
/**
* Initialise the google tracker if it is not done already.
*/
public static void initGoogleTracker()
{
- if (tracker == null)
+ if (useGA4)
+ {
+ String appName = ChannelProperties.getProperty("app_name")
+ + " Desktop";
+ String version = Cache.getProperty("VERSION") + "_"
+ + Cache.getDefault("BUILD_DATE", "unknown");
+ String path = "/"
+ + String.join("/", appName, version, APPLICATION_STARTED);
+ gaTracker = new GoogleAnalytics4();
+ gaTracker.sendAnalytics(path);
+ }
+ else
{
- if (jgoogleanalyticstracker == null)
+ if (tracker == null)
{
- // try to get the tracker class
+ if (jgoogleanalyticstracker == null)
+ {
+ // try to get the tracker class
+ try
+ {
+ jgoogleanalyticstracker = Cache.class.getClassLoader()
+ .loadClass(
+ "com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker");
+ trackerfocus = Cache.class.getClassLoader().loadClass(
+ "com.boxysystems.jgoogleanalytics.FocusPoint");
+ } catch (Exception e)
+ {
+ Console.debug(
+ "com.boxysystems.jgoogleanalytics package is not present - tracking not enabled.");
+ tracker = null;
+ jgoogleanalyticstracker = null;
+ trackerfocus = null;
+ return;
+ }
+ }
+ // now initialise tracker
+ Exception re = null, ex = null;
+ Error err = null;
+ String vrs = "No Version Accessible";
try
{
- jgoogleanalyticstracker = Cache.class.getClassLoader().loadClass(
- "com.boxysystems.jgoogleanalytics.JGoogleAnalyticsTracker");
- trackerfocus = Cache.class.getClassLoader()
- .loadClass("com.boxysystems.jgoogleanalytics.FocusPoint");
+ // Google analytics tracking code for Library Finder
+ tracker = jgoogleanalyticstracker
+ .getConstructor(new Class[]
+ { String.class, String.class, String.class })
+ .newInstance(new Object[]
+ { ChannelProperties.getProperty("app_name") + " Desktop",
+ (vrs = Cache.getProperty("VERSION") + "_"
+ + Cache.getDefault("BUILD_DATE", "unknown")),
+ "UA-9060947-1" });
+ jgoogleanalyticstracker
+ .getMethod("trackAsynchronously", new Class[]
+ { trackerfocus })
+ .invoke(tracker, new Object[]
+ { trackerfocus
+ .getConstructor(new Class[]
+ { String.class })
+ .newInstance(new Object[]
+ { APPLICATION_STARTED }) });
+ } catch (RuntimeException e)
+ {
+ re = e;
} catch (Exception e)
{
- Console.debug(
- "com.boxysystems.jgoogleanalytics package is not present - tracking not enabled.");
- tracker = null;
- jgoogleanalyticstracker = null;
- trackerfocus = null;
- return;
- }
- }
- // now initialise tracker
- Exception re = null, ex = null;
- Error err = null;
- String vrs = "No Version Accessible";
- try
- {
- // Google analytics tracking code for Library Finder
- tracker = jgoogleanalyticstracker
- .getConstructor(new Class[]
- { String.class, String.class, String.class })
- .newInstance(new Object[]
- { ChannelProperties.getProperty("app_name") + " Desktop",
- (vrs = Cache.getProperty("VERSION") + "_"
- + Cache.getDefault("BUILD_DATE", "unknown")),
- "UA-9060947-1" });
- jgoogleanalyticstracker
- .getMethod("trackAsynchronously", new Class[]
- { trackerfocus })
- .invoke(tracker, new Object[]
- { trackerfocus.getConstructor(new Class[] { String.class })
- .newInstance(new Object[]
- { "Application Started." }) });
- } catch (RuntimeException e)
- {
- re = e;
- } catch (Exception e)
- {
- ex = e;
- } catch (Error e)
- {
- err = e;
- }
- if (re != null || ex != null || err != null)
- {
- if (re != null)
+ ex = e;
+ } catch (Error e)
{
- Console.debug("Caught runtime exception in googletracker init:",
- re);
+ err = e;
}
- if (ex != null)
+ if (re != null || ex != null || err != null)
{
- Console.warn(
- "Failed to initialise GoogleTracker for Jalview Desktop with version "
- + vrs,
- ex);
+ if (re != null)
+ {
+ Console.debug("Caught runtime exception in googletracker init:",
+ re);
+ }
+ if (ex != null)
+ {
+ Console.warn(
+ "Failed to initialise GoogleTracker for Jalview Desktop with version "
+ + vrs,
+ ex);
+ }
+ if (err != null)
+ {
+ Console.error(
+ "Whilst initing GoogleTracker for Jalview Desktop version "
+ + vrs,
+ err);
+ }
}
- if (err != null)
+ else
{
- Console.error(
- "Whilst initing GoogleTracker for Jalview Desktop version "
- + vrs,
- err);
+ Console.debug("Successfully initialised tracker.");
}
}
- else
- {
- Console.debug("Successfully initialised tracker.");
- }
}
}
+ private static final String APPLICATION_STARTED = "Application Started.";
+
/**
* get the user's default colour if available
*
if (customProxySet &&
// we have a username but no password for the scheme being
// requested
- (protocol.equalsIgnoreCase("http")
- && (httpUser != null && httpUser.length() > 0
- && (httpPassword == null
- || httpPassword.length == 0)))
+ (protocol.equalsIgnoreCase("http")
+ && (httpUser != null
+ && httpUser.length() > 0
+ && (httpPassword == null
+ || httpPassword.length == 0)))
|| (protocol.equalsIgnoreCase("https")
&& (httpsUser != null
&& httpsUser.length() > 0