private void updateLocalVersions() {
    if (NavigineApp.Navigation == null) return;

    for (int i = 0; i < mInfoList.size(); ++i) {
      LocationInfo info = mInfoList.get(i);
      String versionStr = LocationLoader.getLocalVersion(NavigineApp.AppContext, info.title);
      if (versionStr != null) {
        // Log.d(TAG, info.title + ": " + versionStr);
        info.localModified = versionStr.endsWith("+");
        if (info.localModified) versionStr = versionStr.substring(0, versionStr.length() - 1);
        try {
          info.localVersion = Integer.parseInt(versionStr);
        } catch (Throwable e) {
        }
      } else {
        info.localVersion = -1;

        String mapFile = NavigineApp.Settings.getString("map_file", "");
        if (mapFile.equals(info.archiveFile)) {
          NavigineApp.Navigation.loadArchive(null);
          SharedPreferences.Editor editor = NavigineApp.Settings.edit();
          editor.putString("map_file", "");
          editor.commit();
        }
      }
    }
    mAdapter.updateList();
  }
  private void deleteLocation(LocationInfo info) {
    if (NavigineApp.Navigation == null) return;

    if (info != null) {
      try {
        (new File(info.archiveFile)).delete();
        info.localVersion = -1;
        info.localModified = false;

        String locationDir = LocationLoader.getLocationDir(mContext, info.title);
        File dir = new File(locationDir);
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; ++i) files[i].delete();
        dir.delete();

        String mapFile = NavigineApp.Settings.getString("map_file", "");
        if (mapFile.equals(info.archiveFile)) {
          NavigineApp.Navigation.loadArchive(null);
          SharedPreferences.Editor editor = NavigineApp.Settings.edit();
          editor.putString("map_file", "");
          editor.commit();
        }

        mAdapter.updateList();
      } catch (Throwable e) {
        Log.e(TAG, Log.getStackTraceString(e));
      }
    }
  }
  /** Called when the activity is first created */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "LoaderActivity created");
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.content);

    // Instantiate custom adapter
    mAdapter = new LoaderAdapter();

    // Handle listview and assign adapter
    mListView = (ListView) findViewById(R.id.content_list_view);
    mListView.setAdapter(mAdapter);
    mListView.setVisibility(View.GONE);
    mListView.setOnItemLongClickListener(
        new AdapterView.OnItemLongClickListener() {
          public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
            selectItem(position);
            return true;
          }
        });

    mStatusLabel = (TextView) findViewById(R.id.content_status_label);
    mStatusLabel.setVisibility(View.VISIBLE);

    String userHash = NavigineApp.Settings.getString("user_hash", "");
    if (userHash.length() == 0) showUserHashDialog();
    else refreshMapList();
  }
  private void showUserHashDialog() {
    String userHash = NavigineApp.Settings.getString("user_hash", "");

    LayoutInflater inflater = getLayoutInflater();
    View view = inflater.inflate(R.layout.user_hash_dialog, null);
    _userEdit = (EditText) view.findViewById(R.id.user_hash_edit);
    _userEdit.setText(userHash);
    _userEdit.setTypeface(Typeface.MONOSPACE);
    // _userEdit.addTextChangedListener(new TextWatcher()
    //  {
    //    public void afterTextChanged(Editable s) { }
    //    public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
    //    public void onTextChanged(CharSequence s, int start, int before, int count)
    //    {
    //      String text = _userEdit.getText().toString();
    //      int length  = _userEdit.getText().length();
    //
    //      if (text.endsWith("-"))
    //        return;
    //
    //      if (count <= before)
    //        return;
    //
    //      if (length == 4 || length == 9 || length == 14)
    //      {
    //        _userEdit.setText((text + "-"));
    //        _userEdit.setSelection(length + 1);
    //      }
    //    }
    //  });

    AlertDialog.Builder alertBuilder = new AlertDialog.Builder(mContext);
    alertBuilder.setView(view);
    alertBuilder.setTitle("Enter user ID");
    alertBuilder.setNegativeButton(
        getString(R.string.cancel_button),
        new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dlg, int id) {}
        });

    alertBuilder.setPositiveButton(
        getString(R.string.ok_button),
        new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dlg, int id) {
            String userHash = _userEdit.getText().toString();
            SharedPreferences.Editor editor = NavigineApp.Settings.edit();
            editor.putString("user_hash", userHash);
            editor.commit();
            NavigineApp.applySettings();
            refreshMapList();
          }
        });

    AlertDialog dialog = alertBuilder.create();
    dialog.setCanceledOnTouchOutside(false);
    dialog.show();
  }
  private void selectLocation(LocationInfo info) {
    if (NavigineApp.Navigation == null) return;

    if (info != null && NavigineApp.Navigation.loadArchive(info.archiveFile)) {
      SharedPreferences.Editor editor = NavigineApp.Settings.edit();
      editor.putString("map_file", info.archiveFile);
      editor.commit();
      mAdapter.updateList();
    }
  }
        public void run() {
          if (NavigineApp.Navigation == null) return;

          long timeNow = DateTimeUtils.currentTimeMillis();

          String userHash = NavigineApp.Settings.getString("user_hash", "");
          if (userHash.length() == 0) return;

          if (mLoader >= 0) updateLoader();

          if (Math.abs(timeNow - mUpdateLocationLoadersTime) > 1000) updateLocationLoaders();
        }
  private void refreshMapList() {
    if (mLoader >= 0) return;

    String userHash = NavigineApp.Settings.getString("user_hash", "");
    if (userHash.length() == 0) return;

    // Starting new loader
    String fileName = LocationLoader.getLocationDir(NavigineApp.AppContext, null) + "/maps.xml";
    new File(fileName).delete();
    mLoader = LocationLoader.startLocationLoader(null, fileName, true);
    mLoaderTime = DateTimeUtils.currentTimeMillis();
    mInfoList = new ArrayList<LocationInfo>();
    Log.d(TAG, String.format(Locale.ENGLISH, "Location loader started: %d", mLoader));
  }
  private void updateLocationLoaders() {
    if (NavigineApp.Navigation == null) return;

    long timeNow = DateTimeUtils.currentTimeMillis();
    mUpdateLocationLoadersTime = timeNow;

    synchronized (mLoaderMap) {
      Iterator<Map.Entry<String, LoaderState>> iter = mLoaderMap.entrySet().iterator();
      while (iter.hasNext()) {
        Map.Entry<String, LoaderState> entry = iter.next();

        LoaderState loader = entry.getValue();
        if (loader.state < 100) {
          loader.timeLabel = timeNow;
          if (loader.type == DOWNLOAD) loader.state = LocationLoader.checkLocationLoader(loader.id);
          if (loader.type == UPLOAD) loader.state = LocationLoader.checkLocationUploader(loader.id);
        } else if (loader.state == 100) {
          String archivePath = NavigineApp.Navigation.getArchivePath();
          String locationFile =
              LocationLoader.getLocationFile(NavigineApp.AppContext, loader.location);
          if (archivePath != null && archivePath.equals(locationFile)) {
            Log.d(TAG, "Reloading archive " + archivePath);
            if (NavigineApp.Navigation.loadArchive(archivePath)) {
              SharedPreferences.Editor editor = NavigineApp.Settings.edit();
              editor.putString("map_file", archivePath);
              editor.commit();
            }
          }
          if (loader.type == DOWNLOAD) LocationLoader.stopLocationLoader(loader.id);
          if (loader.type == UPLOAD) LocationLoader.stopLocationUploader(loader.id);
          iter.remove();
        } else {
          // Load failed
          if (Math.abs(timeNow - loader.timeLabel) > 5000) {
            if (loader.type == DOWNLOAD) LocationLoader.stopLocationLoader(loader.id);
            if (loader.type == UPLOAD) LocationLoader.stopLocationUploader(loader.id);
            iter.remove();
          }
        }
      }
    }
    updateLocalVersions();
    mAdapter.updateList();
  }
  private void startUpload(int index) {
    if (NavigineApp.Navigation == null) return;

    String userHash = NavigineApp.Settings.getString("user_hash", "");
    if (userHash.length() == 0) return;

    LocationInfo info = mInfoList.get(index);
    String location = new String(info.title);
    Log.d(TAG, String.format(Locale.ENGLISH, "Start upload: %s", location));

    synchronized (mLoaderMap) {
      if (!mLoaderMap.containsKey(location)) {
        LoaderState loader = new LoaderState();
        loader.location = location;
        loader.type = UPLOAD;
        loader.id = LocationLoader.startLocationUploader(location, info.archiveFile, true);
        mLoaderMap.put(location, loader);
      }
    }
    mAdapter.updateList();
  }
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
      View view = convertView;
      if (view == null) {
        LayoutInflater inflater =
            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.content_list_item, null);
      }
      TextView titleTextView = (TextView) view.findViewById(R.id.list_item_title);
      TextView stateTextView = (TextView) view.findViewById(R.id.list_item_state);
      TextView downTextView = (TextView) view.findViewById(R.id.list_item_downbar);
      Button downloadButton = (Button) view.findViewById(R.id.list_item_download_button);
      Button uploadButton = (Button) view.findViewById(R.id.list_item_upload_button);

      LocationInfo info = mInfoList.get(position);
      String titleText = info.title;
      String stateText = "";

      if (titleText.length() > 30) titleText = titleText.substring(0, 28) + "...";

      synchronized (mLoaderMap) {
        if (mLoaderMap.containsKey(info.title)) {
          LoaderState loader = mLoaderMap.get(info.title);
          if (loader.state < 100) stateText = String.format(Locale.ENGLISH, "%d%%", loader.state);
          else if (loader.state == 100) stateText = String.format(Locale.ENGLISH, "Done!");
          else stateText = String.format(Locale.ENGLISH, "Failed!");
        }
      }

      if (info.localVersion < 0) titleText += " (?)";
      else {
        if (info.localModified)
          titleText += String.format(Locale.ENGLISH, " (v. %d+)", info.localVersion);
        else titleText += String.format(Locale.ENGLISH, " (v. %d)", info.localVersion);
      }

      String mapFile = NavigineApp.Settings.getString("map_file", "");
      if (mapFile.equals(info.archiveFile)) {
        titleTextView.setTypeface(null, Typeface.BOLD);
        view.setBackgroundColor(Color.parseColor("#590E0E"));
      } else {
        titleTextView.setTypeface(null, Typeface.NORMAL);
        view.setBackgroundColor(Color.BLACK);
      }

      titleTextView.setText(titleText);
      stateTextView.setText(stateText);

      if (info.localModified) {
        downloadButton.setVisibility(View.GONE);
        uploadButton.setVisibility(View.VISIBLE);
        downTextView.setText("Version is modified. Upload?");
      } else if (info.serverVersion > info.localVersion) {
        downloadButton.setVisibility(View.VISIBLE);
        uploadButton.setVisibility(View.GONE);
        String downText =
            String.format(Locale.ENGLISH, "Version available: %d", info.serverVersion);
        downTextView.setText(downText);
      } else {
        downloadButton.setVisibility(View.INVISIBLE);
        uploadButton.setVisibility(View.GONE);
        downTextView.setText("Version is up to date");
      }

      downloadButton.setOnClickListener(
          new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              startDownload(position);
            }
          });

      uploadButton.setOnClickListener(
          new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              startUpload(position);
            }
          });

      return view;
    }