I ended up doing something like this (obtain your groupIds for the user however you need to do it):
<java>
@Transactional(readOnly = true)
public List<Task> findCandidateTasks(User context) {
Assert.notNull(context);
String userId = context.getId();
LOG.debug("Getting assigned, candidate user, and candidate group tasks for user: {}", userId);
List<Task> candidateUserTasks = taskService.createTaskQuery().
includeProcessVariables().
taskCandidateUser(userId).
orderByTaskCreateTime().asc().list();
LOG.debug("Found: {} candidate user tasks for user: {}", candidateUserTasks.size(), userId);
List<Task> assignedTasks = taskService.createTaskQuery().includeProcessVariables().
taskAssignee(userId).orderByTaskCreateTime().asc().list();
LOG.debug("Found: {} assigned user tasks for user: {}", assignedTasks.size(), userId);
//Get your groupIds however you do it (identityService, custom method, etc.)
List<String> groupIds = getGroupIdsByUserId(userId);
List<Task> groupCandidateTasks = taskService.createTaskQuery().includeProcessVariables().
taskCandidateGroupIn(groupIds).
orderByTaskCreateTime().asc().list();
LOG.debug("Found: {} group tasks for user: {}", groupTasks.size(), userId);
Set<Task> candidateTasks = Sets.newHashSet();
candidateTasks.add(assigneeTasks);
candidateTasks.add(candidateUserTasks);
candidateTasks.add(candidateGroupTasks);
LOG.debug("got {} total group/user candidate tasks for user {}", candidateTasks.size(), userId);
List<Task> candidateTaskList = Lists.newArrayList(candidateTasks);
Collections.sort(candidateTaskList);
return candidateTaskList;
}
</java>
In my code, I actually created a TaskWrapper class to make sure both sorting works by creation date and equals uses the task id. YMMV.