@Override
  public View onCreateView(
      LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    super.onCreateView(inflater, container, savedInstanceState);
    setRetainInstance(true);
    authServicesLayout =
        (LinearLayout) inflater.inflate(R.layout.singly_auth_services_fragment, container, false);

    this.remoteImageCache = new RemoteImageCache(activity, 2, null, 50);

    // get an instance of the singly client
    singlyClient = SinglyClient.getInstance();

    // get the main list view and put a click listener on it. This will tell
    // us which row was clicked, on the layout xml the checkbox is not focusable
    // or clickable directly, the row handles that
    authListView = (ListView) authServicesLayout.findViewById(R.id.singlyAuthenticatedServicesList);
    authListView.setOnItemClickListener(new ItemClickListener());

    // set the services array adapter into the main list view
    servicesAdapter =
        new AuthenticatedServicesAdapter(activity, services, authServices, remoteImageCache);
    authListView.setAdapter(servicesAdapter);
    return authServicesLayout;
  }
  @Override
  public void onStart() {

    super.onStart();

    // do a call to singly to get all the available services
    singlyClient.doGetApiRequest(
        activity,
        "/services",
        null,
        new AsyncApiResponseHandler() {

          @Override
          public void onSuccess(String response) {

            // new list of services
            List<SinglyService> curServices = new ArrayList<SinglyService>();
            boolean onlyIncluded = !includedServices.isEmpty();

            JsonNode rootNode = JSON.parse(response);
            Map<String, JsonNode> serviceNodes = JSON.getFields(rootNode);

            // loop through the service name to objects
            for (Map.Entry<String, JsonNode> entry : serviceNodes.entrySet()) {

              // parse and add the service to the services list
              JsonNode serviceNode = entry.getValue();
              SinglyService singlyService = new SinglyService();
              singlyService.id = entry.getKey();
              singlyService.name = StringUtils.capitalize(JSON.getString(serviceNode, "name"));

              // if we have an include set only use services in the set
              if (onlyIncluded && !includedServices.contains(singlyService.id)) {
                continue;
              }

              // create a map of the icons and their sizes
              Map<String, String> icons = new HashMap<String, String>();
              List<JsonNode> iconNodes = JSON.getJsonNodes(serviceNode, "icons");
              for (JsonNode iconNode : iconNodes) {
                int height = JSON.getInt(iconNode, "height");
                int width = JSON.getInt(iconNode, "width");
                String source = JSON.getString(iconNode, "source");
                String key = height + "x" + width;
                icons.put(key, source);
              }
              singlyService.icons = icons;

              // if possible retrieve a previously downloaded icon, if not then
              // download and store it in an async manner
              ImageInfo imageInfo = new ImageInfo();
              String id = StringUtils.lowerCase(singlyService.id + "_icon_32x32");
              imageInfo.id = id;
              imageInfo.imageUrl = singlyService.icons.get("32x32");
              imageInfo.width = 32;
              imageInfo.height = 32;
              imageInfo.sample = false;

              singlyService.imageInfo = imageInfo;

              // callback that updates the singly image in a singly row if that
              // row is visible when the image is finished downloading.
              imageInfo.listener =
                  new ImageCacheListener() {

                    @Override
                    public void onSuccess(ImageInfo imageInfo, Bitmap bitmap) {

                      int startRow = authListView.getFirstVisiblePosition();
                      int endRow = authListView.getLastVisiblePosition();
                      for (int i = startRow; i <= endRow; i++) {
                        SinglyService curService = services.get(i);
                        if (curService.imageInfo == imageInfo) {
                          View rowView = authListView.getChildAt(i - startRow);
                          ImageView imageView = (ImageView) rowView.findViewById(R.id.iconView1);
                          imageView.setImageBitmap(bitmap);
                          break;
                        }
                      }
                    }
                  };

              curServices.add(singlyService);
            }

            // sort the services by name
            Collections.sort(
                curServices,
                new Comparator<SinglyService>() {

                  @Override
                  public int compare(SinglyService lhs, SinglyService rhs) {
                    return lhs.name.compareTo(rhs.name);
                  }
                });

            // clear and update the services list
            services.clear();
            services.addAll(curServices);

            // display the changes
            servicesAdapter.notifyDataSetChanged();
            updateAuthenticatedServices();
          }

          @Override
          public void onFailure(Throwable error, String message) {
            Log.e(
                AuthenticatedServicesFragment.class.getSimpleName(),
                "Error getting list of authenticated services",
                error);
          }
        });
  }
  /**
   * Gets the services that the user is authenticated for and then updates the ListView checkboxes
   * for those services.
   */
  protected void updateAuthenticatedServices() {

    // don't get the authenticated services unless we have an access token
    if (!singlyClient.isAuthenticated(activity)) {
      return;
    }

    // get the access token and pass it in as a query parameter
    Map<String, String> qparams = new LinkedHashMap<String, String>();
    qparams.put("verify", "true");

    // get the access token
    Authentication auth = singlyClient.getAuthentication(activity);
    if (StringUtils.isNotBlank(auth.accessToken)) {
      qparams.put("access_token", auth.accessToken);
    }

    // get all the services the user is authenticated against
    singlyClient.doGetApiRequest(
        activity,
        "/profiles",
        qparams,
        new AsyncApiResponseHandler() {

          @Override
          public void onSuccess(String response) {

            // get the set of services from the response and populate the service
            // to user id mapping
            JsonNode root = JSON.parse(response);
            Map<String, JsonNode> profileNodes = JSON.getFields(root);
            for (Map.Entry<String, JsonNode> entry : profileNodes.entrySet()) {

              String profileName = entry.getKey();
              JsonNode profileArrayNode = entry.getValue();

              // ignore the id field which is the singly account id
              if (!profileName.equals("id")) {

                // the JSON is an array with a singly node containing the profile
                if (profileArrayNode.isArray()) {

                  // check if the auth token for the profile is no longer valid
                  // if not valid ignore the service
                  JsonNode profileNode = profileArrayNode.get(0);
                  JsonNode errorNode = JSON.getJsonNode(profileNode, "error");
                  if (errorNode != null) {
                    continue;
                  }

                  // add the profile name and id
                  String profileId = JSON.getString(profileNode, "id");
                  serviceIds.put(profileName, profileId);
                  authServices.add(profileName);
                }
              }
            }

            // notify the list view that the data has changed, update view
            servicesAdapter.notifyDataSetChanged();
          }

          @Override
          public void onFailure(Throwable error, String message) {}
        });
  }