package org.wikipedia.analytics; import android.support.annotation.NonNull; import android.text.format.DateUtils; import org.json.JSONObject; import org.wikipedia.WikipediaApp; import org.wikipedia.history.HistoryEntry; import org.wikipedia.settings.Prefs; import org.wikipedia.settings.RbSwitch; import org.wikipedia.util.ReleaseUtil; public class SessionFunnel extends Funnel { /** * Definition of a "session timeout", as agreed upon by the Apps and Analytics teams. * (currently 30 minutes) */ public static final int DEFAULT_SESSION_TIMEOUT = 30; public static final int MIN_SESSION_TIMEOUT = 1; private static final String SCHEMA_NAME = "MobileWikiAppSessions"; private static final int REVISION = 15522505; private SessionData sessionData; private long leadSectionStartTime; private long restSectionsStartTime; public SessionFunnel(WikipediaApp app) { super(app, SCHEMA_NAME, REVISION, ReleaseUtil.isProdRelease() ? Funnel.SAMPLE_LOG_100 : Funnel.SAMPLE_LOG_ALL); sessionData = Prefs.getSessionData(); if (sessionData.getStartTime() == 0) { long now = System.currentTimeMillis(); sessionData.setStartTime(now); sessionData.setLastTouchTime(now); } touchSession(); } /** * Save the state of the current session. To be called when the main Activity is stopped, * so that we don't have to save its state every time a single parameter is modified. */ public void persistSession() { Prefs.setSessionData(sessionData); } @Override protected void preprocessSessionToken(@NonNull JSONObject eventData) { } /** * Update the timestamp for the current session. If the last-updated time is older than the * defined timeout period, then consider the current session as over, and send the event! */ public void touchSession() { long now = System.currentTimeMillis(); if (hasTimedOut()) { logSessionData(); // start a new session by clearing everything. sessionData = new SessionData(); sessionData.setStartTime(now); } sessionData.setLastTouchTime(now); } public void pageViewed(HistoryEntry entry) { touchSession(); sessionData.addPageViewed(entry); } public void backPressed() { touchSession(); sessionData.addPageFromBack(); } public void leadSectionFetchStart() { leadSectionStartTime = System.currentTimeMillis(); } public void leadSectionFetchEnd() { sessionData.addLeadLatency(System.currentTimeMillis() - leadSectionStartTime); } public void restSectionsFetchStart() { restSectionsStartTime = System.currentTimeMillis(); } public void restSectionsFetchEnd() { sessionData.addRestLatency(System.currentTimeMillis() - restSectionsStartTime); } private boolean hasTimedOut() { return System.currentTimeMillis() - sessionData.getLastTouchTime() > Prefs.getSessionTimeout() * DateUtils.MINUTE_IN_MILLIS; } private void logSessionData() { long sessionLengthSeconds = (sessionData.getLastTouchTime() - sessionData.getStartTime()) / DateUtils.SECOND_IN_MILLIS; log( "length", sessionLengthSeconds, "fromSearch", sessionData.getPagesFromSearch(), "fromRandom", sessionData.getPagesFromRandom(), "fromLanglink", sessionData.getPagesFromLangLink(), "fromInternal", sessionData.getPagesFromInternal(), "fromExternal", sessionData.getPagesFromExternal(), "fromHistory", sessionData.getPagesFromHistory(), "fromReadingList", sessionData.getPagesFromReadingList(), "fromNearby", sessionData.getPagesFromNearby(), "fromDisambig", sessionData.getPagesFromDisambig(), "fromBack", sessionData.getPagesFromBack(), "totalPages", sessionData.getTotalPages(), "leadLatency", sessionData.getLeadLatency(), "restLatency", sessionData.getRestLatency(), "apiMode", RbSwitch.INSTANCE.isRestBaseEnabled() ? 1 : 0 ); } }