<NME> MapDownloaderOpenAndroMaps.java
<BEF> package cgeo.geocaching.downloader;

import cgeo.geocaching.R;
import cgeo.geocaching.models.OfflineMap;
import cgeo.geocaching.storage.ContentStorage;
import cgeo.geocaching.storage.PersistableFolder;
import cgeo.geocaching.ui.dialog.Dialogs;
import cgeo.geocaching.utils.CalendarUtils;
import cgeo.geocaching.utils.Formatter;
import cgeo.geocaching.utils.MatcherWrapper;

import android.app.Activity;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.List;
import java.util.regex.Pattern;

    private static final Pattern PATTERN_MAP = Pattern.compile("<a href=\"([A-Za-z0-9_-]+\\.zip)\">([A-Za-z0-9_-]+)\\.zip<\\/a>[ ]*([0-9]{2}-[A-Za-z]{3}-[0-9]{4}) [0-9]{2}:[0-9]{2}[ ]*([0-9]+)"); // 1:file name, 2:display name, 3:date DD-MMM-YYYY, 4:size (bytes)
    private static final Pattern PATTERN_DIR = Pattern.compile("<a href=\"([A-Z-a-z0-9]+\\/)\">([A-Za-z0-9]+)\\/<\\/a>");  // 1:file name, 2:display name
    private static final Pattern PATTERN_UP = Pattern.compile("<a href=\"(\\.\\.\\/)\">(\\.\\.)\\/<\\/a>"); // 1:relative dir, 2:..
    private static final String ELEVATE_THEME = "Elevate.zip";
    private static final MapDownloaderOpenAndroMaps INSTANCE = new MapDownloaderOpenAndroMaps();

    private MapDownloaderOpenAndroMaps() {
    private MapDownloaderOpenAndroMaps() {
        super(Download.DownloadType.DOWNLOADTYPE_MAP_OPENANDROMAPS, R.string.mapserver_openandromaps_downloadurl, R.string.mapserver_openandromaps_name, R.string.mapserver_openandromaps_info, R.string.mapserver_openandromaps_projecturl, R.string.mapserver_openandromaps_likeiturl);
        companionType = Download.DownloadType.DOWNLOADTYPE_THEME_OPENANDROMAPS;
    }

    @Override
    protected void analyzePage(final Uri uri, final List<Download> list, final @NonNull String page) {
        basicUpMatcher(uri, list, page, PATTERN_UP);

        final MatcherWrapper matchDir = new MatcherWrapper(PATTERN_DIR, page);
        while (matchDir.find()) {
            final Download offlineMap = new Download(matchDir.group(2), Uri.parse(uri + matchDir.group(1)), true, "", "", offlineMapType, ICONRES_FOLDER);
            list.add(offlineMap);
        }

        final MatcherWrapper matchMap = new MatcherWrapper(PATTERN_MAP, page);
        while (matchMap.find()) {
            final Download offlineMap = new Download(matchMap.group(2), Uri.parse(uri + matchMap.group(1)), false, CalendarUtils.yearMonthDay(CalendarUtils.parseDayMonthYearUS(matchMap.group(3))), Formatter.formatBytes(Long.parseLong(matchMap.group(4))), offlineMapType, iconRes);
            list.add(offlineMap);
        }
    }

    @Nullable
    @Override
    protected Download checkUpdateFor(final @NonNull String page, final String remoteUrl, final String remoteFilename) {
        // another change between v4 and v5 OAM: "_" in filenames got replaced by "-"
        final String remoteFilenameNew = remoteFilename.replace("_", "-");
        final MatcherWrapper matchMap = new MatcherWrapper(PATTERN_MAP, page);
        while (matchMap.find()) {
            final String filename = matchMap.group(1);
            if (filename.equals(remoteFilenameNew)) {
                return new Download(matchMap.group(2), Uri.parse(getUpdatedPath(remoteUrl) + "/" + filename), false, CalendarUtils.yearMonthDay(CalendarUtils.parseDayMonthYearUS(matchMap.group(3))), Formatter.formatBytes(Long.parseLong(matchMap.group(4))), offlineMapType, iconRes);
            }
        }
        return null;
    }

    @Override
    @Override
    protected void onFollowup(final Activity activity, final Runnable callback) {
        // check whether Elevate.zip exists in theme folder and ask whether user wants to download it as well, if it does not exist yet
        boolean themeFound = false;
        final List<ContentStorage.FileInformation> mapDirContent = ContentStorage.get().list(PersistableFolder.OFFLINE_MAP_THEMES);
        for (ContentStorage.FileInformation fi : mapDirContent) {
            if (fi.name.equals(ELEVATE_THEME)) {
                themeFound = true;
                break;
            }
        }

        if (!themeFound) {
            Dialogs.confirm(activity, activity.getString(R.string.downloadmap_install_theme_title), activity.getString(R.string.downloadmap_install_theme_info), activity.getString(android.R.string.ok), (d, w) -> {
                final Uri newUri = Uri.parse(activity.getString(R.string.mapserver_elevate_downloadurl) + ELEVATE_THEME);
                MapDownloaderUtils.triggerDownload(activity, OfflineMap.OfflineMapType.MAP_DOWNLOAD_TYPE_ELEVATE.id, newUri, "", System.currentTimeMillis(), callback);
            }, dialog -> callback.run());
        } else {
            callback.run();
        }
    }

    @NonNull
        return getExtrafile(THEME_FILES, activity.getString(R.string.mapserver_openandromaps_themes_downloadurl), Download.DownloadType.DOWNLOADTYPE_THEME_OPENANDROMAPS);
    }

    @Override
    protected String getUpdatePageUrl(final String downloadPageUrl) {
        return getUpdatedPath(downloadPageUrl);
    }

    /**
     * Return an updated path string, reflecting folder structure changes between
     * OAM v4 maps and OAM v5 maps
     * - last segment in path is now completely lower-case
     * - "mapsV4" part has changed to "mapsV5"
     *
     * @param oldPath folder name (without trailing '/', and without filename)
     */
    private String getUpdatedPath(final String oldPath) {
        final int lastSegmentStart = oldPath.lastIndexOf('/');
        return oldPath.substring(0, lastSegmentStart).replace("mapsV4", "mapsV5") + oldPath.substring(lastSegmentStart).toLowerCase();
    }

    @NonNull
    public static MapDownloaderOpenAndroMaps getInstance() {
        return INSTANCE;
    }
}

<MSG> offer to download missing FZK theme after successful FZK map download

<DFF> @@ -2,9 +2,6 @@ package cgeo.geocaching.downloader;
 
 import cgeo.geocaching.R;
 import cgeo.geocaching.models.OfflineMap;
-import cgeo.geocaching.storage.ContentStorage;
-import cgeo.geocaching.storage.PersistableFolder;
-import cgeo.geocaching.ui.dialog.Dialogs;
 import cgeo.geocaching.utils.CalendarUtils;
 import cgeo.geocaching.utils.Formatter;
 import cgeo.geocaching.utils.MatcherWrapper;
@@ -21,7 +18,7 @@ public class MapDownloaderOpenAndroMaps extends AbstractMapDownloader {
     private static final Pattern PATTERN_MAP = Pattern.compile("<a href=\"([A-Za-z0-9_-]+\\.zip)\">([A-Za-z0-9_-]+)\\.zip<\\/a>[ ]*([0-9]{2}-[A-Za-z]{3}-[0-9]{4}) [0-9]{2}:[0-9]{2}[ ]*([0-9]+)"); // 1:file name, 2:display name, 3:date DD-MMM-YYYY, 4:size (bytes)
     private static final Pattern PATTERN_DIR = Pattern.compile("<a href=\"([A-Z-a-z0-9]+\\/)\">([A-Za-z0-9]+)\\/<\\/a>");  // 1:file name, 2:display name
     private static final Pattern PATTERN_UP = Pattern.compile("<a href=\"(\\.\\.\\/)\">(\\.\\.)\\/<\\/a>"); // 1:relative dir, 2:..
-    private static final String ELEVATE_THEME = "Elevate.zip";
+    private static final String[] THEME_FILES = {"Elevate.zip"};
     private static final MapDownloaderOpenAndroMaps INSTANCE = new MapDownloaderOpenAndroMaps();
 
     private MapDownloaderOpenAndroMaps() {
@@ -66,23 +63,7 @@ public class MapDownloaderOpenAndroMaps extends AbstractMapDownloader {
     @Override
     protected void onFollowup(final Activity activity, final Runnable callback) {
         // check whether Elevate.zip exists in theme folder and ask whether user wants to download it as well, if it does not exist yet
-        boolean themeFound = false;
-        final List<ContentStorage.FileInformation> mapDirContent = ContentStorage.get().list(PersistableFolder.OFFLINE_MAP_THEMES);
-        for (ContentStorage.FileInformation fi : mapDirContent) {
-            if (fi.name.equals(ELEVATE_THEME)) {
-                themeFound = true;
-                break;
-            }
-        }
-
-        if (!themeFound) {
-            Dialogs.confirm(activity, activity.getString(R.string.downloadmap_install_theme_title), activity.getString(R.string.downloadmap_install_theme_info), activity.getString(android.R.string.ok), (d, w) -> {
-                final Uri newUri = Uri.parse(activity.getString(R.string.mapserver_elevate_downloadurl) + ELEVATE_THEME);
-                MapDownloaderUtils.triggerDownload(activity, OfflineMap.OfflineMapType.MAP_DOWNLOAD_TYPE_ELEVATE.id, newUri, "", System.currentTimeMillis(), callback);
-            }, dialog -> callback.run());
-        } else {
-            callback.run();
-        }
+        findOrDownload(activity, THEME_FILES, activity.getString(R.string.mapserver_elevate_downloadurl), OfflineMap.OfflineMapType.MAP_DOWNLOAD_TYPE_ELEVATE, callback);
     }
 
     @NonNull

offer to download missing FZK theme after successful FZK map download