@Override
  public void run(MediaSet baseSet) {
    final TreeMap<String, ArrayList<Path>> map = new TreeMap<String, ArrayList<Path>>();
    final ArrayList<Path> untagged = new ArrayList<Path>();

    baseSet.enumerateTotalMediaItems(
        new MediaSet.ItemConsumer() {
          public void consume(int index, MediaItem item) {
            Path path = item.getPath();

            String[] tags = item.getTags();
            if (tags == null || tags.length == 0) {
              untagged.add(path);
              return;
            }
            for (int j = 0; j < tags.length; j++) {
              String key = tags[j];
              ArrayList<Path> list = map.get(key);
              if (list == null) {
                list = new ArrayList<Path>();
                map.put(key, list);
              }
              list.add(path);
            }
          }
        });

    int m = map.size();
    mClusters = new ArrayList<ArrayList<Path>>();
    mNames = new String[m + ((untagged.size() > 0) ? 1 : 0)];
    int i = 0;
    for (Map.Entry<String, ArrayList<Path>> entry : map.entrySet()) {
      mNames[i++] = entry.getKey();
      mClusters.add(entry.getValue());
    }
    if (untagged.size() > 0) {
      mNames[i++] = mUntaggedString;
      mClusters.add(untagged);
    }
  }
  @Override
  public void run(MediaSet baseSet) {
    final int total = baseSet.getTotalMediaItemCount();
    final SmallItem[] buf = new SmallItem[total];
    // Separate items to two sets: with or without lat-long.
    final double[] latLong = new double[2];
    baseSet.enumerateTotalMediaItems(
        new MediaSet.ItemConsumer() {
          public void consume(int index, MediaItem item) {
            if (index < 0 || index >= total) return;
            SmallItem s = new SmallItem();
            s.path = item.getPath();
            item.getLatLong(latLong);
            s.lat = latLong[0];
            s.lng = latLong[1];
            buf[index] = s;
          }
        });

    final ArrayList<SmallItem> withLatLong = new ArrayList<SmallItem>();
    final ArrayList<SmallItem> withoutLatLong = new ArrayList<SmallItem>();
    final ArrayList<Point> points = new ArrayList<Point>();
    for (int i = 0; i < total; i++) {
      SmallItem s = buf[i];
      if (s == null) continue;
      if (GalleryUtils.isValidLocation(s.lat, s.lng)) {
        withLatLong.add(s);
        points.add(new Point(s.lat, s.lng));
      } else {
        withoutLatLong.add(s);
      }
    }

    ArrayList<ArrayList<SmallItem>> clusters = new ArrayList<ArrayList<SmallItem>>();

    int m = withLatLong.size();
    if (m > 0) {
      // cluster the items with lat-long
      Point[] pointsArray = new Point[m];
      pointsArray = points.toArray(pointsArray);
      int[] bestK = new int[1];
      int[] index = kMeans(pointsArray, bestK);

      for (int i = 0; i < bestK[0]; i++) {
        clusters.add(new ArrayList<SmallItem>());
      }

      for (int i = 0; i < m; i++) {
        clusters.get(index[i]).add(withLatLong.get(i));
      }
    }

    ReverseGeocoder geocoder = new ReverseGeocoder(mContext);
    mNames = new ArrayList<String>();
    boolean hasUnresolvedAddress = false;
    mClusters = new ArrayList<ArrayList<SmallItem>>();
    for (ArrayList<SmallItem> cluster : clusters) {
      String name = generateName(cluster, geocoder);
      if (name != null) {
        mNames.add(name);
        mClusters.add(cluster);
      } else {
        // move cluster-i to no location cluster
        withoutLatLong.addAll(cluster);
        hasUnresolvedAddress = true;
      }
    }

    if (withoutLatLong.size() > 0) {
      mNames.add(mNoLocationString);
      mClusters.add(withoutLatLong);
    }

    if (hasUnresolvedAddress) {
      mHandler.post(
          new Runnable() {
            public void run() {
              Toast.makeText(mContext, R.string.no_connectivity, Toast.LENGTH_LONG).show();
            }
          });
    }
  }