@Override
    public View getView(int position, View convertView, ViewGroup parent) {
      if (convertView == null) {
        convertView =
            LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_toc_entry, parent, false);
      }
      Section section = (Section) getItem(position);
      TextView sectionHeading = (TextView) convertView.findViewById(R.id.page_toc_item_text);
      View sectionFiller = convertView.findViewById(R.id.page_toc_filler);

      LinearLayout.LayoutParams indentLayoutParameters =
          new LinearLayout.LayoutParams(sectionFiller.getLayoutParams());
      indentLayoutParameters.width =
          (section.getLevel() - 1)
              * (int) (INDENTATION_WIDTH_DP * WikipediaApp.getInstance().getScreenDensity());
      sectionFiller.setLayoutParams(indentLayoutParameters);

      sectionHeading.setText(Html.fromHtml(section.getHeading()));

      if (section.getLevel() > 1) {
        sectionHeading.setTextColor(
            WikipediaApp.getInstance()
                .getResources()
                .getColor(getThemedAttributeId(parentActivity, R.attr.toc_subsection_text_color)));
      } else {
        sectionHeading.setTextColor(
            WikipediaApp.getInstance()
                .getResources()
                .getColor(getThemedAttributeId(parentActivity, R.attr.toc_section_text_color)));
      }
      return convertView;
    }
 /**
  * Called when an exception is thrown in the background process. Checks whether the exception is
  * an SSLException and, if so, prompts user to try again if the SSLException is the first.
  *
  * <p>Called on the UI Thread.
  *
  * <p>Default implementation just throws it as a RuntimeException, so exceptions are never
  * swallowed. Unless specific exceptions are handled.
  *
  * @param caught The exception that was thrown.
  */
 @Override
 public void onCatch(Throwable caught) {
   if (Utils.throwableContainsSpecificType(caught, SSLException.class)
       && WikipediaApp.getInstance().incSslFailCount() < 2) {
     WikipediaApp.getInstance().setSslFallback(true);
     if (!isCancelled()) {
       NetworkUtils.toastNetworkFail();
     }
     cancel();
     return;
   }
   throw new RuntimeException(caught);
 }
public abstract class ApiTask<T> extends SaneAsyncTask<T> {
  private static final boolean VERBOSE = WikipediaApp.getInstance().isDevRelease();
  private final Api api;

  public ApiTask(int concurrency, Api api) {
    super(concurrency);
    this.api = api;
  }

  @Override
  public T performTask() throws Throwable {
    RequestBuilder request = buildRequest(api);
    if (VERBOSE) {
      Log.v("ApiTask", buildUrl(api.getApiUrl().toString(), request.getParams()));
    }
    ApiResult result = makeRequest(request);
    return processResult(result);
  }

  /**
   * Called when an exception is thrown in the background process. Checks whether the exception is
   * an SSLException and, if so, prompts user to try again if the SSLException is the first.
   *
   * <p>Called on the UI Thread.
   *
   * <p>Default implementation just throws it as a RuntimeException, so exceptions are never
   * swallowed. Unless specific exceptions are handled.
   *
   * @param caught The exception that was thrown.
   */
  @Override
  public void onCatch(Throwable caught) {
    if (Utils.throwableContainsSpecificType(caught, SSLException.class)
        && WikipediaApp.getInstance().incSslFailCount() < 2) {
      WikipediaApp.getInstance().setSslFallback(true);
      if (!isCancelled()) {
        NetworkUtils.toastNetworkFail();
      }
      cancel();
      return;
    }
    throw new RuntimeException(caught);
  }

  protected ApiResult makeRequest(RequestBuilder builder) throws ApiException {
    return builder.get();
  }

  public abstract RequestBuilder buildRequest(Api api);

  public abstract T processResult(ApiResult result) throws Throwable;

  private String buildUrl(String url, Map<String, String> params) {
    Uri.Builder builder = new Uri.Builder().encodedPath(url);
    for (Map.Entry<String, String> param : params.entrySet()) {
      builder.appendQueryParameter(param.getKey(), param.getValue());
    }
    return builder.build().toString();
  }
}
  private void refreshOneSavedPage(@NonNull final PageTitle title) {
    getApiService(title)
        .pageCombo(
            title.getPrefixedText(),
            !WikipediaApp.getInstance().isImageDownloadEnabled(),
            new SaveOtherPageCallback(title) {
              @Override
              protected void onComplete() {
                if (!progressDialog.isShowing()) {
                  isRefreshCancelled = true;
                  // no longer attached to activity!
                  return;
                }
                savedPagesCompleted++;
                progressDialog.setProgress(savedPagesCompleted);
                L.d("Count is " + savedPagesCompleted + " of " + savedPages.size());
                if (savedPagesCompleted == savedPages.size()) {
                  progressDialog.dismiss();
                }
              }

              @Override
              protected void onError() {
                isRefreshCancelled = true;
                if (!progressDialog.isShowing()) {
                  // no longer attached to activity!
                  return;
                }
                progressDialog.dismiss();
                getErrorDialog().show();
              }
            });
  }
  public void setupToC(final Page page, Site site, boolean firstPage) {
    tocProgress.setVisibility(View.GONE);
    tocList.setVisibility(View.VISIBLE);

    headerView.setText(Html.fromHtml(page.getDisplayTitle()));
    headerView.setOnClickListener(
        new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            scrollToSection(page.getSections().get(0));
            wasClicked = true;
            funnel.logClick(0, page.getTitle().getDisplayText());
            hide();
          }
        });

    tocList.setAdapter(new ToCAdapter(page), site.getLanguageCode());
    tocList.setOnItemClickListener(
        new AdapterView.OnItemClickListener() {
          @Override
          public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Section section = (Section) parent.getAdapter().getItem(position);
            scrollToSection(section);
            wasClicked = true;
            funnel.logClick(position, section.getHeading());
            hide();
          }
        });

    funnel =
        new ToCInteractionFunnel(
            WikipediaApp.getInstance(),
            site,
            page.getPageProperties().getPageId(),
            tocList.getAdapter().getCount());

    if (!page.isMainPage() && !firstPage) {
      if (WikipediaApp.getInstance().getOnboardingStateMachine().isTocTutorialEnabled()) {
        showTocOnboarding();
      }
    }
  }
@RunWith(AndroidJUnit4.class)
public class LoginTaskTest {
  private static final Site TEST_WIKI_SITE = new Site("test.wikipedia.org");
  private static final String SUCCESS = "Success";
  private static final String USERNAME = getString(R.string.test_username);
  private static final String PASSWORD = getString(R.string.test_password);

  private final WikipediaApp app = WikipediaApp.getInstance();
  private final TestLatch completionLatch = new TestLatch();

  @Test
  public void testLogin() throws Throwable {
    runOnMainSync(
        new Runnable() {
          @Override
          public void run() {
            loginTestTask.execute();
          }
        });
    completionLatch.await();
  }

  private LoginTask loginTestTask =
      new LoginTask(app, TEST_WIKI_SITE, USERNAME, PASSWORD) {
        @Override
        public void onFinish(LoginResult result) {
          super.onFinish(result);
          assertThat(result.getCode(), equalTo(SUCCESS));
          app.getEditTokenStorage().get(TEST_WIKI_SITE, callback);
        }
      };

  private EditTokenStorage.TokenRetrievedCallback callback =
      new EditTokenStorage.TokenRetrievedCallback() {
        @Override
        public void onTokenRetrieved(String token) {
          assertThat(token.equals("+\\"), is(false));
          completionLatch.countDown();
        }

        @Override
        public void onTokenFailed(Throwable caught) {
          throw new RuntimeException(caught);
        }
      };

  private void runOnMainSync(Runnable r) {
    getInstrumentation().runOnMainSync(r);
  }

  private static String getString(@StringRes int id) {
    return getInstrumentation().getContext().getString(id);
  }
}
  public ToCHandler(
      final AppCompatActivity activity,
      final WikiDrawerLayout slidingPane,
      final CommunicationBridge bridge) {
    this.parentActivity = activity;
    this.bridge = bridge;
    this.slidingPane = slidingPane;

    this.tocList = (ConfigurableListView) slidingPane.findViewById(R.id.page_toc_list);
    ((FrameLayout.LayoutParams) tocList.getLayoutParams())
        .setMargins(0, getContentTopOffsetPx(activity), 0, 0);
    this.tocProgress = (ProgressBar) slidingPane.findViewById(R.id.page_toc_in_progress);

    bridge.addListener(
        "currentSectionResponse",
        new CommunicationBridge.JSEventListener() {
          @Override
          public void onMessage(String messageType, JSONObject messagePayload) {
            int sectionID = messagePayload.optInt("sectionID");
            Log.d("Wikipedia", "current section is " + sectionID);
            if (tocList.getAdapter() == null) {
              return;
            }
            int itemToSelect = 0;
            // Find the list item that corresponds to the returned sectionID.
            // Start with index 1 of the list adapter, since index 0 is the header view,
            // and won't have a Section object associated with it.
            // And end with the second-to-last section, since the last section is the
            // artificial Read More section, and unknown to the WebView.
            // The lead section (id 0) will automatically fall through the loop.
            for (int i = 1; i < tocList.getAdapter().getCount() - 1; i++) {
              if (((Section) tocList.getAdapter().getItem(i)).getId() <= sectionID) {
                itemToSelect = i;
              } else {
                break;
              }
            }
            tocList.setItemChecked(itemToSelect, true);
            tocList.smoothScrollToPosition(itemToSelect);
          }
        });

    headerView =
        (TextView)
            LayoutInflater.from(tocList.getContext())
                .inflate(R.layout.header_toc_list, null, false);
    tocList.addHeaderView(headerView);

    // create a dummy funnel, in case the drawer is pulled out before a page is loaded.
    funnel =
        new ToCInteractionFunnel(
            WikipediaApp.getInstance(), WikipediaApp.getInstance().getSite(), 0, 0);

    slidingPane.setDrawerListener(
        new DrawerLayout.SimpleDrawerListener() {
          private boolean sectionRequested = false;

          @Override
          public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);
            parentActivity.supportInvalidateOptionsMenu();
            funnel.logOpen();
            wasClicked = false;
          }

          @Override
          public void onDrawerClosed(View drawerView) {
            super.onDrawerClosed(drawerView);
            parentActivity.supportInvalidateOptionsMenu();
            if (!wasClicked) {
              funnel.logClose();
            }
            sectionRequested = false;
          }

          @Override
          public void onDrawerSlide(View drawerView, float slideOffset) {
            super.onDrawerSlide(drawerView, slideOffset);
            // make sure the ActionBar is showing
            ((PageActivity) parentActivity).showToolbar();
            ((PageActivity) parentActivity)
                .getSearchBarHideHandler()
                .setForceNoFade(slideOffset != 0);
            // request the current section to highlight, if we haven't yet
            if (!sectionRequested) {
              bridge.sendMessage("requestCurrentSection", new JSONObject());
              sectionRequested = true;
            }
          }
        });
  }
 private void showTocOnboarding() {
   View tocButton = parentActivity.findViewById(R.id.floating_toc_button);
   ToolTipUtil.showToolTip(
       parentActivity, tocButton, R.layout.inflate_tool_tip_toc_button, ToolTip.Position.CENTER);
   WikipediaApp.getInstance().getOnboardingStateMachine().setTocTutorial();
 }
 // TODO: use getResources().getDimensionPixelSize()?  Define leadImageWidth with px, not dp?
 public static int calculateLeadImageWidth() {
   Resources res = WikipediaApp.getInstance().getResources();
   return (int) (res.getDimension(R.dimen.leadImageWidth) / res.getDisplayMetrics().density);
 }
 public RbPageService(final Site site) {
   responseHeaderHandler = WikipediaApp.getInstance().getWikipediaZeroHandler();
   webService = RbPageEndpointsCache.INSTANCE.getRbPageEndpoints(site);
 }