From 8935465c0670c765e265aa9cb94fb32d350bd691 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Tue, 5 Sep 2023 13:43:05 -0600 Subject: [PATCH 01/20] declare camera usage --- mapcache/src/main/AndroidManifest.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mapcache/src/main/AndroidManifest.xml b/mapcache/src/main/AndroidManifest.xml index 200847ca..aa8459f5 100644 --- a/mapcache/src/main/AndroidManifest.xml +++ b/mapcache/src/main/AndroidManifest.xml @@ -3,6 +3,10 @@ package="mil.nga.mapcache" android:installLocation="auto"> + + From 06d88a59b243f11191c77003a8cebce859cf5e38 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 6 Sep 2023 08:33:20 -0600 Subject: [PATCH 02/20] disclaimer warning for leaked window fix --- .../nga/mapcache/GeoPackageMapFragment.java | 59 ++++++++++--------- .../sensors/SensorHandler.java | 2 +- 2 files changed, 33 insertions(+), 28 deletions(-) rename mapcache/src/main/java/mil/nga/mapcache/{ => repository}/sensors/SensorHandler.java (98%) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index f46d9b01..5bf2107b 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -8,9 +8,7 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageManager; -import android.graphics.Color; import android.graphics.Point; -import android.graphics.PorterDuff; import android.hardware.SensorEvent; import android.location.Location; import android.net.Uri; @@ -105,19 +103,15 @@ import java.sql.SQLException; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.regex.Pattern; import mil.nga.geopackage.BoundingBox; import mil.nga.geopackage.GeoPackage; -import mil.nga.geopackage.GeoPackageException; import mil.nga.geopackage.contents.Contents; import mil.nga.geopackage.contents.ContentsDao; import mil.nga.geopackage.db.GeoPackageDataType; @@ -160,7 +154,6 @@ import mil.nga.mapcache.listeners.LayerActiveSwitchListener; import mil.nga.mapcache.listeners.OnDialogButtonClickListener; import mil.nga.mapcache.listeners.SensorCallback; -import mil.nga.mapcache.load.DownloadTask; import mil.nga.mapcache.load.Downloader; import mil.nga.mapcache.load.ILoadTilesTask; import mil.nga.mapcache.load.ImportTask; @@ -168,8 +161,7 @@ import mil.nga.mapcache.preferences.GridType; import mil.nga.mapcache.preferences.PreferencesActivity; import mil.nga.mapcache.repository.GeoPackageModifier; -import mil.nga.mapcache.sensors.SensorHandler; -import mil.nga.mapcache.utils.ProjUtils; +import mil.nga.mapcache.repository.sensors.SensorHandler; import mil.nga.mapcache.utils.SampleDownloader; import mil.nga.mapcache.utils.SwipeController; import mil.nga.mapcache.utils.ViewAnimation; @@ -584,6 +576,11 @@ private enum EditType { */ private Zoomer zoomer; + /** + * Disclaimer message when first launching the app + */ + AlertDialog disclaimerDialog; + /** * Model that contains various states involving the map. */ @@ -1630,29 +1627,37 @@ private void showDisclaimer() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); boolean disclaimerPref = sharedPreferences.getBoolean(getString(R.string.disclaimerPref), false); if (!disclaimerPref) { - LayoutInflater inflater = LayoutInflater.from(getActivity()); - View disclaimerView = inflater.inflate(R.layout.disclaimer_window, null); - Button acceptButton = disclaimerView.findViewById(R.id.accept_button); - Button exitButton = disclaimerView.findViewById(R.id.exit_button); - - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle) - .setView(disclaimerView); - final AlertDialog alertDialog = dialogBuilder.create(); - acceptButton.setOnClickListener((View view) -> { - sharedPreferences.edit().putBoolean(getString(R.string.disclaimerPref), true).apply(); - alertDialog.dismiss(); - }); - exitButton.setOnClickListener((View view) -> getActivity().finish()); + LayoutInflater inflater = LayoutInflater.from(getActivity()); + View disclaimerView = inflater.inflate(R.layout.disclaimer_window, null); + Button acceptButton = disclaimerView.findViewById(R.id.accept_button); + Button exitButton = disclaimerView.findViewById(R.id.exit_button); + + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle) + .setView(disclaimerView); + disclaimerDialog = dialogBuilder.create(); + acceptButton.setOnClickListener((View view) -> { + sharedPreferences.edit().putBoolean(getString(R.string.disclaimerPref), true).apply(); + disclaimerDialog.dismiss(); + }); + exitButton.setOnClickListener((View view) -> getActivity().finish()); - // Prevent the dialog from closing when clicking outside the dialog or the back button - alertDialog.setCanceledOnTouchOutside(false); - alertDialog.setOnKeyListener((DialogInterface arg0, int keyCode, - KeyEvent event) -> true); - alertDialog.show(); + // Prevent the dialog from closing when clicking outside the dialog or the back button + disclaimerDialog.setCanceledOnTouchOutside(false); + disclaimerDialog.setOnKeyListener((DialogInterface arg0, int keyCode, + KeyEvent event) -> true); + disclaimerDialog.show(); } } } + /** + * Manually dismiss the disclaimer dialog on stop, otherwise we have a leak + */ + @Override + public void onStop() { + disclaimerDialog.dismiss(); + super.onStop(); + } /** * Show a warning that the user has selected more features than the current max features setting diff --git a/mapcache/src/main/java/mil/nga/mapcache/sensors/SensorHandler.java b/mapcache/src/main/java/mil/nga/mapcache/repository/sensors/SensorHandler.java similarity index 98% rename from mapcache/src/main/java/mil/nga/mapcache/sensors/SensorHandler.java rename to mapcache/src/main/java/mil/nga/mapcache/repository/sensors/SensorHandler.java index f949f410..610aea74 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/sensors/SensorHandler.java +++ b/mapcache/src/main/java/mil/nga/mapcache/repository/sensors/SensorHandler.java @@ -1,4 +1,4 @@ -package mil.nga.mapcache.sensors; +package mil.nga.mapcache.repository.sensors; import android.app.Service; From b4286af24846ea60b5381a51409cffe8387a76e0 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 6 Sep 2023 14:49:11 -0600 Subject: [PATCH 03/20] null check on disclaimer --- .../src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 5bf2107b..a6aafb5f 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -1655,7 +1655,9 @@ private void showDisclaimer() { */ @Override public void onStop() { - disclaimerDialog.dismiss(); + if(disclaimerDialog != null) { + disclaimerDialog.dismiss(); + } super.onStop(); } From 41fbc5b94e7a88aaa2e36f9f5d8b3bcaa796869d Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Mon, 11 Sep 2023 15:14:57 -0600 Subject: [PATCH 04/20] fix for linked files when selecting modify original during gpkg import --- .../mil/nga/mapcache/load/ImportTask.java | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/load/ImportTask.java b/mapcache/src/main/java/mil/nga/mapcache/load/ImportTask.java index 73e68c3d..cb035791 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/load/ImportTask.java +++ b/mapcache/src/main/java/mil/nga/mapcache/load/ImportTask.java @@ -10,6 +10,7 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.PowerManager; import android.view.LayoutInflater; import android.view.View; @@ -162,31 +163,35 @@ public void onClick(DialogInterface dialog, */ public void importGeoPackageExternalLinkWithPermissions(final String name, final Uri uri, String path) { - // Check for permission - if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - importGeoPackageExternalLink(name, uri, path); - } else { - // Save off the values and ask for permission - importExternalName = name; - importExternalUri = uri; - importExternalPath = path; - - if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - new AlertDialog.Builder(activity, R.style.AppCompatAlertDialogStyle) - .setTitle(R.string.storage_access_rational_title) - .setMessage(R.string.storage_access_rational_message) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MainActivity.MANAGER_PERMISSIONS_REQUEST_ACCESS_IMPORT_EXTERNAL); - } - }) - .create() - .show(); - + if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { + // Check for permission + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + importGeoPackageExternalLink(name, uri, path); } else { - ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MainActivity.MANAGER_PERMISSIONS_REQUEST_ACCESS_IMPORT_EXTERNAL); + // Save off the values and ask for permission + importExternalName = name; + importExternalUri = uri; + importExternalPath = path; + + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + new AlertDialog.Builder(activity, R.style.AppCompatAlertDialogStyle) + .setTitle(R.string.storage_access_rational_title) + .setMessage(R.string.storage_access_rational_message) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MainActivity.MANAGER_PERMISSIONS_REQUEST_ACCESS_IMPORT_EXTERNAL); + } + }) + .create() + .show(); + + } else { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MainActivity.MANAGER_PERMISSIONS_REQUEST_ACCESS_IMPORT_EXTERNAL); + } } + } else { + importGeoPackageExternalLink(name, uri, path); } } From f1fb8b521b66ba6066803291c74e70e69947270c Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 15 Sep 2023 14:51:59 -0600 Subject: [PATCH 05/20] download task / progress dialog removal --- .../mil/nga/mapcache/load/DownloadTask.java | 207 ------------------ 1 file changed, 207 deletions(-) delete mode 100644 mapcache/src/main/java/mil/nga/mapcache/load/DownloadTask.java diff --git a/mapcache/src/main/java/mil/nga/mapcache/load/DownloadTask.java b/mapcache/src/main/java/mil/nga/mapcache/load/DownloadTask.java deleted file mode 100644 index c0e1e075..00000000 --- a/mapcache/src/main/java/mil/nga/mapcache/load/DownloadTask.java +++ /dev/null @@ -1,207 +0,0 @@ -package mil.nga.mapcache.load; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.AsyncTask; -import android.os.PowerManager; - -import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.ViewModelProvider; - -import java.net.URL; - -import mil.nga.geopackage.io.GeoPackageIOUtils; -import mil.nga.geopackage.io.GeoPackageProgress; -import mil.nga.mapcache.GeoPackageUtils; -import mil.nga.mapcache.R; -import mil.nga.mapcache.viewmodel.GeoPackageViewModel; - -/** - * Download a GeoPackage from a URL in the background - */ -public class DownloadTask extends AsyncTask - implements GeoPackageProgress { - - private Integer max = null; - private int progress = 0; - private final String database; - private final String url; - private PowerManager.WakeLock wakeLock; - private Activity activity; - private GeoPackageViewModel geoPackageViewModel; - private String cancel; - private String importLabel; - - /** - * Progress dialog for network operations - */ - private ProgressDialog progressDialog; - - /** - * Constructor - * - * @param database - * @param url - */ - public DownloadTask(String database, String url, FragmentActivity activity) { - this.activity = activity; - this.database = database; - this.url = url; - geoPackageViewModel = new ViewModelProvider(activity).get(GeoPackageViewModel.class); - cancel = activity.getApplicationContext().getResources().getString(R.string.button_cancel_label); - importLabel = activity.getApplicationContext().getResources().getString(R.string.geopackage_import_label); - progressDialog = createDownloadProgressDialog(database, url, this, null); - } - - - /** - * {@inheritDoc} - */ - @Override - public void setMax(int max) { - this.max = max; - } - - /** - * {@inheritDoc} - */ - @Override - public void addProgress(int progress) { - this.progress += progress; - if (max != null) { - int total = (int) (this.progress / ((double) max) * 100); - publishProgress(total); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isActive() { - return !isCancelled(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean cleanupOnCancel() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPreExecute() { - super.onPreExecute(); - progressDialog.show(); - PowerManager pm = (PowerManager) activity.getSystemService( - Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - getClass().getName()); - wakeLock.acquire(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onProgressUpdate(Integer... progress) { - super.onProgressUpdate(progress); - - // If the indeterminate progress dialog is still showing, swap to a - // determinate horizontal bar - if (progressDialog.isIndeterminate()) { - - String messageSuffix = "\n\n" - + GeoPackageIOUtils.formatBytes(max); - - ProgressDialog newProgressDialog = createDownloadProgressDialog( - database, url, this, messageSuffix); - newProgressDialog - .setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - newProgressDialog.setIndeterminate(false); - newProgressDialog.setMax(100); - - newProgressDialog.show(); - progressDialog.dismiss(); - progressDialog = newProgressDialog; - } - - // Set the progress - progressDialog.setProgress(progress[0]); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onCancelled(String result) { - wakeLock.release(); - progressDialog.dismiss(); - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(String result) { - wakeLock.release(); - progressDialog.dismiss(); - if (result != null) { - GeoPackageUtils.showMessage(activity, importLabel, result); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected String doInBackground(String... params) { - try { - URL theUrl = new URL(url); - if (!geoPackageViewModel.importGeoPackage(database, theUrl, this)) { - return "Failed to import GeoPackage '" + database - + "' at url '" + url + "'"; - } - } catch (final Exception e) { - return "Couldn't download GeoPackage from: " + url + "\n\nFull error:\n" + e.toString(); - } - return null; - } - - /** - * Create a download progress dialog - * - * @param database - * @param url - * @param downloadTask - * @param suffix - * @return - */ - public ProgressDialog createDownloadProgressDialog(String database, - String url, final DownloadTask downloadTask, String suffix) { - ProgressDialog dialog = new ProgressDialog(activity); - dialog.setMessage(importLabel + " " - + database + "\n\n" + url + (suffix != null ? suffix : "")); - dialog.setCancelable(false); - dialog.setButton(ProgressDialog.BUTTON_NEGATIVE, - cancel, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - downloadTask.cancel(true); - } - }); - dialog.setIndeterminate(true); - - return dialog; - } - -} - - From 279573c92e32bd47f0fa49c87f342bad33caaab6 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Tue, 19 Sep 2023 14:05:56 -0600 Subject: [PATCH 06/20] add validation to confirm tile url is either xyz or wms --- .../mil/nga/mapcache/utils/UrlValidator.java | 40 ++++++++++++------- .../createtile/NewTileLayerController.java | 9 +++++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java index c0a2b0c8..2a2db457 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java +++ b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java @@ -11,27 +11,40 @@ public class UrlValidator { /** - * Determine if the url is valid based on whether it has xyz or not - * @param context - * @param url - * @return true if the url has xyz info + * A URL is valid if it has either xyz or wms params + * @param context - context for grabbing string resources + * @param url url to validate + * @return true if the url has xyz or wms info */ public static boolean isValidTileUrl(Context context, String url){ - return hasXYZ(context, url); + if(hasXYZ(context, url) || hasWms(url)){ + return true; + } + return false; + } + + /** + * Determine if the url is valid based on whether it has proper wms or not + * @param url url to validate + * @return true if the url has xyz info + */ + public static boolean hasWms(String url){ + if (url.toLowerCase().contains("wms")) { + return true; + } + return false; } /** * Determine if the url has x, y, or z variables * - * @param url + * @param url url to look for xyz data * @return true if it's a valid xyz url */ public static boolean hasXYZ(Context context, String url) { - String replacedUrl = replaceXYZ(context, url, 0, 0, 0); boolean hasXYZ = !replacedUrl.equals(url); - return hasXYZ; } @@ -39,14 +52,13 @@ public static boolean hasXYZ(Context context, String url) { /** * Replace x, y, and z in the url * - * @param url - * @param z - * @param x - * @param y - * @return + * @param url url to modify + * @param z integer to replace the Z param with + * @param x integer to replace the X param with + * @param y integer to replace the Y param with + * @return string with the xyz portion replaced with the given int values */ private static String replaceXYZ(Context context, String url, int z, long x, long y) { - url = url.replaceAll( context.getResources().getString(R.string.tile_generator_variable_z), String.valueOf(z)); diff --git a/mapcache/src/main/java/mil/nga/mapcache/wizards/createtile/NewTileLayerController.java b/mapcache/src/main/java/mil/nga/mapcache/wizards/createtile/NewTileLayerController.java index f981f58e..f8da72a7 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/wizards/createtile/NewTileLayerController.java +++ b/mapcache/src/main/java/mil/nga/mapcache/wizards/createtile/NewTileLayerController.java @@ -19,6 +19,7 @@ import mil.nga.mapcache.R; import mil.nga.mapcache.layersprovider.LayersModel; import mil.nga.mapcache.ogc.wms.WMSUrlProvider; +import mil.nga.mapcache.utils.UrlValidator; import mil.nga.mapcache.viewmodel.GeoPackageViewModel; /** @@ -92,6 +93,12 @@ public void setUrl(LayersModel layersModel) { } } + /** + * Validate name and url fields on change. Set error msg if invalid + * @param observable the observable object. + * @param o an argument passed to the {@code notifyObservers} + * method. either name or url field + */ @Override public void update(Observable observable, Object o) { if (NewTileLayerModel.LAYER_NAME_PROP.equals(o)) { @@ -115,6 +122,8 @@ public void update(Observable observable, Object o) { model.setUrlError("URL is required"); } else if (!URLUtil.isValidUrl(givenUrl)) { model.setUrlError("URL is not valid"); + } else if (!UrlValidator.isValidTileUrl(fragment.getContext(), givenUrl)){ + model.setUrlError("Bad URL format"); } else if (givenUrl.contains("${")) { givenUrl = givenUrl.replaceAll("\\$\\{", "{"); model.setUrl(givenUrl); From f194e0c97b5219f35bcc55b226562d052e3ccf86 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 20 Sep 2023 15:12:39 -0600 Subject: [PATCH 07/20] wms validation improvements --- .../mil/nga/mapcache/utils/UrlValidator.java | 68 +++++++++++++++++-- mapcache/src/test/UrlValidatorTest.java | 38 +++++++++++ 2 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 mapcache/src/test/UrlValidatorTest.java diff --git a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java index 2a2db457..dc0a484a 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java +++ b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java @@ -24,15 +24,73 @@ public static boolean isValidTileUrl(Context context, String url){ } /** - * Determine if the url is valid based on whether it has proper wms or not + * Determine if the url is valid based on whether it has proper wms or not. Looking at required + * variables: https://enterprise.arcgis.com/en/server/latest/publish-services/windows/communicating-with-a-wms-service-in-a-web-browser.htm#GUID-90AAB875-0337-451B-86BD-FC5E30F6990A * @param url url to validate - * @return true if the url has xyz info + * @return true if the url has proper wms variables */ public static boolean hasWms(String url){ - if (url.toLowerCase().contains("wms")) { - return true; + url = url.toLowerCase(); + boolean valid = true; + if (!url.contains("request")) { + valid = false; } - return false; + // GetCapabilities request + if(url.contains("capabilities")){ + if(!url.contains("service=")){ + valid = false; + } + } + // GetMap request + if(url.contains("request=getmap") || url.contains("request=map")){ + String[] getMapVars = {"layers", "styles", "crs", "bbox", "width", "height", "format"}; + for (String item : getMapVars) { + if (!url.contains(item)) { + valid = false; + break; + } + } + if(!url.contains("version") || !url.contains("wmtver")){ + valid = false; + } + if(!url.contains("crs") || !url.contains("srs")){ + valid = false; + } + } + // GetFeatureInfo request + if(url.contains("request=getfeatureinfo") || url.contains("request=feature_info")){ + if(!url.contains("version") || !url.contains("wmtver")){ + valid = false; + } + if(!url.contains("query_layers")){ + valid = false; + } + if(!url.contains("i=") || !url.contains("x=")){ + valid = false; + } + if(!url.contains("j=") || !url.contains("y=")){ + valid = false; + } + } + // GetStyles request + if(url.contains("request=getstyles")) { + if (!url.contains("version=")) { + valid = false; + } + if (!url.contains("layers=")) { + valid = false; + } + } + // GetLegendGraphic request + if(url.contains("request=getlegendgraphic")) { + if (!url.contains("version=")) { + valid = false; + } + if (!url.contains("layer=")) { + valid = false; + } + } + return valid; } diff --git a/mapcache/src/test/UrlValidatorTest.java b/mapcache/src/test/UrlValidatorTest.java new file mode 100644 index 00000000..3dd515a2 --- /dev/null +++ b/mapcache/src/test/UrlValidatorTest.java @@ -0,0 +1,38 @@ +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import mil.nga.mapcache.utils.UrlValidator; + +public class UrlValidatorTest { + @Before + public void setUp(){System.out.println("UrlValidator test ready");} + + @After + public void tearDown(){ + System.out.println("UrlValidator test tear down"); + } + + @Test + public void testBadFormats(){ + String url = "bad"; + boolean isBad = UrlValidator.hasWms(url); + assertFalse("UrlValidator should not approve a bad url format", isBad); + } + + @Test + public void testGetCapabilities(){ + String goodUrl = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetCapabilities&service=WMS"; + assertTrue("UrlValidator did not approve a good url format", UrlValidator.hasWms(goodUrl)); + + String missingService = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetCapabilities"; + String missingRequest = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&service=WMS"; + String basicUrl = "http://sampleserver1.arcgisonline.com"; + assertFalse("UrlValidator should not approve getCapabilities request when missing Service param", UrlValidator.hasWms(missingService)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Request param", UrlValidator.hasWms(missingRequest)); + assertFalse("UrlValidator should not approve getCapabilities request when given a url missing params", UrlValidator.hasWms(basicUrl)); + } +} From 502f7bb412aeeb72988b20d0f06a0f308da32872 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Thu, 21 Sep 2023 15:53:58 -0600 Subject: [PATCH 08/20] tests for getmap in urlvalidator --- .../mil/nga/mapcache/utils/UrlValidator.java | 10 +++---- mapcache/src/test/UrlValidatorTest.java | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java index dc0a484a..dabf5055 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java +++ b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java @@ -50,25 +50,25 @@ public static boolean hasWms(String url){ break; } } - if(!url.contains("version") || !url.contains("wmtver")){ + if(!(url.contains("version") || url.contains("wmtver"))){ valid = false; } - if(!url.contains("crs") || !url.contains("srs")){ + if(!(url.contains("crs") || url.contains("srs"))){ valid = false; } } // GetFeatureInfo request if(url.contains("request=getfeatureinfo") || url.contains("request=feature_info")){ - if(!url.contains("version") || !url.contains("wmtver")){ + if(!(url.contains("version") || url.contains("wmtver"))){ valid = false; } if(!url.contains("query_layers")){ valid = false; } - if(!url.contains("i=") || !url.contains("x=")){ + if(!(url.contains("i=") || url.contains("x="))){ valid = false; } - if(!url.contains("j=") || !url.contains("y=")){ + if(!(url.contains("j=") || url.contains("y="))){ valid = false; } } diff --git a/mapcache/src/test/UrlValidatorTest.java b/mapcache/src/test/UrlValidatorTest.java index 3dd515a2..1ba99cb1 100644 --- a/mapcache/src/test/UrlValidatorTest.java +++ b/mapcache/src/test/UrlValidatorTest.java @@ -35,4 +35,30 @@ public void testGetCapabilities(){ assertFalse("UrlValidator should not approve getCapabilities request when missing Request param", UrlValidator.hasWms(missingRequest)); assertFalse("UrlValidator should not approve getCapabilities request when given a url missing params", UrlValidator.hasWms(basicUrl)); } + + @Test + public void testGetMap(){ + String goodUrl = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&styles=default&format=image/png"; + assertTrue("UrlValidator did not approve a good getMap url format", UrlValidator.hasWms(goodUrl)); + String missingVersion = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&styles=default&format=image/png"; + String missingRequest = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&styles=default&format=image/png"; + String missingLayers = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&styles=default&format=image/png"; + String missingStyles = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&format=image/png"; + String missingCrs = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&styles=default&format=image/png"; + String missingBbox = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&width=760&height=360&layers=0&styles=default&format=image/png"; + String missingWidth = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&height=360&layers=0&styles=default&format=image/png"; + String missingHeight = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&layers=0&styles=default&format=image/png"; + String missingFormat = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&styles=default"; + String basicUrl = "http://sampleserver1.arcgisonline.com"; + assertFalse("UrlValidator should not approve getCapabilities request when missing Version param", UrlValidator.hasWms(missingVersion)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Request param", UrlValidator.hasWms(missingRequest)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Layers param", UrlValidator.hasWms(missingLayers)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Styles param", UrlValidator.hasWms(missingStyles)); + assertFalse("UrlValidator should not approve getCapabilities request when missing CRS/SRS param", UrlValidator.hasWms(missingCrs)); + assertFalse("UrlValidator should not approve getCapabilities request when missing bbox param", UrlValidator.hasWms(missingBbox)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Width param", UrlValidator.hasWms(missingWidth)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Height param", UrlValidator.hasWms(missingHeight)); + assertFalse("UrlValidator should not approve getCapabilities request when missing Format param", UrlValidator.hasWms(missingFormat)); + assertFalse("UrlValidator should not approve getCapabilities request when given a url missing params", UrlValidator.hasWms(basicUrl)); + } } From 1c0f78684857345d51343bc939ba9def85096d5a Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 22 Sep 2023 15:25:50 -0600 Subject: [PATCH 09/20] validation improvements for getMap, getFeatureInfo, getStyles, and getLegendGraphic --- .../mil/nga/mapcache/utils/UrlValidator.java | 2 +- mapcache/src/test/UrlValidatorTest.java | 62 ++++++++++++++++--- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java index dabf5055..742cf711 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java +++ b/mapcache/src/main/java/mil/nga/mapcache/utils/UrlValidator.java @@ -65,7 +65,7 @@ public static boolean hasWms(String url){ if(!url.contains("query_layers")){ valid = false; } - if(!(url.contains("i=") || url.contains("x="))){ + if(!(url.contains("&i=") || url.contains("&x="))){ valid = false; } if(!(url.contains("j=") || url.contains("y="))){ diff --git a/mapcache/src/test/UrlValidatorTest.java b/mapcache/src/test/UrlValidatorTest.java index 1ba99cb1..29008358 100644 --- a/mapcache/src/test/UrlValidatorTest.java +++ b/mapcache/src/test/UrlValidatorTest.java @@ -50,15 +50,57 @@ public void testGetMap(){ String missingHeight = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&layers=0&styles=default&format=image/png"; String missingFormat = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.3.0&request=GetMap&CRS=CRS:84&bbox=-178.217598,18.924782,-66.969271,71.406235&width=760&height=360&layers=0&styles=default"; String basicUrl = "http://sampleserver1.arcgisonline.com"; - assertFalse("UrlValidator should not approve getCapabilities request when missing Version param", UrlValidator.hasWms(missingVersion)); - assertFalse("UrlValidator should not approve getCapabilities request when missing Request param", UrlValidator.hasWms(missingRequest)); - assertFalse("UrlValidator should not approve getCapabilities request when missing Layers param", UrlValidator.hasWms(missingLayers)); - assertFalse("UrlValidator should not approve getCapabilities request when missing Styles param", UrlValidator.hasWms(missingStyles)); - assertFalse("UrlValidator should not approve getCapabilities request when missing CRS/SRS param", UrlValidator.hasWms(missingCrs)); - assertFalse("UrlValidator should not approve getCapabilities request when missing bbox param", UrlValidator.hasWms(missingBbox)); - assertFalse("UrlValidator should not approve getCapabilities request when missing Width param", UrlValidator.hasWms(missingWidth)); - assertFalse("UrlValidator should not approve getCapabilities request when missing Height param", UrlValidator.hasWms(missingHeight)); - assertFalse("UrlValidator should not approve getCapabilities request when missing Format param", UrlValidator.hasWms(missingFormat)); - assertFalse("UrlValidator should not approve getCapabilities request when given a url missing params", UrlValidator.hasWms(basicUrl)); + assertFalse("UrlValidator should not approve getMap request when missing Version param", UrlValidator.hasWms(missingVersion)); + assertFalse("UrlValidator should not approve getMap request when missing Request param", UrlValidator.hasWms(missingRequest)); + assertFalse("UrlValidator should not approve getMap request when missing Layers param", UrlValidator.hasWms(missingLayers)); + assertFalse("UrlValidator should not approve getMap request when missing Styles param", UrlValidator.hasWms(missingStyles)); + assertFalse("UrlValidator should not approve getMap request when missing CRS/SRS param", UrlValidator.hasWms(missingCrs)); + assertFalse("UrlValidator should not approve getMap request when missing bbox param", UrlValidator.hasWms(missingBbox)); + assertFalse("UrlValidator should not approve getMap request when missing Width param", UrlValidator.hasWms(missingWidth)); + assertFalse("UrlValidator should not approve getMap request when missing Height param", UrlValidator.hasWms(missingHeight)); + assertFalse("UrlValidator should not approve getMap request when missing Format param", UrlValidator.hasWms(missingFormat)); + assertFalse("UrlValidator should not approve getMap request when given a url missing params", UrlValidator.hasWms(basicUrl)); + } + + @Test + public void testGetFeatureInfo(){ + String goodUrl = "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&request=GetFeatureInfo&layers=0&styles=default&SRS=EPSG:4326&bbox=-125.192865,11.2289864971264,-66.105824,62.5056715028736&width=1044&height=906&format=text/html&X=500&Y=400&query_layers=0"; + assertTrue("UrlValidator did not approve a good getFeatureInfo url format", UrlValidator.hasWms(goodUrl)); + String missingVersion = "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?request=GetFeatureInfo&layers=0&styles=default&SRS=EPSG:4326&bbox=-125.192865,11.2289864971264,-66.105824,62.5056715028736&width=1044&height=906&format=text/html&X=500&Y=400&query_layers=0"; + String missingRequest = "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&layers=0&styles=default&SRS=EPSG:4326&bbox=-125.192865,11.2289864971264,-66.105824,62.5056715028736&width=1044&height=906&format=text/html&X=500&Y=400&query_layers=0"; + String missingQueryLayers = "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&request=GetFeatureInfo&layers=0&styles=default&SRS=EPSG:4326&bbox=-125.192865,11.2289864971264,-66.105824,62.5056715028736&width=1044&height=906&format=text/html&X=500&Y=400"; + String missingIX = "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&request=GetFeatureInfo&layers=0&styles=default&SRS=EPSG:4326&bbox=-125.192865,11.2289864971264,-66.105824,62.5056715028736&width=1044&height=906&format=text/html&Y=400&query_layers=0"; + String missingJY = "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&request=GetFeatureInfo&layers=0&styles=default&SRS=EPSG:4326&bbox=-125.192865,11.2289864971264,-66.105824,62.5056715028736&width=1044&height=906&format=text/html&X=500&query_layers=0"; + assertFalse("UrlValidator should not approve getFeatureInfo request when missing Version param", UrlValidator.hasWms(missingVersion)); + assertFalse("UrlValidator should not approve getFeatureInfo request when missing Request param", UrlValidator.hasWms(missingRequest)); + assertFalse("UrlValidator should not approve getFeatureInfo request when missing queary_layers param", UrlValidator.hasWms(missingQueryLayers)); + assertFalse("UrlValidator should not approve getFeatureInfo request when missing i or x param", UrlValidator.hasWms(missingIX)); + assertFalse("UrlValidator should not approve getFeatureInfo request when missing j or y param", UrlValidator.hasWms(missingJY)); + + } + + @Test + public void testGetStyles(){ + String goodUrl = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StateCityHighway_USA/MapServer/WMSServer?version=1.3.0&request=GetStyles&layers=0,1,2"; + assertTrue("UrlValidator did not approve a good getStyles url format", UrlValidator.hasWms(goodUrl)); + String missingVersion = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StateCityHighway_USA/MapServer/WMSServer?request=GetStyles&layers=0,1,2"; + String missingRequest = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StateCityHighway_USA/MapServer/WMSServer?version=1.3.0&layers=0,1,2"; + String missingLayers = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StateCityHighway_USA/MapServer/WMSServer?version=1.3.0&request=GetStyles"; + assertFalse("UrlValidator should not approve getStyles request when missing Version param", UrlValidator.hasWms(missingVersion)); + assertFalse("UrlValidator should not approve getStyles request when missing Request param", UrlValidator.hasWms(missingRequest)); + assertFalse("UrlValidator should not approve getStyles request when missing layers param", UrlValidator.hasWms(missingLayers)); + } + + @Test + public void testGetLegendGraphic(){ + String goodUrl = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&request=GetLegendGraphic&layer=0&style=default&format=image/png&width=95&height=65"; + assertTrue("UrlValidator did not approve a good getLegendGraphic url format", UrlValidator.hasWms(goodUrl)); + String missingVersion = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?request=GetLegendGraphic&layer=0&style=default&format=image/png&width=95&height=65"; + String missingRequest = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&layer=0&style=default&format=image/png&width=95&height=65"; + String missingLayer = "http://sampleserver1.arcgisonline.com/ArcGIS/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/WMSServer?version=1.1.1&request=GetLegendGraphic&style=default&format=image/png&width=95&height=65"; + assertFalse("UrlValidator should not approve getLegendGraphic request when missing Version param", UrlValidator.hasWms(missingVersion)); + assertFalse("UrlValidator should not approve getLegendGraphic request when missing Request param", UrlValidator.hasWms(missingRequest)); + assertFalse("UrlValidator should not approve getLegendGraphic request when missing layer param", UrlValidator.hasWms(missingLayer)); + } } From 2b0a17c2ec34a35ccbc090afabef5703eeeba596 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Thu, 5 Oct 2023 14:12:27 -0600 Subject: [PATCH 10/20] url test update --- mapcache/build.gradle | 14 ++++++++++---- mapcache/src/test/UrlValidatorTest.java | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/mapcache/build.gradle b/mapcache/build.gradle index 8c004eb4..336ed1ae 100644 --- a/mapcache/build.gradle +++ b/mapcache/build.gradle @@ -13,6 +13,7 @@ android { defaultConfig { applicationId "mil.nga.mapcache" resValue "string", "applicationId", applicationId + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" minSdkVersion 28 targetSdkVersion 33 versionCode 56 @@ -41,7 +42,7 @@ android { sourceSets { main { java { - srcDirs 'src/main/java', 'src/test' + srcDirs 'src/main/java', 'src/test', 'src/androidTest' } } } @@ -71,11 +72,16 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' implementation 'org.locationtech.jts:jts-core:1.18.2' + implementation 'com.github.matomo-org:matomo-sdk-android:v2.0.0' implementation 'junit:junit:4.12' testImplementation 'androidx.multidex:multidex:2.0.1' - testImplementation 'junit:junit:4.13.1' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - implementation 'com.github.matomo-org:matomo-sdk-android:v2.0.0' + testImplementation 'junit:junit:4.12' + testImplementation "org.robolectric:robolectric:4.7.3" + testImplementation 'androidx.test.espresso:espresso-core:3.5.1' + testImplementation 'androidx.test:core:1.5.0' + testImplementation 'androidx.test.ext:junit:1.1.5' + testImplementation 'androidx.test:runner:1.5.2' + testImplementation 'androidx.test:rules:1.5.0' } configure extensions.android, { diff --git a/mapcache/src/test/UrlValidatorTest.java b/mapcache/src/test/UrlValidatorTest.java index 29008358..516c9fd8 100644 --- a/mapcache/src/test/UrlValidatorTest.java +++ b/mapcache/src/test/UrlValidatorTest.java @@ -7,9 +7,16 @@ import mil.nga.mapcache.utils.UrlValidator; -public class UrlValidatorTest { +public class UrlValidatorTest{ + + // private Context appContext; @Before - public void setUp(){System.out.println("UrlValidator test ready");} + public void setUp(){ + //TODO: get app context so we can test xyz properly using ApplicaitonProvider + // appContext = ApplicationProvider.getApplicationContext(); + // Context testContext = RuntimeEnvironment.application; + System.out.println("UrlValidator test ready"); + } @After public void tearDown(){ @@ -19,8 +26,10 @@ public void tearDown(){ @Test public void testBadFormats(){ String url = "bad"; - boolean isBad = UrlValidator.hasWms(url); - assertFalse("UrlValidator should not approve a bad url format", isBad); + boolean isBadWms = UrlValidator.hasWms(url); +// boolean isBadXyz = UrlValidator.hasXYZ(ApplicationProvider.getApplicationContext(), url); + assertFalse("UrlValidator should not approve a bad wms url format", isBadWms); +// assertFalse("UrlValidator should not approve a bad xyz url format", isBadXyz); } @Test From 2b2719753cccafc3e9e16185ef74a0415f6cd2f6 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Thu, 5 Oct 2023 14:28:41 -0600 Subject: [PATCH 11/20] url validation improvement on saved tile url page --- .../java/mil/nga/mapcache/preferences/TileUrlFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mapcache/src/main/java/mil/nga/mapcache/preferences/TileUrlFragment.java b/mapcache/src/main/java/mil/nga/mapcache/preferences/TileUrlFragment.java index fbc68874..93dda819 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/preferences/TileUrlFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/preferences/TileUrlFragment.java @@ -37,6 +37,7 @@ import mil.nga.mapcache.R; import mil.nga.mapcache.utils.HttpUtils; +import mil.nga.mapcache.utils.UrlValidator; import mil.nga.mapcache.utils.ViewAnimation; /** @@ -243,6 +244,9 @@ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } else{ addButton.setEnabled(true); } + if (!UrlValidator.isValidTileUrl(getContext(), url)){ + inputText.setError("warning: poor url format. This may not work."); + } } }); From c43d0044c5a7ed6e17511f83cf3cc2ce46d623ab Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Mon, 9 Oct 2023 15:03:38 -0600 Subject: [PATCH 12/20] better error handling for failed geometries --- .../mil/nga/mapcache/FeatureRowProcessor.java | 6 +-- .../nga/mapcache/MapFeaturesUpdateTask.java | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/FeatureRowProcessor.java b/mapcache/src/main/java/mil/nga/mapcache/FeatureRowProcessor.java index d589d81b..c944f12e 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/FeatureRowProcessor.java +++ b/mapcache/src/main/java/mil/nga/mapcache/FeatureRowProcessor.java @@ -216,10 +216,8 @@ private void processFeatureRow(MapFeaturesUpdateTask task, String database, Feat } } } catch (Exception e) { - new Handler(Looper.getMainLooper()).post(() -> { - Toast toast = Toast.makeText(context, "Error loading geometry", Toast.LENGTH_SHORT); - toast.show(); - }); + // set task error + task.setErrorCount(task.getErrorCount() + 1); } } } diff --git a/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java b/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java index a328f416..200992df 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java +++ b/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java @@ -1,7 +1,10 @@ package mil.nga.mapcache; import android.app.Activity; +import android.os.Handler; +import android.os.Looper; import android.util.Log; +import android.widget.Toast; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.Marker; @@ -86,6 +89,11 @@ public class MapFeaturesUpdateTask implements Runnable { */ private boolean filter; + /** + * Keep track of any errors when displaying features on the map + */ + private int errorCount = 0; + /** * Constructor. * @@ -265,6 +273,8 @@ private void displayFeatures(GeoPackage geoPackage, StyleCache styleCache, Strin // Get the GeoPackage and feature DAO String database = geoPackage.getName(); + setErrorCount(0); + Map dataAccessObjects = model.getFeatureDaos().get(database); if (dataAccessObjects != null) { FeatureDao featureDao = dataAccessObjects.get(features); @@ -336,8 +346,16 @@ private void displayFeatures(GeoPackage geoPackage, StyleCache styleCache, Strin + ", row: " + cursor.getPosition(), e); } } + if(getErrorCount() > 0){ + new Handler(Looper.getMainLooper()).post(() -> { + Toast toast = Toast.makeText(activity, "Error loading geometry", Toast.LENGTH_SHORT); + toast.show(); + }); + setErrorCount(0); + } } + } indexer.close(); @@ -361,6 +379,8 @@ private void displayFeatures(GeoPackage geoPackage, StyleCache styleCache, Strin private void processFeatureIndexResults(FeatureIndexResults indexResults, String database, FeatureDao featureDao, GoogleMapShapeConverter converter, StyleCache styleCache, AtomicInteger count, final int maxFeatures, final boolean editable) { try { + setErrorCount(0); + for (FeatureRow row : indexResults) { if (cancelled || count.get() >= maxFeatures) { @@ -384,6 +404,14 @@ private void processFeatureIndexResults(FeatureIndexResults indexResults, String } } finally { indexResults.close(); + if(getErrorCount() > 0){ + new Handler(Looper.getMainLooper()).post(() -> { + Toast toast = Toast.makeText(activity, getErrorCount() + " Geometries failed to load", Toast.LENGTH_SHORT); + toast.show(); + }); + setErrorCount(0); + + } } } @@ -402,6 +430,15 @@ private void addMarkerShape(long featureId, String database, String tableName, G model.getMarkerIds().put(marker.getId(), markerFeature); } } + /** + * Update the number of errors encountered while processing features + */ + public int getErrorCount() { + return errorCount; + } + public void setErrorCount(int errorCount) { + this.errorCount = errorCount; + } @Override public void run() { From ed92beaae065631b29fb21dc18dab21aab7eda2f Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Wed, 11 Oct 2023 10:45:04 -0600 Subject: [PATCH 13/20] fix for geometry count --- .../java/mil/nga/mapcache/MapFeaturesUpdateTask.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java b/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java index 200992df..481357f6 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java +++ b/mapcache/src/main/java/mil/nga/mapcache/MapFeaturesUpdateTask.java @@ -346,9 +346,9 @@ private void displayFeatures(GeoPackage geoPackage, StyleCache styleCache, Strin + ", row: " + cursor.getPosition(), e); } } - if(getErrorCount() > 0){ + int totalErrors = getErrorCount(); + if(totalErrors > 0){ new Handler(Looper.getMainLooper()).post(() -> { - Toast toast = Toast.makeText(activity, "Error loading geometry", Toast.LENGTH_SHORT); toast.show(); }); @@ -404,9 +404,10 @@ private void processFeatureIndexResults(FeatureIndexResults indexResults, String } } finally { indexResults.close(); - if(getErrorCount() > 0){ + int totalErrors = getErrorCount(); + if(totalErrors > 0){ new Handler(Looper.getMainLooper()).post(() -> { - Toast toast = Toast.makeText(activity, getErrorCount() + " Geometries failed to load", Toast.LENGTH_SHORT); + Toast toast = Toast.makeText(activity, totalErrors + " Geometries failed to load", Toast.LENGTH_SHORT); toast.show(); }); setErrorCount(0); From 611312de6da5ac8376b0b5b941d5b701139ea840 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Wed, 11 Oct 2023 12:58:51 -0600 Subject: [PATCH 14/20] geopackage-android-map 6.7.2 --- mapcache/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapcache/build.gradle b/mapcache/build.gradle index 336ed1ae..e7ee7fc9 100644 --- a/mapcache/build.gradle +++ b/mapcache/build.gradle @@ -61,7 +61,7 @@ dependencies { api 'com.google.android.material:material:1.6.0' api 'androidx.preference:preference:1.2.1' api 'androidx.lifecycle:lifecycle-extensions:2.2.0' - api 'mil.nga.geopackage.map:geopackage-android-map:6.7.1' // comment out to build locally + api 'mil.nga.geopackage.map:geopackage-android-map:6.7.2' // comment out to build locally //api project(':geopackage-map') // uncomment me to build locally api 'mil.nga.mgrs:mgrs-android:2.2.2' api 'mil.nga.gars:gars-android:1.2.2' From c2008d28c5f87c4e5c440ac2d5d86bc14fca12c7 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Mon, 16 Oct 2023 14:56:10 -0600 Subject: [PATCH 15/20] add error info label for gpkg errors --- mapcache/src/main/res/layout/detail_header_layout.xml | 10 ++++++++++ mapcache/src/main/res/values-night/colors.xml | 1 + mapcache/src/main/res/values/color.xml | 4 ++++ mapcache/src/main/res/values/colors.xml | 1 + mapcache/src/main/res/values/styles.xml | 6 ++++++ 5 files changed, 22 insertions(+) diff --git a/mapcache/src/main/res/layout/detail_header_layout.xml b/mapcache/src/main/res/layout/detail_header_layout.xml index c51fde29..477a2948 100644 --- a/mapcache/src/main/res/layout/detail_header_layout.xml +++ b/mapcache/src/main/res/layout/detail_header_layout.xml @@ -80,9 +80,19 @@ android:id="@+id/header_text_features" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginBottom="4dp" android:text="Feature layers" android:textAppearance="@style/textAppearanceSubtitle2_light_heavy" /> + + + @color/white87 @color/white75 + @color/yellowWarningTextNight @color/nga_accent_primary @color/nav_not_selected_dark @color/white50 diff --git a/mapcache/src/main/res/values/color.xml b/mapcache/src/main/res/values/color.xml index 89868b5b..79d02235 100644 --- a/mapcache/src/main/res/values/color.xml +++ b/mapcache/src/main/res/values/color.xml @@ -70,6 +70,8 @@ #C4FFFFFF + #FFC107 + @@ -83,6 +85,8 @@ #7EFFFFFF #05FFFFFF #954A37 + #CFAE4A + #1E5659 diff --git a/mapcache/src/main/res/values/colors.xml b/mapcache/src/main/res/values/colors.xml index ce0e8aee..d0198464 100644 --- a/mapcache/src/main/res/values/colors.xml +++ b/mapcache/src/main/res/values/colors.xml @@ -23,6 +23,7 @@ @color/black87 @color/black50 + @color/yellowWarningText @color/nga_primary_light @color/nav_not_selected @color/nav_not_selected diff --git a/mapcache/src/main/res/values/styles.xml b/mapcache/src/main/res/values/styles.xml index 4ed532c8..5af09988 100644 --- a/mapcache/src/main/res/values/styles.xml +++ b/mapcache/src/main/res/values/styles.xml @@ -191,6 +191,12 @@ @color/textSecondaryColor 0.0071 +