@Override
    public View getChildView(
        int groupPosition,
        int childPosition,
        boolean isLastChild,
        View convertView,
        ViewGroup parent) {
      if (convertView == null) {
        convertView = mInflater.inflate(R.layout.custom_contact_list_filter_group, parent, false);
      }

      final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1);
      final TextView text2 = (TextView) convertView.findViewById(android.R.id.text2);
      final CheckBox checkbox = (CheckBox) convertView.findViewById(android.R.id.checkbox);

      final AccountDisplay account = mAccounts.get(groupPosition);
      final GroupDelta child = (GroupDelta) this.getChild(groupPosition, childPosition);
      if (child != null) {
        // Handle normal group, with title and checkbox
        final boolean groupVisible = child.getVisible();
        checkbox.setVisibility(View.VISIBLE);
        checkbox.setChecked(groupVisible);

        final CharSequence groupTitle = child.getTitle(mContext);
        text1.setText(groupTitle);
        text2.setVisibility(View.GONE);
      } else {
        // When unknown child, this is "more" footer view
        checkbox.setVisibility(View.GONE);
        text1.setText(R.string.display_more_groups);
        text2.setVisibility(View.GONE);
      }

      return convertView;
    }
 @Override
 public long getChildId(int groupPosition, int childPosition) {
   final GroupDelta child = (GroupDelta) getChild(groupPosition, childPosition);
   if (child != null) {
     final Long childId = child.getId();
     return childId != null ? childId : Long.MIN_VALUE;
   } else {
     return Long.MIN_VALUE;
   }
 }
 /**
  * Build set of {@link ContentProviderOperation} to persist any user changes to {@link
  * GroupDelta} rows under this {@link AccountWithDataSet}.
  */
 public void buildDiff(ArrayList<ContentProviderOperation> diff) {
   for (GroupDelta group : mSyncedGroups) {
     final ContentProviderOperation oper = group.buildDiff();
     if (oper != null) diff.add(oper);
   }
   for (GroupDelta group : mUnsyncedGroups) {
     final ContentProviderOperation oper = group.buildDiff();
     if (oper != null) diff.add(oper);
   }
 }
    @Override
    public AccountSet loadInBackground() {
      Context context = getContext();
      final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
      final ContentResolver resolver = context.getContentResolver();

      final AccountSet accounts = new AccountSet();
      for (AccountWithDataSet account : accountTypes.getAccounts(false)) {
        final AccountType accountType = accountTypes.getAccountTypeForAccount(account);
        if (accountType.isExtension() && !account.hasData(context)) {
          // Extension with no data -- skip.
          continue;
        }

        AccountDisplay accountDisplay =
            new AccountDisplay(resolver, account.name, account.type, account.dataSet);

        final Uri.Builder groupsUri =
            Groups.CONTENT_URI
                .buildUpon()
                .appendQueryParameter(Groups.ACCOUNT_NAME, account.name)
                .appendQueryParameter(Groups.ACCOUNT_TYPE, account.type);
        if (account.dataSet != null) {
          groupsUri.appendQueryParameter(Groups.DATA_SET, account.dataSet).build();
        }
        final Cursor cursor = resolver.query(groupsUri.build(), null, null, null, null);
        if (cursor == null) {
          continue;
        }
        android.content.EntityIterator iterator = ContactsContract.Groups.newEntityIterator(cursor);
        try {
          boolean hasGroups = false;

          // Create entries for each known group
          while (iterator.hasNext()) {
            final ContentValues values = iterator.next().getEntityValues();
            final GroupDelta group = GroupDelta.fromBefore(values);
            accountDisplay.addGroup(group);
            hasGroups = true;
          }
          // Create single entry handling ungrouped status
          accountDisplay.mUngrouped =
              GroupDelta.fromSettings(
                  resolver, account.name, account.type, account.dataSet, hasGroups);
          accountDisplay.addGroup(accountDisplay.mUngrouped);
        } finally {
          iterator.close();
        }

        accounts.add(accountDisplay);
      }

      return accounts;
    }
 protected void handleRemoveSync(
     final AccountDisplay account,
     final GroupDelta child,
     final int syncMode,
     CharSequence title) {
   final boolean shouldSyncUngrouped = account.mUngrouped.getShouldSync();
   if (syncMode == SYNC_MODE_EVERYTHING
       && shouldSyncUngrouped
       && !child.equals(account.mUngrouped)) {
     // Warn before removing this group when it would cause ungrouped to stop syncing
     final AlertDialog.Builder builder = new AlertDialog.Builder(this);
     final CharSequence removeMessage =
         this.getString(R.string.display_warn_remove_ungrouped, title);
     builder.setTitle(R.string.menu_sync_remove);
     builder.setMessage(removeMessage);
     builder.setNegativeButton(android.R.string.cancel, null);
     builder.setPositiveButton(
         android.R.string.ok,
         new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int which) {
             // Mark both this group and ungrouped to stop syncing
             account.setShouldSync(account.mUngrouped, false);
             account.setShouldSync(child, false);
             mAdapter.notifyDataSetChanged();
           }
         });
     builder.show();
   } else {
     // Mark this group to not sync
     account.setShouldSync(child, false);
     mAdapter.notifyDataSetChanged();
   }
 }
  /**
   * Handle any clicks on {@link ExpandableListAdapter} children, which usually mean toggling its
   * visible state.
   */
  @Override
  public boolean onChildClick(
      ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
    final CheckBox checkbox = (CheckBox) view.findViewById(android.R.id.checkbox);

    final AccountDisplay account = (AccountDisplay) mAdapter.getGroup(groupPosition);
    final GroupDelta child = (GroupDelta) mAdapter.getChild(groupPosition, childPosition);
    if (child != null) {
      checkbox.toggle();
      child.putVisible(checkbox.isChecked());
    } else {
      // Open context menu for bringing back unsynced
      this.openContextMenu(view);
    }
    return true;
  }
 /**
  * Add the given {@link GroupDelta} internally, filing based on its {@link
  * GroupDelta#getShouldSync()} status.
  */
 private void addGroup(GroupDelta group) {
   if (group.getShouldSync()) {
     mSyncedGroups.add(group);
   } else {
     mUnsyncedGroups.add(group);
   }
 }
 public int compare(GroupDelta object1, GroupDelta object2) {
   final Long id1 = object1.getId();
   final Long id2 = object2.getId();
   if (id1 == null && id2 == null) {
     return 0;
   } else if (id1 == null) {
     return -1;
   } else if (id2 == null) {
     return 1;
   } else if (id1 < id2) {
     return -1;
   } else if (id1 > id2) {
     return 1;
   } else {
     return 0;
   }
 }
  protected void showRemoveSync(
      ContextMenu menu, final AccountDisplay account, final GroupDelta child, final int syncMode) {
    final CharSequence title = child.getTitle(this);

    menu.setHeaderTitle(title);
    menu.add(R.string.menu_sync_remove)
        .setOnMenuItemClickListener(
            new OnMenuItemClickListener() {
              public boolean onMenuItemClick(MenuItem item) {
                handleRemoveSync(account, child, syncMode, title);
                return true;
              }
            });
  }
  protected void showAddSync(ContextMenu menu, final AccountDisplay account, final int syncMode) {
    menu.setHeaderTitle(R.string.dialog_sync_add);

    // Create item for each available, unsynced group
    for (final GroupDelta child : account.mUnsyncedGroups) {
      if (!child.getShouldSync()) {
        final CharSequence title = child.getTitle(this);
        menu.add(title)
            .setOnMenuItemClickListener(
                new OnMenuItemClickListener() {
                  public boolean onMenuItemClick(MenuItem item) {
                    // Adding specific group for syncing
                    if (child.mUngrouped && syncMode == SYNC_MODE_EVERYTHING) {
                      account.setShouldSync(true);
                    } else {
                      account.setShouldSync(child, true);
                    }
                    mAdapter.notifyDataSetChanged();
                    return true;
                  }
                });
      }
    }
  }
 /**
  * Set {@link GroupDelta#putShouldSync(boolean)}, and file internally based on updated state.
  */
 public void setShouldSync(GroupDelta child, boolean shouldSync, boolean attemptRemove) {
   child.putShouldSync(shouldSync);
   if (shouldSync) {
     if (attemptRemove) {
       mUnsyncedGroups.remove(child);
     }
     mSyncedGroups.add(child);
     Collections.sort(mSyncedGroups, sIdComparator);
   } else {
     if (attemptRemove) {
       mSyncedGroups.remove(child);
     }
     mUnsyncedGroups.add(child);
   }
 }
 public static GroupDelta fromAfter(ContentValues after) {
   final GroupDelta entry = new GroupDelta();
   entry.mBefore = null;
   entry.mAfter = after;
   return entry;
 }
 public static GroupDelta fromBefore(ContentValues before) {
   final GroupDelta entry = new GroupDelta();
   entry.mBefore = before;
   entry.mAfter = new ContentValues();
   return entry;
 }