diff --git a/app/src/main/java/net/osmtracker/activity/TrackLogger.java b/app/src/main/java/net/osmtracker/activity/TrackLogger.java
index 6ab9153e7..0cbb5f405 100644
--- a/app/src/main/java/net/osmtracker/activity/TrackLogger.java
+++ b/app/src/main/java/net/osmtracker/activity/TrackLogger.java
@@ -57,6 +57,7 @@
import java.io.File;
import java.util.Date;
import java.util.HashSet;
+import java.util.UUID;
/**
@@ -609,6 +610,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (currentPhotoFile != null && currentPhotoFile.exists()) {
Intent intent = new Intent(OSMTracker.INTENT_TRACK_WP);
+ intent.putExtra(OSMTracker.INTENT_KEY_UUID, UUID.randomUUID().toString());
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, currentTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, getResources().getString(R.string.wpt_stillimage));
intent.putExtra(OSMTracker.INTENT_KEY_LINK, currentPhotoFile.getName());
@@ -632,6 +634,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Send an intent to inform service to track the waypoint.
Intent intent = new Intent(OSMTracker.INTENT_TRACK_WP);
+ intent.putExtra(OSMTracker.INTENT_KEY_UUID, UUID.randomUUID().toString());
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, currentTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, getResources().getString(R.string.wpt_stillimage));
intent.putExtra(OSMTracker.INTENT_KEY_LINK, destFile.getName());
@@ -778,7 +781,7 @@ private void startCamera() {
*/
private void startGallery() {
Intent galleryIntent = new Intent();
- galleryIntent.setType("image/*");
+ galleryIntent.setType(DataHelper.MIME_TYPE_IMAGE);
galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(galleryIntent, REQCODE_GALLERY_CHOSEN);
}
diff --git a/app/src/main/java/net/osmtracker/activity/WaypointList.java b/app/src/main/java/net/osmtracker/activity/WaypointList.java
index bb249e107..0c046c7ee 100644
--- a/app/src/main/java/net/osmtracker/activity/WaypointList.java
+++ b/app/src/main/java/net/osmtracker/activity/WaypointList.java
@@ -1,13 +1,28 @@
package net.osmtracker.activity;
-import net.osmtracker.db.TrackContentProvider;
-import net.osmtracker.db.WaypointListAdapter;
-
+import android.app.AlertDialog;
import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.Intent;
import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
import android.widget.CursorAdapter;
+import android.widget.EditText;
import android.widget.ListView;
+import androidx.core.content.FileProvider;
+import net.osmtracker.R;
+import net.osmtracker.db.DataHelper;
+import net.osmtracker.db.TrackContentProvider;
+import net.osmtracker.db.WaypointListAdapter;
+import net.osmtracker.listener.EditWaypointDialogOnClickListener;
+
+import java.io.File;
/**
* Activity that lists the previous waypoints tracked by the user.
@@ -17,6 +32,8 @@
*/
public class WaypointList extends ListActivity {
+ private static final String TAG = WaypointList.class.getSimpleName();
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -29,12 +46,12 @@ protected void onCreate(Bundle savedInstanceState) {
@Override
protected void onResume() {
Long trackId = getIntent().getExtras().getLong(TrackContentProvider.Schema.COL_TRACK_ID);
-
+
Cursor cursor = getContentResolver().query(TrackContentProvider.waypointsUri(trackId),
null, null, null, TrackContentProvider.Schema.COL_TIMESTAMP + " desc");
startManagingCursor(cursor);
setListAdapter(new WaypointListAdapter(WaypointList.this, cursor));
-
+
super.onResume();
}
@@ -52,4 +69,152 @@ protected void onPause() {
super.onPause();
}
+ /**
+ * Handles the selection of a waypoint from the list and opens an edit dialog.
+ * This dialog allows the user to update the waypoint's name and preview attached files (images or audio).
+ *
+ * @param l The ListView where the item was clicked.
+ * @param v The view that was clicked.
+ * @param position The position of the clicked item.
+ * @param id The ID of the clicked waypoint.
+ */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ final Cursor cursor = ((CursorAdapter) getListAdapter()).getCursor();
+ final DataHelper dataHelper = new DataHelper(l.getContext());
+ LayoutInflater inflater = this.getLayoutInflater();
+
+ // Inflate the waypoint edit dialog layout
+ final View editWaypointDialog = inflater.inflate(R.layout.edit_waypoint_dialog, null);
+ final EditText editWaypointName = editWaypointDialog.findViewById(R.id.edit_waypoint_et_name);
+
+ Button buttonPreview = editWaypointDialog.findViewById(R.id.edit_waypoint_button_preview);
+ Button buttonUpdate = editWaypointDialog.findViewById(R.id.edit_waypoint_button_update);
+ Button buttonDelete = editWaypointDialog.findViewById(R.id.edit_waypoint_button_delete);
+ Button buttonCancel = editWaypointDialog.findViewById(R.id.edit_waypoint_button_cancel);
+
+ // Retrieve existing waypoint name
+ String oldName = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
+ editWaypointName.setText(oldName);
+ editWaypointName.setSelection(oldName.length());
+
+ // Retrieve waypoint details
+ final long trackId = cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_TRACK_ID));
+ final String uuid = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_UUID));
+ final String link = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LINK));
+
+ final String filePath = (link != null) ? DataHelper.getTrackDirectory(trackId, l.getContext()) + "/" + link : null;
+ File file = (filePath != null) ? new File(filePath) : null;
+
+ if (file != null && file.exists()) {
+ try {
+ if (isImageFile(filePath) || isAudioFile(filePath)) {
+ buttonPreview.setVisibility(View.VISIBLE);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error handling file: " + filePath, e);
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setCancelable(true);
+ AlertDialog alert = builder.create();
+
+ // Preview button
+ buttonPreview.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
+ @Override
+ public void onClick(View view) {
+ if (filePath != null) {
+ File file = new File(filePath);
+ Uri fileUri = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) ?
+ FileProvider.getUriForFile(getApplicationContext(), DataHelper.FILE_PROVIDER_AUTHORITY, file) :
+ Uri.fromFile(file);
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ if (isImageFile(filePath)) {
+ intent.setDataAndType(fileUri, DataHelper.MIME_TYPE_IMAGE);
+ } else if (isAudioFile(filePath)) {
+ intent.setDataAndType(fileUri, DataHelper.MIME_TYPE_AUDIO);
+ }
+
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivity(intent);
+ }
+ }
+ alert.dismiss();
+ }
+ });
+
+ // Update waypoint name
+ buttonUpdate.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
+ @Override
+ public void onClick(View view) {
+ String newName = editWaypointName.getText().toString();
+ dataHelper.updateWayPoint(trackId, uuid, newName, link);
+ alert.dismiss();
+ }
+ });
+
+ // Delete waypoint
+ buttonDelete.setOnClickListener(new EditWaypointDialogOnClickListener(alert, cursor) {
+ @Override
+ public void onClick(View view) {
+ new AlertDialog.Builder(WaypointList.this)
+ .setTitle(getString(R.string.delete_waypoint_confirm_dialog_title))
+ .setMessage(getString(R.string.delete_waypoint_confirm_dialog_msg))
+ .setPositiveButton(getString(R.string.delete_waypoint_confirm_bt_ok), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dataHelper.deleteWayPoint(uuid, filePath);
+ cursor.requery();
+ alert.dismiss();
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(getString(R.string.delete_waypoint_confirm_bt_cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .show();
+ }
+ });
+
+ // Cancel button
+ buttonCancel.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
+ @Override
+ public void onClick(View view) {
+ alert.dismiss();
+ }
+ });
+
+ alert.setView(editWaypointDialog);
+ alert.show();
+
+ super.onListItemClick(l, v, position, id);
+ }
+
+ /**
+ * Checks if a given file path corresponds to an image.
+ *
+ * @param path The file path.
+ * @return True if the file is an image, false otherwise.
+ */
+ private boolean isImageFile(String path) {
+ return path.endsWith(DataHelper.EXTENSION_JPG);
+ }
+
+ /**
+ * Checks if a given file path corresponds to an audio file.
+ *
+ * @param path The file path.
+ * @return True if the file is an audio file, false otherwise.
+ */
+ private boolean isAudioFile(String path) {
+ return path.endsWith(DataHelper.EXTENSION_3GPP);
+ }
+
}
diff --git a/app/src/main/java/net/osmtracker/db/DataHelper.java b/app/src/main/java/net/osmtracker/db/DataHelper.java
index 47b9ea680..aafdbb751 100644
--- a/app/src/main/java/net/osmtracker/db/DataHelper.java
+++ b/app/src/main/java/net/osmtracker/db/DataHelper.java
@@ -58,6 +58,16 @@ public class DataHelper {
*/
public static final String MIME_TYPE_GPX = "application/gpx+xml";
+ /**
+ * Audio Files MIME
+ */
+ public static final String MIME_TYPE_AUDIO = "audio/*";
+
+ /**
+ * Image Files MIME
+ */
+ public static final String MIME_TYPE_IMAGE = "image/*";
+
/**
* APP sign plus FileProvider = authority
*/
@@ -258,16 +268,25 @@ public void updateWayPoint(long trackId, String uuid, String name, String link)
}
/**
- * Deletes a waypoint
+ * Deletes a waypoint and its file associated (if exists)
*
* @param uuid
* Unique ID of the target waypoint
+ *
+ * @param filepath
+ * file attached to the waypoint
*/
- public void deleteWayPoint(String uuid) {
+ public void deleteWayPoint(String uuid, String filepath) {
Log.v(TAG, "Deleting waypoint with uuid '" + uuid);
if (uuid != null) {
contentResolver.delete(Uri.withAppendedPath(TrackContentProvider.CONTENT_URI_WAYPOINT_UUID, uuid), null, null);
}
+
+ // delete file if exists
+ File file = (filepath != null) ? new File(filepath) : null;
+ if (file != null && file.exists() && file.delete()) {
+ Log.v(TAG, "File deleted: " + filepath);
+ }
}
@@ -407,27 +426,6 @@ public static File getTrackDirectory(long trackId, Context context) {
return _return;
}
- /* method not in use. TODO: delete code.
- public static File getGPXTrackFile(long trackId, ContentResolver contentResolver, Context context) {
-
- String trackName = getTrackNameInDB(trackId, contentResolver);
-
- File sdRoot = Environment.getExternalStorageDirectory();
-
- // The location where the user has specified gpx files and associated content to be written
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- String userGPXExportDirectoryName = prefs.getString(
- OSMTracker.Preferences.KEY_STORAGE_DIR, OSMTracker.Preferences.VAL_STORAGE_DIR);
-
- // Build storage track path for file creation
- String completeGPXTrackPath = sdRoot + userGPXExportDirectoryName.trim() +
- File.separator + trackName.trim() + File.separator +
- trackName.trim() + DataHelper.EXTENSION_GPX;
-
- return new File(completeGPXTrackPath);
- }
- */
-
public static String getTrackNameInDB(long trackId, ContentResolver contentResolver) {
String trackName = "";
Uri trackUri = ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, trackId);
diff --git a/app/src/main/java/net/osmtracker/db/TrackContentProvider.java b/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
index ffb551ed0..38f344221 100644
--- a/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
+++ b/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
@@ -471,7 +471,6 @@ public static final class Schema {
public static final String TBL_TRACKPOINT = "trackpoint";
public static final String TBL_WAYPOINT = "waypoint";
public static final String TBL_TRACK = "track";
-
public static final String COL_ID = "_id";
public static final String COL_TRACK_ID = "track_id";
public static final String COL_UUID = "uuid";
diff --git a/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java b/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
index b8f847a45..2f8ebc75c 100644
--- a/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
+++ b/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
@@ -13,6 +13,7 @@
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.RelativeLayout;
+import android.widget.TableLayout;
import android.widget.TextView;
/**
@@ -45,15 +46,15 @@ public WaypointListAdapter(Context context, Cursor c) {
@Override
public void bindView(View view, Context context, Cursor cursor) {
- RelativeLayout rl = (RelativeLayout) view;
- bind(cursor, rl, context);
+ TableLayout tl = (TableLayout) view;
+ bind(cursor, tl, context);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup vg) {
- RelativeLayout rl = (RelativeLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.waypointlist_item,
+ TableLayout tl = (TableLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.waypointlist_item,
vg, false);
- return bind(cursor, rl, context);
+ return bind(cursor, tl, context);
}
/**
@@ -61,16 +62,16 @@ public View newView(Context context, Cursor cursor, ViewGroup vg) {
*
* @param cursor
* Cursor to pull data
- * @param rl
+ * @param tl
* RelativeView representing one item
* @param context
* Context, to get resources
* @return The relative view with data bound.
*/
- private View bind(Cursor cursor, RelativeLayout rl, Context context) {
- TextView vName = (TextView) rl.findViewById(R.id.wplist_item_name);
- TextView vLocation = (TextView) rl.findViewById(R.id.wplist_item_location);
- TextView vTimestamp = (TextView) rl.findViewById(R.id.wplist_item_timestamp);
+ private View bind(Cursor cursor, TableLayout tl, Context context) {
+ TextView vName = (TextView) tl.findViewById(R.id.wplist_item_name);
+ TextView vLocation = (TextView) tl.findViewById(R.id.wplist_item_location);
+ TextView vTimestamp = (TextView) tl.findViewById(R.id.wplist_item_timestamp);
// Bind name
String name = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
@@ -103,8 +104,7 @@ private View bind(Cursor cursor, RelativeLayout rl, Context context) {
// Bind timestamp
Date ts = new Date(cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_TIMESTAMP)));
vTimestamp.setText(DATE_FORMATTER.format(ts));
-
- return rl;
+ return tl;
}
}
diff --git a/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java b/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java
new file mode 100644
index 000000000..181a11ba2
--- /dev/null
+++ b/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java
@@ -0,0 +1,24 @@
+package net.osmtracker.listener;
+
+import android.app.AlertDialog;
+import android.database.Cursor;
+import android.view.View;
+
+/**
+ * Class that implements an OnClickListener to display an edit waypoint dialog.
+ */
+public class EditWaypointDialogOnClickListener implements View.OnClickListener {
+
+ private Cursor cursor;
+
+ protected AlertDialog alert;
+
+ protected EditWaypointDialogOnClickListener(AlertDialog alert, Cursor cu) {
+ this.cursor = cu; // Assigns the received cursor to the class attribute
+ this.alert = alert; // Assigns the received alert to the class attribute
+ }
+
+ @Override
+ public void onClick(View view) {
+ }
+}
diff --git a/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java b/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
index 149f9af5b..4b5f35b4c 100644
--- a/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
+++ b/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
@@ -1,16 +1,18 @@
package net.osmtracker.listener;
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
+import java.util.UUID;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
import net.osmtracker.db.TrackContentProvider;
+
/**
* Listener for standard waypoint tag button.
* Sends an Intent to track waypoint. Waypoint name is the
@@ -36,6 +38,7 @@ public void onClick(View view) {
Intent intent = new Intent(OSMTracker.INTENT_TRACK_WP);
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, currentTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, label);
+ intent.putExtra(OSMTracker.INTENT_KEY_UUID, UUID.randomUUID().toString());
String packageName = view.getContext().getPackageName();
intent.setPackage(packageName);
diff --git a/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java b/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
index 77ae1987c..fb785d279 100644
--- a/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
+++ b/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
@@ -148,8 +148,15 @@ public void onReceive(Context context, Intent intent) {
// Delete an existing waypoint
Bundle extras = intent.getExtras();
if (extras != null) {
+ Long trackId = extras.getLong(TrackContentProvider.Schema.COL_TRACK_ID);
String uuid = extras.getString(OSMTracker.INTENT_KEY_UUID);
- dataHelper.deleteWayPoint(uuid);
+ String link = extras.getString(OSMTracker.INTENT_KEY_LINK);
+ String filePath = null;
+ try {
+ filePath = link.equals("null") ? null : DataHelper.getTrackDirectory(trackId, context) + "/" + link;
+ }
+ catch(NullPointerException ne){}
+ dataHelper.deleteWayPoint(uuid, filePath);
}
} else if (OSMTracker.INTENT_START_TRACKING.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
diff --git a/app/src/main/res/layout/edit_waypoint_dialog.xml b/app/src/main/res/layout/edit_waypoint_dialog.xml
new file mode 100644
index 000000000..dfffa9478
--- /dev/null
+++ b/app/src/main/res/layout/edit_waypoint_dialog.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/waypointlist_item.xml b/app/src/main/res/layout/waypointlist_item.xml
index 6245f7748..e43e7adff 100644
--- a/app/src/main/res/layout/waypointlist_item.xml
+++ b/app/src/main/res/layout/waypointlist_item.xml
@@ -1,35 +1,46 @@
-
-
+ android:layout_height="match_parent"
+ android:stretchColumns="*">
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0cdd1f8d3..667faed17 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -84,6 +84,17 @@
Grabar voz
Tomar foto
Nota de texto
+
+ Nombre/contenido del punto
+ Nombre del punto
+ Abrir archivo
+ Guardar
+ Eliminar
+ Cancelar
+ Eliminar punto
+ ¿Eliminar este punto?
+ Eliminar
+ Cancelar
Configuración
Puntos
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2f6d0a601..dfc879ac9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -84,7 +84,7 @@
Waiting for OpenStreetMap server response…
Error while uploading track
The OSM server returned an error: ({0}) message {1}
- Autorization error. Would you like to clear the saved OpenStreetMap credentials?
+ Authorization error. Would you like to clear the saved OpenStreetMap credentials?
OpenStreetMap upload succeeded
@@ -92,6 +92,18 @@
Take photo
Text note
+
+ Waypoint Name/text
+ Enter waypoint name
+ Open file
+ Save
+ Delete
+ Cancel
+ Delete waypoint
+ Delete this waypoint?
+ Delete
+ Cancel
+
Settings
Waypoints
@@ -160,7 +172,8 @@
Wait for heading…
Heading can\'t be determined
Export process finished successfully
-
+ Position not available
+ {0} {1} / {2} {3}
OpenStreetMap track display