private void displayNonLeadSection(int index) {
    activity.updateProgressBar(
        true,
        false,
        PageActivity.PROGRESS_BAR_MAX_VALUE / model.getPage().getSections().size() * index);

    try {
      final Page page = model.getPage();
      JSONObject wrapper = new JSONObject();
      wrapper.put("sequence", pageSequenceNum);
      if (index < page.getSections().size()) {
        JSONObject section = page.getSections().get(index).toJSON();
        wrapper.put("section", section);
        wrapper.put("index", index);
        if (sectionTargetFromIntent > 0 && sectionTargetFromIntent < page.getSections().size()) {
          // if we have a section to scroll to (from our Intent):
          wrapper.put("fragment", page.getSections().get(sectionTargetFromIntent).getAnchor());
        } else if (sectionTargetFromTitle != null) {
          // if we have a section to scroll to (from our PageTitle):
          wrapper.put("fragment", sectionTargetFromTitle);
        }
        Log.d(TAG, "Added request for section " + section.getString("line"));
      } else {
        wrapper.put("noMore", true);
      }
      // give it our expected scroll position, in case we need the page to be pre-scrolled upon
      // loading.
      wrapper.put(
          "scrollY", (int) (stagedScrollY / activity.getResources().getDisplayMetrics().density));
      bridge.sendMessage("displaySection", wrapper);
    } catch (JSONException e) {
      ACRA.getErrorReporter().handleException(e);
    }
  }
  private void setState(int state, int subState) {
    if (!fragment.isAdded()) {
      return;
    }
    this.state = state;
    this.subState = subState;
    activity.supportInvalidateOptionsMenu();

    // FIXME: Move this out into a PageComplete event of sorts
    if (state == STATE_COMPLETE_FETCH) {
      fragment.setupToC(model, isFirstPage());

      // add the page to cache!
      if (cacheOnComplete) {
        app.getPageCache()
            .put(
                model.getTitleOriginal(),
                model.getPage(),
                new PageCache.CachePutListener() {
                  @Override
                  public void onPutComplete() {}

                  @Override
                  public void onPutError(Throwable e) {
                    Log.e(TAG, "Failed to add page to cache.", e);
                  }
                });
      }
    }
  }
  private void displayLeadSection() {
    try {
      final Page page = model.getPage();
      final PageProperties pageProperties = page.getPageProperties();

      JSONObject marginPayload = new JSONObject();
      int margin =
          DimenUtil.roundedPxToDp(activity.getResources().getDimension(R.dimen.content_margin));
      marginPayload.put("marginLeft", margin);
      marginPayload.put("marginRight", margin);
      bridge.sendMessage("setMargins", marginPayload);

      JSONObject leadSectionPayload = new JSONObject();
      leadSectionPayload.put("sequence", pageSequenceNum);
      leadSectionPayload.put("title", page.getDisplayTitle());
      leadSectionPayload.put("section", page.getSections().get(0).toJSON());
      leadSectionPayload.put(
          "string_page_similar_titles", activity.getString(R.string.page_similar_titles));
      leadSectionPayload.put("string_page_issues", activity.getString(R.string.button_page_issues));
      leadSectionPayload.put("string_table_infobox", activity.getString(R.string.table_infobox));
      leadSectionPayload.put("string_table_other", activity.getString(R.string.table_other));
      leadSectionPayload.put("string_table_close", activity.getString(R.string.table_close));
      leadSectionPayload.put("string_expand_refs", activity.getString(R.string.expand_refs));
      leadSectionPayload.put("isBeta", app.getReleaseType() != WikipediaApp.RELEASE_PROD);
      leadSectionPayload.put("siteLanguage", model.getTitle().getSite().getLanguageCode());
      leadSectionPayload.put("isMainPage", page.isMainPage());
      leadSectionPayload.put("apiLevel", Build.VERSION.SDK_INT);
      bridge.sendMessage("displayLeadSection", leadSectionPayload);
      Log.d(TAG, "Sent message 'displayLeadSection' for page: " + page.getDisplayTitle());

      Utils.setupDirectionality(
          model.getTitle().getSite().getLanguageCode(), Locale.getDefault().getLanguage(), bridge);

      // Hide edit pencils if anon editing is disabled by remote killswitch or if this is a file
      // page
      JSONObject miscPayload = new JSONObject();
      boolean isAnonEditingDisabled =
          app.getRemoteConfig().getConfig().optBoolean("disableAnonEditing", false)
              && !app.getUserInfoStorage().isLoggedIn();
      miscPayload.put("noedit", (isAnonEditingDisabled || page.isFilePage() || page.isMainPage()));
      miscPayload.put("protect", !pageProperties.canEdit());
      bridge.sendMessage("setPageProtected", miscPayload);
    } catch (JSONException e) {
      // This should never happen
      throw new RuntimeException(e);
    }

    if (webView.getVisibility() != View.VISIBLE) {
      webView.setVisibility(View.VISIBLE);
    }

    refreshView.setRefreshing(false);
    activity.updateProgressBar(true, true, 0);
  }
  public void loadSavedPage() {
    new LoadSavedPageTask(model.getTitle()) {
      @Override
      public void onFinish(Page result) {
        // have we been unwittingly detached from our Activity?
        if (!fragment.isAdded()) {
          Log.d("PageViewFragment", "Detached from activity, so stopping update.");
          return;
        }

        // Save history entry and page image url
        new SaveHistoryTask(model.getCurEntry(), app).execute();

        model.setPage(result);
        editHandler.setPage(model.getPage());
        // kick off the lead image layout
        leadImagesHandler.beginLayout(
            new LeadImagesHandler.OnLeadImageLayoutListener() {
              @Override
              public void onLayoutComplete() {
                if (!fragment.isAdded()) {
                  return;
                }
                searchBarHideHandler.setFadeEnabled(leadImagesHandler.isLeadImageEnabled());
                // when the lead image is laid out, load the lead section and the rest
                // of the sections into the webview.
                displayLeadSection();
                displayNonLeadSection(1);

                // rewrite the image URLs in the webview, so that they're loaded from
                // local storage.
                fragment.readUrlMappings();

                setState(STATE_COMPLETE_FETCH, SUBSTATE_SAVED_PAGE_LOADED);
              }
            });
      }

      @Override
      public void onCatch(Throwable caught) {

        /*
        If anything bad happens during loading of a saved page, then simply bounce it
        back to the online version of the page, and re-save the page contents locally when it's done.
         */

        Log.d("LoadSavedPageTask", "Error loading saved page: " + caught.getMessage());
        caught.printStackTrace();

        fragment.refreshPage(true);
      }
    }.execute();
  }
 private void performActionForState(int forState) {
   if (!fragment.isAdded()) {
     return;
   }
   switch (forState) {
     case STATE_NO_FETCH:
       activity.updateProgressBar(true, true, 0);
       // hide the lead image...
       leadImagesHandler.hide();
       bottomContentHandler.hide();
       activity.getSearchBarHideHandler().setFadeEnabled(false);
       new LeadSectionFetchTask(pageSequenceNum).execute();
       break;
     case STATE_INITIAL_FETCH:
       new RestSectionsFetchTask(pageSequenceNum).execute();
       break;
     case STATE_COMPLETE_FETCH:
       editHandler.setPage(model.getPage());
       // kick off the lead image layout
       leadImagesHandler.beginLayout(
           new LeadImagesHandler.OnLeadImageLayoutListener() {
             @Override
             public void onLayoutComplete() {
               if (!fragment.isAdded()) {
                 return;
               }
               searchBarHideHandler.setFadeEnabled(leadImagesHandler.isLeadImageEnabled());
               // when the lead image layout is complete, load the lead section and
               // the other sections into the webview.
               displayLeadSection();
               displayNonLeadSection(1);
             }
           });
       break;
     default:
       // This should never happen
       throw new RuntimeException("Unknown state encountered " + state);
   }
 }
 /** Push the current page title onto the backstack. */
 private void pushBackStack() {
   PageBackStackItem item = new PageBackStackItem(model.getTitleOriginal(), model.getCurEntry());
   backStack.add(item);
 }
 private void updateThumbnail(String thumbUrl) {
   model.getTitle().setThumbUrl(thumbUrl);
   model.getTitleOriginal().setThumbUrl(thumbUrl);
   fragment.invalidateTabs();
 }
  private void loadPageOnWebViewReady(boolean tryFromCache) {
    // stage any section-specific link target from the title, since the title may be
    // replaced (normalized)
    sectionTargetFromTitle = model.getTitle().getFragment();

    Utils.setupDirectionality(
        model.getTitle().getSite().getLanguageCode(), Locale.getDefault().getLanguage(), bridge);

    // hide the native top and bottom components...
    leadImagesHandler.hide();
    bottomContentHandler.hide();
    bottomContentHandler.setTitle(model.getTitle());

    if (model.getCurEntry().getSource() == HistoryEntry.SOURCE_SAVED_PAGE) {
      state = STATE_NO_FETCH;
      loadSavedPage();
    } else if (tryFromCache) {
      // is this page in cache??
      app.getPageCache()
          .get(
              model.getTitleOriginal(),
              pageSequenceNum,
              new PageCache.CacheGetListener() {
                @Override
                public void onGetComplete(Page page, int sequence) {
                  if (sequence != pageSequenceNum) {
                    return;
                  }
                  if (page != null) {
                    Log.d(
                        TAG, "Using page from cache: " + model.getTitleOriginal().getDisplayText());
                    model.setPage(page);
                    model.setTitle(page.getTitle());
                    // load the current title's thumbnail from sqlite
                    updateThumbnail(
                        PageImage.PERSISTENCE_HELPER.getImageUrlForTitle(app, model.getTitle()));
                    // Save history entry...
                    new SaveHistoryTask(model.getCurEntry(), app).execute();
                    // don't re-cache the page after loading.
                    cacheOnComplete = false;
                    state = STATE_COMPLETE_FETCH;
                    setState(state);
                    performActionForState(state);
                  } else {
                    // page isn't in cache, so fetch it from the network...
                    loadPageFromNetwork();
                  }
                }

                @Override
                public void onGetError(Throwable e, int sequence) {
                  Log.e(TAG, "Failed to get page from cache.", e);
                  if (sequence != pageSequenceNum) {
                    return;
                  }
                  // something failed when loading it from cache, so fetch it from network...
                  loadPageFromNetwork();
                }
              });
    } else {
      loadPageFromNetwork();
    }
  }