private boolean isPushNotificationPingNeeded() {
      if (mLastPingDate == null) {
        // first startup
        return false;
      }

      Date now = new Date();
      if (DateTimeUtils.secondsBetween(now, mLastPingDate) >= DEFAULT_TIMEOUT) {
        mLastPingDate = now;
        return true;
      }
      return false;
    }
    @Override
    public void onTrimMemory(final int level) {
      if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        // We're in the Background
        mIsInBackground = true;
        String lastActivityString = AppPrefs.getLastActivityStr();
        ActivityId lastActivity = ActivityId.getActivityIdFromName(lastActivityString);
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("last_visible_screen", lastActivity.toString());
        if (mApplicationOpenedDate != null) {
          Date now = new Date();
          properties.put("time_in_app", DateTimeUtils.secondsBetween(now, mApplicationOpenedDate));
          mApplicationOpenedDate = null;
        }
        AnalyticsTracker.track(AnalyticsTracker.Stat.APPLICATION_CLOSED, properties);
        AnalyticsTracker.endSession(false);
        onAppGoesToBackground();
      } else {
        mIsInBackground = false;
      }

      boolean evictBitmaps = false;
      switch (level) {
        case TRIM_MEMORY_COMPLETE:
        case TRIM_MEMORY_MODERATE:
        case TRIM_MEMORY_RUNNING_MODERATE:
        case TRIM_MEMORY_RUNNING_CRITICAL:
        case TRIM_MEMORY_RUNNING_LOW:
          evictBitmaps = true;
          break;
        default:
          break;
      }

      if (evictBitmaps && mBitmapCache != null) {
        mBitmapCache.evictAll();
      }
    }
    @Override
    protected void onPostExecute(Boolean result) {
      mIsPostTaskRunning = false;

      if (!isAdded()) {
        return;
      }

      if (!result) {
        // post couldn't be loaded which means it doesn't exist in db, so request it from
        // the server if it hasn't already been requested
        if (!mHasAlreadyRequestedPost) {
          mHasAlreadyRequestedPost = true;
          AppLog.i(T.READER, "reader post detail > post not found, requesting it");
          requestPost();
        }
        return;
      }

      if (!canShowFooter()) {
        mLayoutFooter.setVisibility(View.GONE);
      }

      // add padding to the scrollView to make room for the top and bottom toolbars - this also
      // ensures the scrollbar matches the content so it doesn't disappear behind the toolbars
      int topPadding = (mAutoHideToolbarListener != null ? mToolbarHeight : 0);
      int bottomPadding = (canShowFooter() ? mLayoutFooter.getHeight() : 0);
      mScrollView.setPadding(0, topPadding, 0, bottomPadding);

      // scrollView was hidden in onCreateView, show it now that we have the post
      mScrollView.setVisibility(View.VISIBLE);

      // render the post in the webView
      mRenderer = new ReaderPostRenderer(mReaderWebView, mPost);
      mRenderer.beginRender();

      txtTitle.setText(
          mPost.hasTitle() ? mPost.getTitle() : getString(R.string.reader_untitled_post));

      followButton.setVisibility(mIsLoggedOutReader ? View.GONE : View.VISIBLE);
      if (!mIsLoggedOutReader) {
        followButton.setIsFollowed(mPost.isFollowedByCurrentUser);
        followButton.setOnClickListener(
            new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                togglePostFollowed();
              }
            });
      }

      // clicking the header shows blog preview
      if (getPostListType() != ReaderPostListType.BLOG_PREVIEW) {
        layoutHeader.setOnClickListener(
            new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                ReaderActivityLauncher.showReaderBlogPreview(v.getContext(), mPost);
              }
            });
      }

      if (mPost.hasBlogName()) {
        txtBlogName.setText(mPost.getBlogName());
        txtBlogName.setVisibility(View.VISIBLE);
      } else if (mPost.hasBlogUrl()) {
        txtBlogName.setText(UrlUtils.getDomainFromUrl(mPost.getBlogUrl()));
        txtBlogName.setVisibility(View.VISIBLE);
      } else {
        txtBlogName.setVisibility(View.GONE);
      }

      int avatarSz = getResources().getDimensionPixelSize(R.dimen.avatar_sz_medium);
      if (mPost.hasBlogUrl()) {
        String imageUrl = GravatarUtils.blavatarFromUrl(mPost.getBlogUrl(), avatarSz);
        imgAvatar.setImageUrl(imageUrl, WPNetworkImageView.ImageType.BLAVATAR);
      } else {
        imgAvatar.setImageUrl(
            mPost.getPostAvatarForDisplay(avatarSz), WPNetworkImageView.ImageType.AVATAR);
      }

      if (mPost.hasAuthorName()) {
        txtAuthor.setText(mPost.getAuthorName());
        txtAuthor.setVisibility(View.VISIBLE);
      } else {
        txtAuthor.setVisibility(View.GONE);
      }

      String dateLine;
      if (mPost.hasBlogUrl()) {
        dateLine =
            UrlUtils.getDomainFromUrl(mPost.getBlogUrl())
                + " \u2022 "
                + DateTimeUtils.javaDateToTimeSpan(mPost.getDatePublished());
      } else {
        dateLine = DateTimeUtils.javaDateToTimeSpan(mPost.getDatePublished());
      }
      txtDateLine.setText(dateLine);

      final String tagToDisplay = mPost.getTagForDisplay(null);
      if (!TextUtils.isEmpty(tagToDisplay)) {
        txtTag.setText(ReaderUtils.makeHashTag(tagToDisplay));
        txtTag.setOnClickListener(
            new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                ReaderTag tag = new ReaderTag(tagToDisplay, ReaderTagType.FOLLOWED);
                ReaderActivityLauncher.showReaderTagPreview(v.getContext(), tag);
              }
            });
      }

      if (canShowFooter() && mLayoutFooter.getVisibility() != View.VISIBLE) {
        AniUtils.fadeIn(mLayoutFooter, AniUtils.Duration.LONG);
      }

      refreshIconCounts();
    }