<NME> MapProviderFactory.java
<BEF> package cgeo.geocaching.maps;

import cgeo.geocaching.CgeoApplication;
import cgeo.geocaching.R;
import cgeo.geocaching.activity.ActivityMixin;
import cgeo.geocaching.databinding.LocSwitchActionBinding;
import cgeo.geocaching.downloader.CompanionFileUtils;
import cgeo.geocaching.maps.google.v2.GoogleMapProvider;
import cgeo.geocaching.maps.interfaces.MapProvider;
import cgeo.geocaching.maps.interfaces.MapSource;
import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.storage.ContentStorage;
import cgeo.geocaching.storage.PersistableFolder;
import cgeo.geocaching.ui.TextParam;
import cgeo.geocaching.ui.dialog.SimpleDialog;
import cgeo.geocaching.unifiedmap.tileproviders.TileProviderFactory;
import cgeo.geocaching.utils.Log;

import android.app.Activity;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.CheckBox;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Pair;
import androidx.core.view.MenuCompat;

        }
    }

    private static MapProviderFactory getFactory() {
        if (null == instance) {
            initInstance();
        }

    }

    public static SortedMap<Integer, String> getMapSources() {
        return getFactory().mapSources;
    }

    public static boolean IsValidSourceId(int sourceId) {
        return getFactory().mapSources.containsKey(sourceId);
    }

    public static boolean IsSameProvider(int sourceId1, int sourceId2) {
        for (MapProvider mp : getFactory().mapProviders) {
            if (mp.IsMySource(sourceId1) && mp.IsMySource(sourceId2)) {
                return true;
            }
        MapsforgeMapProvider.getInstance();
    }
    }

    public static MapProvider getMapProvider(int sourceId) {
        for (MapProvider mp : getFactory().mapProviders) {
            if (mp.IsMySource(sourceId)) {
                return mp;
            }
        }
        return getFactory().mapProviders[0];
    }

    public static int getSourceOrdinalFromId(int sourceId) {

        int sourceOrdinal = 1;
        for (int key : getFactory().mapSources.keySet()) {
            if (sourceId == key) {
                return sourceOrdinal;
            }
        }

        // Assume that Google Maps is available and working
    }

    public static int getSourceIdFromOrdinal(int sourceOrdinal) {
        int count = 1;
        for (int key : getFactory().mapSources.keySet()) {
            if (sourceOrdinal == count) {
                return key;
            }
            count++;
        }
        return getFactory().mapSources.firstKey();
    }

    public static void addMapviewMenuItems(Menu parentMenu, int groupId, int currentSource) {

        SortedMap<Integer, String> mapSources = getFactory().mapSources;

        for (int key : mapSources.keySet()) {
            parentMenu.add(groupId, key, 0, mapSources.get(key)).setCheckable(true).setChecked(key == currentSource);
        for (MapSource mapSource : mapSources.values()) {
            final int id = mapSource.getNumericalId();
            parentMenu.add(R.id.menu_group_map_sources, id, i, mapSource.getName()).setCheckable(true).setChecked(id == currentSource);
            i++;
        }
        parentMenu.setGroupCheckable(R.id.menu_group_map_sources, true, true);
        parentMenu.add(R.id.menu_group_offlinemaps, R.id.menu_download_offlinemap, mapSources.size(), '<' + activity.getString(R.string.downloadmap_title) + '>');
        parentMenu.add(R.id.menu_group_offlinemaps, R.id.menu_delete_offlinemap, mapSources.size() + 1, '<' + activity.getString(R.string.delete_offlinemap_title) + '>');
    }

    /** displays a list of offline map sources and deletes selected item after confirmation */
    public static void showDeleteMenu(final Activity activity) {
        final int currentSource = Settings.getMapSource().getNumericalId();

        // build list of available offline map sources except currently active one
        final List<Pair<String, Integer>> list = new ArrayList<>();
        for (MapSource mapSource : mapSources.values()) {
            if (mapSource instanceof MapsforgeMapProvider.OfflineMapSource && mapSource.getNumericalId() != currentSource) {
                list.add(new Pair<>(mapSource.getName(), mapSource.getNumericalId()));
            }
        }
        if (list.isEmpty()) {
            ActivityMixin.showToast(activity, R.string.no_deletable_offlinemaps);
            return;
        }

        SimpleDialog.of(activity).setTitle(TextParam.id(R.string.delete_offlinemap_title)).selectSingle(list, (l, pos) -> TextParam.text(l.first), -1, SimpleDialog.SingleChoiceMode.NONE, (l, pos) -> {
            final MapsforgeMapProvider.OfflineMapSource mapSource = (MapsforgeMapProvider.OfflineMapSource) getMapSource(l.second);
            if (mapSource != null) {
                final ContentStorage cs = ContentStorage.get();
                final ContentStorage.FileInformation fi = cs.getFileInfo(mapSource.getMapUri());
                if (fi != null) {
                    SimpleDialog.of(activity).setTitle(TextParam.id(R.string.delete_offlinemap_title)).setMessage(TextParam.text(String.format(activity.getString(R.string.delete_file_confirmation), fi.name))).confirm((dialog, which) -> {
                        final Uri cf = CompanionFileUtils.companionFileExists(cs.list(PersistableFolder.OFFLINE_MAPS), fi.name);
                        cs.delete(mapSource.getMapUri());
                        if (cf != null) {
                            cs.delete(cf);
                        }
                        ActivityMixin.showShortToast(activity, String.format(activity.getString(R.string.file_deleted_info), fi.name));
                        MapsforgeMapProvider.getInstance().updateOfflineMaps();
                        if (Settings.showUnifiedMap()) {
                            TileProviderFactory.buildTileProviderList(true);
                        }
                        ActivityMixin.invalidateOptionsMenu(activity);
                    });
                }
            }
        });
    }

    public static CheckBox createLocSwitchMenuItem(final Activity activity, final Menu menu) {
        final MenuItem item = menu.findItem(R.id.menu_toggle_mypos);
        final LocSwitchActionBinding binding = LocSwitchActionBinding.inflate(LayoutInflater.from(activity));
        binding.getRoot().setOnClickListener(v -> binding.locSwitch.performClick());
        item.setActionView(binding.getRoot());

        return binding.locSwitch;
    }

    @Nullable
    public static MapSource getMapSource(final String stringId) {
        return mapSources.get(stringId);
    }

    /**
     * Return a map source by id.
     *
     * @param id the map source id
     * @return the map source, or <tt>null</tt> if <tt>id</tt> does not correspond to a registered map source
     */
    @Nullable
    public static MapSource getMapSource(final int id) {
        for (final MapSource mapSource : mapSources.values()) {
            if (mapSource.getNumericalId() == id) {
                return mapSource;
            }
        }
        return null;
    }

    /**
     * Return a map source if there is at least one.
     *
     * @return the first map source in the collection, or <tt>null</tt> if there are none registered
     */
    public static MapSource getAnyMapSource() {
        return mapSources.isEmpty() ? null : mapSources.entrySet().iterator().next().getValue();
    }

    public static void registerMapSource(final MapSource mapSource) {
        mapSources.put(mapSource.getId(), mapSource);
    }

    /**
     * Fills the "Map language" submenu with the languages provided by the current map
     * Makes menu visible if more than one language is available, invisible if not
     *
     * @param menu
     */
    public static void addMapViewLanguageMenuItems(final Menu menu) {
        final MenuItem parentMenu = menu.findItem(R.id.menu_select_language);
        if (languages != null) {
            final int currentLanguage = Settings.getMapLanguageId();
            final SubMenu subMenu = parentMenu.getSubMenu();
            subMenu.add(R.id.menu_group_map_languages, MAP_LANGUAGE_DEFAULT_ID, 0, R.string.switch_default).setCheckable(true).setChecked(MAP_LANGUAGE_DEFAULT_ID == currentLanguage);
            for (int i = 0; i < languages.length; i++) {
                final int languageId = languages[i].hashCode();
                subMenu.add(R.id.menu_group_map_languages, languageId, i, languages[i]).setCheckable(true).setChecked(languageId == currentLanguage);
            }
            subMenu.setGroupCheckable(R.id.menu_group_map_languages, true, true);
            parentMenu.setVisible(languages.length > 1);
        } else {
            parentMenu.setVisible(false);
        }
    }

    /**
     * Return a language by id.
     *
     * @param id the language id
     * @return the language, or <tt>null</tt> if <tt>id</tt> does not correspond to a registered language
     */
    @Nullable
    public static String getLanguage(final int id) {
        if (languages != null) {
            for (final String language : languages) {
                if (language.hashCode() == id) {
                    return language;
                }
            }
        }
        return null;
    }

    public static void setLanguages(final String[] newLanguages) {
        languages = newLanguages;
    }

    /**
     * remove offline map sources after changes of the settings
     */
    public static void deleteOfflineMapSources() {
        final Iterator<Map.Entry<String, MapSource>> it = mapSources.entrySet().iterator();
        while (it.hasNext()) {
            final MapSource mapSource = it.next().getValue();
            if (mapSource instanceof MapsforgeMapProvider.OfflineMapSource || mapSource instanceof MapsforgeMapProvider.OfflineMultiMapSource) {
                it.remove();
            }
        }
    }
}

<MSG> Mapsource refactor little cleanup:
Rename singleton method to getInstance
Bugfix for ordinal from mapsourceId
<DFF> @@ -33,7 +33,7 @@ public class MapProviderFactory {
         }
     }
 
-    private static MapProviderFactory getFactory() {
+    private static MapProviderFactory getInstance() {
         if (null == instance) {
             initInstance();
         }
@@ -41,15 +41,15 @@ public class MapProviderFactory {
     }
 
     public static SortedMap<Integer, String> getMapSources() {
-        return getFactory().mapSources;
+        return getInstance().mapSources;
     }
 
     public static boolean IsValidSourceId(int sourceId) {
-        return getFactory().mapSources.containsKey(sourceId);
+        return getInstance().mapSources.containsKey(sourceId);
     }
 
     public static boolean IsSameProvider(int sourceId1, int sourceId2) {
-        for (MapProvider mp : getFactory().mapProviders) {
+        for (MapProvider mp : getInstance().mapProviders) {
             if (mp.IsMySource(sourceId1) && mp.IsMySource(sourceId2)) {
                 return true;
             }
@@ -58,18 +58,18 @@ public class MapProviderFactory {
     }
 
     public static MapProvider getMapProvider(int sourceId) {
-        for (MapProvider mp : getFactory().mapProviders) {
+        for (MapProvider mp : getInstance().mapProviders) {
             if (mp.IsMySource(sourceId)) {
                 return mp;
             }
         }
-        return getFactory().mapProviders[0];
+        return getInstance().mapProviders[0];
     }
 
     public static int getSourceOrdinalFromId(int sourceId) {
 
-        int sourceOrdinal = 1;
-        for (int key : getFactory().mapSources.keySet()) {
+        int sourceOrdinal = 0;
+        for (int key : getInstance().mapSources.keySet()) {
             if (sourceId == key) {
                 return sourceOrdinal;
             }
@@ -79,19 +79,19 @@ public class MapProviderFactory {
     }
 
     public static int getSourceIdFromOrdinal(int sourceOrdinal) {
-        int count = 1;
-        for (int key : getFactory().mapSources.keySet()) {
+        int count = 0;
+        for (int key : getInstance().mapSources.keySet()) {
             if (sourceOrdinal == count) {
                 return key;
             }
             count++;
         }
-        return getFactory().mapSources.firstKey();
+        return getInstance().mapSources.firstKey();
     }
 
     public static void addMapviewMenuItems(Menu parentMenu, int groupId, int currentSource) {
 
-        SortedMap<Integer, String> mapSources = getFactory().mapSources;
+        SortedMap<Integer, String> mapSources = getInstance().mapSources;
 
         for (int key : mapSources.keySet()) {
             parentMenu.add(groupId, key, 0, mapSources.get(key)).setCheckable(true).setChecked(key == currentSource);

Mapsource refactor little cleanup: Rename singleton method to getInstance Bugfix for ordinal from mapsourceId