<NME> GpxExport.java
<BEF> package cgeo.geocaching.export;

import cgeo.geocaching.LogEntry;
import cgeo.geocaching.R;
import cgeo.geocaching.cgCache;
import cgeo.geocaching.cgWaypoint;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.storage.PersistableFolder;
import cgeo.geocaching.ui.dialog.Dialogs;
import cgeo.geocaching.utils.AsyncTaskWithProgress;
import cgeo.geocaching.utils.FileNameCreator;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.ShareUtils;
import cgeo.geocaching.utils.UriUtils;

import org.apache.commons.lang3.StringEscapeUtils;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Environment;

import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

public class GpxExport extends AbstractExport {

    private String fileName = "geocache.gpx"; // used in tests
    private String title = null;

    public GpxExport() {

    @Override
    public void export(final List<cgCache> caches, final Activity activity) {
        new ExportTask(caches, activity).execute((Void) null);
    }

    private class ExportTask extends AsyncTask<Void, Integer, Boolean> {
        final View layout = View.inflate(activity, R.layout.gpx_export_dialog, null);
        builder.setView(layout);

        final TextView text = layout.findViewById(R.id.info);
        text.setText(activity.getString(R.string.export_confirm_message, PersistableFolder.GPX.toUserDisplayableValue(), fileName));

        final CheckBox includeFoundStatus = layout.findViewById(R.id.include_found_status);
        includeFoundStatus.setChecked(Settings.getIncludeFoundStatus());

        builder.setPositiveButton(R.string.export, (dialog, which) -> {
            Settings.setIncludeFoundStatus(includeFoundStatus.isChecked());
            dialog.dismiss();
            new ExportTask(activity).execute(geocodes);
        });

        return builder.create();
    }

    private static String[] getGeocodes(final List<Geocache> caches) {
        return Geocache.getGeocodes(caches).toArray(new String[caches.size()]);
    }

    protected class ExportTask extends AsyncTaskWithProgress<String, Uri> {

        /**
         * Instantiates and configures the task for exporting field notes.
         *
         * @param activity optional: Show a progress bar and toasts
         */
        public ExportTask(final Activity activity) {
            super(activity, getProgressTitle());
        }

        @Override
        protected Uri doInBackgroundInternal(final String[] geocodes) {
            final List<String> allGeocodes = new ArrayList<>(Arrays.asList(geocodes));

            setMessage(CgeoApplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, allGeocodes.size(), allGeocodes.size()));

            final Uri uri = ContentStorage.get().create(PersistableFolder.GPX, fileName);
            if (uri == null) {
                return null;
                gpx.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                gpx.write("<gpx version=\"1.0\" creator=\"c:geo - http://www.cgeo.org\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd\">");


                for (int i = 0; i < caches.size(); i++) {
                    // reload the cache. otherwise logs, attributes and other detailed information is not available
                    final cgCache cache = cgeoapplication.getInstance().loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY);
                }

                writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
                new GpxSerializer().writeGPX(allGeocodes, writer, ExportTask.this::publishProgress);
            } catch (final IOException e) {
                Log.e("GpxExport.ExportTask export", e);
                // delete partial GPX file on error
                ContentStorage.get().delete(uri);

                return null;
            } finally {
                IOUtils.closeQuietly(writer);
            }

            return uri;
        }

        @Override
        protected void onPostExecuteInternal(final Uri uri) {
            final Activity activityLocal = activity;
            if (activityLocal != null) {
                if (uri != null) {
                    ShareUtils.shareOrDismissDialog(activityLocal, uri, "application/xml", R.string.export, getName() + ' ' + activityLocal.getString(R.string.export_exportedto) + ": " + UriUtils.toUserDisplayableString(uri));
                } else {
                    ActivityMixin.showToast(activityLocal, activityLocal.getString(R.string.export_failed));
                }
            }
        }

    }
}
                progress.dismiss();
                if (result) {
                    ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString());
                } else {
                    ActivityMixin.showToast(activity, getString(R.string.export_failed));
                }

<MSG> Merge pull request #1571 from triakcz/bluetooth_gpx_sharing

GPX export sharing menu after successful export
<DFF> @@ -2,6 +2,7 @@ package cgeo.geocaching.export;
 
 import cgeo.geocaching.LogEntry;
 import cgeo.geocaching.R;
+import cgeo.geocaching.Settings;
 import cgeo.geocaching.cgCache;
 import cgeo.geocaching.cgWaypoint;
 import cgeo.geocaching.cgeoapplication;
@@ -16,9 +17,15 @@ import cgeo.geocaching.utils.Log;
 import org.apache.commons.lang3.StringEscapeUtils;
 
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Environment;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -39,7 +46,48 @@ class GpxExport extends AbstractExport {
 
     @Override
     public void export(final List<cgCache> caches, final Activity activity) {
-        new ExportTask(caches, activity).execute((Void) null);
+        if (null == activity) {
+            // No activity given, so no user interaction possible.
+            // Start export with default parameters.
+            new ExportTask(caches, activity).execute((Void) null);
+
+        } else {
+            // Show configuration dialog
+            new ExportOptionsDialog(caches, activity).show();
+        }
+    }
+
+    /**
+     * A dialog to allow the user to set options for the export.
+     *
+     * Currently available option is: opening of share menu after successful export
+     */
+    private class ExportOptionsDialog extends AlertDialog {
+        public ExportOptionsDialog(final List<cgCache> caches, final Activity activity) {
+            super(activity);
+
+            View layout = activity.getLayoutInflater().inflate(R.layout.gpx_export_dialog, null);
+            setView(layout);
+
+            final CheckBox shareOption = (CheckBox) layout.findViewById(R.id.share);
+
+            shareOption.setChecked(Settings.getShareAfterExport());
+
+            shareOption.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Settings.setShareAfterExport(shareOption.isChecked());
+                }
+            });
+
+            ((Button) layout.findViewById(R.id.export)).setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    dismiss();
+                    new ExportTask(caches, activity).execute((Void) null);
+                }
+            });
+        }
     }
 
     private class ExportTask extends AsyncTask<Void, Integer, Boolean> {
@@ -88,7 +136,6 @@ class GpxExport extends AbstractExport {
                 gpx.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                 gpx.write("<gpx version=\"1.0\" creator=\"c:geo - http://www.cgeo.org\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd\">");
 
-
                 for (int i = 0; i < caches.size(); i++) {
                     // reload the cache. otherwise logs, attributes and other detailed information is not available
                     final cgCache cache = cgeoapplication.getInstance().loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY);
@@ -322,6 +369,13 @@ class GpxExport extends AbstractExport {
                 progress.dismiss();
                 if (result) {
                     ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString());
+                    if (Settings.getShareAfterExport()) {
+                        Intent shareIntent = new Intent();
+                        shareIntent.setAction(Intent.ACTION_SEND);
+                        shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportFile));
+                        shareIntent.setType("application/xml");
+                        activity.startActivity(Intent.createChooser(shareIntent, getString(R.string.export_gpx_to)));
+                    }
                 } else {
                     ActivityMixin.showToast(activity, getString(R.string.export_failed));
                 }

Merge pull request #1571 from triakcz/bluetooth_gpx_sharing