OsmDroid and MapQuest: How can I use JPEG tiles? - android

I am new to OSM and OSMdroid.
I was following this pretty good tutorial to show offline maps. So basically what I have done is:
Created a tile package in zip format with Mobile Atlas Creator
Used MapQuest source, JPEG format
Put the zip into the right folder: /mnt/sdcard/osmdroid/
The problem was the tiles were not rendered. I got a blank page.
I found this solution, to solve my problem.
But now, it is bothering me that I have to use PNG files, that takes significantly more space. It is not really efficient for my app because the user will have to download a much larger package.
MY QUESTION IS: How can I use JPEG tiles with OSMDroid and MapQuest?
Thanks in advance.

This works to get JPGs instead of PNGs:
MapView myOpenMapView;
myOpenMapView = (MapView) findViewById(R.id.openmapview);
myOpenMapView.setTileSource(new XYTileSource("MapquestOSM", ResourceProxy.string.mapquest_osm, 0, 18, 256, ".jpg", new String[] {
"http://otile1.mqcdn.com/tiles/1.0.0/map/", "http://otile2.mqcdn.com/tiles/1.0.0/map/", "http://otile3.mqcdn.com/tiles/1.0.0/map/",
"http://otile4.mqcdn.com/tiles/1.0.0/map/" }));
Notice ".jpg" in line 3.

I created a tile source that suppoort jpg, you can take a look and adapt your case,
Please note that getTileRelativeFilenameString won't contain .title ext. That part will be added by (MapTileFilesystemProvider)
import java.io.File;
import java.io.InputStream;
import java.util.Random;
import org.osmdroid.ResourceProxy;
import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase.LowMemoryException;
import org.osmdroid.tileprovider.tilesource.ITileSource;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
public class MapTilerCustomDataSource implements ITileSource {
private static int globalOrdinal = 0;
private final int mMinimumZoomLevel;
private final int mMaximumZoomLevel;
private final int mOrdinal;
protected final String mName;
protected final String mImageFilenameEnding;
protected final Random random = new Random();
private final int mTileSizePixels;
private final string mResourceId;
public MapTilerCustomDataSource() {
mResourceId = null;
mOrdinal = globalOrdinal++;
mName = "MapquestOSM";
mMinimumZoomLevel = 0;
mMaximumZoomLevel = 20;
mTileSizePixels = 256;
mImageFilenameEnding = ".jpg";
}
#Override
public String getTileRelativeFilenameString(final MapTile tile) {
final StringBuilder sb = new StringBuilder();
sb.append(pathBase());
sb.append('/');
sb.append(tile.getZoomLevel());
sb.append('/');
sb.append(tile.getX());
sb.append('/');
sb.append(tile.getY());
sb.append(imageFilenameEnding());
return sb.toString();
}
#Override
public Drawable getDrawable(String aFilePath) throws LowMemoryException {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
final Bitmap bitmap = BitmapFactory.decodeFile(aFilePath);
if (bitmap != null) {
return new BitmapDrawable(bitmap);
} else {
// if we couldn't load it then it's invalid - delete it
try {
new File(aFilePath).delete();
} catch (final Throwable e) {
}
}
} catch (final OutOfMemoryError e) {
System.gc();
}
return null;
}
#Override
public Drawable getDrawable(InputStream aFileInputStream) throws LowMemoryException {
try {
// default implementation will load the file as a bitmap and create
// a BitmapDrawable from it
final Bitmap bitmap = BitmapFactory.decodeStream(aFileInputStream);
if (bitmap != null) {
return new BitmapDrawable(bitmap);
}
} catch (final OutOfMemoryError e) {
System.gc();
}
return null;
}
#Override
public int ordinal() {
return mOrdinal;
}
#Override
public String name() {
return mName;
}
public String pathBase() {
return mName;
}
public String imageFilenameEnding() {
return mImageFilenameEnding;
}
#Override
public int getMinimumZoomLevel() {
return mMinimumZoomLevel;
}
#Override
public int getMaximumZoomLevel() {
return mMaximumZoomLevel;
}
#Override
public int getTileSizePixels() {
return mTileSizePixels;
}
#Override
public String localizedName(final ResourceProxy proxy) {
return proxy.getString(mResourceId);
}
}

Download 'xxx.JPG.tile' files and rename them to 'xxx.PNG.tile'.

Related

How to properly display <img> tags found in returned json object

I am fetching json from the web using volley. One of the json objects ("content"), has <img> tags embedded in the string returned.
With the code below, I have successfully parsed and displayed the objects but the images in "content" are not displaying.
So I wanted the images to display in the positions that they are found in the "content" object.
FruitDetails
public class FruitDetails extends AppCompatActivity {
private final String TAG = "FruitDetails";
TextView fruitTitle, fruitContent;
NetworkImageView authorImg;
ImageLoader AuthImgLoader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fruit_details);
fruitTitle = (TextView) findViewById(R.id.dfruit_title);
fruitContent = (TextView) findViewById(R.id.dfruit_content);
authorImg = (NetworkImageView) findViewById(R.id.author_img);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (savedInstanceState != null) {
fruitTitle.setText(savedInstanceState.getString("fruitTitle"));
fruitContent.setText(savedInstanceState.getString("fruitContent"));
} else {
loadFruit();
}
}
private void loadFruit() {
Log.d(TAG, "loadFruit called");
final ProgressBar progressBar;
progressBar = (ProgressBar) findViewById(R.id.progress_circle);
progressBar.setVisibility(View.VISIBLE);
int news_id = getIntent().getIntExtra("FruitId", -1);
Log.d(TAG, "You clicked fruit id " + news_id);
final JsonObjectRequest jsonObjReq = new JsonObjectRequest( DetailConfig.GET_DURL + news_id, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("Debug", response.toString());
//Dismissing progressbar;
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
//Calling method to parse json array
parseFruit(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("", "Error: " + error.getMessage());
}
});
//Creating request queue
RequestQueue requestQueue = Volley.newRequestQueue(this);
//Adding request to queue
requestQueue.add(jsonObjReq);
}
//This method will parse json data of fruit
private void parseFruit(JSONObject jsonObject) {
Log.d(TAG, "Parsing fruit array");
try {
String title = jsonObject.getString(DetailConfig.TAG_DFRUIT_TITLE);
fruitTitle.setText(Html.fromHtml(title));
JSONObject pAuthor = jsonObject.getJSONObject("author");
String authorimg = pAuthor.getString("avatar");
AuthImgLoader = VolleyRequest.getInstance(getApplicationContext()).getImageLoader();
AuthImgLoader.get(authorimg, ImageLoader.getImageListener(authorImg, R.drawable.ic_author, R.drawable.ic_author));
authorImg.setImageUrl(authorimg, AuthImgLoader);
String content = jsonObject.getString(DetailConfig.TAG_DFRUIT_CONTENT);
fruitContent.setText(Html.fromHtml(content));
} catch (JSONException w) {
w.printStackTrace();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("fruitTitle", fruitTitle.getText().toString());
outState.putString("fruitContent", fruitContent.getText().toString());
}
}
I have seen the accepted answer to similar question but I am having problems trying to implement it it keeps telling me "cannot resolve symbol urlDrawable.
UILImageGetter
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.view.View;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
public class UILImageGetter implements Html.ImageGetter{
Context c;
TextView conatiner;
UrlImageDownloader urlDrawable;
public UILImageGetter(View textView, Context context) {
this.c = context;
this.conatiner = (TextView) textView;
}
#Override
public Drawable getDrawable(String source) {
urlDrawable = new UrlImageDownloader(c.getResources(), source);
urlDrawable.mDrawable = c.getResources().getDrawable(R.drawable.default_thumb);
ImageLoader.getInstance().loadImage(source, new SimpleListener(urlDrawable));
return urlDrawable;
}
private class SimpleListener extends SimpleImageLoadingListener {
UrlImageDownloader mUrlImageDownloader;
public SimpleListener(UrlImageDownloader downloader) {
super();
mUrlImageDownloader= downloader;
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
int width = loadedImage.getWidth();
int height = loadedImage.getHeight();
int newWidth = width;
int newHeight = height;
if (width > conatiner.getWidth()) {
newWidth = conatiner.getWidth();
newHeight = (newWidth * height) / width;
}
if (view != null) {
view.getLayoutParams().width = newWidth;
view.getLayoutParams().height = newHeight;
}
Drawable result = new BitmapDrawable(c.getResources(), loadedImage);
result.setBounds(0, 0, newWidth, newHeight);
mUrlImageDownloader.setBounds(0, 0, newWidth, newHeight);
mUrlImageDownloader.mDrawable = result;
conatiner.setHeight((conatiner.getHeight() + result.getIntrinsicHeight()));
conatiner.invalidate();
}
}
private class UrlImageDownloader extends BitmapDrawable {
public Drawable mDrawable;
public UrlImageDownloader(Resources resources, String filepath) {
super(resources, filepath);
mDrawable = new BitmapDrawable(resources, filepath);
}
#Override
public void draw(Canvas canvas) {
if (mDrawable != null) {
mDrawable.draw(canvas);
}
}
}
}
The easiest one is if the Json object is just a string with the following syntax (without the spaces in the img tags):
<img src= "https://www.myWebstie/images/myimgx92dp.png" />
and!!! you know it won't change then you can just substring it:
String response = "this is your image link";
int x = response.length();
x -= 3;
String subString = new String(response.substring(10, x));
so for example 10 for img tag + the src= at the beggining of the string and 3 for the closing tag at the end of the string.
As for your example you should notice that sometimes there is additional info in the img tag like width and height so make sure you substring the link without it and also learn from it how big the picture should be in your app (dont forget that there's px vs dp difference).
I checked the content you posted here.
If this is realy the website you are trying to parse I would suggest to find a 3rd party library to parse html objects.
Seems like jsoup might do the trick but I have never used it or Html.ImageGetter before although jsoup might seem as the better option for the content you added.
you will just need to try both and check what's the better solution for you.
Check this as well.
Please be aware that if you are going to download and show multiple images and maybe even videos you should use volley for the download as well (after you retrieved all the images links).
If it's just one picture as mentioned before and that content was just for example I would still recommend to just substring it.
I simply used UILImageGetter above, then came to FruitDetails and wrote
protected com.nostra13.universalimageloader.core.ImageLoader mImageLoader;
before onCreate.
In onCreate, I wrote
mImageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
mImageLoader.init(ImageLoaderConfiguration.createDefault(this));.
Then I also changed
fruitContent.setText(Html.fromHtml(content));
in FruitDetails to
Spanned spanned = Html.fromHtml(content, new UILImageGetter(fruitContent, this), null);
fruitContent.setText(spanned);
This solved my problem.

POI's billboard are not shown

I'm using the ARexampleLocationBased tutorial ,the POIS ar correctly shown but the annottations are not shown ,,the program works correct but the billoboards image down the POI's are not visible
package pfg.proyecto.com.proyecto;
import android.view.View;
import com.metaio.sdk.ARELActivity;
public class ARELViewActivity extends ARELActivity
{
#Override
protected int getGUILayout()
{
return R.layout.activity_main;
}
public void onButtonClick(View v)
{
finish();
}
}
and this is the main activity code
package pfg.proyecto.com.proyecto;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextPaint;
import android.util.Log;
import android.view.View;
import com.metaio.cloud.plugin.util.MetaioCloudUtils;
import com.metaio.sdk.ARELInterpreterAndroidJava;
import com.metaio.sdk.ARViewActivity;
import com.metaio.sdk.MetaioDebug;
import com.metaio.sdk.jni.AnnotatedGeometriesGroupCallback;
import com.metaio.sdk.jni.EGEOMETRY_FOCUS_STATE;
import com.metaio.sdk.jni.IAnnotatedGeometriesGroup;
import com.metaio.sdk.jni.IGeometry;
import com.metaio.sdk.jni.IMetaioSDKCallback;
import com.metaio.sdk.jni.IRadar;
import com.metaio.sdk.jni.ImageStruct;
import com.metaio.sdk.jni.LLACoordinate;
import com.metaio.sdk.jni.Rotation;
import com.metaio.sdk.jni.SensorValues;
import com.metaio.sdk.jni.Vector3d;
import com.metaio.tools.io.AssetsManager;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.locks.Lock;
public class MainActivity extends ARViewActivity
{
private IAnnotatedGeometriesGroup mAnnotatedGeometriesGroup;
private MyAnnotatedGeometriesGroupCallback mAnnotatedGeometriesGroupCallback;
/**
* Geometries
*/
private IGeometry mLondonGeo;
private IGeometry mMunichGeo;
private IGeometry mRomeGeo;
private IGeometry mTokyoGeo;
private IGeometry mParisGeo;
private IRadar mRadar;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Set GPS tracking configuration
boolean result = metaioSDK.setTrackingConfiguration("GPS", false);
new LoadAssets().execute(0);
MetaioDebug.log("Tracking data loaded: " + result);
}
#Override
protected void onDestroy()
{
// Break circular reference of Java objects
if (mAnnotatedGeometriesGroup != null)
{
mAnnotatedGeometriesGroup.registerCallback(null);
}
if (mAnnotatedGeometriesGroupCallback != null)
{
mAnnotatedGeometriesGroupCallback.delete();
mAnnotatedGeometriesGroupCallback = null;
}
super.onDestroy();
}
#Override
public void onDrawFrame()
{
if (metaioSDK != null && mSensors != null)
{
SensorValues sensorValues = mSensors.getSensorValues();
float heading = 0.0f;
if (sensorValues.hasAttitude())
{
float m[] = new float[9];
sensorValues.getAttitude().getRotationMatrix(m);
Vector3d v = new Vector3d(m[6], m[7], m[8]);
v.normalize();
heading = (float)(-Math.atan2(v.getY(), v.getX()) - Math.PI / 2.0);
}
IGeometry geos[] = new IGeometry[] {mLondonGeo, mParisGeo, mRomeGeo, mTokyoGeo,};
Rotation rot = new Rotation((float)(Math.PI / 2.0), 0.0f, -heading);
for (IGeometry geo : geos)
{
if (geo != null)
{
geo.setRotation(rot);
}
}
}
super.onDrawFrame();
}
public void onButtonClick(View v)
{
finish();
}
#Override
protected int getGUILayout()
{
return R.layout.activity_main;
}
#Override
protected IMetaioSDKCallback getMetaioSDKCallbackHandler()
{
return null;
}
#Override
protected void loadContents()
{
mAnnotatedGeometriesGroup = metaioSDK.createAnnotatedGeometriesGroup();
mAnnotatedGeometriesGroupCallback = new MyAnnotatedGeometriesGroupCallback();
mAnnotatedGeometriesGroup.registerCallback(mAnnotatedGeometriesGroupCallback);
// Clamp geometries' Z position to range [5000;200000] no matter how close or far they are
// away.
// This influences minimum and maximum scaling of the geometries (easier for development).
metaioSDK.setLLAObjectRenderingLimits(5, 200);
// Set render frustum accordingly
metaioSDK.setRendererClippingPlaneLimits(10, 220000);
// let's create LLA objects for known cities
LLACoordinate munich = new LLACoordinate(48.142573, 11.550321, 0, 0);
LLACoordinate london = new LLACoordinate(51.50661, -0.130463, 0, 0);
LLACoordinate tokyo = new LLACoordinate(35.657464, 139.773865, 0, 0);
LLACoordinate rome = new LLACoordinate(41.90177, 12.45987, 0, 0);
LLACoordinate paris = new LLACoordinate(48.85658, 2.348671, 0, 0);
LLACoordinate parque = new LLACoordinate(36.465985, -6.201081, 0, 0);
// Load some POIs. Each of them has the same shape at its geoposition. We pass a string
// (const char*) to IAnnotatedGeometriesGroup::addGeometry so that we can use it as POI
// title
// in the callback, in order to create an annotation image with the title on it.
mLondonGeo = createPOIGeometry(london);
mAnnotatedGeometriesGroup.addGeometry(mLondonGeo, "London");
mParisGeo = createPOIGeometry(paris);
mAnnotatedGeometriesGroup.addGeometry(mParisGeo, "Paris");
mRomeGeo = createPOIGeometry(rome);
mAnnotatedGeometriesGroup.addGeometry(mRomeGeo, "Rome");
mTokyoGeo = createPOIGeometry(tokyo);
mAnnotatedGeometriesGroup.addGeometry(mTokyoGeo, "Tokyo");
File metaioManModel =
AssetsManager.getAssetPathAsFile(getApplicationContext(),
"metaioman.md2");
if (metaioManModel != null)
{
mMunichGeo = metaioSDK.createGeometry(metaioManModel);
if (mMunichGeo != null)
{
mMunichGeo.setTranslationLLA(munich);
mMunichGeo.setLLALimitsEnabled(true);
mMunichGeo.setScale(500);
}
else
{
MetaioDebug.log(Log.ERROR, "Error loading geometry: " + metaioManModel);
}
}
// create radar
mRadar = metaioSDK.createRadar();
mRadar.setBackgroundTexture(AssetsManager.getAssetPathAsFile(getApplicationContext(),
"radar.png"));
mRadar.setObjectsDefaultTexture(AssetsManager.getAssetPathAsFile(getApplicationContext(),
"yellow.png"));
mRadar.setRelativeToScreen(IGeometry.ANCHOR_TL);
// add geometries to the radar
mRadar.add(mLondonGeo);
mRadar.add(mMunichGeo);
mRadar.add(mTokyoGeo);
mRadar.add(mParisGeo);
mRadar.add(mRomeGeo);
}
private IGeometry createPOIGeometry(LLACoordinate lla)
{
final File path =
AssetsManager.getAssetPathAsFile(getApplicationContext(),
"ExamplePOI.obj");
if (path != null)
{
IGeometry geo = metaioSDK.createGeometry(path);
geo.setTranslationLLA(lla);
geo.setLLALimitsEnabled(true);
geo.setScale(100);
return geo;
}
else
{
MetaioDebug.log(Log.ERROR, "Missing files for POI geometry");
return null;
}
}
#Override
protected void onGeometryTouched(final IGeometry geometry)
{
MetaioDebug.log("Geometry selected: " + geometry);
mSurfaceView.queueEvent(new Runnable()
{
#Override
public void run()
{
mRadar.setObjectsDefaultTexture(AssetsManager.getAssetPathAsFile(getApplicationContext(),
"yellow.png"));
mRadar.setObjectTexture(geometry, AssetsManager.getAssetPathAsFile(getApplicationContext(),
"red.png"));
mAnnotatedGeometriesGroup.setSelectedGeometry(geometry);
}
});
}
final class MyAnnotatedGeometriesGroupCallback extends AnnotatedGeometriesGroupCallback
{
Bitmap mAnnotationBackground, mEmptyStarImage, mFullStarImage;
int mAnnotationBackgroundIndex;
ImageStruct texture;
String[] textureHash = new String[1];
TextPaint mPaint;
Lock geometryLock;
Bitmap inOutCachedBitmaps[] = new Bitmap[] {mAnnotationBackground, mEmptyStarImage, mFullStarImage};
int inOutCachedAnnotationBackgroundIndex[] = new int[] {mAnnotationBackgroundIndex};
public MyAnnotatedGeometriesGroupCallback()
{
mPaint = new TextPaint();
mPaint.setFilterBitmap(true); // enable dithering
mPaint.setAntiAlias(true); // enable anti-aliasing
}
#Override
public IGeometry loadUpdatedAnnotation(IGeometry geometry, Object userData, IGeometry existingAnnotation)
{
if (userData == null)
{
return null;
}
if (existingAnnotation != null)
{
// We don't update the annotation if e.g. distance has changed
return existingAnnotation;
}
String title = (String)userData; // as passed to addGeometry
LLACoordinate location = geometry.getTranslationLLA();
float distance = (float)MetaioCloudUtils.getDistanceBetweenTwoCoordinates(location, mSensors.getLocation());
Bitmap thumbnail = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
try
{
texture =
ARELInterpreterAndroidJava.getAnnotationImageForPOI(title, title, distance, "5", thumbnail,
null,
metaioSDK.getRenderSize(), MainActivity.this,
mPaint, inOutCachedBitmaps, inOutCachedAnnotationBackgroundIndex, textureHash);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (thumbnail != null)
thumbnail.recycle();
thumbnail = null;
}
mAnnotationBackground = inOutCachedBitmaps[0];
mEmptyStarImage = inOutCachedBitmaps[1];
mFullStarImage = inOutCachedBitmaps[2];
mAnnotationBackgroundIndex = inOutCachedAnnotationBackgroundIndex[0];
IGeometry resultGeometry = null;
if (texture != null)
{
if (geometryLock != null)
{
geometryLock.lock();
}
try
{
// Use texture "hash" to ensure that SDK loads new texture if texture changed
resultGeometry = metaioSDK.createGeometryFromImage(textureHash[0], texture, true, false);
}
finally
{
if (geometryLock != null)
{
geometryLock.unlock();
}
}
}
return resultGeometry;
}
#Override
public void onFocusStateChanged(IGeometry geometry, Object userData, EGEOMETRY_FOCUS_STATE oldState,
EGEOMETRY_FOCUS_STATE newState)
{
MetaioDebug.log("onFocusStateChanged for " + (String)userData + ", " + oldState + "->" + newState);
}
}
public class LoadAssets extends AsyncTask<Integer, Integer, Boolean> {
#Override
protected Boolean doInBackground(Integer... params) {
try
{
// Extract all assets and overwrite existing files if debug build
AssetsManager.extractAllAssets(getApplicationContext(), BuildConfig.DEBUG);
}
catch (IOException e)
{
MetaioDebug.log(Log.ERROR, "Error extracting assets: " + e.getMessage());
MetaioDebug.printStackTrace(Log.ERROR, e);
return false;
}
return true;
}
}
}
Somebody knows what is wrong?
if you use skd 5.3 or later you must copy assets/junaio folder of tutorial(example_SDK) into your assets folder so your poi shown.
i've just found it i am working on that right know. my poi.obj rotates...

android OutOfMemory issue when saving data

What i am doing is drawing application and there are two function needed for this project is the undo and redo .. so i need to save a list of previous drawing after each time the user draw and pull up his finger from the screen ..
This is the code when saving previous drawing
public void saveState() {
State mUndoState = new State();
saveState(mSurface.getBitmap(), mUndoState);
}
private void saveState(Bitmap bitmap, State state) {
state.mBuffer = new byte[bitmap.getRowBytes() * bitmap.getHeight()];
Buffer byteBuffer = ByteBuffer.wrap(state.mBuffer);
bitmap.copyPixelsToBuffer(byteBuffer);
mListUndoState.add(state);
System.out.println("Size now: " + (mListUndoState.size() - 1));
mListRedoState.clear();
mListRedoState.add(state);
// StylesFactory.saveState(state.stylesState);
}
private static class State {
byte[] mBuffer = null;
// final HashMap<Integer, Object> stylesState = new HashMap<Integer,
// Object>();
}
The problem is android devices only have 16 MB of heap memory .. what is the best way to deal with this issue? .. i can only save it to ListUndoState 7 to 10 times and then i got out of memory exception .. i want to get unlimited undo action or at least not less then 50 times.
Here is the full class that save the previous drawing for undo and redo.
package com.appshouse.drawgram.utli;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Bitmap;
public class HistoryHelper {
private final Surface mSurface;
private List<State> mListUndoState = new ArrayList<State>();
private List<State> mListRedoState = new ArrayList<State>();
public HistoryHelper(Surface surface) {
mSurface = surface;
}
public void undo() {
int length = mListUndoState.size() - 1;
if (length <= 0) {
System.out.println("no element is list");
return;
}
System.out.println("history undo size: " + length);
restoreState(mSurface.getBitmap(), mListUndoState.get(length - 1));
mListRedoState.add(mListUndoState.get(length));
mListUndoState.remove(length);
}
public void redo() {
int length = mListRedoState.size() - 1;
if (length <= 0) {
System.out.println("no element is list");
return;
}
System.out.println("history undo size: " + length);
restoreState(mSurface.getBitmap(), mListRedoState.get(length));
mListUndoState.add(mListRedoState.get(length));
mListRedoState.remove(length);
}
private void restoreState(Bitmap bitmap, State state) {
Buffer byteBuffer = ByteBuffer.wrap(state.mBuffer);
bitmap.copyPixelsFromBuffer(byteBuffer);
// StylesFactory.restoreState(state.stylesState);
}
public void saveState() {
State mUndoState = new State();
saveState(mSurface.getBitmap(), mUndoState);
}
private void saveState(Bitmap bitmap, State state) {
state.mBuffer = new byte[bitmap.getRowBytes() * bitmap.getHeight()];
Buffer byteBuffer = ByteBuffer.wrap(state.mBuffer);
bitmap.copyPixelsToBuffer(byteBuffer);
mListUndoState.add(state);
System.out.println("Size now: " + (mListUndoState.size() - 1));
mListRedoState.clear();
mListRedoState.add(state);
// StylesFactory.saveState(state.stylesState);
}
private static class State {
byte[] mBuffer = null;
// final HashMap<Integer, Object> stylesState = new HashMap<Integer,
// Object>();
}
}
As hieuxit suggest i save the bitmap in file-cache to get ride of this problem but i don't know if this is the best solution .. so if anyone got better solution please provide it to me.
Here is what i change in the previous class
package com.appshouse.drawgram.utli;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.widget.Toast;
public class HistoryHelper {
private final Surface mSurface;
int undoAvailable = 0;
int redoAvailable = 0;
Context context;
public HistoryHelper(Context c, Surface surface) {
mSurface = surface;
context = c;
}
public void undo() {
if (undoAvailable > 1) {
undoAvailable--;
new LoadRestoreState().execute();
redoAvailable ++;
} else {
Toast.makeText(context, "End of Undo Data", Toast.LENGTH_LONG)
.show();
}
}
public void redo() {
if(redoAvailable >= 1)
{
undoAvailable ++;
new LoadRestoreState().execute();
redoAvailable --;
}else
{
Toast.makeText(context, "End of Redo Data", Toast.LENGTH_LONG)
.show();
}
}
public void saveState() {
new LoadSaveState().execute(mSurface.getBitmap());
}
private class LoadSaveState extends AsyncTask<Bitmap, Void, Void>
{
#Override
protected Void doInBackground(Bitmap... params) {
undoAvailable++;
FileCache fileCache = new FileCache(context, String.valueOf(undoAvailable));
String str_buffer = Common.getStringDrawing(params[0]);
fileCache.writeFile(str_buffer);
redoAvailable = 0;
return null;
}
}
private class LoadRestoreState extends AsyncTask<Void , Void, Void>
{
#Override
protected Void doInBackground(Void... params) {
FileCache getFileChach = new FileCache(context,
String.valueOf(undoAvailable));
String image_str = getFileChach.readFile();
Bitmap drawing = Common.getBitmapFromString(image_str);
byte[] buffer = new byte[drawing.getRowBytes()
* drawing.getHeight()];
Buffer byteBuffer = ByteBuffer.wrap(buffer);
drawing.copyPixelsToBuffer(byteBuffer);
byteBuffer = ByteBuffer.wrap(buffer);
mSurface.getBitmap().copyPixelsFromBuffer(byteBuffer);
return null;
}
}
}
Where the FileCache class is:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import android.content.Context;
public class FileCache {
private File cacheDir;
private File TempFile;
public FileCache(Context context, String Filename) {
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
TempFile = new File(cacheDir.getPath(), Filename);
}
public void writeFile(String buffer)
{
FileWriter writer = null;
try {
writer = new FileWriter(TempFile);
writer.write(buffer);
System.out.println(TempFile + " File - data: " + buffer.toString()
);
writer.close();
} catch (IOException e) {
}
}
public String readFile() {
String strLine = "";
StringBuilder text = new StringBuilder();
try {
FileReader fileReader = new FileReader(TempFile);
BufferedReader bufferReader = new BufferedReader(fileReader);
while ((strLine = bufferReader.readLine()) != null) {
text.append(strLine + "\n");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("get text String: "+text.toString());
return text.toString();
}
public void clear() {
File[] files = cacheDir.listFiles();
if (files == null)
return;
for (File f : files)
f.delete();
}
}
And Here is Two Function that i created in Common class:
public static String getStringDrawing(Bitmap bm) {
String image_str = "";
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] byte_arr;
bm.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte_arr = stream.toByteArray();
image_str = Base64.encodeBytes(byte_arr);
return image_str;
}
public static Bitmap getBitmapFromString(String img_str)
{
Bitmap bmImg = null;
try {
byte[] encodeByte = null;
encodeByte = Base64.decode(img_str);
bmImg = BitmapFactory.decodeByteArray(encodeByte, 0,
encodeByte.length);
bmImg = Bitmap.createBitmap(bmImg);
} catch (Exception e) {
e.printStackTrace();
}
return bmImg;
}
of-course you need to download base64 class for converting the image to string and vice versa

How to use assets as a source for osmdroid offline maps

I'm trying to use osmdroid for offline map usage in my app, but the thing which I'm not able to find is how to set the tile source from the /assets folder.
So I've got the tiles (from Mobile Atlas Creator), placed in /assets/maps.zip
and I'm trying something like this:
final ITileSource tileSource =
new XYTileSource("maps", null, 15, 17, 256, "png", "/assets");
mapview.setBuiltInZoomControls(true);
mapview.setTileSource(tileSource);
mapview.getController().setZoom(15);
mapview.setUseDataConnection(false);
.. which seems to be wrong some way. So can anybody point me on how to do that?
OK, the question has been asked a couple of years ago,
but there is still no clear info on how to use offline maps from the assets folder.
So here is my solution.
Create the maps with Mobile Atlas Creator.
I used Atlas format = osmdroid zip and maps source = OpenStreetMap 4UMaps.eu
Unzip the maps and put into your assets folder:
assets/map/14, where 14 - is for instance a folder with the corresponding zoom level (you will probably have several folders)
Add classes which I found somewhere on the internet:
import android.content.res.AssetManager;
import android.graphics.drawable.Drawable;
import org.osmdroid.ResourceProxy.string;
import org.osmdroid.tileprovider.tilesource.BitmapTileSourceBase;
import org.osmdroid.tileprovider.util.StreamUtils;
import java.io.InputStream;
/**
* Custom tile source,
* used to load map tiles from the assets
*/
public class AssetsTileSource extends BitmapTileSourceBase {
private final AssetManager mAssetManager;
public AssetsTileSource(final AssetManager assetManager, final String aName, final string aResourceId,
final int aZoomMinLevel, final int aZoomMaxLevel, final int aTileSizePixels,
final String aImageFilenameEnding) {
super(aName, aZoomMinLevel, aZoomMaxLevel, aTileSizePixels, aImageFilenameEnding);
mAssetManager = assetManager;
}
#Override
public Drawable getDrawable(final String aFilePath) {
InputStream inputStream = null;
try {
inputStream = mAssetManager.open(aFilePath);
if (inputStream != null) {
final Drawable drawable = getDrawable(inputStream);
return drawable;
}
} catch (final Throwable e) {
// Tile does not exist in assets folder.
// Ignore silently
} finally {
if (inputStream != null) {
StreamUtils.closeStream(inputStream);
}
}
return null;
}
}
and
/**
* Map tile provider, loads tile from assets folder
*/
public class MapTileFileAssetsProvider extends MapTileModuleProviderBase {
protected ITileSource mTileSource;
public MapTileFileAssetsProvider(final ITileSource pTileSource) {
super(OpenStreetMapTileProviderConstants.NUMBER_OF_TILE_FILESYSTEM_THREADS, OpenStreetMapTileProviderConstants.TILE_FILESYSTEM_MAXIMUM_QUEUE_SIZE);
mTileSource = pTileSource;
}
#Override
public boolean getUsesDataConnection() {
return false;
}
#Override
protected String getName() {
return "Assets Folder Provider";
}
#Override
protected String getThreadGroupName() {
return "assetsfolder";
}
#Override
protected Runnable getTileLoader() {
return new TileLoader();
}
#Override
public int getMinimumZoomLevel() {
return mTileSource != null ? mTileSource.getMinimumZoomLevel() : Constants.MAP_ZOOM_ZOOM_MAX;
}
#Override
public int getMaximumZoomLevel() {
return mTileSource != null ? mTileSource.getMaximumZoomLevel() : Constants.MAP_ZOOM_ZOOM_MIN;
}
#Override
public void setTileSource(final ITileSource pTileSource) {
mTileSource = pTileSource;
}
private class TileLoader extends MapTileModuleProviderBase.TileLoader {
#Override
public Drawable loadTile(final MapTileRequestState pState) throws CantContinueException {
if (mTileSource == null) {
return null;
}
final MapTile pTile = pState.getMapTile();
String path = mTileSource.getTileRelativeFilenameString(pTile);
Drawable drawable;
try {
drawable = mTileSource.getDrawable(path);
} catch (final LowMemoryException e) {
// low memory so empty the queue
throw new CantContinueException(e);
}
return drawable;
}
}
}
Add the constants:
// map min zoom level
public static final int MAP_ZOOM_ZOOM_MIN = 12;
// map max zoom level
public static final int MAP_ZOOM_ZOOM_MAX = 14;
// maps folder name in assets
public static final String MAP_ASSETS_FOLDER_NAME = "map";
As you probably guessed, they represent minimum and maximum zoom level, which we previously created with the Mobile Atlas Creator.
The last, but not the least,
the snippet of implementation code:
// making the map not use internet
mBinding.mapView.setUseDataConnection(false);
// Initializing the tile provider to use offline maps from the assets
// This will load for instance from /map/14/12345/12345.png
AssetsTileSource tileSource = new AssetsTileSource(
getAssets(),
Constants.MAP_ASSETS_FOLDER_NAME,
ResourceProxy.string.offline_mode,
Constants.MAP_ZOOM_ZOOM_MIN,
Constants.MAP_ZOOM_ZOOM_MAX,
256, ".png");
MapTileModuleProviderBase moduleProvider = new
MapTileFileAssetsProvider(tileSource);
SimpleRegisterReceiver simpleReceiver = new
SimpleRegisterReceiver(this);
MapTileProviderArray tileProviderArray = new
MapTileProviderArray(tileSource, simpleReceiver, new
MapTileModuleProviderBase[] { moduleProvider });
mBinding.mapView.setTileProvider(tileProviderArray);
// not forget to invalidate the map on zoom
mBinding.mapView.setMapListener(new MapListener() {
#Override
public boolean onScroll(ScrollEvent scrollEvent) {
return false;
}
#Override
public boolean onZoom(ZoomEvent zoomEvent) {
mBinding.mapView.invalidate();
return false;
}
});
asset path is "file:///android_asset/" if you need a inputstream you get it via AssetManager.
Take a look at BitmapAssetTileSource. Also the iBurn-2012 might be useful to see it in action.

Change an ImageView inside a ListView

I saw a code from Android Hive, and I learned how to send array JSON from the PHP script to my Android / Java code. I successfully retrieved all the details from my online database and displayed them in my desired format.
The problem is, I don't know hot to set an image's src when it is inside a ListView.
Here's my code.
ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
JSONParser jParser = new JSONParser();
JSONObject json = jParser.getJSONFromUrl("http://domain.com/directory/database/retrieveComments.php?placeId=" + stringPlaceId);
try
{
commentsRatingsArray = json.getJSONArray("commentsRatings");
for(int i = 0; i < commentsRatingsArray.length(); i++)
{
JSONObject jsonObject = commentsRatingsArray.getJSONObject(i);
String dbUserFullName = jsonObject.getString(TAG_FULLNAME);
String dbUserEmail = jsonObject.getString(TAG_EMAIL);
String dbComment = jsonObject.getString(TAG_COMMENT);
String dbRating = jsonObject.getString(TAG_RATING);
String dbDate = jsonObject.getString(TAG_DATE);
String dbTime = jsonObject.getString(TAG_TIME);
HashMap<String, String> map = new HashMap<String, String>();
map.put(TAG_FULLNAME, dbUserFullName);
map.put(TAG_EMAIL, dbUserEmail);
map.put(TAG_COMMENT, dbComment);
map.put(TAG_RATING, dbRating);
map.put(TAG_DATE, dbDate);
map.put(TAG_TIME, dbTime);
list.add(map);
}
}
catch (Exception e)
{
e.printStackTrace();
Toast.makeText(getBaseContext(), "Connection to the server is lost. Please check your internet connection.", Toast.LENGTH_SHORT).show();
}
ListAdapter adapter = new SimpleAdapter
(DisplayCommentsRatings.this, list, R.layout.commentrating,
new String[] { TAG_FULLNAME, TAG_EMAIL, TAG_COMMENT, TAG_DATE, TAG_TIME },
new int[] {R.id.tvUserFullName, R.id.tvUserEmail, R.id.tvUserComment, R.id.tvDate, R.id.tvTime });
setListAdapter(adapter);
Please help me, thanks.
For that i would suggest you to define a custom adapter for your ListView.
You can create custom adapter class by extending either BaseAdapter or ArrayAdapter.
override getView() method.
Follow ViewHolder pattern while overiding getView() method.
Here, Ravi has written about: Android custom ListView with Images and Text.
And the best solution so far: Andoid - Lazy Load of Images in ListView
I think you will have to make your own custom adapter (extending BaseAdapter) and update the image inside the getView method. There is a lot of tuts on Google.
Good luck =)
You can set your image with an URL pointing to the SD name of the file for example.
http://developer.android.com/reference/android/widget/SimpleAdapter.html#setViewImage(android.widget.ImageView, int)
But i think that is a lot easier to extend from BaseAdapter and pass your own Map or Array to it and then you can inflate with any image that you want, download it and set it, etc.
This is an example of one Adapter for devices :) you dont need to start with viewHolder pattern.
public class DevicesAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Device> devices;
public DevicesAdapter(Context context, List<Device> devices) {
inflater = LayoutInflater.from(context);
this.devices = devices;
}
#Override
public int getCount() {
return devices.size();
}
#Override
public Object getItem(int position) {
return devices.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
row = inflater.inflate(R.layout.account_devices_row, null);
}
TextView description = (TextView) row.findViewById(R.id.device_text);
description.setText(devices.get(position).getLabel());
return row;
}
}
Regards
They are correct above. You will want to extend a BaseAdapter and overwrite the getView method. You will also want to lazy load the image since you are will be downloading them and shouldn't tie up the UI thread while this action is being performed. Below is my Lazy Load class. Simple create a new class (I call mine LazyLoadImage.java) and stick this code in it. Below are the different ways you can use the class:
To lazy load an image with a placeHolder:
new LazyLoadImage(ImageView imageView, String urlString, Bitmap placeHolder);
To lazy load an image without a placeHolder:
new LazyLoadImage(ImageView imageView, String urlString);
To manually clear the cache:
new LazyLoadImage().clearCache();
If you are targeting OS's below 12 then you will need to include "android-support-v4.jar" in the project.
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;
public class LazyLoadImage extends AsyncTask<String, Void, Bitmap> {
ImageView mDestination;
//Set up cache size and cache
private static int mCacheSize = 4 * 1024 * 1024; // 4 mb
private static LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(mCacheSize) {
#Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
public LazyLoadImage(ImageView destination, String urlString) {
mDestination = destination;
if (mCache.get(urlString) != null) {
mDestination.setImageBitmap(mCache.get(urlString));
}else {
this.execute(urlString);
}
}
public LazyLoadImage(ImageView destination, String urlString, Bitmap placeHolder) {
mDestination = destination;
if (mCache.get(urlString) != null) {
mDestination.setImageBitmap(mCache.get(urlString));
}else {
setPlaceHolder(urlString, placeHolder);
this.execute(urlString);
}
}
public LazyLoadImage() {
}
private void setPlaceHolder(String urlString, Bitmap placeholder) {
mDestination.setImageBitmap(placeholder);
}
public void clearCache() {
mCache.evictAll();
}
#Override
protected Bitmap doInBackground(String... arg0) {
//If the URI that is passed in arg0[0] is already in mCache then I return it without downloading it again
if (mCache.get(arg0[0]) != null) {
return mCache.get(arg0[0]);
}else {
Bitmap lazyImage = null;
URL myFileUrl = null;
try {
myFileUrl= new URL(arg0[0]);
HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
lazyImage = BitmapFactory.decodeStream(is);
//Store the image in mCache for quick assess from anywhere in app
synchronized (mCache) {
if (mCache.get(arg0[0]) == null) {
mCache.put(arg0[0], lazyImage);
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return lazyImage;
}
}
#Override
protected void onCancelled(Bitmap result) {
}
#Override
protected void onPostExecute(Bitmap result) {
/*
* The returned image to the ImageView that was passed in on create
* (either from mCache or when downloaded the first time)
*/
mDestination.setImageBitmap(result);
super.onPostExecute(result);
}
}

Categories

Resources