I have the following problem:
suppose that until now, I am using R.drawable.img to set the image in some imageviews
with
imgView.setImage(aProduct.getImgId());
the simplified product class look like this:
class Product{
int imgId;
....
public void setImgId(int id){
this.imgId=id;
}
public int getImgId(){
return this.imgId
}
...
}
my application is now "evolved" because the user can add customized products
taking the img from the camera and getting the Uri of the picture.
and to set the image on the ImageView imgView.setImgURI(Uri)
Now my question is:
what would be the best approach to have a mixed int/Uri image resources ambient?
can I obtain the Uri of a "R.drawable.img"?
I'm not sure if my question is clear, I mean:
I have to check, before to set the imageview, if my product has an Uri or an int Id,
and then make an "if" to call the appropriate method, or there is a simpler solution?
Thank you for reading, and sorry for my english.
Regards.
Your problem is that there are basically 3 types of image resources:
R.id... resources: internal resources, such as icons you put into the res folder
content URI's: local files or content provider resources such as content:// or file:///sdcard/...
remote file URL's: images on the web, such as http://...
You are looking for a way to pass around one identifier that can deal with all three. My solution was to pass around a string: either the toString() of the URI's, or just the string respresentation of the R.id integer.
I'm using the following code, for example, to deal with this and make the code work with it:
public static FastBitmapDrawable returnAndCacheCover(String cover, ImageRepresentedProductArtworkCreator artworkCreator) {
Bitmap bitmap = null;
Uri coverUri = null;
boolean mightBeUri = false;
//Might be a resId. Needs to be cached. //TODO: problem: resId of default cover may not survive across versions of the app.
try {
bitmap = BitmapFactory.decodeResource(Collectionista.getInstance().getResources(), Integer.parseInt(cover));
} catch (NumberFormatException e) {
//Not a resId after all.
mightBeUri=true;
}
if(bitmap==null || mightBeUri){
//Is not a resId. Might be a contentUri.
try {
coverUri = Uri.parse(cover);
} catch (NullPointerException ne) {
//Is null
return null;
}
}
if(coverUri!=null){
if(coverUri.getScheme().equals("content")){
//A contentUri. Needs to be cached.
try {
bitmap = MediaStore.Images.Media.getBitmap(Collectionista.getInstance().getContentResolver(), coverUri);
} catch (FileNotFoundException e) {
return null;
} catch (IOException e) {
return null;
}
}else{
//Might be a web uri. Needs to be cached.
bitmap = loadCoverFromWeb(cover);
}
}
return new FastBitmapDrawable(bitmap);
}
You might be interested to take over the logic part. Ofcourse cover is the string in question here.
Forget android.resource:// as a replacement for the R.id... integer. Claims are going round it does not work: http://groups.google.com/group/android-developers/browse_thread/thread/a672d71dd7df4b2d
To find out how to implement loadCoverFromWeb, have a look around in other questions or ping me. This web stuff is kind of a field of it's own.
(Based on GPLv3 code out of my app Collectionista: https://code.launchpad.net/~pjv/collectionista/trunk)
Related
Background
I've noticed a weird column for MediaStore.Images.ImageColumns called "MINI_THUMB_MAGIC" .
the documentation says just that :
The mini thumb id.
Type: INTEGER
Constant Value: "mini_thumb_magic"
The question
my guess is that this field is related to MediaStore.Images.Thumbnails .
Is it correct ? if not, what is this and how do you use it?
if it is correct , i have other questions related to it:
Is it a mini sized image of the original one? does it use the same aspect ratio or does it do center-cropping on it?
how come the size of "MICRO" is square (96 x 96) and the size of "MINI" is a non-square rectangle ( 512 x 384 ) ?
How do you use it? My guess is that it's done by using "THUMB_DATA", which is a blob, so you use it like this, but then what is the purpose of using "getThumbnail" if you already have this field?
does it get a rotated thumbnail in case the orientation value is not 0 ? meaning that if I wish to show it, I won't need to rotate the image?
Is it possible to do a query of the images together with their thumbnails? maybe using inner join?
Is it available for all Android devices and versions?
Why is it even called "magic" ? Is it because it's also available for videos (and for some reason doesn't exist for music, as it could be the album's cover photo, for example) ?
Check this file: https://github.com/android/platform_packages_providers_mediaprovider/blob/master/src/com/android/providers/media/MediaThumbRequest.java in the Android source code. This value is some magic number which allows to determine if the thumbnail is still valid. I didn't investigate that file further, but it should be no bit issue to dive deeper.
To your questions:
No, no mini-sized image
Well, I guess it's a definition by Google who want to have a square thumbnail for some lists, where only very small previews should be visible and where many items should fit on the screen and there's another thumbnail format where the images are bigger...
I don't know that, but according to Google's doc, one (THUMB_DATA) is only some raw byte array of the thumbnail (dunno in which format) and the other one (getThumbnail) retrieves a full-fledged bitmap object...
don't know
don't know
I guess so, as it's part of AOSP source code.
The word "magic" is often used for some kind of identifier. There are "magic packets" who can wake up a computer from sleep or shutdown over the network, there are magic numbers on hard disks, where some sectors (e.g. the MBR) has the hexadecimal values AA 55 on its last two byte positions, there are also magic numbers in image files which help software packages determine the image type (e.g. GIF files begin with GIF89a or GIF87a (ASCII), JPEG files begin with FF D8 hexadecimal) and there are many, many more examples. So, magic numbers are a very common term here :-)
According to the source code at the following URL, the Magic Number is the Id of the original image * a constant. That value is then used to check for a long int. If the int isn't as expected, it's considered out of sync with the image media.
http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/4.4_r1-robolectric-0/android/media/MiniThumbFile.java#MiniThumbFile.getMagic%28long%29
// Get the magic number for the specified id in the mini-thumb file.
// Returns 0 if the magic is not available.
public synchronized long getMagic(long id) {
// check the mini thumb file for the right data. Right is
// defined as having the right magic number at the offset
// reserved for this "id".
RandomAccessFile r = miniThumbDataFile();
if (r != null) {
long pos = id * BYTES_PER_MINTHUMB;
FileLock lock = null;
try {
mBuffer.clear();
mBuffer.limit(1 + 8);
lock = mChannel.lock(pos, 1 + 8, true);
// check that we can read the following 9 bytes
// (1 for the "status" and 8 for the long)
if (mChannel.read(mBuffer, pos) == 9) {
mBuffer.position(0);
if (mBuffer.get() == 1) {
return mBuffer.getLong();
}
}
} catch (IOException ex) {
Log.v(TAG, "Got exception checking file magic: ", ex);
} catch (RuntimeException ex) {
// Other NIO related exception like disk full, read only channel..etc
Log.e(TAG, "Got exception when reading magic, id = " + id +
", disk full or mount read-only? " + ex.getClass());
} finally {
try {
if (lock != null) lock.release();
}
catch (IOException ex) {
// ignore it.
}
}
}
return 0;
}
I got the runtime exception when trying to get the original Id of a thumbnail by looking up the thumbnail's path. (BTW, the disk isn't full and it's not read-only.)
It's a bit strange parameter...
While exploring the Gallery source code,
noticed that the value is being read from the cursor, but then is Never used:
#Override
protected BaseImage loadImageFromCursor(Cursor cursor) {
long id = cursor.getLong(INDEX_ID);
String dataPath = cursor.getString(INDEX_DATA_PATH);
long dateTaken = cursor.getLong(INDEX_DATE_TAKEN);
if (dateTaken == 0) {
dateTaken = cursor.getLong(INDEX_DATE_MODIFIED) * 1000;
}
// here they read it ====>>
long miniThumbMagic = cursor.getLong(INDEX_MINI_THUMB_MAGIC);
int orientation = cursor.getInt(INDEX_ORIENTATION);
String title = cursor.getString(INDEX_TITLE);
String mimeType = cursor.getString(INDEX_MIME_TYPE);
if (title == null || title.length() == 0) {
title = dataPath;
}
// and not use at all ==>>>
return new Image(this, mContentResolver, id, cursor.getPosition(),
contentUri(id), dataPath, mimeType, dateTaken, title,
orientation);
}
Maybe it was used on the previous APIs.
ref: https://android.googlesource.com/platform/packages/apps/Gallery/+/android-8.0.0_r12/src/com/android/camera/gallery/ImageList.java?autodive=0%2F%2F.
and videos list:
https://android.googlesource.com/platform/packages/apps/Gallery/+/android-8.0.0_r12/src/com/android/camera/gallery/VideoList.java?autodive=0%2F%2F
The problem
Hi there,
I'm developing an application where the user specifies some pictures and how long they are going to be on the screen.So sometimes he wants to create something like a small animation or viewing the images for a small amount of time.The problem is that after some time the images are not previewed when they should and we have a few ms of error.In the application that i'm developing time matters so I would like some help on what the problem might be.
The code
So let me explain how it works.I take the pictures from my web app and then I save them in a HashMap
Bitmap image = ImageOperations(url,String.valueOf(frameNum) + ".jpg");
ImageMap.put(String.valueOf(frameNum), image);
where the mathod ImageOperations is like that:
private Bitmap ImageOperations(String url, String saveFilename) {
try {
Display display = getWindowManager().getDefaultDisplay();
InputStream is = (InputStream) this.fetch(url);
Bitmap theImage = BitmapFactory.decodeStream(is);
if (theImage.getHeight() >= 700 || theImage.getWidth() >= 700) {
theImage = Bitmap.createScaledBitmap(theImage,
display.getWidth(), display.getHeight() - 140, true);
}
return theImage;
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
So later I run a thread that updates the UI when the user specified.The method that updates it is this one.
public void setPictures(int NumOfFrame) {
if (frameArray.get(NumOfFrame - 1).frame_pic.contains("n/a") != true) {
ImagePlace.setImageBitmap(ImageMap.get(String.valueOf(NumOfFrame)));
} else {
ImagePlace.setImageDrawable(null);
}
}
After we update the image we put the thread for sleep and when runs again it updates the thread.Is there something that creates the problem?Does it have to do with Garbage collection?
Thank you in advance
Probably the issue is in increasing heap size when it loads additional images. I would suggest You to do some profiling so things will be much clearer and You'll get full picture of timings for the app.
First you are missing a null check at here:
ImageMap.get(String.valueOf(NumOfFrame))
And you do not recycle the old bitmap at here:
theImage.recycle(); // missing line
theImage = Bitmap.createScaledBitmap(theImage,
display.getWidth(), display.getHeight() - 140, true);
It may lead to outofmemory exceptions, with is most likely from your description of the problem.
Also I am not sure if BitmapFactory.decodeStream throw exception when he fails. You need to add a null point check there too.
Iv'e been using the ImageGetter() interface in the Html.fromHtml() to retrieve image urls from a long html String. But since I only really need one image I don't want to go through the entire String and look for images. I would simply like to stop when I find the first image.
Any suggestions?
Html.fromHtml(html, new ImageGetter() {
#Override
public Drawable getDrawable(String source) {
item.setImageUrl(source);
return null;
}
}, null);
EDIT : for now I only retrieve the last image in the html String so the ImageGetter will only retrieve that image.
private String getLastImage(String htmlContent){
String img = "";
try{
img = htmlContent.substring(htmlContent.lastIndexOf("<img"));
}catch (Exception e) {
e.printStackTrace();
}
return img;
}
Have a boolean field in your ImageGetter. In getDrawable(), fetch the image only if the boolean is false and then set it to true; return null otherwise.
To completely avoid parsing the rest of the HTML, you can not use Html.fromHtml(). Instead you may try using XMLPullParser. Just keep pulling tags until you get an <img> tag, at which point get the value of its <src> attribute.
But you may have to handle inputs which are not strict XHTML properly.
Abstract:
reading images from file
with toggled bits to make unusable for preview tools
cant use encryption, to much power needed
can I either optimize the code below, or is there a better approach
Longer description:
I am trying to improve my code, maybe you got some ideas or improvements for the following situation. Please be aware that I neither try to beat the CIA, nor care much if somebody "brakes" the encryption.
The background is simple: My app loads a bunch of images from a server into a folder on the SD card. I do NOT want the images to be simple JPG files, because in this case the media indexer would list them in the library, and a user could simply copy the whole folder to his harddrive.
The obvious way to go is encryption. But a full blown AES or other encryption does not make sense, for two reasons: I would have to store the passkey in the app, so anyone could get the key with some effort anyway. And the price for decrypting images on the fly is way too high (we are talking about e.g. a gallery with 30 200kB pictures).
So I decided to toggle some bits in the image. This makes the format unreadable for image tools (or previews), but is pretty easy undone when reading the images. For "encrypting" I use some C# tool, the "decrypt" lines are the following ones:
public class CustomInputStream extends InputStream {
private String _fileName;
private BufferedInputStream _stream;
public CustomInputStream(String fileName) {
_fileName = fileName;
}
public void Open() throws IOException {
int len = (int) new File(_fileName).length();
_stream = new BufferedInputStream(new FileInputStream(_fileName), len);
}
#Override
public int read() throws IOException {
int value = _stream.read() ^ (1 << 7);
return value;
}
#Override
public void close() throws IOException {
_stream.close();
}
}
I tried overwriting the other methods (read with more then one byte) too, but this kills the BitmapFactory - not sure why, maybe I did something wrong. Here is the code for the image bitmap creation:
Bitmap bitmap = null;
try {
InputStream i = CryptoProvider.GetInstance().GetDecoderStream(path);
bitmap = BitmapFactory.decodeStream(i);
i.close();
} catch (Exception e1) {
_logger.Error("Cant load image " + path + " ERROR " + e1);
}
if (bitmap == null) {
_logger.Error("Image is NULL for path " + path);
}
return bitmap;
Do you have any feedback on the chosen approach? Any way to optimize it, or a completely different approach for Android devices?
You could try XORing the bytestream with the output of a fast PRNG. Just use a different seed for each file and you're done.
note: As already noted in the question, such methods are trivial to bypass.
Does anyone know of a way to tell which folder Android system will resolve to when I request a particular drawable from the Resources (drawable-long vs. drawable-notlong, for example)? I want to make sure it's loading resources the way I would expect it to (for instance, to make sure the Tattoo is really ldpi and not mdpi since it's right on the borderline).
I could always print out the intrinsic size of the drawable after I fetch it from Resources, and check that against the various image sizes I have, but that's kind of a pain. Plus, my company allows its clients to skin their app before it's released, so after it's been skinned I won't be able to rely on that method.
I tried using Resources.getName(resId) but that doesn't give the folder name, and as far as I know you can get a AssetFileDescriptor for the resource but you can't get an actual filename.
UPDATE:
Since asking this question I have discovered that you can access many of the resource qualifier names through the Configuration and DisplayMetrics classes (both accessible via getResources()). DisplayMetrics can give you the ldpi/mdpi/hdpi designation, and Configuration can give you small/normal/large screen size, long/notlong aspect ratio, locale, mobile network codes, and other hardware information. However, this still does not positively confirm that you are getting the resources you expect if you have a complicated resource set. It also does not protect you from bugs that report the incorrect values, like the Motorola Droid bug where the DisplayMetrics reports the wrong xdpi and ydpi (it reports 96dpi where the real value is more like 265dpi! Terrible bug.).
A different approach would be to use two different images, with the same name, red in one resource folder blue in the other. When it inflates, you'll be able to see which one it is coming from.
If you really want to go crazy, make an image saying "ldpi" or "xhdpi" etc for each folder and when you start your project, you can note which ones came up for each device you wanted explicit testing for.
From Mark Allison, same idea but the values directory with strings that contains the name of the bucket it's in - it's less effort than creating images!
I know, you write, that you can go through all resources and compare sizes with the bitmap size. But I don't know, if you thought it same way as I did, so this may help you or someone else.
So this is the way I did it. At first I load sizes (in bytes) for all drawable images.
String defType = "drawable";
String drawableDirPrefix = "res/drawable";
SparseArray<SparseArray<String>> resourceDirs = new SparseArray<SparseArray<String>>();
ZipInputStream apk = null;
try {
apk = new ZipInputStream(new FileInputStream(getPackageResourcePath()));
ZipEntry entry = null;
while ((entry = apk.getNextEntry()) != null) {
String resourcePath = entry.getName();
if (resourcePath.startsWith(drawableDirPrefix)) {
int firstSlashPos = resourcePath.indexOf('/');
int lastSlashPos = resourcePath.lastIndexOf('/');
int dotPos = resourcePath.lastIndexOf('.');
String resourceDir = resourcePath.substring(firstSlashPos + 1, lastSlashPos);
String resourceName = resourcePath.substring(lastSlashPos + 1, dotPos);
int resourceId = getResources().getIdentifier(resourceName, defType, getPackageName());
int resourceSize = (int) entry.getSize();
SparseArray<String> resourceInfo = resourceDirs.get(resourceId);
if (resourceInfo == null) {
resourceInfo = new SparseArray<String>();
resourceInfo.append(resourceSize, resourceDir);
resourceDirs.append(resourceId, resourceInfo);
} else {
resourceInfo.append(resourceSize, resourceDir);
}
}
}
} catch (IOException e) {
Log.e("tag", "Error", e);
} finally {
if (apk != null) {
try {
apk.close();
} catch (IOException e) {
Log.e("tag", "Error", e);
}
}
}
Then, when I want to know the folder, I can compare the bitmap size to loaded sizes.
InputStream bitmapStream = null;
try {
int bitmapId = R.drawable.icon;
bitmapStream = getResources().openRawResource(bitmapId);
int bitmapSize = bitmapStream.available();
String bitmapDir = resourceDirs.get(bitmapId).get(bitmapSize);
Log.i("tag", bitmapDir);
} catch (Exception e) {
Log.e("tag", "Error", e);
} finally {
if (bitmapStream != null) {
try {
bitmapStream.close();
} catch (IOException e) {
Log.e("tag", "Error", e);
}
}
}
Well, this will work only if the images have different sizes. Or you can compare other things, like width, height, etc.
May be it is not, what you are looking for, but at least it satisfied my needs.