/**
  * Add a dependency to this Node. The dependency includes information about the node that this
  * node is dependency upon and the nature of the dependency.
  *
  * @param dependency
  */
 public void addDependency(Dependency dependency) {
   if (dependencies == null) {
     dependencies = new ArrayList<Dependency>();
     nodeDependencies = new ArrayList<Node>();
   }
   dependencies.add(dependency);
   if (!nodeDependencies.contains(dependency.node)) {
     nodeDependencies.add(dependency.node);
   }
   Node dependencyNode = dependency.node;
   if (dependencyNode.nodeDependents == null) {
     dependencyNode.nodeDependents = new ArrayList<Node>();
   }
   dependencyNode.nodeDependents.add(this);
 }
  @Override
  public AnimatorSet clone() {
    final AnimatorSet anim = (AnimatorSet) super.clone();
    /*
     * The basic clone() operation copies all items. This doesn't work very well for
     * AnimatorSet, because it will copy references that need to be recreated and state
     * that may not apply. What we need to do now is put the clone in an uninitialized
     * state, with fresh, empty data structures. Then we will build up the nodes list
     * manually, as we clone each Node (and its animation). The clone will then be sorted,
     * and will populate any appropriate lists, when it is started.
     */
    anim.mNeedsSort = true;
    anim.mTerminated = false;
    anim.mStarted = false;
    anim.mPlayingSet = new ArrayList<Animator>();
    anim.mNodeMap = new HashMap<Animator, Node>();
    anim.mNodes = new ArrayList<Node>();
    anim.mSortedNodes = new ArrayList<Node>();

    // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
    // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
    // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
    HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
    for (Node node : mNodes) {
      Node nodeClone = node.clone();
      nodeCloneMap.put(node, nodeClone);
      anim.mNodes.add(nodeClone);
      anim.mNodeMap.put(nodeClone.animation, nodeClone);
      // Clear out the dependencies in the clone; we'll set these up manually later
      nodeClone.dependencies = null;
      nodeClone.tmpDependencies = null;
      nodeClone.nodeDependents = null;
      nodeClone.nodeDependencies = null;
      // clear out any listeners that were set up by the AnimatorSet; these will
      // be set up when the clone's nodes are sorted
      ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
      if (cloneListeners != null) {
        ArrayList<AnimatorListener> listenersToRemove = null;
        for (AnimatorListener listener : cloneListeners) {
          if (listener instanceof AnimatorSetListener) {
            if (listenersToRemove == null) {
              listenersToRemove = new ArrayList<AnimatorListener>();
            }
            listenersToRemove.add(listener);
          }
        }
        if (listenersToRemove != null) {
          for (AnimatorListener listener : listenersToRemove) {
            cloneListeners.remove(listener);
          }
        }
      }
    }
    // Now that we've cloned all of the nodes, we're ready to walk through their
    // dependencies, mapping the old dependencies to the new nodes
    for (Node node : mNodes) {
      Node nodeClone = nodeCloneMap.get(node);
      if (node.dependencies != null) {
        for (Dependency dependency : node.dependencies) {
          Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
          Dependency cloneDependency = new Dependency(clonedDependencyNode, dependency.rule);
          nodeClone.addDependency(cloneDependency);
        }
      }
    }

    return anim;
  }