Skip to content

Instantly share code, notes, and snippets.

@thesjg
Last active December 12, 2015 01:58
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save thesjg/4694437 to your computer and use it in GitHub Desktop.
Save thesjg/4694437 to your computer and use it in GitHub Desktop.
/*
* Needs a tile layer, something like (for Leaflet) ..
*
* var tl = L.tileLayer("content://com.pahasapatrails.m.tiles/{style}/{z}/{x}/{y}.png",
* { style: "XXX", minZoom: 9, maxZoom: 13, tms: true });
* map.addLayer(tl);
*
*
* Also needs a provider entry under the application tag in the Android
* Manifest, something like the following ..
*
* <provider android:name="com.pahasapatrails.m.TileContentProvider"
* android:authorities="com.pahasapatrails.m.tiles" />
*
*/
package com.pahasapatrails.m;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
public class TileContentProvider extends ContentProvider {
// private String databaseList[] = null;
// private SQLiteDatabase databases[];
private SQLiteDatabase handle;
private String query = "SELECT tile_data FROM images INNER JOIN map ON " +
"images.tile_id = map.tile_id WHERE zoom_level = ? " +
" AND tile_column = ? AND tile_row = ?";
/*
* onCreate is called when the application manifest is read at startup time.
* Do one-time initialization magic here, the provider will keep running
* so we only have to open each database once and cache the handle.
*/
@Override
public boolean onCreate()
{
/*
* Delete the cache, just in case anything stuck around somehow
*/
File cacheDir = this.getContext().getCacheDir();
String[] children = cacheDir.list();
for (int i = 0; i < children.length; i++) {
new File(cacheDir, children[i]).delete();
}
/*
* Setup the database connection(s)
*
* XXX: Hardcoded database filename
*/
String dbname = "PahaSapaTrailscomMobile9-13.mbtiles";
File dbfile = this.getContext().getDatabasePath(dbname);
handle = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
return (true);
}
/*
* Query the MBTiles DB and return content-type
*
* XXX: Does this ever even get called by a webview?
*/
@Override
public String getType(Uri uri)
{
Log.e("error", "TileContentProvider: getType: " + uri);
return ("image/png");
}
/*
* If only concerned with HONEYCOMB and later then openFile could simply
* use simpleQueryForBlobFileDescriptor and return that descriptor --
* at least ostensibly, untested.
*/
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException
{
if (mode != "r")
throw new FileNotFoundException("mode can only be of type read-only");
/*
* Parse uri, path looks like /scheme/zoom/x/y.png
*
* XXX: Assumes png
*/
String path[] = uri.getPath().split("/");
String params[] = { path[2], path[3], path[4].replace(".png", "") };
String outfilename = params[0] + "-" + params[1] + "-" + params[2] + ".png";
try {
File file = new File(this.getContext().getCacheDir(), outfilename);
Cursor cursor = handle.rawQuery(query, params);
if (!cursor.moveToFirst())
throw new FileNotFoundException("No results from query");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bos.write(cursor.getBlob(0));
cursor.close();
ParcelFileDescriptor ret = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
bos.close();
/*
* Unlink underneath? Actual file will *poof* once the
* ParcelFileDescriptor we are returning gets closed.
*/
file.delete();
handle.setTransactionSuccessful();
return (ret);
} catch (IOException e) {
e.printStackTrace();
/* Return static error image instead? */
throw new FileNotFoundException("Error setting up tile");
}
}
/*
* query, delete, insert and update operations are specifically not supported,
* the application must download the mbtiles database files and place them
* in the expected data directory.
*/
@Override
public Cursor query(Uri uri, String[] arg1, String arg2, String[] arg3, String arg4)
{
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public int delete(Uri uri, String arg1, String[] arg2)
{
throw new UnsupportedOperationException();
}
@Override
public Uri insert(Uri uri, ContentValues arg1)
{
throw new UnsupportedOperationException();
}
@Override
public int update(Uri uri, ContentValues arg1, String arg2, String[] arg3)
{
throw new UnsupportedOperationException();
}
}
@adube
Copy link

adube commented Feb 26, 2013

Hi,

I didn't know about gist. I just tried your code and it was throwing an exception. I found that the

handle.setTransactionSuccessful();

was the cause, so I added some validation to it :

if (handle.inTransaction())
{
    handle.setTransactionSuccessful();
}

Here's the end result in a fork : https://gist.github.com/adube/5041097

Thanks for sharing your code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment