forked from upstream-mirrors/OpenTracks
Show on map: draft via virtual file (with two content providers).
Exported content provider that temporary needs to grant access to the internal content provider.
This commit is contained in:
@@ -175,6 +175,11 @@ limitations under the License.
|
|||||||
<!-- Providers -->
|
<!-- Providers -->
|
||||||
<provider
|
<provider
|
||||||
android:name=".content.CustomContentProvider"
|
android:name=".content.CustomContentProvider"
|
||||||
|
android:authorities="${applicationId}.content"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true" />
|
||||||
|
<provider
|
||||||
|
android:name=".content.ShareContentProvider"
|
||||||
android:authorities="${applicationId}"
|
android:authorities="${applicationId}"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true" />
|
android:grantUriPermissions="true" />
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -25,9 +26,7 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.StrictMode;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
@@ -47,30 +46,21 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
||||||
import androidx.loader.content.CursorLoader;
|
import androidx.loader.content.CursorLoader;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.ShareContentProvider;
|
||||||
import de.dennisguse.opentracks.content.TracksColumns;
|
import de.dennisguse.opentracks.content.TracksColumns;
|
||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
|
||||||
import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter;
|
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
|
||||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
@@ -257,17 +247,17 @@ public class TrackListActivity extends AbstractTrackActivity implements ConfirmD
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
// if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, "Enabling strict mode");
|
// Log.d(TAG, "Enabling strict mode");
|
||||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
// StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||||
.detectAll()
|
// .detectAll()
|
||||||
.penaltyLog()
|
// .penaltyLog()
|
||||||
.build());
|
// .build());
|
||||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
// StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||||
.detectAll()
|
// .detectAll()
|
||||||
.penaltyLog()
|
// .penaltyLog()
|
||||||
.build());
|
// .build());
|
||||||
}
|
// }
|
||||||
|
|
||||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
@@ -566,8 +556,7 @@ public class TrackListActivity extends AbstractTrackActivity implements ConfirmD
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save tracks in one file and send an intent to show it with a map application.
|
* Send intent to show tracks on a map (needs an another app).
|
||||||
* TODO: Exported file is not deleted automatically.
|
|
||||||
*
|
*
|
||||||
* @param trackIds
|
* @param trackIds
|
||||||
*/
|
*/
|
||||||
@@ -576,45 +565,14 @@ public class TrackListActivity extends AbstractTrackActivity implements ConfirmD
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Track[] tracks = new Track[trackIds.length];
|
Intent intent = new Intent();
|
||||||
for (int i = 0; i < trackIds.length; i++) {
|
intent.setAction(android.content.Intent.ACTION_VIEW);
|
||||||
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
|
intent.setDataAndType(ShareContentProvider.createURI(trackIds), ShareContentProvider.MIME);
|
||||||
}
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
try {
|
||||||
TrackFileFormat trackFileFormat = TrackFileFormat.KML;
|
startActivity(intent);
|
||||||
TrackExporter trackExporter = new FileTrackExporter(contentProviderUtils, tracks,
|
} catch (ActivityNotFoundException e) {
|
||||||
trackFileFormat.newTrackWriter(this, tracks.length > 1), null);
|
Toast.makeText(this, "No app installed that can show the tracks on a map.", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
File directory = new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
|
||||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String fileName = FileUtils.buildUniqueFileName(directory, tracks[0].getName(), trackFileFormat.getExtension());
|
|
||||||
File file = new File(directory, fileName);
|
|
||||||
|
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
|
||||||
if (trackExporter.writeTrack(fileOutputStream)) {
|
|
||||||
Uri fileUri = FileProvider.getUriForFile(this, FileUtils.FILEPROVIDER, file);
|
|
||||||
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setAction(android.content.Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(fileUri, "application/vnd.google-earth.kml+xml");
|
|
||||||
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
startActivity(intent);
|
|
||||||
} else {
|
|
||||||
if (!file.delete()) {
|
|
||||||
Log.d(TAG, "Unable to delete file");
|
|
||||||
}
|
|
||||||
Log.e(TAG, "Unable to export track");
|
|
||||||
}
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.e(TAG, "Unable to open file " + file.getName(), e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Unable to close file output stream", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public interface ContentProviderUtils {
|
|||||||
* The authority (the first part of the URI) for the app's content
|
* The authority (the first part of the URI) for the app's content
|
||||||
* provider.
|
* provider.
|
||||||
*/
|
*/
|
||||||
String AUTHORITY = BuildConfig.APPLICATION_ID;
|
String AUTHORITY = BuildConfig.APPLICATION_ID + ".content";
|
||||||
/**
|
/**
|
||||||
* The default {@link LocationFactory} which creates a location each time.
|
* The default {@link LocationFactory} which creates a location each time.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
package de.dennisguse.opentracks.content;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ProviderInfo;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.BuildConfig;
|
||||||
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
|
import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter;
|
||||||
|
import de.dennisguse.opentracks.io.file.exporter.TrackWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A content provider that mimics the behavior of {@link androidx.core.content.FileProvider}, which shares virtual (non-existing) files.
|
||||||
|
* The actual content is generated by accessing {@link CustomContentProvider} on request.
|
||||||
|
*/
|
||||||
|
public class ShareContentProvider extends ContentProvider {
|
||||||
|
|
||||||
|
public static final String TAG = ShareContentProvider.class.getCanonicalName();
|
||||||
|
|
||||||
|
public static final String SHAREPROVIDER = BuildConfig.APPLICATION_ID;
|
||||||
|
|
||||||
|
private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
|
||||||
|
|
||||||
|
public static String MIME = "application/kml+xml";
|
||||||
|
|
||||||
|
public static Uri createURI(long[] trackIds) {
|
||||||
|
if (trackIds.length == 0) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (long trackId : trackIds) {
|
||||||
|
builder.append(trackId).append(",");
|
||||||
|
}
|
||||||
|
builder.deleteCharAt(builder.lastIndexOf(","));
|
||||||
|
|
||||||
|
return Uri.parse("content://" + SHAREPROVIDER + "/" + builder + ".kml");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] parseURI(Uri uri) {
|
||||||
|
String lastPathSegment = uri.getLastPathSegment();
|
||||||
|
if (lastPathSegment == null) {
|
||||||
|
return new long[]{};
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] lastPathSegmentSplit = lastPathSegment.replace(".kml", "").split(",");
|
||||||
|
long[] trackIds = new long[lastPathSegmentSplit.length];
|
||||||
|
for (int i = 0; i < trackIds.length; i++) {
|
||||||
|
trackIds[i] = Long.valueOf(lastPathSegmentSplit[i]);
|
||||||
|
}
|
||||||
|
return trackIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachInfo(Context context, ProviderInfo info) {
|
||||||
|
super.attachInfo(context, info);
|
||||||
|
if (!info.grantUriPermissions) {
|
||||||
|
throw new SecurityException("Provider must grant uri permissions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||||
|
// ContentProvider has already checked granted permissions
|
||||||
|
if (projection == null) {
|
||||||
|
projection = COLUMNS;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] cols = new String[projection.length];
|
||||||
|
Object[] values = new Object[projection.length];
|
||||||
|
int i = 0;
|
||||||
|
for (String col : projection) {
|
||||||
|
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
|
||||||
|
cols[i] = OpenableColumns.DISPLAY_NAME;
|
||||||
|
values[i++] = uri.getLastPathSegment();
|
||||||
|
} else if (OpenableColumns.SIZE.equals(col)) {
|
||||||
|
cols[i] = OpenableColumns.SIZE;
|
||||||
|
values[i++] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cols = Arrays.copyOf(cols, i);
|
||||||
|
values = Arrays.copyOf(values, i);
|
||||||
|
|
||||||
|
final MatrixCursor cursor = new MatrixCursor(cols, 1);
|
||||||
|
cursor.addRow(values);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri) {
|
||||||
|
return MIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||||
|
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(getContext());
|
||||||
|
|
||||||
|
final Uri customContentProviderURI = Uri.parse("content://" + ContentProviderUtils.AUTHORITY + "/tracks");
|
||||||
|
getContext().grantUriPermission(getCallingPackage(), customContentProviderURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
||||||
|
long[] trackIds = parseURI(uri);
|
||||||
|
final Track[] tracks = new Track[trackIds.length];
|
||||||
|
for (int i = 0; i < trackIds.length; i++) {
|
||||||
|
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackWriter kmlTrackWriter = TrackFileFormat.KML.newTrackWriter(getContext(), false);
|
||||||
|
final FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, tracks, kmlTrackWriter, null);
|
||||||
|
|
||||||
|
PipeDataWriter pipeDataWriter = new PipeDataWriter<String>() {
|
||||||
|
@Override
|
||||||
|
public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable String args) {
|
||||||
|
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||||
|
fileTrackExporter.writeTrack(fileOutputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "Oops closing " + e);
|
||||||
|
Toast.makeText(getContext(), "", Toast.LENGTH_SHORT).show();
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
getContext().revokeUriPermission(customContentProviderURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return openPipeHelper(uri, getType(uri), null, null, pipeDataWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,8 +19,6 @@ package de.dennisguse.opentracks.content;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for the track points table.
|
* Constants for the track points table.
|
||||||
*
|
*
|
||||||
@@ -29,7 +27,7 @@ import de.dennisguse.opentracks.BuildConfig;
|
|||||||
public interface TrackPointsColumns extends BaseColumns {
|
public interface TrackPointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "trackpoints";
|
String TABLE_NAME = "trackpoints";
|
||||||
Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.APPLICATION_ID + "/trackpoints");
|
Uri CONTENT_URI = Uri.parse("content://" + ContentProviderUtils.AUTHORITY + "/trackpoints");
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ package de.dennisguse.opentracks.content;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for the tracks table.
|
* Constants for the tracks table.
|
||||||
*
|
*
|
||||||
@@ -33,7 +31,7 @@ public interface TracksColumns extends BaseColumns {
|
|||||||
/**
|
/**
|
||||||
* Tracks provider uri.
|
* Tracks provider uri.
|
||||||
*/
|
*/
|
||||||
Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.APPLICATION_ID + "/tracks");
|
Uri CONTENT_URI = Uri.parse("content://" + ContentProviderUtils.AUTHORITY + "/tracks");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track content type.
|
* Track content type.
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ package de.dennisguse.opentracks.content;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for waypoints table.
|
* Constants for waypoints table.
|
||||||
*
|
*
|
||||||
@@ -29,7 +27,7 @@ import de.dennisguse.opentracks.BuildConfig;
|
|||||||
public interface WaypointsColumns extends BaseColumns {
|
public interface WaypointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "waypoints";
|
String TABLE_NAME = "waypoints";
|
||||||
Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.APPLICATION_ID + "/waypoints");
|
Uri CONTENT_URI = Uri.parse("content://" + ContentProviderUtils.AUTHORITY + "/waypoints");
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|||||||
Reference in New Issue
Block a user