package cgeo.geocaching.apps.navi; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.App; import cgeo.geocaching.apps.cache.WhereYouGoApp; import cgeo.geocaching.apps.navi.GoogleNavigationApp.GoogleNavigationBikeApp; import cgeo.geocaching.apps.navi.GoogleNavigationApp.GoogleNavigationDrivingApp; import cgeo.geocaching.apps.navi.GoogleNavigationApp.GoogleNavigationTransitApp; import cgeo.geocaching.apps.navi.GoogleNavigationApp.GoogleNavigationWalkingApp; import cgeo.geocaching.apps.navi.OruxMapsApp.OruxOfflineMapApp; import cgeo.geocaching.apps.navi.OruxMapsApp.OruxOnlineMapApp; import cgeo.geocaching.apps.navi.SygicNavigationApp.SygicNavigationDrivingApp; import cgeo.geocaching.apps.navi.SygicNavigationApp.SygicNavigationWalkingApp; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.maps.mapsforge.v6.NewMap; import cgeo.geocaching.models.Geocache; import cgeo.geocaching.models.Waypoint; import cgeo.geocaching.settings.Settings; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.MenuItem; import android.widget.ArrayAdapter; import java.util.ArrayList; import java.util.List; public final class NavigationAppFactory { private NavigationAppFactory() { // utility class } public enum NavigationAppsEnum { /** The internal compass activity */ COMPASS(new CompassApp(), 0, R.string.pref_navigation_menu_compass), /** The external radar app */ RADAR(new RadarApp(), 1, R.string.pref_navigation_menu_radar), /** The selected map */ INTERNAL_MAP(new InternalMap(), 2, R.string.pref_navigation_menu_internal_map), /** The new internal map */ INTERNAL_MAP_NEW(new InternalMap(NewMap.class, R.string.cache_menu_mfbeta), 25, R.string.pref_navigation_menu_internal_new_map), /** The internal static map activity, when stored */ STATIC_MAP(new StaticMapApp(), 3, R.string.pref_navigation_menu_static_map), /** The internal static map activity, when not yet stored */ DOWNLOAD_STATIC_MAPS(new DownloadStaticMapsApp(), 20, R.string.pref_navigation_menu_static_map_download), /** The external Locus app */ LOCUS(new LocusApp(), 4, R.string.pref_navigation_menu_locus), /** The external RMaps app */ RMAPS(new RMapsApp(), 5, R.string.pref_navigation_menu_rmaps), /** Google Maps */ GOOGLE_MAPS(new GoogleMapsApp(), 6, R.string.pref_navigation_menu_google_maps), /** Google Navigation */ GOOGLE_NAVIGATION(new GoogleNavigationDrivingApp(), 7, R.string.pref_navigation_menu_google_navigation), /** Google Streetview */ GOOGLE_STREETVIEW(new StreetviewApp(), 8, R.string.pref_navigation_menu_google_streetview), /** The external OruxMaps app */ ORUX_MAPS(new OruxOnlineMapApp(), 9, R.string.pref_navigation_menu_oruxmaps), /** The external OruxMaps app */ ORUX_MAPS_OFFLINE(new OruxOfflineMapApp(), 24, R.string.pref_navigation_menu_oruxmaps_offline), /** The external navigon app */ NAVIGON(new NavigonApp(), 10, R.string.pref_navigation_menu_navigon), /** The external Sygic app in walking mode */ SYGIC_WALKING(new SygicNavigationWalkingApp(), 11, R.string.pref_navigation_menu_sygic_walking), /** The external Sygic app in driving mode */ SYGIC_DRIVING(new SygicNavigationDrivingApp(), 23, R.string.pref_navigation_menu_sygic_driving), /** The external OsmAnd app */ OSM_AND(new OsmAndApp(), 26, R.string.pref_navigation_menu_osmand), /** Google Navigation in walking mode */ GOOGLE_NAVIGATION_WALK(new GoogleNavigationWalkingApp(), 12, R.string.pref_navigation_menu_google_walk), /** Google Navigation in bike mode */ GOOGLE_NAVIGATION_BIKE(new GoogleNavigationBikeApp(), 21, R.string.pref_navigation_menu_google_bike), /** Google Navigation in transit mode */ GOOGLE_NAVIGATION_TRANSIT(new GoogleNavigationTransitApp(), 14, R.string.pref_navigation_menu_google_transit), /** Google Maps Directions */ GOOGLE_MAPS_DIRECTIONS(new GoogleMapsDirectionApp(), 13, R.string.pref_navigation_menu_google_maps_directions), WHERE_YOU_GO(new WhereYouGoApp(), 16, R.string.pref_navigation_menu_where_you_go), PEBBLE(new PebbleApp(), 17, R.string.pref_navigation_menu_pebble), MAPSWITHME(new MapsMeApp(), 22, R.string.pref_navigation_menu_mapswithme); NavigationAppsEnum(final App app, final int id, final int preferenceKey) { this.app = app; this.id = id; this.preferenceKey = preferenceKey; if (preferenceKey == 0 || preferenceKey == -1) { throw new IllegalStateException("Every navigation app must have a boolean preference in the settings to be enabled/disabled."); } } /** * The app instance to use */ public final App app; /** * The id - used in c:geo settings */ public final int id; /** * key of the related preference in the navigation menu preference screen, used for disabling the preference UI */ public final int preferenceKey; /* * display app name in array adapter * * @see java.lang.Enum#toString() */ @Override public String toString() { return app.getName(); } } /** * Default way to handle selection of navigation tool.<br /> * A dialog is created for tool selection and the selected tool is started afterwards. * <p /> * Delegates to {@link #showNavigationMenu(Activity, Geocache, Waypoint, Geopoint, boolean, boolean)} with * {@code showInternalMap = true} and {@code showDefaultNavigation = false} * */ public static void showNavigationMenu(final Activity activity, final Geocache cache, final Waypoint waypoint, final Geopoint destination) { showNavigationMenu(activity, cache, waypoint, destination, true, false); } /** * Specialized way to handle selection of navigation tool.<br /> * A dialog is created for tool selection and the selected tool is started afterwards. * * @param cache * may be {@code null} * @param waypoint * may be {@code null} * @param destination * may be {@code null} * @param showInternalMap * should be {@code false} only when called from within the internal map * @param showDefaultNavigation * should be {@code false} by default * * @see #showNavigationMenu(Activity, Geocache, Waypoint, Geopoint) */ public static void showNavigationMenu(final Activity activity, final Geocache cache, final Waypoint waypoint, final Geopoint destination, final boolean showInternalMap, final boolean showDefaultNavigation) { final List<NavigationAppsEnum> items = new ArrayList<>(); final int defaultNavigationTool = Settings.getDefaultNavigationTool(); for (final NavigationAppsEnum navApp : getActiveNavigationApps()) { if ((showInternalMap || !(navApp.app instanceof InternalMap)) && (showDefaultNavigation || defaultNavigationTool != navApp.id)) { boolean add = false; if (cache != null && navApp.app instanceof CacheNavigationApp && navApp.app.isEnabled(cache)) { add = true; } if (waypoint != null && navApp.app instanceof WaypointNavigationApp && ((WaypointNavigationApp) navApp.app).isEnabled(waypoint)) { add = true; } if (destination != null && navApp.app instanceof GeopointNavigationApp) { add = true; } if (add) { items.add(navApp); } } } if (items.size() == 1) { invokeNavigation(activity, cache, waypoint, destination, items.get(0).app); return; } /* * Using an ArrayAdapter with list of NavigationAppsEnum items avoids * handling between mapping list positions allows us to do dynamic filtering of the list based on use case. */ final ArrayAdapter<NavigationAppsEnum> adapter = new ArrayAdapter<>(activity, android.R.layout.select_dialog_item, items); final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.cache_menu_navigate); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int item) { final NavigationAppsEnum selectedItem = adapter.getItem(item); invokeNavigation(activity, cache, waypoint, destination, selectedItem.app); } }); final AlertDialog alert = builder.create(); alert.show(); } /** * Returns all installed navigation apps. * */ @NonNull static List<NavigationAppsEnum> getInstalledNavigationApps() { final List<NavigationAppsEnum> installedNavigationApps = new ArrayList<>(); for (final NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { if (appEnum.app.isInstalled()) { installedNavigationApps.add(appEnum); } } return installedNavigationApps; } /** * @return all navigation apps, which are installed and activated in the settings */ @NonNull static List<NavigationAppsEnum> getActiveNavigationApps() { final List<NavigationAppsEnum> activeApps = new ArrayList<>(); for (final NavigationAppsEnum appEnum : getInstalledNavigationApps()) { if (Settings.isUseNavigationApp(appEnum)) { activeApps.add(appEnum); } } return activeApps; } /** * Returns all installed navigation apps for default navigation. * */ @NonNull public static List<NavigationAppsEnum> getInstalledDefaultNavigationApps() { final List<NavigationAppsEnum> installedNavigationApps = new ArrayList<>(); for (final NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { if (appEnum.app.isInstalled() && appEnum.app.isUsableAsDefaultNavigationApp()) { installedNavigationApps.add(appEnum); } } return installedNavigationApps; } /** * Handles menu selections for menu entries created with * {@link #showNavigationMenu(Activity, Geocache, Waypoint, Geopoint)}. * */ public static boolean onMenuItemSelected(final MenuItem item, final Activity activity, final Geocache cache) { final App menuItem = getAppFromMenuItem(item); navigateCache(activity, cache, menuItem); return menuItem != null; } private static void navigateCache(final Activity activity, final Geocache cache, @Nullable final App app) { if (app instanceof CacheNavigationApp) { final CacheNavigationApp cacheApp = (CacheNavigationApp) app; if (cache.getCoords() != null) { cacheApp.navigate(activity, cache); } } } private static void navigateWaypoint(final Activity activity, final Waypoint waypoint, @Nullable final App app) { if (app instanceof WaypointNavigationApp) { final WaypointNavigationApp waypointApp = (WaypointNavigationApp) app; if (waypoint.getCoords() != null) { waypointApp.navigate(activity, waypoint); } } } private static void navigateGeopoint(final Activity activity, final Geopoint destination, final App app) { if (app instanceof GeopointNavigationApp) { final GeopointNavigationApp geopointApp = (GeopointNavigationApp) app; if (destination != null) { geopointApp.navigate(activity, destination); } } } @Nullable private static App getAppFromMenuItem(final MenuItem item) { final int id = item.getItemId(); for (final NavigationAppsEnum navApp : NavigationAppsEnum.values()) { if (navApp.id == id) { return navApp.app; } } return null; } /** * Starts the default navigation tool if correctly set and installed or the compass app as default fallback. * * */ public static void startDefaultNavigationApplication(final int defaultNavigation, final Activity activity, final Geocache cache) { if (cache == null || cache.getCoords() == null) { ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_location_unknown)); return; } navigateCache(activity, cache, getDefaultNavigationApplication(defaultNavigation)); } @NonNull private static App getDefaultNavigationApplication(final int defaultNavigation) { if (defaultNavigation == 2) { return getNavigationAppForId(Settings.getDefaultNavigationTool2()); } return getNavigationAppForId(Settings.getDefaultNavigationTool()); } /** * Starts the default navigation tool if correctly set and installed or the compass app as default fallback. * */ public static void startDefaultNavigationApplication(final int defaultNavigation, final Activity activity, final Waypoint waypoint) { if (waypoint == null || waypoint.getCoords() == null) { ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_location_unknown)); return; } navigateWaypoint(activity, waypoint, getDefaultNavigationApplication(defaultNavigation)); } /** * Starts the default navigation tool if correctly set and installed or the compass app as default fallback. * */ public static void startDefaultNavigationApplication(final int defaultNavigation, final Activity activity, final Geopoint destination) { if (destination == null) { ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_location_unknown)); return; } navigateGeopoint(activity, destination, getDefaultNavigationApplication(defaultNavigation)); } /** * Returns the default navigation tool if correctly set and installed or the compass app as default fallback * * @return never {@code null} */ @NonNull public static App getDefaultNavigationApplication() { return getDefaultNavigationApplication(1); } @NonNull private static App getNavigationAppForId(final int navigationAppId) { final List<NavigationAppsEnum> installedNavigationApps = getInstalledNavigationApps(); for (final NavigationAppsEnum navigationApp : installedNavigationApps) { if (navigationApp.id == navigationAppId) { // redirect navigation tools between old and new map on the fly. // if the global checkbox and the selected tools don't match, then offline maps may not work due to different theme versions if (navigationApp == NavigationAppsEnum.INTERNAL_MAP && Settings.useNewMapAsDefault()) { return NavigationAppsEnum.INTERNAL_MAP_NEW.app; } if (navigationApp == NavigationAppsEnum.INTERNAL_MAP_NEW && !Settings.useNewMapAsDefault()) { return NavigationAppsEnum.INTERNAL_MAP.app; } return navigationApp.app; } } // default navigation tool wasn't set already or couldn't be found (not installed any more for example) return NavigationAppsEnum.COMPASS.app; } private static void invokeNavigation(final Activity activity, final Geocache cache, final Waypoint waypoint, final Geopoint destination, final App app) { if (cache != null) { navigateCache(activity, cache, app); } else if (waypoint != null) { navigateWaypoint(activity, waypoint, app); } else { navigateGeopoint(activity, destination, app); } } }