package com.robert.maps.applib.tileprovider;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.andnav.osm.views.util.StreamUtils;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Message;
import com.robert.maps.applib.utils.SimpleThreadFactory;
import com.robert.maps.applib.utils.Ut;
public class TileProviderTAR extends TileProviderFileBase {
private ExecutorService mThreadPool = Executors.newSingleThreadExecutor(new SimpleThreadFactory("TileProviderTAR"));
private File mMapFile;
private String mMapID;
private ProgressDialog mProgressDialog;
private boolean mStopIndexing;
public TileProviderTAR(Context ctx, final String filename, final String mapid, MapTileMemCache aTileCache) {
super(ctx);
mTileURLGenerator = new TileURLGeneratorTAR(filename);
mTileCache = aTileCache == null ? new MapTileMemCache() : aTileCache;
mMapFile = new File(filename);
mMapID = mapid;
if(needIndex(mapid, mMapFile.length(), mMapFile.lastModified(), false)) {
mProgressDialog = new ProgressDialog(ctx);
mProgressDialog.setTitle("Indexing");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMax((int)(mMapFile.length()/1024));
mProgressDialog.setCancelable(true);
mProgressDialog.setButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog) {
mStopIndexing = true;
}
});
mProgressDialog.show();
mProgressDialog.setProgress(0);
CreateTarIndex();
new IndexTask().execute(mMapFile.length(), mMapFile.lastModified());
}
}
private void CreateTarIndex() {
this.mIndexDatabase.execSQL("DROP TABLE IF EXISTS '" + mMapID + "'");
this.mIndexDatabase.execSQL("CREATE TABLE IF NOT EXISTS '" + mMapID + "' (name VARCHAR(100), offset INTEGER NOT NULL, size INTEGER NOT NULL, PRIMARY KEY(name) );");
this.mIndexDatabase.delete("ListCashTables", "name = '" + mMapID + "'", null);
}
private void addTarIndexRow(String aName, int aOffset, int aSize) {
final ContentValues cv = new ContentValues();
cv.put("name", aName);
cv.put("offset", aOffset);
cv.put("size", aSize);
this.mIndexDatabase.insert("'" + mMapID + "'", null, cv);
}
private void CommitIndex(long aSizeFile, long aLastModifiedFile, int zoomMinInCashFile, int zoomMaxInCashFile) {
this.mIndexDatabase.delete("ListCashTables", "name = '" + mMapID + "'", null);
final ContentValues cv = new ContentValues();
cv.put("name", mMapID);
cv.put("lastmodified", aLastModifiedFile);
cv.put("size", aSizeFile);
cv.put("minzoom", zoomMinInCashFile);
cv.put("maxzoom", zoomMaxInCashFile);
this.mIndexDatabase.insert("ListCashTables", null, cv);
}
private boolean findTarIndex(final String aName, Param4ReadData aData) {
boolean ret = false;
final Cursor c = this.mIndexDatabase.rawQuery("SELECT offset, size FROM '" + mMapID + "' WHERE name = '"
+ aName + ".jpg' OR name = '" + aName + ".png'", null);
if (c != null) {
if (c.moveToFirst()) {
aData.offset = c.getInt(c.getColumnIndexOrThrow("offset"));
aData.size = c.getInt(c.getColumnIndexOrThrow("size"));
ret = true;
}
c.close();
}
return ret;
}
public void updateMapParams(TileSource tileSource) {
tileSource.ZOOM_MINLEVEL = ZoomMinInCashFile(mMapID);
tileSource.ZOOM_MAXLEVEL = ZoomMaxInCashFile(mMapID);
}
private class IndexTask extends AsyncTask<Long, Void, Boolean> {
@Override
protected Boolean doInBackground(Long... params) {
try {
mStopIndexing = false;
long fileLength = mMapFile.length();
long fileModified = mMapFile.lastModified();
int minzoom = 24, maxzoom = 0;
InputStream in = null;
in = new BufferedInputStream(new FileInputStream(mMapFile), 8192);
String name; // 100 name of file
// int mode; // file mode
// int uid; // owner user ID
// int gid; // owner group ID
int tileSize; // 12 length of file in bytes
// int mtime; // 12 modify time of file
// int chksum; // checksum for header
// byte[] link = new byte[1]; // indicator for links
// String linkname; // 100 name of linked file
int offset = 0, skip = 0;
while (in.available() > 0) {
name = Ut.readString(in, 100).trim().replace('\\', '/');
// mode = Integer.decode("0" + Util.readString(in, 8).trim());
// uid = Integer.decode("0" + Util.readString(in, 8).trim());
// gid = Integer.decode("0" + Util.readString(in, 8).trim());
in.skip(24);
tileSize = Integer.decode("0" + Ut.readString(in, 12).trim());
// mtime = Integer.decode("0" + Util.readString(in, 12).trim());
// in.read(link);
// linkname = Util.readString(in, 100);
in.skip(12 + 1 + 100);
in.skip(512 - 100 - 8 - 8 - 8 - 12 - 12 - 1 - 100);
offset += 512;
if (tileSize > 0) {
addTarIndexRow(name, offset, tileSize);
Ut.d(name);
if(tileSize % 512 == 0)
skip = tileSize;
else
skip = tileSize + 512 - tileSize % 512;
in.skip(skip);
offset += skip;
int zoom = Integer.parseInt(name.substring(0, 2)) - 1;
if (zoom > maxzoom)
maxzoom = zoom;
if (zoom < minzoom)
minzoom = zoom;
}
mProgressDialog.setProgress((int) (offset/1024));
if(mStopIndexing)
break;
}
if (!mStopIndexing)
CommitIndex(fileLength, fileModified, minzoom, maxzoom);
} catch (Exception e) {
Ut.e(e.getLocalizedMessage());
return false;
}
return !mStopIndexing;
}
@Override
protected void onPostExecute(Boolean result) {
if(result)
Message.obtain(mCallbackHandler, MessageHandlerConstants.MAPTILEFSLOADER_INDEXIND_SUCCESS_ID).sendToTarget();
if(mProgressDialog != null)
mProgressDialog.dismiss();
}
}
@Override
public void Free() {
Ut.d("TileProviderTAR Free");
mThreadPool.shutdown();
super.Free();
}
private class Param4ReadData {
public int offset, size;
Param4ReadData(int offset, int size) {
this.offset = offset;
this.size = size;
}
}
public Bitmap getTile(final int x, final int y, final int z) {
final String tileurl = mTileURLGenerator.Get(x, y, z);
FileInputStream stream;
try {
stream = new FileInputStream(mMapFile);
} catch (FileNotFoundException e1) {
return mLoadingMapTile;
}
final InputStream in = new BufferedInputStream(stream, 8192);
final Bitmap bmp = mTileCache.getMapTile(tileurl);
if(bmp != null)
return bmp;
if (this.mPending.contains(tileurl))
return super.getTile(x, y, z);
mPending.add(tileurl);
this.mThreadPool.execute(new Runnable() {
public void run() {
OutputStream out = null;
try {
Param4ReadData Data = new Param4ReadData(0, 0);
if(findTarIndex(tileurl, Data)) {
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
out = new BufferedOutputStream(dataStream, StreamUtils.IO_BUFFER_SIZE);
byte[] tmp = new byte[Data.size];
in.skip(Data.offset);
int read = in.read(tmp);
if (read > 0) {
out.write(tmp, 0, read);
}
out.flush();
in.skip(Data.size % 512);
final byte[] data = dataStream.toByteArray();
final Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
mTileCache.putTile(tileurl, bmp);
SendMessageSuccess();
}
} catch (OutOfMemoryError e) {
SendMessageFail();
System.gc();
} catch (Exception e) {
SendMessageFail();
} finally {
StreamUtils.closeStream(in);
StreamUtils.closeStream(out);
}
mPending.remove(tileurl);
}
});
return mLoadingMapTile;
}
}