From e378ba5cad7ad03f11346c630b0d48a112cbec0f Mon Sep 17 00:00:00 2001 From: Kevin Salazar Date: Tue, 11 Feb 2025 11:58:06 -0600 Subject: [PATCH 1/6] preview, edit and delete waypoints --- .../net/osmtracker/activity/TrackLogger.java | 3 + .../net/osmtracker/activity/WaypointList.java | 159 +++++++++++++++++- .../java/net/osmtracker/db/DataHelper.java | 34 ++-- .../osmtracker/db/WaypointListAdapter.java | 8 +- .../ConfirmDeleteWaypointListener.java | 18 ++ .../EditWaypointDialogOnClickListener.java | 23 +++ .../listener/TagButtonOnClickListener.java | 9 +- .../net/osmtracker/service/gps/GPSLogger.java | 5 +- .../main/res/layout/edit_waypoint_dialog.xml | 76 +++++++++ app/src/main/res/layout/waypointlist_item.xml | 57 +++++-- app/src/main/res/values/strings.xml | 16 +- 11 files changed, 360 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/net/osmtracker/listener/ConfirmDeleteWaypointListener.java create mode 100644 app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java create mode 100644 app/src/main/res/layout/edit_waypoint_dialog.xml diff --git a/app/src/main/java/net/osmtracker/activity/TrackLogger.java b/app/src/main/java/net/osmtracker/activity/TrackLogger.java index 6ab9153e7..5a6b0da20 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()); diff --git a/app/src/main/java/net/osmtracker/activity/WaypointList.java b/app/src/main/java/net/osmtracker/activity/WaypointList.java index f337e7c51..2d1150c5a 100644 --- a/app/src/main/java/net/osmtracker/activity/WaypointList.java +++ b/app/src/main/java/net/osmtracker/activity/WaypointList.java @@ -1,11 +1,30 @@ package net.osmtracker.activity; -import net.osmtracker.db.TrackContentProvider; -import net.osmtracker.db.WaypointListAdapter; - +import android.app.AlertDialog; import android.app.ListActivity; import android.database.Cursor; +import android.media.MediaPlayer; +import android.net.Uri; +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.ImageView; +import android.widget.ListView; + +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.Arrays; /** * Activity that lists the previous waypoints tracked by the user. @@ -15,6 +34,9 @@ */ public class WaypointList extends ListActivity { + private static final String TAG = "Waypoint List" ; + private static final Logger log = LoggerFactory.getLogger(WaypointList.class); + @Override protected void onResume() { Long trackId = getIntent().getExtras().getLong(TrackContentProvider.Schema.COL_TRACK_ID); @@ -41,4 +63,135 @@ 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); + final ImageView waypointPreviewImage = editWaypointDialog.findViewById(R.id.waypoint_preview_image); + final Button waypointPlayAudio = editWaypointDialog.findViewById(R.id.waypoint_play_audio); + + 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("name")); + editWaypointName.setText(oldName); + editWaypointName.setSelection(oldName.length()); + + // Retrieve waypoint details + final long trackId = cursor.getLong(cursor.getColumnIndex("track_id")); + final String uuid = cursor.getString(cursor.getColumnIndex("uuid")); + final String link = cursor.getString(cursor.getColumnIndex("link")); + + Log.d(TAG, "***********************\n" + 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)) { + waypointPreviewImage.setVisibility(View.VISIBLE); + waypointPreviewImage.setImageURI(Uri.fromFile(file)); + } else if (isAudioFile(filePath)) { + waypointPlayAudio.setVisibility(View.VISIBLE); + waypointPlayAudio.setOnClickListener(v1 -> playAudio(filePath)); + } + } 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(); + + // 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) { + dataHelper.deleteWayPoint(uuid, filePath); + cursor.requery(); + alert.dismiss(); + } + }); + + // 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) { + String[] imageExtensions = {"jpg", "jpeg", "png", "gif", "bmp", "webp"}; + String ext = path.substring(path.lastIndexOf(".") + 1).toLowerCase(); + return Arrays.asList(imageExtensions).contains(ext); + } + + /** + * 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(".3gpp") || path.endsWith(".mp3") || path.endsWith(".wav") || path.endsWith(".ogg"); + } + + /** + * Plays an audio file using MediaPlayer. + * + * @param filePath The path of the audio file. + */ + private void playAudio(String filePath) { + MediaPlayer mediaPlayer = new MediaPlayer(); + try { + mediaPlayer.setDataSource(filePath); + mediaPlayer.prepare(); + mediaPlayer.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } diff --git a/app/src/main/java/net/osmtracker/db/DataHelper.java b/app/src/main/java/net/osmtracker/db/DataHelper.java index 47b9ea680..07e261f59 100644 --- a/app/src/main/java/net/osmtracker/db/DataHelper.java +++ b/app/src/main/java/net/osmtracker/db/DataHelper.java @@ -258,16 +258,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 +416,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/WaypointListAdapter.java b/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java index b8f847a45..2ace0e0e1 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,13 +46,13 @@ public WaypointListAdapter(Context context, Cursor c) { @Override public void bindView(View view, Context context, Cursor cursor) { - RelativeLayout rl = (RelativeLayout) view; + TableLayout rl = (TableLayout) view; bind(cursor, rl, context); } @Override public View newView(Context context, Cursor cursor, ViewGroup vg) { - RelativeLayout rl = (RelativeLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.waypointlist_item, + TableLayout rl = (TableLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.waypointlist_item, vg, false); return bind(cursor, rl, context); } @@ -67,7 +68,7 @@ public View newView(Context context, Cursor cursor, ViewGroup vg) { * Context, to get resources * @return The relative view with data bound. */ - private View bind(Cursor cursor, RelativeLayout rl, Context context) { + private View bind(Cursor cursor, TableLayout 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); @@ -103,7 +104,6 @@ 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; } diff --git a/app/src/main/java/net/osmtracker/listener/ConfirmDeleteWaypointListener.java b/app/src/main/java/net/osmtracker/listener/ConfirmDeleteWaypointListener.java new file mode 100644 index 000000000..e65a73671 --- /dev/null +++ b/app/src/main/java/net/osmtracker/listener/ConfirmDeleteWaypointListener.java @@ -0,0 +1,18 @@ +package net.osmtracker.listener; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.database.Cursor; +import android.view.View; + +public class ConfirmDeleteWaypointListener implements DialogInterface.OnClickListener { + protected AlertDialog alert; + + protected ConfirmDeleteWaypointListener(AlertDialog alert) { + this.alert = alert; + } + + // public void onClick(DialogInterface dialog, int which){}; + public void onClick(DialogInterface dialog, int which){} + +} 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..8a2563a06 --- /dev/null +++ b/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java @@ -0,0 +1,23 @@ +package net.osmtracker.listener; + +import android.app.AlertDialog; +import android.app.ListActivity; +import android.content.DialogInterface; +import android.database.Cursor; +import android.view.View; + +import java.util.List; + +public class EditWaypointDialogOnClickListener implements View.OnClickListener { + private Cursor cursor; + protected AlertDialog alert; + + protected EditWaypointDialogOnClickListener(AlertDialog alert, Cursor cu) { + this.cursor = cu; + this.alert = alert; + } + + // public void onClick(DialogInterface dialog, int which){}; + 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..abb06cd7f 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,11 @@ 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); + final String filePath = link.equals("null")? null : DataHelper.getTrackDirectory(trackId, context) + "/" + link; + 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..41ace304b --- /dev/null +++ b/app/src/main/res/layout/edit_waypoint_dialog.xml @@ -0,0 +1,76 @@ + + + + + + +