public CacheListAdapter(
      final Activity activity, final List<cgCache> list, CacheListType cacheListType) {
    super(activity, 0, list);

    this.res = activity.getResources();
    this.list = list;
    this.cacheListType = cacheListType;
    if (cacheListType == CacheListType.HISTORY) {
      cacheComparator = new VisitComparator();
    }

    final DisplayMetrics metrics = new DisplayMetrics();
    activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    pixelDensity = metrics.density;

    for (final CacheType cacheType : CacheType.values()) {
      gcIconDrawables.put(
          cacheType, activity.getResources().getDrawable(cgBase.getCacheIcon(cacheType)));
    }

    if (Settings.isLightSkin()) {
      ratingBcgs[0] = R.drawable.favourite_background_red_light;
      ratingBcgs[1] = R.drawable.favourite_background_orange_light;
      ratingBcgs[2] = R.drawable.favourite_background_green_light;
    } else {
      ratingBcgs[0] = R.drawable.favourite_background_red_dark;
      ratingBcgs[1] = R.drawable.favourite_background_orange_dark;
      ratingBcgs[2] = R.drawable.favourite_background_green_dark;
    }
  }
  @Override
  public View getView(final int position, final View rowView, final ViewGroup parent) {
    if (inflater == null) {
      inflater = ((Activity) getContext()).getLayoutInflater();
    }

    if (position > getCount()) {
      Log.w(Settings.tag, "CacheListAdapter.getView: Attempt to access missing item #" + position);
      return null;
    }

    cgCache cache = getItem(position);

    View v = rowView;

    if (v == null) {
      v = inflater.inflate(R.layout.cache, null);

      holder = new cgCacheView();
      holder.checkbox = (CheckBox) v.findViewById(R.id.checkbox);
      holder.oneInfo = (RelativeLayout) v.findViewById(R.id.one_info);
      holder.oneCheckbox = (RelativeLayout) v.findViewById(R.id.one_checkbox);
      holder.logStatusMark = (ImageView) v.findViewById(R.id.log_status_mark);
      holder.text = (TextView) v.findViewById(R.id.text);
      holder.directionLayout = (RelativeLayout) v.findViewById(R.id.direction_layout);
      holder.distance = (cgDistanceView) v.findViewById(R.id.distance);
      holder.direction = (cgCompassMini) v.findViewById(R.id.direction);
      holder.dirImgLayout = (RelativeLayout) v.findViewById(R.id.dirimg_layout);
      holder.dirImg = (ImageView) v.findViewById(R.id.dirimg);
      holder.inventory = (RelativeLayout) v.findViewById(R.id.inventory);
      holder.favourite = (TextView) v.findViewById(R.id.favourite);
      holder.info = (TextView) v.findViewById(R.id.info);

      v.setTag(holder);
    } else {
      holder = (cgCacheView) v.getTag();
    }

    if (cache.isOwn()) {
      if (Settings.isLightSkin()) {
        holder.oneInfo.setBackgroundResource(R.color.owncache_background_light);
        holder.oneCheckbox.setBackgroundResource(R.color.owncache_background_light);
      } else {
        holder.oneInfo.setBackgroundResource(R.color.owncache_background_dark);
        holder.oneCheckbox.setBackgroundResource(R.color.owncache_background_dark);
      }
    } else {
      if (Settings.isLightSkin()) {
        holder.oneInfo.setBackgroundResource(R.color.background_light);
        holder.oneCheckbox.setBackgroundResource(R.color.background_light);
      } else {
        holder.oneInfo.setBackgroundResource(R.color.background_dark);
        holder.oneCheckbox.setBackgroundResource(R.color.background_dark);
      }
    }

    final touchListener touchLst = new touchListener(cache.getGeocode(), cache.getName(), cache);
    v.setOnClickListener(touchLst);
    v.setOnLongClickListener(touchLst);
    v.setOnTouchListener(touchLst);
    v.setLongClickable(true);

    if (selectMode) {
      if (cache.isStatusCheckedView()) {
        moveRight(holder, cache, true); // move fast when already slided
      } else {
        moveRight(holder, cache, false);
      }
    } else if (cache.isStatusChecked()) {
      holder.checkbox.setChecked(true);
      if (cache.isStatusCheckedView()) {
        moveRight(holder, cache, true); // move fast when already slided
      } else {
        moveRight(holder, cache, false);
      }
    } else {
      holder.checkbox.setChecked(false);
      if (cache.isStatusCheckedView()) {
        moveLeft(holder, cache, false);
      } else {
        holder.oneInfo.clearAnimation();
      }
    }

    holder.checkbox.setOnClickListener(new checkBoxListener(cache));

    distances.add(holder.distance);
    holder.distance.setContent(cache.getCoords());
    compasses.add(holder.direction);
    holder.direction.setContent(cache.getCoords());

    if (cache.isFound() && cache.isLogOffline()) {
      holder.logStatusMark.setImageResource(R.drawable.mark_green_orange);
      holder.logStatusMark.setVisibility(View.VISIBLE);
    } else if (cache.isFound()) {
      holder.logStatusMark.setImageResource(R.drawable.mark_green_more);
      holder.logStatusMark.setVisibility(View.VISIBLE);
    } else if (cache.isLogOffline()) {
      holder.logStatusMark.setImageResource(R.drawable.mark_orange);
      holder.logStatusMark.setVisibility(View.VISIBLE);
    } else {
      holder.logStatusMark.setVisibility(View.GONE);
    }

    if (cache.getNameSp() == null) {
      cache.setNameSp((new Spannable.Factory()).newSpannable(cache.getName()));
      if (cache.isDisabled() || cache.isArchived()) { // strike
        cache
            .getNameSp()
            .setSpan(
                new StrikethroughSpan(),
                0,
                cache.getNameSp().toString().length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
      }
    }

    holder.text.setText(cache.getNameSp(), TextView.BufferType.SPANNABLE);
    if (gcIconDrawables.containsKey(cache.getType())) { // cache icon
      holder.text.setCompoundDrawablesWithIntrinsicBounds(
          gcIconDrawables.get(cache.getType()), null, null, null);
    } else { // unknown cache type, "mystery" icon
      holder.text.setCompoundDrawablesWithIntrinsicBounds(
          gcIconDrawables.get(CacheType.MYSTERY), null, null, null);
    }

    if (holder.inventory.getChildCount() > 0) {
      holder.inventory.removeAllViews();
    }

    ImageView tbIcon = null;
    if (cache.getInventoryItems() > 0) {
      tbIcon = (ImageView) inflater.inflate(R.layout.trackable_icon, null);
      tbIcon.setImageResource(R.drawable.trackable_all);

      holder.inventory.addView(tbIcon);
      holder.inventory.setVisibility(View.VISIBLE);
    } else {
      holder.inventory.setVisibility(View.GONE);
    }

    boolean setDiDi = false;
    if (cache.getCoords() != null) {
      holder.direction.setVisibility(View.VISIBLE);
      holder.direction.updateAzimuth(azimuth);
      if (coords != null) {
        holder.distance.update(coords);
        holder.direction.updateCoords(coords);
      }
      setDiDi = true;
    } else {
      if (cache.getDistance() != null) {
        holder.distance.setDistance(cache.getDistance());
        setDiDi = true;
      }
      if (cache.getDirection() != null) {
        holder.direction.setVisibility(View.VISIBLE);
        holder.direction.updateAzimuth(azimuth);
        holder.direction.updateHeading(cache.getDirection());
        setDiDi = true;
      }
    }

    if (setDiDi) {
      holder.directionLayout.setVisibility(View.VISIBLE);
      holder.dirImgLayout.setVisibility(View.GONE);
    } else {
      holder.directionLayout.setVisibility(View.GONE);
      holder.distance.clear();

      final Bitmap dirImgPre =
          BitmapFactory.decodeFile(
              cgDirectionImg.getDirectionFile(cache.getGeocode(), false).getPath());
      final Bitmap dirImg;
      if (dirImgPre != null) { // null happens for invalid caches (not yet released)
        dirImg = dirImgPre.copy(Bitmap.Config.ARGB_8888, true);
        dirImgPre.recycle();
      } else {
        dirImg = null;
      }

      if (dirImg != null) {
        if (!Settings.isLightSkin()) {
          int length = dirImg.getWidth() * dirImg.getHeight();
          int[] pixels = new int[length];
          dirImg.getPixels(
              pixels, 0, dirImg.getWidth(), 0, 0, dirImg.getWidth(), dirImg.getHeight());
          for (int i = 0; i < length; i++) {
            if (pixels[i] == 0xff000000) { // replace black with white
              pixels[i] = 0xffffffff;
            }
          }
          dirImg.setPixels(
              pixels, 0, dirImg.getWidth(), 0, 0, dirImg.getWidth(), dirImg.getHeight());
        }

        holder.dirImg.setImageBitmap(dirImg);
        holder.dirImgLayout.setVisibility(View.VISIBLE);
      } else {
        holder.dirImg.setImageBitmap(null);
        holder.dirImgLayout.setVisibility(View.GONE);
      }
    }

    holder.favourite.setText(String.format("%d", cache.getFavoritePoints()));

    int favoriteBack;
    // set default background, neither vote nor rating may be available
    if (Settings.isLightSkin()) {
      favoriteBack = R.drawable.favourite_background_light;
    } else {
      favoriteBack = R.drawable.favourite_background_dark;
    }
    float myVote = cache.getMyVote();
    if (myVote > 0) { // use my own rating for display, if I have voted
      if (myVote >= 4) {
        favoriteBack = ratingBcgs[2];
      } else if (myVote >= 3) {
        favoriteBack = ratingBcgs[1];
      } else if (myVote > 0) {
        favoriteBack = ratingBcgs[0];
      }
    } else {
      float rating = cache.getRating();
      if (rating >= 3.5) {
        favoriteBack = ratingBcgs[2];
      } else if (rating >= 2.1) {
        favoriteBack = ratingBcgs[1];
      } else if (rating > 0.0) {
        favoriteBack = ratingBcgs[0];
      }
    }
    holder.favourite.setBackgroundResource(favoriteBack);

    if (cacheListType == CacheListType.HISTORY && cache.getVisitedDate() > 0) {
      StringBuilder cacheInfo = new StringBuilder(50);
      cacheInfo.append(cgBase.formatTime(cache.getVisitedDate()));
      cacheInfo.append("; ");
      cacheInfo.append(cgBase.formatDate(cache.getVisitedDate()));
      holder.info.setText(cacheInfo.toString());
    } else {
      ArrayList<String> infos = new ArrayList<String>();
      if (StringUtils.isNotBlank(cache.getGeocode())) {
        infos.add(cache.getGeocode());
      }
      if (cache.hasDifficulty()) {
        infos.add("D " + String.format("%.1f", cache.getDifficulty()));
      }
      if (cache.hasTerrain()) {
        infos.add("T " + String.format("%.1f", cache.getTerrain()));
      }

      // don't show "not chosen" for events and virtuals, that should be the normal case
      if (cache.getSize() != CacheSize.UNKNOWN && cache.showSize()) {
        infos.add(cache.getSize().getL10n());
      } else if (cache.isEventCache() && cache.getHiddenDate() != null) {
        infos.add(cgBase.formatShortDate(cache.getHiddenDate().getTime()));
      }

      if (cache.isMembers()) {
        infos.add(res.getString(R.string.cache_premium));
      }
      if (cacheListType != CacheListType.OFFLINE
          && cacheListType != CacheListType.HISTORY
          && cache.getReason() > 0) {
        infos.add(res.getString(R.string.cache_offline));
      }
      holder.info.setText(StringUtils.join(infos, SEPARATOR));
    }

    return v;
  }