@Nullable
    private ImmutablePair<List<ImmutableTriple<ContentStorage.FileInformation, Folder, Integer>>, ImmutablePair<Integer, Integer>> copyAllFirstPassCollectInfo(final Folder source, final Folder target, final AtomicBoolean cancelFlag) {

        //We create a "marker file" in the target folder so we recognize it in tree.
        // That way we can find out whether source=target or target is in source or source is in target
        final String targetMarkerFileName = FileNameCreator.DEFAULT.createName();
        final Uri targetMarkerFileUri = pls.create(target, targetMarkerFileName);
        if (targetMarkerFileUri == null) {
            //this means we can't write to target
            return null;
        }

        final int[] copyCounts = new int[]{0, 0};

        final int[] onTargetNeededPath = {-1}; //helper counter to flag forlders needed for target
        final boolean[] markerFoundInSubdir = {false, false}; //helper flags to flag forlders needed for target
        //triplet of each entry will contain: source file, target folder for that file, flags as above
        final List<ImmutableTriple<ContentStorage.FileInformation, Folder, Integer>> listToCopy = new ArrayList<>();
        final Stack<Folder> targetFolderStack = new Stack<>();
        targetFolderStack.push(target);
        treeWalk(source, fi -> {
            if (cancelFlag != null && cancelFlag.get()) {
                return TreeWalkResult.STOP;
            }
            if (fi.left.isDirectory) {
                if (fi.right) {
                    copyCounts[1]++;
                    targetFolderStack.push(Folder.fromFolder(targetFolderStack.peek(), fi.left.name));
                    listToCopy.add(new ImmutableTriple<>(fi.left, targetFolderStack.peek(), COPY_FLAG_DIR_BEFORE));
                    if (onTargetNeededPath[0] >= 0) {
                        onTargetNeededPath[0]++;
                    }
                    return TreeWalkResult.CONTINUE;
                } else {
                    markerFoundInSubdir[1] |= onTargetNeededPath[0] == 0;
                    listToCopy.add(new ImmutableTriple<>(fi.left, targetFolderStack.pop(), onTargetNeededPath[0] == 0 ? COPY_FLAG_DIR_NEEDED_FOR_TARGET : 0));
                    if (onTargetNeededPath[0] > 0) {
                        onTargetNeededPath[0]--;
                    }
                    return TreeWalkResult.CONTINUE;
                }
            } else {
                if (fi.left.name.equals(targetMarkerFileName)) {
                    markerFoundInSubdir[0] = true;
                    onTargetNeededPath[0] = 0;
                } else {
                    copyCounts[0]++;
                    listToCopy.add(new ImmutableTriple<>(fi.left, targetFolderStack.peek(), 0));
                }
                return TreeWalkResult.CONTINUE;
            }
        });
        //delete marker file
        pls.delete(targetMarkerFileUri);

        final boolean sourceTargetSameDir = markerFoundInSubdir[0] && !markerFoundInSubdir[1];
        return new ImmutablePair<>(sourceTargetSameDir ? Collections.emptyList() : listToCopy, new ImmutablePair<>(copyCounts[0], copyCounts[1]));
    }
copyAll First Pass: collect all files to copy, create target folder for each file, mark source folders to keep on move (if target is in source)
returns in left the list of files. Returns in right the files/dirs found in source for copying
main/src/main/java/cgeo/geocaching/storage/FolderUtils.java