/*
* Copyright (C) 2013 jonas.oreland@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.runnerup.content;
import android.annotation.TargetApi;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import org.runnerup.BuildConfig;
import org.runnerup.db.DBHelper;
import org.runnerup.export.format.FacebookCourse;
import org.runnerup.export.format.GPX;
import org.runnerup.export.format.GoogleStaticMap;
import org.runnerup.export.format.NikeXML;
import org.runnerup.export.format.RunKeeper;
import org.runnerup.export.format.TCX;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.List;
@TargetApi(Build.VERSION_CODES.FROYO)
public class ActivityProvider extends ContentProvider {
// The authority is the symbolic name for the provider class
public static final String AUTHORITY = "org.runnerup.activity.provider";
@SuppressWarnings("WeakerAccess")
public static final String GPX_MIME = "application/gpx+xml";
@SuppressWarnings("WeakerAccess")
public static final String TCX_MIME = "application/vnd.garmin.tcx+xml";
@SuppressWarnings("WeakerAccess")
public static final String NIKE_MIME = "application/nike+xml";
@SuppressWarnings("WeakerAccess")
public static final String MAPS_MIME = "application/maps";
@SuppressWarnings("WeakerAccess")
public static final String FACEBOOK_COURSE_MIME = "application/facebook.course";
//public static final String RUNKEEPER_MIME = "application/runkeeper+xml";
// UriMatcher used to match against incoming requests
@SuppressWarnings("WeakerAccess")
static final int GPX = 1;
@SuppressWarnings("WeakerAccess")
static final int TCX = 2;
@SuppressWarnings("WeakerAccess")
static final int NIKE = 3;
@SuppressWarnings("WeakerAccess")
static final int MAPS = 4;
@SuppressWarnings("WeakerAccess")
static final int FACEBOOK_COURSE = 5;
@SuppressWarnings("WeakerAccess")
static final int RUNKEEPER = 6;
private UriMatcher uriMatcher;
@Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "gpx/#/*", GPX);
uriMatcher.addURI(AUTHORITY, "tcx/#/*", TCX);
uriMatcher.addURI(AUTHORITY, "nike+xml/#/*", NIKE);
uriMatcher.addURI(AUTHORITY, "maps/#/*", MAPS);
uriMatcher.addURI(AUTHORITY, "facebook.course/#/*", FACEBOOK_COURSE);
uriMatcher.addURI(AUTHORITY, "runkeeper/#/*", RUNKEEPER);
return true;
}
private Pair<File, OutputStream> openCacheFile(String name) {
for (int i = 0; i < 3; i++) {
try {
Context ctx = getContext();
if (BuildConfig.DEBUG && ctx == null) { throw new AssertionError(); }
//noinspection UnusedAssignment
File path = null;
switch (i) {
case 0:
default:
path = ctx.getExternalCacheDir();
break;
case 1:
path = ctx.getExternalFilesDir("tcx");
break;
case 2:
path = ctx.getCacheDir();
break;
}
@SuppressWarnings("ConstantConditions") final File file = new File(path.getAbsolutePath() + File.separator + name);
final OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
Log.e(getClass().getName(), Integer.toString(i) + ": putting cache file in: "
+ file.getAbsolutePath());
//noinspection Convert2Diamond
return new Pair<File, OutputStream>(file, out);
} catch (IOException | NullPointerException ignored) {
}
}
return null;
}
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
final int res = uriMatcher.match(uri);
Log.e(getClass().getName(), "match(" + uri.toString() + "): " + res);
switch (res) {
case GPX:
case TCX:
case NIKE:
case MAPS:
case FACEBOOK_COURSE:
case RUNKEEPER:
final List<String> list = uri.getPathSegments();
final String id = list.get(list.size() - 2);
final long activityId = Long.parseLong(id);
final String parcelFile = "activity." + list.get(list.size() - 3);
final Pair<File, OutputStream> out = openCacheFile(parcelFile);
if (out == null) {
Log.e(getClass().getName(), "Failed to open cacheFile(" + parcelFile + ")");
return null;
}
Log.e(getClass().getName(), "activity: " + activityId + ", file: "
+ out.first.getAbsolutePath());
SQLiteDatabase mDB = DBHelper.getReadableDatabase(getContext());
try {
if (res == TCX) {
TCX tcx = new TCX(mDB);
tcx.export(activityId, new OutputStreamWriter(out.second));
Log.e(getClass().getName(), "export tcx");
} else if (res == GPX) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.getContext());
//The data must exist if log, use the log option as a possibility to "deactivate" too
boolean enabled = prefs.getBoolean(this.getContext().getString(org.runnerup.R.string.pref_log_gpx_accuracy), false);
GPX gpx = new GPX(mDB, true, enabled);
gpx.export(activityId, new OutputStreamWriter(out.second));
Log.e(getClass().getName(), "export gpx");
} else if (res == NIKE) {
NikeXML xml = new NikeXML(mDB);
xml.export(activityId, new OutputStreamWriter(out.second));
} else if (res == MAPS) {
GoogleStaticMap map = new GoogleStaticMap(mDB);
String str = map.export(activityId, 2000);
out.second.write(str.getBytes());
} else if (res == FACEBOOK_COURSE) {
FacebookCourse map = new FacebookCourse(getContext(), mDB);
final boolean includeMap = true;
String str = map.export(activityId, includeMap, null).toString();
out.second.write(str.getBytes());
} else {
//noinspection ConstantConditions
if (res == RUNKEEPER) {
RunKeeper map = new RunKeeper(mDB);
map.export(activityId, new OutputStreamWriter(out.second));
}
}
out.second.flush();
out.second.close();
Log.e(getClass().getName(), "wrote " + out.first.length() + " bytes...");
} catch (Exception e) {
e.printStackTrace();
}
DBHelper.closeDB(mDB);
//noinspection UnnecessaryLocalVariable
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(out.first,
ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
}
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
@Override
public int update(@NonNull Uri uri, ContentValues contentvalues, String s,
String[] as) {
return 0;
}
@Override
public int delete(@NonNull Uri uri, String s, String[] as) {
return 0;
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues contentvalues) {
return null;
}
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri)) {
case GPX:
return GPX_MIME;
case TCX:
return TCX_MIME;
case NIKE:
return NIKE_MIME;
case MAPS:
return MAPS_MIME;
case FACEBOOK_COURSE:
return FACEBOOK_COURSE_MIME;
}
return null;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String s, String[] as1,
String s1) {
return null;
}
}