Android Open Source - playnomics-android Session






From Project

Back to project page playnomics-android.

License

The source code is released under:

Apache License

If you think the Android project playnomics-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.playnomics.android.session;
// ww w .  j  a  v a2  s. co m
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import android.app.Activity;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.playnomics.android.client.IEventWorker;
import com.playnomics.android.client.IHttpConnectionFactory;
import com.playnomics.android.client.SegmentationClient;
import com.playnomics.android.events.AppPageEvent;
import com.playnomics.android.events.AppPauseEvent;
import com.playnomics.android.events.AppResumeEvent;
import com.playnomics.android.events.AppRunningEvent;
import com.playnomics.android.events.AppStartEvent;
import com.playnomics.android.events.CustomEvent;
import com.playnomics.android.events.ImplicitEvent;
import com.playnomics.android.events.TransactionEvent;
import com.playnomics.android.events.UserInfoEvent;
import com.playnomics.android.messaging.MessagingManager;
import com.playnomics.android.push.GcmManager;
import com.playnomics.android.push.GcmManager.ICloudMessagingHandler;
import com.playnomics.android.sdk.IGoogleCloudMessageConfig;
import com.playnomics.android.sdk.IPlacementDelegate;
import com.playnomics.android.sdk.IPlaynomicsSegmentationDelegate;
import com.playnomics.android.sdk.IPushConfig;
import com.playnomics.android.sdk.IPushNotificationDelegate;
import com.playnomics.android.util.CacheFile;
import com.playnomics.android.util.CacheFile.ICacheFileHandler;
import com.playnomics.android.util.ContextWrapper;
import com.playnomics.android.util.EventTime;
import com.playnomics.android.util.IConfig;
import com.playnomics.android.util.LargeGeneratedId;
import com.playnomics.android.util.Logger;
import com.playnomics.android.util.Logger.LogLevel;
import com.playnomics.android.util.Util;

public class Session implements SessionStateMachine, TouchEventHandler,
    HeartBeatHandler, ICallbackProcessor, ICloudMessagingHandler {
  // session
  private SessionState sessionState;

  private final Object syncLock = new Object();
  public SessionState getSessionState() {
    synchronized (syncLock) {
      return sessionState;
    }
  }
  
  private void setSessionState(SessionState sessionState){
    synchronized (syncLock) {
      this.sessionState = sessionState;
    }
  }

  private Logger logger;
  private IEventWorker eventWorker;
  private Util util;
  private IConfig config;
  private ContextWrapper contextWrapper;
  private IActivityObserver observer;
  private IHeartBeatProducer producer;
  private MessagingManager messagingManager;
  private CacheFile cacheFile;
  //push notifications
  
  private String unprocessedRegistrationId;
  private String unprocessedPushInteractionUrl;
  
  private IPushNotificationDelegate notificationDelegate;
  
  //session data
  private Long applicationId;

  public Long getApplicationId() {
    return applicationId;
  }

  private String userId;

  public String getUserId(){
    return userId;
  }
  
  public String getAndroidId() {
    return androidId;
  }

  private String androidId;

  private LargeGeneratedId sessionId;

  LargeGeneratedId getSessionId() {
    return sessionId;
  }

  private LargeGeneratedId instanceId;

  LargeGeneratedId getInstanceId() {
    return instanceId;
  }

  private EventTime sessionStartTime;

  EventTime getSessionStartTime() {
    return sessionStartTime;
  }

  private EventTime sessionPauseTime;

  EventTime getSessionPauseTime() {
    return sessionPauseTime;
  }

  private AtomicInteger sequence;
  private AtomicInteger touchEvents;
  private AtomicInteger allTouchEvents;  
  
  public void setOverrideEventsUrl(String eventsUrl){
    config.setOverrideEventsUrl(eventsUrl);
  }
  
  public void setOverrideMessagingUrl(String messagingUrl){
    config.setOverrideMessagingUrl(messagingUrl);
  }
  
  public void setLogLevel(LogLevel logLevel){
    logger.setLogLevel(logLevel);
  }
  
  public Session(IConfig config, Util util,
      IHttpConnectionFactory connectionFactory, Logger logger,
      IEventWorker eventWorker,
      IActivityObserver activityObserver, IHeartBeatProducer producer,
      MessagingManager messagingManager, CacheFile cacheFile) {
    this.logger = logger;
    this.sessionState = SessionState.NOT_STARTED;
    this.util = util;
    this.config = config;
    this.eventWorker = eventWorker;
    this.observer = activityObserver;
    this.producer = producer;
    this.messagingManager = messagingManager;
    // this is done to make Session more testable
    // we want MessagingManager to be mocked out when we start the session
    this.messagingManager.setSession(this);
    this.cacheFile = cacheFile;
  }
  
  public void enablePushNotifications(IPushConfig pushConfig, IPushNotificationDelegate notificationDelegate){
    this.notificationDelegate = notificationDelegate;
    if(  (pushConfig instanceof IGoogleCloudMessageConfig) == false )
      return;

    if(  (util.isGooglePlaySdkAvailable() == false) ||
      (contextWrapper.pushSettingsOutdated() == false) ) {
      return;
    }

    try{
      IGoogleCloudMessageConfig gcmConfig = (IGoogleCloudMessageConfig) pushConfig;
      //Google Cloud Messaging;
      GcmManager manager = new GcmManager(logger, util, this, gcmConfig);
      //settings are out-dated, so we need to get a new registration ID
      int resultCode = util.getGooglePlayServiceStatus(contextWrapper.getContext());

      if (resultCode != ConnectionResult.SUCCESS) {
        if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
          logger.log(LogLevel.WARNING, "Google Play Services are not up-to-date on this device.");
          notificationDelegate.onPushRegistrationFailure(resultCode);
        } else {
          logger.log(LogLevel.ERROR, "Google Play Services are not available on this device.");
          notificationDelegate.onPushRegistrationFailure();
        }
        return;
      }

      manager.preformRegistration(contextWrapper.getContext());
    } catch(Exception ex){
      logger.log(LogLevel.ERROR, ex, "Could not enable push notifications");
    }
  }

  public void start(ContextWrapper contextWrapper, Long applicationId, String userId) {
    try {
      if (getSessionState() == SessionState.STARTED || getSessionState() == SessionState.PAUSED) {
        return;
      }
      
      this.applicationId = applicationId;
      if (this.applicationId == null) {
        throw new NullPointerException("Application ID must be set");
      }
      
      this.userId = userId;

      setSessionState(SessionState.STARTED);
      this.contextWrapper = contextWrapper;
      
      androidId = util.getDeviceIdFromContext(contextWrapper.getContext());
      
      if (Util.stringIsNullOrEmpty(this.userId)) {
        this.userId = androidId;
      }

      sequence = new AtomicInteger(1);
      touchEvents = new AtomicInteger(0);
      allTouchEvents = new AtomicInteger(0);

      // send appRunning or appPage
      LargeGeneratedId lastSessionId = contextWrapper
          .getPreviousSessionId();

      EventTime lastEventTime = contextWrapper.getLastEventTime();
      GregorianCalendar threeMinutesAgo = new GregorianCalendar(
          Util.TIME_ZONE_GMT);
      threeMinutesAgo.add(Calendar.MINUTE, -3);

      boolean sessionLapsed = lastEventTime == null || 
          (lastEventTime != null && lastEventTime.compareTo(threeMinutesAgo) < 0) || 
          lastSessionId == null;

      ImplicitEvent implicitEvent;
      if (sessionLapsed) {
        sessionId = new LargeGeneratedId(util);
        instanceId = sessionId;

        implicitEvent = new AppStartEvent(config, getSessionInfo(),
            instanceId);
        sessionStartTime = implicitEvent.getEventTime();
        contextWrapper.setLastSessionStartTime(sessionStartTime);
        contextWrapper.setPreviousSessionId(sessionId);
      } else {
        sessionId = lastSessionId;
        instanceId = new LargeGeneratedId(util);
        implicitEvent = new AppPageEvent(config, getSessionInfo(),
            instanceId);
        sessionStartTime = contextWrapper.getLastSessionStartTime();
      }

      eventWorker.enqueueEvent(implicitEvent);

      lookForVersionChange();

      if(unprocessedRegistrationId != null){
        //if we received a push registration ID 
        //before we could start the session, send this registration ID
        //to the API
        onDeviceRegistered(unprocessedRegistrationId);
        unprocessedRegistrationId = null;
      }

      if(unprocessedPushInteractionUrl != null){
        //if we received a push notification before the session was started,
        //so process it now
        onPushNotificationInteracted(unprocessedPushInteractionUrl);
        unprocessedPushInteractionUrl = null;
      }
      
      eventWorker.start();
      producer.start(this);
      observer.setStateMachine(this);

      cacheFile.setContext(contextWrapper.getContext());      
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not start session");
      setSessionState(SessionState.NOT_STARTED);
    }
  }

  public void pause() {
    try {
      if (sessionState != SessionState.STARTED || applicationId == null) {
        return;
      }
      if (producer.isRunningForLongTime()) {
        // running for a long time queue appRunning
        queueAppRunningEvent();
      }
      producer.stop();

      sessionPauseTime = new EventTime();
      AppPauseEvent event = new AppPauseEvent(config, getSessionInfo(),
          instanceId, sessionStartTime, sequence.get(),
          touchEvents.get(), allTouchEvents.get(),
          producer.getHeartBeatIntervalInMinutes());
      sequence.incrementAndGet();
      eventWorker.enqueueEvent(event);
      eventWorker.stop();

      Set<String> unprocessUrls = eventWorker.getAllUnprocessedEvents();
      Runnable writeTask = cacheFile.writeSetToFile(unprocessUrls);
      util.startTaskOnBackgroundThread(writeTask);
      
      setSessionState(SessionState.PAUSED);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not pause the session");
    }
  }

  public void resume() {
    try {
      if (getSessionState() != SessionState.PAUSED || applicationId == null) {
        return;
      }

      GregorianCalendar thirtyMinutesAgo = new GregorianCalendar(
          Util.TIME_ZONE_GMT);
      thirtyMinutesAgo.add(Calendar.MINUTE, -config.getAppPauseTimeoutMinutes());
      
      if(sessionPauseTime.compareTo(thirtyMinutesAgo) < 0){
        //the session has been paused for longer than 30 minutes
        //we should treat this as a completely new session
        setSessionState(SessionState.NOT_STARTED);
        start(contextWrapper, applicationId, userId);
        return;
      }
      
      AppResumeEvent event = new AppResumeEvent(config, getSessionInfo(),
          instanceId, sessionStartTime, sessionPauseTime,
          sequence.get());
      eventWorker.enqueueEvent(event);
      eventWorker.start();
      producer.start(this);
      
      ICacheFileHandler handler = new ICacheFileHandler() {
        public void onReadSetComplete(Set<String> data) {
          if(data != null){
            for(String url : data){
              eventWorker.enqueueEventUrl(url);
            }
          }
        }
      };
      
      Runnable readTask = cacheFile.readSetFromFile(handler);
      util.startTaskOnBackgroundThread(readTask);

      setSessionState(SessionState.STARTED);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not resume the session");
    }
  }

  public void queueAppRunningEvent() {
    try {
      sequence.incrementAndGet();
      AppRunningEvent event = new AppRunningEvent(config,
          getSessionInfo(), instanceId, sessionStartTime,
          sequence.get(), touchEvents.get(), allTouchEvents.get(),
          producer.getHeartBeatIntervalInMinutes());
      eventWorker.enqueueEvent(event);
      //reset the touch events
      touchEvents.set(0);
      //update the last event time
      contextWrapper.setLastEventTime(event.getEventTime());
    } catch (UnsupportedEncodingException exception) {
      logger.log(LogLevel.ERROR, exception, "Could not log appRunning");
    }
  }

  public void onHeartBeat(long heartbeatIntervalInMillSeconds) {
    queueAppRunningEvent();
  }

  public void onTouchEventReceived() {
    touchEvents.incrementAndGet();
    allTouchEvents.incrementAndGet();
  }

  private GameSessionInfo getSessionInfo() {
    return new GameSessionInfo(applicationId, userId, androidId, sessionId);
  }

  private void assertSessionStarted(String name) {
    if (!(getSessionState() == SessionState.STARTED || getSessionState() == SessionState.PAUSED)) {
      throw new IllegalStateException("Session must be started " + name);
    }
  }

  // explicit events
  public void transactionInUSD(float priceInUSD, int quantity) {
    try {
      assertSessionStarted("transactionInUSD");
      TransactionEvent event = new TransactionEvent(config, util,
          getSessionInfo(), quantity, priceInUSD);
      eventWorker.enqueueEvent(event);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not send transaction");
    }
  }

  public void attributeInstall(String source, String campaign,
      Date installDate) {
    try {
      assertSessionStarted("attributeInstall");
      UserInfoEvent event = new UserInfoEvent(config, getSessionInfo(),
          source, campaign, installDate);
      eventWorker.enqueueEvent(event);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex,
          "Could not send install attribution information");
    }
  }

  public void customEvent(String customEventName) {
    try {
      assertSessionStarted("customEvent");
      CustomEvent event = new CustomEvent(config, util, getSessionInfo(),
          customEventName);
      eventWorker.enqueueEvent(event);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not send custom event");
    }
  }

  // activity pause/resume
  public void onActivityResumed(Activity activity) {
    try {
      assertSessionStarted("onActivityResumed");
      observer.observeNewActivity(activity, this);
      messagingManager.onActivityResumed(activity);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not attach activity");
    }
  }

  public void onActivityPaused(Activity activity) {
    try {
      assertSessionStarted("onActivityPaused");
      observer.forgetLastActivity();
      messagingManager.onActivityPaused(activity);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not detach activity");
    }
  }

  public void processUrlCallback(String url) {
    eventWorker.enqueueEventUrl(url);
  }
  
  public void fetchUserSegmentIds(final IPlaynomicsSegmentationDelegate delegate) {
    SegmentationClient segmentationClient = new SegmentationClient(Session.this, config, logger);
    segmentationClient.fetchUserSegmentIds(delegate);
  }

  public void setUserGender(String gender) {
    try {
      assertSessionStarted("setUserGender");
      UserInfoEvent event = new UserInfoEvent(config,
          getSessionInfo());
      event.setGender(gender);
      eventWorker.enqueueEvent(event);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not send user gender");
    }
  }

  public void setUserBirthYear(int year) {
    try {
      assertSessionStarted("setUserBirthYear");
      UserInfoEvent event = new UserInfoEvent(config,
          getSessionInfo());
      event.setBirthYear(year);
      eventWorker.enqueueEvent(event);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not send user birth year");
    }
  }

  /* Messaging */
  public void preloadPlacements(String[] placementNames){
    try{
      assertSessionStarted("preloadPlacements");
      messagingManager.preloadPlacements(placementNames);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not preload placements");
    }
  }
  
  public void showPlacement(String placementName, Activity activity, IPlacementDelegate delegate){
    try{
      assertSessionStarted("showPlacement");
      messagingManager.showPlacement(placementName, activity, delegate);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not preload placements");
    }
  }
  
  public void hidePlacement(String placementName){
    try{
      assertSessionStarted("hidePlacement");
      messagingManager.hidePlacement(placementName);
    } catch (Exception ex) {
      logger.log(LogLevel.ERROR, ex, "Could not preload placements");
    }
  }

  @Override
  public void onDeviceRegistered(String registrationId) {
    try {
      
      if(sessionState == SessionState.NOT_STARTED){
        //if the session has not started, then just save the registration ID for later
        //we can't register yet, because the userId and applicationId are null
        unprocessedRegistrationId = registrationId;
        return;
      }
      
      logger.log(LogLevel.DEBUG, "Received pushRegistrationId: %s", registrationId);
      contextWrapper.setPushRegistrationId(registrationId);
      notificationDelegate.onPushRegistrationSuccess(registrationId);
      
      UserInfoEvent userInfoEvent = new UserInfoEvent(config, getSessionInfo(), registrationId);
      eventWorker.enqueueEvent(userInfoEvent);
      
    } catch (UnsupportedEncodingException ex) {
      logger.log(LogLevel.ERROR, ex, "Could not send pushRegistrationId to server %s", registrationId);
    }
  }

  @Override
  public void onDeviceRegistrationFailed(Exception exception) {
    logger.log(LogLevel.WARNING, exception, "Failed to get a registration ID");
    notificationDelegate.onPushRegistrationFailure(exception);
  }
  
  @Override
  public void onPushNotificationInteracted(String pushInteractedUrl){
    if(sessionState == SessionState.NOT_STARTED){
      unprocessedPushInteractionUrl = pushInteractedUrl;
    } else {
      StringBuilder builder = new StringBuilder(pushInteractedUrl);
      builder.append(String.format("&a=%d", applicationId));
      builder.append(String.format("&u=%s", userId));
      builder.append(String.format("&androidId=%s", getAndroidId()));
      builder.append(String.format("&pt=%s", contextWrapper.getPushRegistrationId()));
      processUrlCallback(builder.toString());
    }
  }

  private void lookForVersionChange() {
    boolean isAppVersionChanged = contextWrapper.isAppVersionChanged();
    boolean isAndroidVersionChanged = contextWrapper.isAndroidVersionChanged();
    if (isAppVersionChanged || isAndroidVersionChanged) {
      UserInfoEvent userInfoEvent = new UserInfoEvent(config, getSessionInfo());
      userInfoEvent.setAppVersion(contextWrapper.getApplicationVersionName());
      userInfoEvent.setDeviceModel(Util.getDeviceModel());
      userInfoEvent.setDeviceManufacturer(Util.getDeviceManufacturer());
      userInfoEvent.setDeviceOSVersion(Util.getAndroidOSVersion());
      try {
        eventWorker.enqueueEvent(userInfoEvent);
      } catch (UnsupportedEncodingException e) {
      }
    }
  }
}




Java Source Code List

com.playnomics.PlaynomicsMoreTestActivity.java
com.playnomics.PlaynomicsTestAppActivity.java
com.playnomics.RichDataFrameDelegate.java
com.playnomics.android.client.AssetClient.java
com.playnomics.android.client.EventQueue.java
com.playnomics.android.client.EventWorker.java
com.playnomics.android.client.HttpConnectionFactory.java
com.playnomics.android.client.IEventQueue.java
com.playnomics.android.client.IEventWorker.java
com.playnomics.android.client.IHttpConnectionFactory.java
com.playnomics.android.client.PlacementDataClient.java
com.playnomics.android.client.SegmentationClient.java
com.playnomics.android.events.AppPageEvent.java
com.playnomics.android.events.AppPauseEvent.java
com.playnomics.android.events.AppResumeEvent.java
com.playnomics.android.events.AppRunningEvent.java
com.playnomics.android.events.AppStartEvent.java
com.playnomics.android.events.CustomEvent.java
com.playnomics.android.events.ExplicitEvent.java
com.playnomics.android.events.ImplicitEvent.java
com.playnomics.android.events.PlaynomicsEvent.java
com.playnomics.android.events.TransactionEvent.java
com.playnomics.android.events.UserInfoEvent.java
com.playnomics.android.messaging.CloseButton.java
com.playnomics.android.messaging.HtmlAdFactory.java
com.playnomics.android.messaging.HtmlAd.java
com.playnomics.android.messaging.HtmlCloseButton.java
com.playnomics.android.messaging.MessagingManager.java
com.playnomics.android.messaging.NativeCloseButton.java
com.playnomics.android.messaging.Placement.java
com.playnomics.android.messaging.Position.java
com.playnomics.android.messaging.Target.java
com.playnomics.android.messaging.ui.IPlayViewFactory.java
com.playnomics.android.messaging.ui.PlayDialog.java
com.playnomics.android.messaging.ui.PlayViewFactory.java
com.playnomics.android.messaging.ui.PlayWebView.java
com.playnomics.android.messaging.ui.RenderTaskFactory.java
com.playnomics.android.push.GcmBroadcastReceiver.java
com.playnomics.android.push.GcmIntentService.java
com.playnomics.android.push.GcmManager.java
com.playnomics.android.sdk.IGoogleCloudMessageConfig.java
com.playnomics.android.sdk.IPlacementDelegate.java
com.playnomics.android.sdk.IPlaynomicsPlacementDelegate.java
com.playnomics.android.sdk.IPlaynomicsPlacementRawDelegate.java
com.playnomics.android.sdk.IPlaynomicsSegmentationDelegate.java
com.playnomics.android.sdk.IPushConfig.java
com.playnomics.android.sdk.IPushNotificationDelegate.java
com.playnomics.android.sdk.Playnomics.java
com.playnomics.android.segments.UserSegmentIds.java
com.playnomics.android.session.ActivityObserver.java
com.playnomics.android.session.GameSessionInfo.java
com.playnomics.android.session.HeartBeatHandler.java
com.playnomics.android.session.HeartBeatProducer.java
com.playnomics.android.session.IActivityObserver.java
com.playnomics.android.session.ICallbackProcessor.java
com.playnomics.android.session.IHeartBeatProducer.java
com.playnomics.android.session.SessionStateMachine.java
com.playnomics.android.session.Session.java
com.playnomics.android.session.TouchEventHandler.java
com.playnomics.android.session.WindowCallbackProxy.java
com.playnomics.android.util.AndroidLogger.java
com.playnomics.android.util.AsyncTaskRunner.java
com.playnomics.android.util.CacheFile.java
com.playnomics.android.util.Config.java
com.playnomics.android.util.ContextWrapper.java
com.playnomics.android.util.EventTime.java
com.playnomics.android.util.IAsyncCall.java
com.playnomics.android.util.IConfig.java
com.playnomics.android.util.IRandomGenerator.java
com.playnomics.android.util.LargeGeneratedId.java
com.playnomics.android.util.LogWriter.java
com.playnomics.android.util.Logger.java
com.playnomics.android.util.Util.java