@Override
  /**
   * When this activity stops, this method fires the javascript 'blur' and 'stop' events. Blur
   * events will only fire if the activity is not a tab activity.
   */
  protected void onStop() {
    inForeground = false;
    super.onStop();

    Log.d(TAG, "Activity " + this + " onStop", Log.DEBUG_MODE);

    if (getTiApp().isRestartPending()) {
      if (!isFinishing()) {
        finish();
      }
      return;
    }

    if (activityProxy != null) {
      activityProxy.fireSyncEvent(TiC.EVENT_STOP, null);
    }

    synchronized (lifecycleListeners.synchronizedList()) {
      for (OnLifecycleEvent listener : lifecycleListeners.nonNull()) {
        try {
          TiLifecycle.fireLifecycleEvent(this, listener, TiLifecycle.LIFECYCLE_ON_STOP);

        } catch (Throwable t) {
          Log.e(TAG, "Error dispatching lifecycle event: " + t.getMessage(), t);
        }
      }
    }
    KrollRuntime.suggestGC();
  }
  @Override
  /**
   * When this activity starts, this method updates the current activity to this if necessary and
   * fire javascript 'start' and 'focus' events. Focus events will only fire if the activity is not
   * a tab activity.
   */
  protected void onStart() {
    inForeground = true;
    super.onStart();
    if (isFinishing()) {
      return;
    }

    // Newer versions of Android appear to turn this on by default.
    // Turn if off until an activity indicator is shown.
    setProgressBarIndeterminateVisibility(false);

    Log.d(TAG, "Activity " + this + " onStart", Log.DEBUG_MODE);

    TiApplication tiApp = getTiApp();

    if (tiApp.isRestartPending()) {
      if (!isFinishing()) {
        finish();
      }
      return;
    }

    updateTitle();

    if (activityProxy != null) {
      // we only want to set the current activity for good in the resume state but we need it right
      // now.
      // save off the existing current activity, set ourselves to be the new current activity
      // temporarily
      // so we don't run into problems when we give the proxy the event
      Activity tempCurrentActivity = tiApp.getCurrentActivity();
      tiApp.setCurrentActivity(this, this);

      activityProxy.fireEvent(TiC.EVENT_START, null);

      // set the current activity back to what it was originally
      tiApp.setCurrentActivity(this, tempCurrentActivity);
    }

    synchronized (lifecycleListeners.synchronizedList()) {
      for (OnLifecycleEvent listener : lifecycleListeners.nonNull()) {
        try {
          TiLifecycle.fireLifecycleEvent(this, listener, TiLifecycle.LIFECYCLE_ON_START);

        } catch (Throwable t) {
          Log.e(TAG, "Error dispatching lifecycle event: " + t.getMessage(), t);
        }
      }
    }
    // store current configuration orientation
    // This fixed bug with double orientation chnage firing when activity starts in landscape
    previousOrientation = getResources().getConfiguration().orientation;
  }
  @Override
  /**
   * When this activity pauses, this method sets the current activity to null, fires a javascript
   * 'pause' event, and if the activity is finishing, remove all dialogs associated with it.
   */
  protected void onPause() {
    inForeground = false;
    super.onPause();
    isResumed = false;

    Log.d(TAG, "Activity " + this + " onPause", Log.DEBUG_MODE);

    TiApplication tiApp = getTiApp();
    if (tiApp.isRestartPending()) {
      releaseDialogs(true);
      if (!isFinishing()) {
        finish();
      }
      return;
    }

    if (!windowStack.empty()) {
      windowStack.peek().onWindowFocusChange(false);
    }

    TiApplication.updateActivityTransitionState(true);
    tiApp.setCurrentActivity(this, null);
    TiUIHelper.showSoftKeyboard(getWindow().getDecorView(), false);

    if (this.isFinishing()) {
      releaseDialogs(true);
    } else {
      // release non-persistent dialogs when activity hides
      releaseDialogs(false);
    }

    if (activityProxy != null) {
      activityProxy.fireSyncEvent(TiC.EVENT_PAUSE, null);
    }

    synchronized (lifecycleListeners.synchronizedList()) {
      for (OnLifecycleEvent listener : lifecycleListeners.nonNull()) {
        try {
          TiLifecycle.fireLifecycleEvent(this, listener, TiLifecycle.LIFECYCLE_ON_PAUSE);

        } catch (Throwable t) {
          Log.e(TAG, "Error dispatching lifecycle event: " + t.getMessage(), t);
        }
      }
    }

    // Checkpoint for ti.end event
    if (tiApp != null) {
      tiApp.postAnalyticsEvent(TiAnalyticsEventFactory.createAppEndEvent());
    }
  }
  @Override
  /**
   * When the activity resumes, this method updates the current activity to this and fires a
   * javascript 'resume' event.
   */
  protected void onResume() {
    inForeground = true;
    super.onResume();
    if (isFinishing()) {
      return;
    }

    Log.d(TAG, "Activity " + this + " onResume", Log.DEBUG_MODE);

    TiApplication tiApp = getTiApp();
    if (tiApp.isRestartPending()) {
      if (!isFinishing()) {
        finish();
      }
      return;
    }

    if (!windowStack.empty()) {
      windowStack.peek().onWindowFocusChange(true);
    }

    tiApp.setCurrentActivity(this, this);
    TiApplication.updateActivityTransitionState(false);

    if (activityProxy != null) {
      // Fire the sync event with a timeout, so the main thread won't be blocked too long to get an
      // ANR. (TIMOB-13253)
      activityProxy.fireSyncEvent(TiC.EVENT_RESUME, null, 4000);
    }

    synchronized (lifecycleListeners.synchronizedList()) {
      for (OnLifecycleEvent listener : lifecycleListeners.nonNull()) {
        try {
          TiLifecycle.fireLifecycleEvent(this, listener, TiLifecycle.LIFECYCLE_ON_RESUME);

        } catch (Throwable t) {
          Log.e(TAG, "Error dispatching lifecycle event: " + t.getMessage(), t);
        }
      }
    }

    isResumed = true;

    // Checkpoint for ti.start event
    String deployType = tiApp.getAppProperties().getString("ti.deploytype", "unknown");
    tiApp.postAnalyticsEvent(TiAnalyticsEventFactory.createAppStartEvent(tiApp, deployType));
  }
  @Override
  /**
   * When this activity is destroyed, this method removes it from the activity stack, performs clean
   * up, and fires javascript 'destroy' event.
   */
  protected void onDestroy() {
    Log.d(TAG, "Activity " + this + " onDestroy", Log.DEBUG_MODE);

    inForeground = false;
    TiApplication tiApp = getTiApp();
    // Clean up dialogs when activity is destroyed.
    releaseDialogs(true);

    if (tiApp.isRestartPending()) {
      super.onDestroy();
      if (!isFinishing()) {
        finish();
      }
      return;
    }

    synchronized (lifecycleListeners.synchronizedList()) {
      for (OnLifecycleEvent listener : lifecycleListeners.nonNull()) {
        try {
          TiLifecycle.fireLifecycleEvent(this, listener, TiLifecycle.LIFECYCLE_ON_DESTROY);

        } catch (Throwable t) {
          Log.e(TAG, "Error dispatching lifecycle event: " + t.getMessage(), t);
        }
      }
    }

    super.onDestroy();

    boolean isFinishing = isFinishing();

    // If the activity is finishing, remove the windowId and supportHelperId so the window and
    // supportHelper can be released.
    // If the activity is forced to destroy by Android OS, keep the windowId and supportHelperId so
    // the activity can be recovered.
    if (isFinishing) {
      int windowId = getIntentInt(TiC.INTENT_PROPERTY_WINDOW_ID, -1);
      TiActivityWindows.removeWindow(windowId);
      TiActivitySupportHelpers.removeSupportHelper(supportHelperId);
    }

    fireOnDestroy();

    if (layout instanceof TiCompositeLayout) {
      Log.e(TAG, "Layout cleanup.", Log.DEBUG_MODE);
      ((TiCompositeLayout) layout).removeAllViews();
    }
    layout = null;

    // LW windows
    if (window == null && view != null) {
      view.releaseViews();
      view = null;
    }

    if (window != null) {
      window.closeFromActivity(isFinishing);
      window = null;
    }

    if (menuHelper != null) {
      menuHelper.destroy();
      menuHelper = null;
    }

    if (activityProxy != null) {
      activityProxy.release();
      activityProxy = null;
    }

    // Don't dispose the runtime if the activity is forced to destroy by Android,
    // so we can recover the activity later.
    KrollRuntime.decrementActivityRefCount(isFinishing);
    KrollRuntime.suggestGC();
  }