Skip to content
5 changes: 4 additions & 1 deletion app/src/main/java/net/osmtracker/activity/TrackLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import java.io.File;
import java.util.Date;
import java.util.HashSet;
import java.util.UUID;


/**
Expand Down Expand Up @@ -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());
Expand All @@ -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());
Expand Down Expand Up @@ -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);
}
Expand Down
175 changes: 170 additions & 5 deletions app/src/main/java/net/osmtracker/activity/WaypointList.java
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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);
Expand All @@ -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();
}

Expand All @@ -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);
}

}
44 changes: 21 additions & 23 deletions app/src/main/java/net/osmtracker/db/DataHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);
}
}


Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
22 changes: 11 additions & 11 deletions app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
import android.widget.TextView;

/**
Expand Down Expand Up @@ -45,32 +46,32 @@ 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);
}

/**
* Do the binding between data and item view.
*
* @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));
Expand Down Expand Up @@ -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;
}

}
Loading