content: /**
   * Copyright 2010-present Facebook.
   *
   * 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 com.facebook.internal;
  
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.support.v4.content.LocalBroadcastManager;
  import com.facebook.Session;
  import com.facebook.SessionState;
  
  /**
   * com.facebook.internal is solely for the use of other packages within the Facebook SDK for Android. Use of
   * any of the classes in this package is unsupported, and they may be modified or removed without warning at
   * any time.
   */
  public class SessionTracker {
  
      private Session session;
      private final Session.StatusCallback callback;
      private final BroadcastReceiver receiver;
      private final LocalBroadcastManager broadcastManager;
      private boolean isTracking = false;
  
      /**
       * Constructs a SessionTracker to track the active Session object.
       * 
       * @param context the context object.
       * @param callback the callback to use whenever the active Session's 
       *                 state changes
       */
      public SessionTracker(Context context, Session.StatusCallback callback) {
          this(context, callback, null);
      }
      
      /**
       * Constructs a SessionTracker to track the Session object passed in.
       * If the Session is null, then it will track the active Session instead.
       * 
       * @param context the context object.
       * @param callback the callback to use whenever the Session's state changes
       * @param session the Session object to track
       */
      SessionTracker(Context context, Session.StatusCallback callback, Session session) {
          this(context, callback, session, true);
      }
      
      /**
       * Constructs a SessionTracker to track the Session object passed in.
       * If the Session is null, then it will track the active Session instead.
       * 
       * @param context the context object.
       * @param callback the callback to use whenever the Session's state changes
       * @param session the Session object to track
       * @param startTracking whether to start tracking the Session right away
       */
      public SessionTracker(Context context, Session.StatusCallback callback, Session session, boolean startTracking) {
          this.callback = new CallbackWrapper(callback);
          this.session = session;
          this.receiver = new ActiveSessionBroadcastReceiver();
          this.broadcastManager = LocalBroadcastManager.getInstance(context);
  
          if (startTracking) {
              startTracking();
          }
      }
  
      /**
       * Returns the current Session that's being tracked.
       * 
       * @return the current Session associated with this tracker
       */
      public Session getSession() {
          return (session == null) ? Session.getActiveSession() : session;
      }
  
      /**
       * Returns the current Session that's being tracked if it's open, 
       * otherwise returns null.
       * 
       * @return the current Session if it's open, otherwise returns null
       */
      public Session getOpenSession() {
          Session openSession = getSession();
          if (openSession != null && openSession.isOpened()) {
              return openSession;
          }
          return null;
      }
  
      /**
       * Set the Session object to track.
       * 
       * @param newSession the new Session object to track
       */
      public void setSession(Session newSession) {
          if (newSession == null) {
              if (session != null) {
                  // We're current tracking a Session. Remove the callback
                  // and start tracking the active Session.
                  session.removeCallback(callback);
                  session = null;
                  addBroadcastReceiver();
                  if (getSession() != null) {
                      getSession().addCallback(callback);
                  }
              }
          } else {
              if (session == null) {
                  // We're currently tracking the active Session, but will be
                  // switching to tracking a different Session object.
                  Session activeSession = Session.getActiveSession();
                  if (activeSession != null) {
                      activeSession.removeCallback(callback);
                  }
                  broadcastManager.unregisterReceiver(receiver);
              } else {
                  // We're currently tracking a Session, but are now switching 
                  // to a new Session, so we remove the callback from the old 
                  // Session, and add it to the new one.
                  session.removeCallback(callback);
              }
              session = newSession;
              session.addCallback(callback);
          }
      }
  
      /**
       * Start tracking the Session (either active or the one given). 
       */
      public void startTracking() {
          if (isTracking) {
              return;
          }
          if (this.session == null) {
              addBroadcastReceiver();
          }        
          // if the session is not null, then add the callback to it right away
          if (getSession() != null) {
              getSession().addCallback(callback);
          }
          isTracking = true;
      }
  
      /**
       * Stop tracking the Session and remove any callbacks attached
       * to those sessions.
       */
      public void stopTracking() {
          if (!isTracking) {
              return;
          }
          Session session = getSession();
          if (session != null) {
              session.removeCallback(callback);
          }
          broadcastManager.unregisterReceiver(receiver);
          isTracking = false;
      }
      
      /**
       * Returns whether it's currently tracking the Session.
       * 
       * @return true if currently tracking the Session
       */
      public boolean isTracking() {
          return isTracking;
      }
  
      /**
       * Returns whether it's currently tracking the active Session.
       *
       * @return true if the currently tracked session is the active Session.
       */
      public boolean isTrackingActiveSession() {
          return session == null;
      }
      
      private void addBroadcastReceiver() {
          IntentFilter filter = new IntentFilter();
          filter.addAction(Session.ACTION_ACTIVE_SESSION_SET);
          filter.addAction(Session.ACTION_ACTIVE_SESSION_UNSET);
          
          // Add a broadcast receiver to listen to when the active Session
          // is set or unset, and add/remove our callback as appropriate    
          broadcastManager.registerReceiver(receiver, filter);
      }
  
      /**
       * The BroadcastReceiver implementation that either adds or removes the callback
       * from the active Session object as it's SET or UNSET.
       */
      private class ActiveSessionBroadcastReceiver extends BroadcastReceiver {
          @Override
          public void onReceive(Context context, Intent intent) {
              if (Session.ACTION_ACTIVE_SESSION_SET.equals(intent.getAction())) {
                  Session session = Session.getActiveSession();
                  if (session != null) {
                      session.addCallback(SessionTracker.this.callback);
                  }
              }
          }
      }
  
      private class CallbackWrapper implements Session.StatusCallback {
  
          private final Session.StatusCallback wrapped;
          public CallbackWrapper(Session.StatusCallback wrapped) {
              this.wrapped = wrapped;
          }
  
          @Override
          public void call(Session session, SessionState state, Exception exception) {
              if (wrapped != null && isTracking()) {
                  wrapped.call(session, state, exception);
              }
              // if we're not tracking the Active Session, and the current session
              // is closed, then start tracking the Active Session.
              if (session == SessionTracker.this.session && state.isClosed()) {
                  setSession(null);
              }
          }
      }
  }
  