DiskLruCache not getting bitmap from key - android

I am using JakeWhartons DiskLruCache and some code from a post in SO. I am able to add the bitmaps with the keys but unable to retrieve it in getBitmap(), instead it returns null. Am I creating a new cache every time? Not sure on this. Here is my code:
AsyncTask:
public BitmapWorkerAsyncTask(ImageView imageView,Context context1) {
imageViewReference = new WeakReference<ImageView>(imageView);
diskLruCache = new DiskLruImageCache(context1, "thumbnails",
(int) (20 * Math.pow(2, 20)), CompressFormat.JPEG, 70);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Object... params) {
// Get the passed arguments here
view = (View) params[0];
songId = (Long) params[1];
albumId = (Long) params[2];
context = (Context) params[3];
data = (Long) params[2];
// Check disk cache
Bitmap bitmap = diskLruCache.getBitmap(String.valueOf(albumId)); // returns null
}
DiskLruImageCache:
public DiskLruImageCache(Context context, String uniqueName,
int diskCacheSize, CompressFormat compressFormat, int quality) {
try {
final File diskCacheDir = getDiskCacheDir(context, uniqueName);
// Log.d(TAG, "file cache dir="+diskCacheDir.getAbsolutePath());
mDiskCache = DiskLruCache.open(diskCacheDir, APP_VERSION,
VALUE_COUNT, diskCacheSize);
mCompressFormat = compressFormat;
mCompressQuality = quality;
} catch (IOException e) {
e.printStackTrace();
}
}
public void put(String key, Bitmap data) {
DiskLruCache.Editor editor = null;
try {
editor = mDiskCache.edit(key);
if (editor == null) {
return;
}
if (writeBitmapToFile(data, editor)) {
mDiskCache.flush();
editor.commit();
if (BuildConfig.DEBUG) {
Log.d("cache_test_DISK_", "image put on disk cache " + key
+ "size aft adding=" + mDiskCache.get(key));
}
}
}
public Bitmap getBitmap(String key) {
Bitmap bitmap = null;
DiskLruCache.Snapshot snapshot = null;
try {
Log.d("cache_test_DISK_", "bitmap got=" + mDiskCache.get(key)
+ " key=" + key);
snapshot = mDiskCache.get(key); // null here
if (snapshot == null) {
return null;
}
}

Related

How to store and Retrieve images from the local device in android programmatically ?

My task is to take pictures and uploading it in cloud storage, In upload screen I have two buttons one is "Upload" and other is "Park",
i) if I select "Upload" it should upload the images on cloud.
ii) if I select "Park" it should save the entire data into a activity with a button Load1.
What is Park ?
park should have the entire user data's stored previously
On clicking this load button I need to show the previously stored images in the thumbnail, eg. If I am taking 10 images I am showing those 10 images in the thumbnail as shown in below screenshot, while i m selecting the load it is showing 10th image for the three default images it is not showing all the 10 images which i have taken. How can I make it work properly?
This is my CameraActivity code where I am saving the images:
private void saveAndShowImage(byte[] data) {
bmp1 = BitmapFactory.decodeByteArray(data, 0, data.length);
//rotate the image by 90degree.
bmp1 = RotateBitmap(bmp1, 90);
bmp1 = Singleton.getResizedBitmap(bmp1);
imageName = "img" + currentDateTimeString + ".png";
saveImage(this, imageName, bmp1);
LogEvent.Log(TAG,"bmp1"+bmp1);
boolean isObjectReplaced = false;
int size = 3;
if(bitmapArray.size() < 3) {
size = bitmapArray.size();
}
for (int i = 0; i < size; i++) {
if (bitmapArray.get(i).imageName == null) {
bitmapArray.set(i, new ImageNote("", currentDateTimeString, imageName));
isObjectReplaced = true;
break;
}
}
if (!isObjectReplaced) {
bitmapArray.add(new ImageNote("", currentDateTimeString, imageName));
}
Log.d(currentDateTimeString, "ss");
Log.i("imagesize", "ImageWidth = " + bmp1.getWidth() + "ImageHeight = " + bmp1.getHeight());
CameraActivity.this.runOnUiThread(new Runnable() {
public void run() {
if (imageCount > 2) {
gv.scrollTo(convertDpToPixel(590 * bitmapArray.size()) + 10);
}
adapter.notifyDataSetChanged();
}
});
}
//it is saving the image and show in the gridview .
private class ImageSaveAndShow extends AsyncTask<String, String, Boolean> {
private byte[] data;
public ImageSaveAndShow(byte[] data, Camera camera) {
this.data = data;
}
#Override
protected Boolean doInBackground(String... strings) {
saveAndShowImage(data);
return true;
}
}
public static void saveImage(Context context, String filename,
Bitmap bitmap) {
File cacheDir = context.getExternalFilesDir(null);
File backgroundImagesDir = new File(cacheDir.getAbsolutePath() + "/"
+ "LoadProof");
if (!backgroundImagesDir.isDirectory()) {
backgroundImagesDir.mkdir();
Log.i(TAG, "LoadProof Directory Created");
}
OutputStream out = null;
File file = null;
try {
file = new File(backgroundImagesDir.getAbsolutePath() + "/" + filename);
Log.i(TAG, "saved to " + file.toString());
// Storing the path to show the previously stored image
if(loadClickCount==1) {
SharedPreferences prefernces = context.getSharedPreferences("parkload", Context.MODE_APPEND);
SharedPreferences.Editor editor = prefernces.edit();
editor.putString("Path", String.valueOf(file));
editor.commit();
String old = prefernces.getString("Path",null);
LogEvent.Log(TAG,"llll" + " " + old + " " + path);
}
out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
file = null;
Log.i(TAG, "File Written");
bitmap = null;
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
AdaperCode
public class Adapter extends ArrayAdapter<ImageNote> {
private final ArrayList<ImageNote> objects;
private Context context;
String TAG = "Adapter";
public Adapter(Context context, ArrayList<ImageNote> objects) {
super(context, 0, objects);
this.objects = objects;
this.context = context;
}
#Override public View getView(final int pos, View convertView, ViewGroup parent) {
ViewHolder holder = null;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.cell_image, null);
holder = new ViewHolder();
holder.ivCampic = (ImageView) convertView.findViewById(R.id.ivCampic);
holder.ivDeletion = (ImageView) convertView.findViewById(R.id.ivDeletion);
holder.tvPos = (TextView) convertView.findViewById(R.id.txt_number_camera);
convertView.setTag(holder);
} else {
convertView = inflater.inflate(R.layout.cell_image, null);
holder = new ViewHolder();
holder.ivCampic = (ImageView) convertView.findViewById(R.id.ivCampic);
holder.ivDeletion = (ImageView) convertView.findViewById(R.id.ivDeletion);
holder.tvPos = (TextView) convertView.findViewById(R.id.txt_number_camera);
}
if (objects.get(pos).imageName == null && pos < 3 && park1==false) {
holder.ivCampic.setImageResource(R.drawable.placeholder);
holder.ivDeletion.setVisibility(View.INVISIBLE);
holder.tvPos.setVisibility(View.INVISIBLE);
} else if (park1 == true) {
// Assigning previously stored path
SharedPreferences prefernces = context.getSharedPreferences("parkload", Context.MODE_APPEND);
String load1Path = prefernces.getString("Path", null);
LogEvent.Log(TAG,"lllll"+ " " + load1Path);
String path = load1Path;
Bitmap bmp = BitmapFactory.decodeFile(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (bmp != null) {
bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
}
holder.ivCampic.setImageBitmap(bmp);
holder.ivDeletion.setVisibility(View.VISIBLE);
holder.tvPos.setVisibility(View.VISIBLE);
} else {
// Assigning path of the pictures from camera directly
String path = context.getExternalFilesDir(null).getAbsolutePath() + "/LoadProof/"
+ objects.get(pos).imageName;
Bitmap bmp = BitmapFactory.decodeFile(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (bmp != null) {
bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
}
holder.ivCampic.setImageBitmap(bmp);
holder.ivDeletion.setVisibility(View.VISIBLE);
holder.tvPos.setVisibility(View.VISIBLE);
}
LogEvent.Log(TAG,"text1 /"+String.valueOf(pos + 1)+"pos "+pos);
holder.tvPos.setText(String.valueOf(pos+1));
final ViewHolder finalHolder = holder;
holder.ivDeletion.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
objects.remove(pos);
CameraActivity.imageCount--;
if (bitmapArray.size() < 3) {
bitmapArray.add(new ImageNote("", null, null));
finalHolder.ivCampic.setImageResource(R.drawable.placeholder);
finalHolder.ivDeletion.setVisibility(View.INVISIBLE);
if (pos == bitmapArray.size() - 1) {
//it is the last image which has been deleted when size()<=3.
finalHolder.ivCampic.setImageResource(R.drawable.placeholder);
finalHolder.ivDeletion.setVisibility(View.INVISIBLE);
finalHolder.tvPos.setVisibility(View.INVISIBLE);
} else {
//it is not the last image which has been deleted when size()<=3.
finalHolder.ivCampic.setImageResource(R.drawable.placeholder);
finalHolder.ivDeletion.setVisibility(View.INVISIBLE);
}
} else {
// when bitmapArray.size()>3
view.setVisibility(View.GONE);
}
notifyDataSetChanged();
}
});
return convertView;
}
private class ViewHolder {
public ImageView ivCampic;
public ImageView ivDeletion;
public TextView tvPos;
}
}
Since you are overwriting the path in the shared preference, only the latest path can be retrieved. It will be better if you could store it as an array in a file. so you can retrieve the path using the array index and no overwriting will happen.

List view showing images jumbled after downloading images using asynctask

I have used the async task to download the data and then showing those images in the list view I used the cache to store the images as they are repeating but not in an order. But the images gets jumbled up and sometimes they don't download. I tried searching this, but couldn't find that.
This was one of mine dream company question and i didn't clear because of this.
Please help me around.
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String BASE_URL = "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=android&start=";
private static final String IMAGE_JSON_KEY = "unescapedUrl";
private static final String RESULTS_JSON_KEY = "results";
private static final String RESPONSE_DATA_JSON_KEY = "responseData";
private int mCurrentPage;
private ListView mListView;
private Context mContext;
ArrayList<String> mImageUrls;
private LruCache<String, Bitmap> mCache;
private CustomListAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
// Initialize variables
mContext = this;
mCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap value) {
// The cache size will be measured in bytes rather than number of items.
return value.getByteCount();
}
};
mListView = (ListView) findViewById(R.id.list_view);
mAdapter = new CustomListAdapter();
mImageUrls = new ArrayList<>();
// If the urls are wrong then
if (mImageUrls.isEmpty() || checkDiff()) {
fetchNewImageUrls();
}
// Set the adapter to the list view
mListView.setAdapter(mAdapter);
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private String mUrl;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(String... params) {
String imageUrl = params[0];
mUrl = imageUrl;
Bitmap bitmap = getBitmapFromMemCache(imageUrl);
if (bitmap == null) {
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
// Closing the stream after getting the sample size
InputStream inputStream = connection.getInputStream();
byte[] imageByteArray = convertToByteArray(inputStream);
bitmap = decodeSampledBitmap(imageByteArray, 200, 200);
inputStream.close();
if (bitmap != null) {
Log.d(TAG, "Image downloaded: " + imageUrl);
addBitmapToMemoryCache(imageUrl, bitmap);
} else {
Log.d(TAG, "Null Bitmap downloaded for: " + imageUrl);
}
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else {
Log.d(TAG, "Already present in the Cache");
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView) imageViewReference.get();
if (imageView != null && imageView.getTag().equals(mUrl)) {
imageView.setImageBitmap(bitmap);
}
}
}
}
public class CustomListAdapter extends BaseAdapter {
#Override
public int getCount() {
return mImageUrls.size();
}
#Override
public Object getItem(int position) {
return mImageUrls.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG, "Get View is called for position: " + position);
View view = convertView;
Holder holder = null;
// Holder represents the elements of the view to use
// Here are initialized
if (view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.row_item, parent, false);
holder = new Holder();
holder.mRowImage = (ImageView) view.findViewById(R.id.image_view);
holder.mRowText = (TextView) view.findViewById(R.id.row_text);
view.setTag(holder);
} else {
holder = (Holder) view.getTag();
}
// Set default image background
holder.mRowImage.setImageResource(R.drawable.ic_launcher);
// here do operations in holder variable example
holder.mRowText.setText("Image Number: " + position);
// Set the tag for the imageview
holder.mRowImage.setTag(mImageUrls.get(position));
new BitmapWorkerTask(holder.mRowImage).execute(mImageUrls.get(position));
return view;
}
}
public static class Holder {
TextView mRowText;
ImageView mRowImage;
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return (Bitmap) mCache.get(key);
}
public Bitmap decodeSampledBitmap(byte[] imageByteArray, int reqWidth, int reqHeight) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length, options);
return bm;
}
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
Log.d(TAG, "Sample size is: " + inSampleSize);
return inSampleSize;
}
private void fetchNewImageUrls() {
new AsyncTask<String, Void, Boolean>() {
#Override
protected Boolean doInBackground(String... params) {
try {
StringBuilder response = new StringBuilder();
URL url = new URL(params[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
response.append(inputLine);
in.close();
connection.disconnect();
String resp = response.toString();
Log.d(TAG, "Response is: " + response);
// Parsing the response
JSONObject jsonObject = new JSONObject(resp);
JSONObject jsonObject1 = jsonObject.getJSONObject(RESPONSE_DATA_JSON_KEY);
JSONArray jsonArray = jsonObject1.getJSONArray(RESULTS_JSON_KEY);
for (int i = 0; i < 4; i++) {
JSONObject dataObject = jsonArray.getJSONObject(i);
mImageUrls.add(dataObject.getString(IMAGE_JSON_KEY));
}
mCurrentPage++;
Log.d(TAG, "Number of image urls are: " + mImageUrls.size());
return true;
} catch (JSONException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
#Override
protected void onPostExecute(Boolean value) {
super.onPostExecute(value);
if (checkDiff() && value) {
Log.d(TAG, "Again fetching the Images");
fetchNewImageUrls();
}
if (!value) {
Log.d(TAG, "Error while getting the response");
}
mAdapter.notifyDataSetChanged();
}
}.execute(BASE_URL + mCurrentPage);
}
private boolean checkDiff() {
int diff = mImageUrls.size() - mCurrentPage * 4;
Log.d(TAG, "Diff is: " + diff);
return diff < 8;
}
public static byte[] convertToByteArray(InputStream input) {
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
return output.toByteArray();
}
}
Since you do not cancel the unfinished "out of view" downloads these may interfere with your gui.
Example
listview line1 shows item#1 with has an async task to download image "a" not completed yet
scroll down, line1 is now invisible;
listview line8 becomes visible recycling item#1 new async task to download image "x"
async task to download image "a" finishes displaying wrong image. Line8 shows image "a" instead of "x"
to solve this you have to cancel the pending unnecessary unfinished async task-s
public static class Holder {
ImageView mRowImage;
String mImageUrl;
// neccessary to cancel unfinished download
BitmapWorkerTask mDownloader;
}
static class BitmapWorkerTask extends AsyncTask<Holder, Void, Bitmap> {
Holder mHolder;
protected Bitmap doInBackground(Holder... holders) {
mHolder = holders[0];
...
}
protected void onPostExecute(...) {
mHolder.mDownloader = null;
if (!isCancelled()) {
this.mHolder.mRowImage.setImageBitmap(image);
}
this.mHolder = null;
}
}
public class CustomListAdapter extends BaseAdapter {
#Override
public View getView(int position, ...) {
...
if (view == null) {
holder = ...
...
} else {
holder = (Holder) view.getTag();
}
...
// cancel unfinished mDownloader
if (holder.mDownloader != null) {
holder.mDownloader.cancel(false);
holder.mDownloader = null;
}
holder.mImageUrl = mImageUrls.get(position);
holder.mDownloader = new BitmapWorkerTask()
holder.mDownloader.execute(holder);
}
}
Here is a working example for this combination of Adapter + Holder + AsyncTask
[Update]
Potential problems with this solution.
Most modern android versions execute only one async task at a time. If you are fetching the images via the web you cannot download multible images at the same time. See running-parallel-asynctask#stackoverflow for details.
There may be promlems with configuration change(like orientation). See #Selvin-s comment below. I have posted this question "what-happens-with-view-references-in-unfinished-asynctask-after-orientation-chan#stackoverflow" to find out more about it.

How to fix exif warnings and problems in JPG files?

I wrote my own camera app. This app writes exif information in the jpg files. It works well but I have some problems with the exifInferface class, e.g., I get the following errors when re-read the JPG file:
Warning Invalid EXIF text encoding
Warning Invalid size (8589934590) for IFD0 tag 0x8827
Warning Bad IFD1 directory
I know that my IFD0 pointer at the exif-Information is broken. It may be that while writing the exif information the pointer is broken?
However, I've Googled and found nothing
I use this class to read an write the exif-informations:
public class ExifHelper {
private String aperature = null;
private String exposureTime = null;
private String flash = null;
private String focalLength = null;
private String gpsAltitude = null;
private String gpsAltitudeRef = null;
private String gpsDateStamp = null;
private String gpsLatitude = null;
private String gpsLatitudeRef = null;
private String gpsLongitude = null;
private String gpsLongitudeRef = null;
private String gpsProcessingMethod = null;
private String gpsTimestamp = null;
private String iso = null;
private String make = null;
private String model = null;
private String imageLength = null;
private String imageWidth = null;
private String orientation = null;
private String whiteBalance = null;
private String exifVersion = null;
private String time = null;
private ExifInterface inFile = null;
private ExifInterface outFile = null;
final static String TAG = "ExifHelper";
/**
* The file before it is compressed
*
* #param filePath
* #throws IOException
*/
public void createInFile(String filePath) throws IOException {
this.inFile = new ExifInterface(filePath);
}
/**
* The file after it has been compressed
*
* #param filePath
* #throws IOException
*/
public void createOutFile(String filePath) throws IOException {
this.outFile = new ExifInterface(filePath);
}
/**
* Reads all the EXIF data from the input file.
*/
public void readExifData() {
this.aperature = inFile.getAttribute(ExifInterface.TAG_APERTURE);
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
this.focalLength = inFile.getAttribute(ExifInterface.TAG_FOCAL_LENGTH);
this.gpsAltitude = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE);
this.gpsAltitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF);
this.gpsDateStamp = inFile.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
this.gpsLatitude = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
this.gpsLatitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
this.gpsLongitude = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
this.gpsLongitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
this.gpsProcessingMethod = inFile.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD);
this.gpsTimestamp = inFile.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
this.imageLength = inFile.getAttribute(ExifInterface.TAG_IMAGE_LENGTH);
this.imageWidth = inFile.getAttribute(ExifInterface.TAG_IMAGE_WIDTH);
this.iso = inFile.getAttribute(ExifInterface.TAG_ISO);
this.make = inFile.getAttribute(ExifInterface.TAG_MAKE);
this.model = inFile.getAttribute(ExifInterface.TAG_MODEL);
this.orientation = inFile.getAttribute(ExifInterface.TAG_ORIENTATION);
this.whiteBalance = inFile.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
this.exifVersion = inFile.getAttribute("ExifVersion");
}
/**
* Writes the previously stored EXIF data to the output file.
* #param pictureDate
* #param orientationValues
* #param accelValues
*
* #throws IOException
*/
public void writeExifData(String pictureDate) throws IOException {
// Don't try to write to a null file
if (this.outFile == null) {
return;
}
if (this.aperature != null) {
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperature);
}
if (this.exposureTime != null) {
this.outFile.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, this.exposureTime);
}
if (this.flash != null) {
this.outFile.setAttribute(ExifInterface.TAG_FLASH, this.flash);
}
if (this.focalLength != null) {
this.outFile.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, this.focalLength);
}
if (this.gpsAltitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, this.gpsAltitude);
}
if (this.gpsAltitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, this.gpsAltitudeRef);
}
if (this.gpsDateStamp != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, this.gpsDateStamp);
}
if (this.gpsLatitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE, this.gpsLatitude);
}
if (this.gpsLatitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, this.gpsLatitudeRef);
}
if (this.gpsLongitude != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, this.gpsLongitude);
}
if (this.gpsLongitudeRef != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, this.gpsLongitudeRef);
}
if (this.gpsProcessingMethod != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, this.gpsProcessingMethod);
}
if (this.gpsTimestamp != null) {
this.outFile.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, this.gpsTimestamp);
}
if (this.iso != null) {
this.outFile.setAttribute(ExifInterface.TAG_ISO, this.iso);
}
if (this.make != null) {
this.outFile.setAttribute(ExifInterface.TAG_MAKE, this.make);
}
if (this.model != null) {
this.outFile.setAttribute(ExifInterface.TAG_MODEL, this.model);
}
if (this.orientation != null) {
this.outFile.setAttribute(ExifInterface.TAG_ORIENTATION, this.orientation);
}
if (this.whiteBalance != null) {
this.outFile.setAttribute(ExifInterface.TAG_WHITE_BALANCE, this.whiteBalance);
}
if (this.exifVersion != null) {
this.outFile.setAttribute("ExifVersion", this.exifVersion);
}
this.outFile.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, this.imageLength);
this.outFile.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, this.imageWidth);
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, pictureDate);
String mString = exifRandomZahlen();
this.outFile.setAttribute("UserComment", mString);
this.outFile.saveAttributes();
}
I also write 2 images form the takepicture-method. One the original there is no problem and the second with modify exif informations. There is the problem with the exifinterface I think by writing it back to the JPG.
I used the tool DumpImage to view the exif informations. This tool is from the metaworking group (www.metadataworkinggroup.org)
So I have a big question, How I can fix this broken exif data? For example the IDF0 pointer
somebody know or have the same problem?
I get, for example, the Tag TAG_DATETIME two times in my exif information
this is the class for saveing the photo:
public class Photo extends Activity implements PictureCallback {
public interface OnPictureTakenListener {
void pictureTaken(File pictureFile, File pictureFilePatched, String exifDateString);
}
private final Context context;
private OnPictureTakenListener listener;
public Photo(Context ourContext, OnPictureTakenListener theListener) {
this.context = ourContext;
this.listener = theListener;
}
#SuppressLint("SimpleDateFormat")
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Date date = new Date();
File pictureFileDir = getDir();
if (!pictureFileDir.exists() && !pictureFileDir.mkdirs())
{
Log.d(AndroidCamera.DEBUG_TAG,
"Can't create directory to save image.");
Toast.makeText(context, "Can't create directory to save image.",
Toast.LENGTH_LONG).show();
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
SimpleDateFormat dateConverter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
String exifDateString = dateFormat.format(date);
String datePicture = dateConverter.format(date);
String photoFile = "Picture_" + datePicture + ".jpg";
String photoFilePatched = "Picture_" + datePicture + "_patched.jpg";
String filename = pictureFileDir.getPath() + File.separator + photoFile;
String filenamePatched = pictureFileDir.getPath() + File.separator + photoFilePatched;
File pictureFile = new File(filename);
File pictureFilePatched = new File (filenamePatched);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
FileOutputStream fosPatched = new FileOutputStream(pictureFilePatched);
fos.write(data);
fosPatched.write(data);
fos.close();
fosPatched.close();
Toast.makeText(context, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
} catch (Exception error) {
Log.d(AndroidCamera.DEBUG_TAG, "File" + filename + " not saved: "
+ error.getMessage());
Toast.makeText(context, "Image could not be saved.",
Toast.LENGTH_LONG).show();
}
listener.pictureTaken(pictureFile,pictureFilePatched,exifDateString);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
}
private File getDir() {
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
return new File(sdDir, "Camera");
}
static final int REQUEST_IMAGE_CAPTURE = 1;
}
here in main activity i call on one from the two pictures to write the exif infortmations:
camera.takePicture(null, null, new Photo(this,
new Photo.OnPictureTakenListener() {
public void pictureTaken(final File pictureFile,final File pictureFilePatched , final String date) {
final String fileName = pictureFile.getPath();
final String fileNamePatched = pictureFilePatched.getPath();
final String dateTime = date;
// don't start picture preview immediately, but a little
// delayed...
continueWithPreview.postDelayed(new Runnable() {
#Override
public void run() {
try {
// EXIF Matadata change
ExifHelper exifHelper = new ExifHelper();
exifHelper.createInFile(fileName);
//EXIF Metadata read
exifHelper.readExifData();
exifHelper.createOutFile(fileNamePatched);
//Exif Metadata write
exifHelper.writeExifData(dateTime);
} catch (IOException e) {
e.printStackTrace();
Log.e("PictureActivity", e.getLocalizedMessage());
}
if (null != camera)
{
camera.startPreview();
Toast.makeText(AndroidCamera.this, "started!",
Toast.LENGTH_SHORT).show();
}
}
}, 2500);

Getting SDard(s) path and size in KitKat 4.4.2

I have a device info app on Google Play, and within the app I have storage information. I know in Android 4.4 there has been some changes in regards to accessing external SDcards. Internal doesn't seem to give me a problem. My question is, how can I reliably get the size of SDcards on KitKat?
I have the required permissions listed, as this worked fine on earlier versions of Android. I have searched here on SO, and I always seem to get one of the same errors. I do not need to write to SDcards, only read for size availabilty.
I have a StorageUtils class that came from SO, sorry I can't remember the link.
public class StorageUtils {
private static final String TAG = "StorageUtils";
public static class StorageInfo {
public final String path;
public final boolean internal;
public final boolean readonly;
public final int display_number;
StorageInfo(String path, boolean internal, boolean readonly,
int display_number) {
this.path = path;
this.internal = internal;
this.readonly = readonly;
this.display_number = display_number;
}
}
public static ArrayList<StorageInfo> getStorageList() {
ArrayList<StorageInfo> list = new ArrayList<StorageInfo>();
String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_internal = !Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState();
boolean def_path_available = def_path_state
.equals(Environment.MEDIA_MOUNTED)
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState()
.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
BufferedReader buf_reader = null;
try {
HashSet<String> paths = new HashSet<String>();
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
String line;
int cur_display_number = 1;
Log.d(TAG, "/proc/mounts");
while ((line = buf_reader.readLine()) != null) {
Log.d(TAG, line);
if (line.contains("vfat") || line.contains("/mnt")) {
StringTokenizer tokens = new StringTokenizer(line, " ");
String unused = tokens.nextToken(); // device
String mount_point = tokens.nextToken(); // mount point
if (paths.contains(mount_point)) {
continue;
}
unused = tokens.nextToken(); // file system
List<String> flags = Arrays.asList(tokens.nextToken()
.split(",")); // flags
boolean readonly = flags.contains("ro");
if (mount_point.equals(def_path)) {
paths.add(def_path);
list.add(new StorageInfo(def_path, def_path_internal,
readonly, -1));
} else if (line.contains("/dev/block/vold")) {
if (!line.contains("/mnt/secure")
&& !line.contains("/mnt/asec")
&& !line.contains("/mnt/obb")
&& !line.contains("/dev/mapper")
&& !line.contains("tmpfs")) {
paths.add(mount_point);
list.add(new StorageInfo(mount_point, false,
readonly, cur_display_number++));
}
}
}
}
if (!paths.contains(def_path) && def_path_available) {
list.add(new StorageInfo(def_path, def_path_internal,
def_path_readonly, -1));
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (buf_reader != null) {
try {
buf_reader.close();
} catch (IOException ex) {
}
}
}
return list;
}
public static String getReadableFileSize(long bytes, boolean si) {
int unit = si ? 1000 : 1024;
if (bytes < unit)
return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1)
+ (si ? "" : "i");
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
#SuppressLint("NewApi")
public static long getFreeSpace(String path) {
StatFs statFs = new StatFs(path);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
long sdAvailSize = statFs.getFreeBlocksLong()
* statFs.getBlockSizeLong();
return sdAvailSize;
} else {
#SuppressWarnings("deprecation")
double sdAvailSize = (double) statFs.getFreeBlocks()
* (double) statFs.getBlockSize();
return (long) sdAvailSize;
}
}
public static long getUsedSpace(String path) {
return getTotalSpace(path) - getFreeSpace(path);
}
#SuppressLint("NewApi")
public static long getTotalSpace(String path) {
StatFs statFs = new StatFs(path);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
long sdTotalSize = statFs.getBlockCountLong()
* statFs.getBlockSizeLong();
return sdTotalSize;
} else {
#SuppressWarnings("deprecation")
double sdTotalSize = (double) statFs.getBlockCount()
* statFs.getBlockSize();
return (long) sdTotalSize;
}
}
/**
* getSize()[0] is /mnt/sdcard. getSize()[1] is size of sd (example 12.0G),
* getSize()[2] is used, [3] is free, [4] is blksize
*
* #return
* #throws IOException
*/
public static String[] getSize() throws IOException {
String memory = "";
Process p = Runtime.getRuntime().exec("df /mnt/sdcard");
InputStream is = p.getInputStream();
int by = -1;
while ((by = is.read()) != -1) {
memory += new String(new byte[] { (byte) by });
}
for (String df : memory.split("/n")) {
if (df.startsWith("/mnt/sdcard")) {
String[] par = df.split(" ");
List<String> pp = new ArrayList<String>();
for (String pa : par) {
if (!pa.isEmpty()) {
pp.add(pa);
}
}
return pp.toArray(new String[pp.size()]);
}
}
return null;
}
}
And here is my fragment in which I try to display the SDcard path and size.
public class CpuMemFragment extends Fragment {
// CPU
String devCpuInfo;
TextView tvCpuInfo;
// RAM
String devRamInfo;
TextView tvRamInfo;
// Storage
String devStorageA, devStorageB;
TextView tvStorageAName, tvStorageA, tvStorageB, tvStorageBName;
AdView adView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.cpu_mem, container, false);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
// *** CPU ***
//
devCpuInfo = readCpuInfo();
//
// #################################
// *** RAM ***
//
devRamInfo = readTotalRam();
//
// #################################
// *** STORAGE ***
//
ArrayList<StorageInfo> storageInfoList = StorageUtils.getStorageList();
tvStorageAName = (TextView) getView().findViewById(R.id.tvStorageAName);
tvStorageBName = (TextView) getView().findViewById(R.id.tvStorageBName);
if (storageInfoList.size() > 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& !storageInfoList.get(0).internal) {
kitKatWorkaround(0);
}
tvStorageAName.setText(storageInfoList.get(0).path);
devStorageA = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(storageInfoList.get(0).path)),
true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(storageInfoList.get(0).path)), true);
if (storageInfoList.size() > 1) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& !storageInfoList.get(0).internal) {
kitKatWorkaround(1);
}
tvStorageBName.setText(storageInfoList.get(1).path);
devStorageB = StorageUtils.getReadableFileSize(
StorageUtils.getUsedSpace(storageInfoList.get(1).path)
+ (StorageUtils.getUsedSpace("system/")), true)
+ "/"
+ StorageUtils.getReadableFileSize((StorageUtils
.getTotalSpace(storageInfoList.get(1).path)),
true);
} else {
devStorageB = "N/A";
}
} else {
devStorageA = "N/A";
devStorageB = "N/A";
}
//
// #################################
// CPU
tvCpuInfo = (TextView) getView().findViewById(R.id.tvCpuInfo);
tvCpuInfo.setText(devCpuInfo);
//
// #################################
// RAM
tvRamInfo = (TextView) getView().findViewById(R.id.tvRamInfo);
tvRamInfo.setText(devRamInfo);
//
// #################################
// STORAGE
tvStorageA = (TextView) getView().findViewById(R.id.tvStorageA);
tvStorageA.setText(devStorageA);
tvStorageB = (TextView) getView().findViewById(R.id.tvStorageB);
tvStorageB.setText(devStorageB);
//
// #################################
// Look up the AdView as a resource and load a request.
adView = (AdView) getActivity().findViewById(R.id.adCpuMemBanner);
AdRequest adRequest = new AdRequest.Builder().build();
adView.loadAd(adRequest);
}
#Override
public void onPause() {
if (adView != null) {
adView.pause();
}
super.onPause();
}
#Override
public void onResume() {
super.onResume();
if (adView != null) {
adView.resume();
}
}
#Override
public void onDestroy() {
if (adView != null) {
adView.destroy();
}
super.onDestroy();
}
private static synchronized String readCpuInfo() {
ProcessBuilder cmd;
String result = "";
try {
String[] args = { "/system/bin/cat", "/proc/cpuinfo" };
cmd = new ProcessBuilder(args);
Process process = cmd.start();
InputStream in = process.getInputStream();
byte[] re = new byte[1024];
while (in.read(re) != -1) {
System.out.println(new String(re));
result = result + new String(re);
}
in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return result;
}
public static synchronized String readTotalRam() {
String load = "";
try {
RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
load = reader.readLine();
reader.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return load;
}
public void kitKatWorkaround(int index) {
String path1 = Environment.getExternalStorageDirectory().getPath();
//Storage A
if (index == 0) {
tvStorageAName.setText(path1);
devStorageA = StorageUtils.getReadableFileSize(
(StorageUtils.getUsedSpace(path1)), true)
+ "/"
+ StorageUtils.getReadableFileSize(
(StorageUtils.getTotalSpace(path1)), true);
}
//Storage B
if (index == 1) {
tvStorageBName.setText(path1);
devStorageB = StorageUtils.getReadableFileSize(
StorageUtils.getUsedSpace(path1)), true)
+ "/"
+ StorageUtils.getReadableFileSize(
(StorageUtils.getTotalSpace(path1)), true);
}
}
}
This results in the EACCES error, or an invalid path (access denied) on KitKat. Please help, and thank you greatly for your time.
You can find all shared storage devices by calling Context.getExternalFilesDirs() or Context.getExternalCacheDirs(). Then, you can call File.getUsableSpace() or use android.os.StatFs to check available disk space at each location.
These APIs are also available in ContextCompat in the support library to seamlessly work on pre-KitKat devices.

Android: Bitmap not shown in ImageView

I'm developing an Activity which loads a list of URLs of images and displays them in a Gallery view. For better performance I decided to asynchronously load the images and cache them on the SD card.
I found this:
http://blog.jteam.nl/2009/09/17/exploring-the-world-of-android-part-2/
and adapted the code. As long as I didn't cache the files on the SD card everything worked fine, but now I see a strange behavior. I guess it's easier to describe the problem with code (see comments):
public class AsyncImageLoader {
private final static String TAG = "AsyncImageLoader";
private HashMap<String, SoftReference<Bitmap>> bitmapMap;
public AsyncImageLoader() {
this.bitmapMap = new HashMap<String, SoftReference<Bitmap>>();
}
// This method is called to load images asynchronously.
// If the Bitmap is cached in bitmapMap and the reference is
// still valid, the Bitmap is returned immediately.
// If the Bitmap is not in bitmapMap, another thread is started
// to load the image. Once it has been loaded, a callback method
// is called.
public Bitmap loadBitmap(final String imageUrl,
final IImageLoadListener imageCallback, final int minWidth,
final int minHeight) {
if (this.bitmapMap.containsKey(imageUrl)) {
SoftReference<Bitmap> softReference = this.bitmapMap.get(imageUrl);
Bitmap bitmap = softReference.get();
if (bitmap != null) {
Log.d(TAG, "Using a previously loaded Bitmap container");
return bitmap;
}
}
Log.d(TAG, "Need to load the Bitmap container");
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
imageCallback.imageLoaded((Bitmap) message.obj, imageUrl);
}
};
new Thread() {
#Override
public void run() {
Bitmap bitmap = loadImageFromUrl(imageUrl, minWidth, minHeight);
bitmapMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
Message message = handler.obtainMessage(0, bitmap);
handler.sendMessage(message);
}
}.start();
return null;
}
// Here is part of the magic behind the scenes:
// I get an InputStream (see below) and open it just
// to get the Bitmap's dimensions. Then I calculate the
// required width (best size for minWidth and minHeight)
// and then I create a downsampled Bitmap.
private synchronized static Bitmap loadImageFromUrl(String urlString,
int minWidth, int minHeight) {
Bitmap bitmap = null;
try {
InputStream is = getInputStream(urlString);
BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null,
options1);
is.close();
int tempWidth = options1.outWidth;
int tempHeight = options1.outHeight;
int scale = 1;
while (true) {
if (tempWidth / 2 < minWidth || tempHeight / 2 < minHeight) {
break;
}
tempWidth /= 2;
tempHeight /= 2;
scale *= 2;
}
BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream(getInputStream(urlString),
null, options2);
// More magic: Once the Bitmap has been downloaded,
// I create a file on the SD card so that I don't
// need to download it again.
File cacheFile = getCacheFile(urlString);
if (cacheFile == null) {
cacheImage(urlString, bitmap);
}
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
return bitmap;
}
// I check whether a cache file exists for the URL. If it exists
// I create the InputStream from this file and I create the InputStream
// from the URL otherwise.
private synchronized static InputStream getInputStream(String urlString)
throws IOException {
File cacheFile = getCacheFile(urlString);
if (cacheFile != null) {
// HERE seems to be something wrong. The file
// is valid and can be read, but if I return a
// FileInputStream (or InputStream using
// cacheFile.toURL().openStream()) the image is not
// shown.
Log.d(TAG, "Using " + cacheFile.getAbsolutePath());
return new FileInputStream(cacheFile);
} else {
// In this case, the image is shown everytime!
Log.d(TAG, "Downloading " + urlString);
URL url = new URL(urlString);
return url.openStream();
}
}
// This is just a helper method that returns the cache directory
// and if it doesn't exist it will be created first.
private synchronized static File getImageCacheDir() {
File imageCacheDir = new File(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/" + Commons.APP_DIRECTORY + "/cache/");
if (!imageCacheDir.exists()) {
imageCacheDir.mkdirs();
}
return imageCacheDir;
}
// This method returns a File object for the cache file if it exists
// and returns null otherwise.
private static File getCacheFile(String urlString) {
File file = new File(getImageCacheDir(), CryptoUtils.md5(urlString)
+ ".jpg");
if (!file.exists()) {
return null;
}
return file;
}
// Even more magic:
// At first I create a file with a .tmp extension just in case two
// threads try to read and write at the same time. The image is
// downloaded into the temporary file. If this succeeds the .tmp
// extension will be removed.
private synchronized static void cacheImage(String urlString, Bitmap bitmap) {
String filename = CryptoUtils.md5(urlString) + ".jpg";
File tmpFile = new File(getImageCacheDir(), filename + ".tmp");
if (tmpFile.exists()) {
Log.d(TAG, "Another process seems to create the cache file.");
return;
}
File file = new File(getImageCacheDir(), filename);
FileOutputStream os = null;
try {
os = new FileOutputStream(tmpFile);
boolean retval = bitmap
.compress(Bitmap.CompressFormat.JPEG, 90, os);
if (retval) {
tmpFile.renameTo(file);
Log.d(TAG, "Created cache image. Renaming temporary file.");
} else {
tmpFile.delete();
Log.d(TAG,
"Could not create cache image. Deleting temporary file.");
}
} catch (FileNotFoundException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
}
}
public interface IImageLoadListener {
public void imageLoaded(Bitmap imageBitmap, String imageUrl);
}
}
Here is another big chunk of code:
public class DisplayPhotoActivity extends Activity {
private final static int PROGRESS_DIALOG = 1;
private final static int PHOTO_PICKER = 2;
private final static int GALLERY_REQUEST_CODE = 1;
private final static int CAMERA_REQUEST_CODE = 2;
private AsyncImageLoader asyncImageLoader;
private Gallery gallery;
private List<Photo> images;
private ImageAdapter imageAdapter;
private int loadingCounter;
private Uri outputFileUri;
private Display display;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.display_photo);
this.asyncImageLoader = new AsyncImageLoader();
this.loadingCounter = 0;
this.images = new ArrayList<Photo>();
this.imageAdapter = new ImageAdapter(this, this.images);
final ImageView imageView = (ImageView) findViewById(R.id.large_image);
this.gallery = (Gallery) findViewById(R.id.gallery);
this.gallery.setAdapter(this.imageAdapter);
// The Gallery is on top of the Activity and there is a bigger
// ImageView below it. I want the image to be shown in the bigger
// ImageView when it has been clicked in the Gallery.
this.gallery.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// Here is the part where I load the Bitmap.
// This part works!
Bitmap bitmap = asyncImageLoader.loadBitmap(images
.get(position).getUrl(),
new AsyncImageLoader.IImageLoadListener() {
public void imageLoaded(Bitmap imageBitmap,
String imageUrl) {
if (imageBitmap != null) {
imageView.setImageBitmap(imageBitmap);
gallery.invalidate();
}
loadingCounter--;
if (loadingCounter == 0) {
getParent()
.setProgressBarIndeterminateVisibility(
false);
}
}
}, 300, 300);
if (bitmap != null) {
Log.d("DisplayPhotoActivity", "Image was cached. (I)");
imageView.setImageBitmap(bitmap);
gallery.invalidate();
loadingCounter--;
if (loadingCounter == 0) {
getParent()
.setProgressBarIndeterminateVisibility(false);
}
}
}
});
this.display = (Display) getIntent().getParcelableExtra("display");
populatePhotos(true);
}
// There is a known bug and this is the bug fix.
// See: http://code.google.com/p/android/issues/detail?id=8488
#Override
public void onPause() {
super.onPause();
System.gc();
}
#Override
public void onDestroy() {
super.onDestroy();
this.asyncImageLoader = null;
System.gc();
}
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Loading...");
return progressDialog;
case PHOTO_PICKER:
final CharSequence[] items = {
getText(R.string.photo_picker_choose_from_gallery),
getText(R.string.photo_picker_take_a_photo) };
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getText(R.string.photo_picker_title));
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (item) {
case 0:
onChooseFromGalleryClick();
break;
case 1:
onTakeAPhoto();
}
}
});
return builder.create();
default:
return null;
}
}
private void onChooseFromGalleryClick() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,
getText(R.string.photo_picker_choose_from_gallery)),
GALLERY_REQUEST_CODE);
}
private void onTakeAPhoto() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File appDirectory = new File(Environment.getExternalStorageDirectory(),
Commons.APP_DIRECTORY);
if (!appDirectory.exists()) {
appDirectory.mkdirs();
}
File photo = new File(appDirectory, "/pic.jpg");
this.outputFileUri = Uri.fromFile(photo);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, this.outputFileUri);
startActivityForResult(Intent.createChooser(cameraIntent,
getText(R.string.photo_picker_take_a_photo)),
CAMERA_REQUEST_CODE);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem updateMenuItem = menu.add(this.getResources().getString(
R.string.update_menuitem_text));
updateMenuItem
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
populatePhotos(true);
return false;
}
});
updateMenuItem.setIcon(R.drawable.ic_menu_refresh);
MenuItem uploadPhotoMenuItem = menu.add(this.getResources().getString(
R.string.photo_picker_title));
uploadPhotoMenuItem
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
showDialog(PHOTO_PICKER);
return true;
}
});
uploadPhotoMenuItem.setIcon(getResources().getDrawable(
android.R.drawable.ic_menu_add));
return true;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
Uri uri = null;
switch (requestCode) {
case GALLERY_REQUEST_CODE:
uri = data.getData();
break;
case CAMERA_REQUEST_CODE:
uri = this.outputFileUri;
break;
}
if (uri == null) {
return;
}
Display display = this.getIntent().getParcelableExtra("display");
this.startService(PhotoUploadService.getIntent(this, display, uri));
}
}
private void populatePhotos(boolean forceSync) {
new ImageListLoader().execute(this.display.getId(), forceSync ? 1 : 0);
}
private void updateInitialImage(Bitmap bitmap) {
ImageView imageView = (ImageView) findViewById(R.id.large_image);
if (imageView.getDrawable() == null) {
Log.d("DisplayPhotoActivity", "No initial image set.");
imageView.setImageBitmap(bitmap);
}
}
public class ImageListLoader extends AsyncTask<Integer, Void, Void> {
#Override
protected void onPreExecute() {
showDialog(PROGRESS_DIALOG);
}
#Override
protected Void doInBackground(Integer... params) {
if (params.length < 1) {
return null;
}
int displayId = params[0];
boolean forceSync = params.length == 2 && params[1] == 1;
List<Photo> photos = LocalDatabaseHelper.getInstance()
.getPhotosByDisplayId(displayId, forceSync);
images.clear();
for (Photo photo : photos) {
images.add(photo);
}
Log.d("FOO", "Size: " + images.size());
runOnUiThread(new Runnable() {
public void run() {
imageAdapter.notifyDataSetChanged();
}
});
return null;
}
#Override
protected void onPostExecute(Void result) {
dismissDialog(PROGRESS_DIALOG);
}
}
public class ImageAdapter extends BaseAdapter {
private Context context;
private List<Photo> images;
private int galleryItemBackground;
public ImageAdapter(Context context, List<Photo> images) {
this.context = context;
this.images = images;
TypedArray attr = this.context
.obtainStyledAttributes(R.styleable.DisplayPhotoActivity);
this.galleryItemBackground = attr
.getResourceId(
R.styleable.DisplayPhotoActivity_android_galleryItemBackground,
0);
attr.recycle();
}
public int getCount() {
return this.images.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
getParent().setProgressBarIndeterminateVisibility(true);
loadingCounter++;
final ImageView imageView = new ImageView(this.context);
String imageUrl = this.images.get(position).getUrl();
Log.d("DisplayPhotoActivity", "getView(): " + imageUrl);
// Here is almost the same code as in the OnClickListener
// above, but this does not work as expected. If the image
// has never been cached (neither as a SoftReference nor as
// a file on the SD card) the image is downloaded and shown.
// No problem... But if the image is cached, it won't show up!
Bitmap bitmap = asyncImageLoader.loadBitmap(
imageUrl,
new AsyncImageLoader.IImageLoadListener() {
public void imageLoaded(Bitmap imageBitmap,
String imageUrl) {
Log.d("DisplayPhotoActivity", " - imageLoaded:");
if (imageBitmap != null) {
Log.d("DisplayPhotoActivity", " - imageBitmap != null");
updateInitialImage(imageBitmap);
Log.d("DisplayPhotoActivity", " - updateInitialImage(imageBitmap)");
imageView.setImageBitmap(imageBitmap);
Log.d("DisplayPhotoActivity", " - setImageBitmap(imageBitmap)");
int width = Math.round((150 * imageBitmap
.getWidth()) / imageBitmap.getHeight());
imageView
.setLayoutParams(new Gallery.LayoutParams(
width, 150));
//imageView.invalidate();
//gallery.invalidate();
}
loadingCounter--;
if (loadingCounter == 0) {
getParent()
.setProgressBarIndeterminateVisibility(
false);
}
}
}, 300, 300);
if (bitmap != null) {
// TODO: Something doesn't work here...
Log.d("DisplayPhotoActivity", "Image was cached");
Log.d("DisplayPhotoActivity",
bitmap.getWidth() + "x" + bitmap.getHeight());
updateInitialImage(bitmap);
imageView.setImageBitmap(bitmap);
int width = Math.round((150 * bitmap.getWidth())
/ bitmap.getHeight());
imageView.setLayoutParams(new Gallery.LayoutParams(width, 150));
imageView.invalidate();
gallery.invalidate();
loadingCounter--;
if (loadingCounter == 0) {
getParent().setProgressBarIndeterminateVisibility(false);
}
}
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setBackgroundResource(galleryItemBackground);
imageView.setImageDrawable(getResources().getDrawable(
R.drawable.loading));
imageView.setLayoutParams(new Gallery.LayoutParams(200, 150));
return imageView;
}
public float getScale(boolean focused, int offset) {
return Math.max(0, 1.0f / (float) Math.pow(2, Math.abs(offset)));
}
}
}
Here is the DDMS output:
07-25 02:34:58.496: DEBUG/DisplayPhotoActivity(23589): getView(): http://[...]/files/814efa535ed97cf44ea3dc3a1c15c3fb/1_1311540992715.jpg
07-25 02:34:58.496: DEBUG/AsyncImageLoader(23589): Need to load the Bitmap container
07-25 02:34:58.503: DEBUG/DisplayPhotoActivity(23589): getView(): http://[...]/files/814efa535ed97cf44ea3dc3a1c15c3fb/1_1311540992715.jpg
07-25 02:34:58.503: DEBUG/AsyncImageLoader(23589): Need to load the Bitmap container
07-25 02:35:01.031: DEBUG/AsyncImageLoader(23589): Created cache image. Renaming temporary file.
07-25 02:35:01.031: DEBUG/DisplayPhotoActivity(23589): - imageLoaded:
07-25 02:35:01.031: DEBUG/DisplayPhotoActivity(23589): - imageBitmap != null
07-25 02:35:01.031: DEBUG/DisplayPhotoActivity(23589): No initial image set.
07-25 02:35:01.031: DEBUG/DisplayPhotoActivity(23589): - updateInitialImage(imageBitmap)
07-25 02:35:01.031: DEBUG/DisplayPhotoActivity(23589): - setImageBitmap(imageBitmap)
07-25 02:35:01.597: DEBUG/AsyncImageLoader(23589): Using /mnt/sdcard/PDCollector/cache/4057d5bd1cd9ba7204371ec422ea884a.jpg
07-25 02:35:01.605: DEBUG/AsyncImageLoader(23589): Using /mnt/sdcard/PDCollector/cache/4057d5bd1cd9ba7204371ec422ea884a.jpg
07-25 02:35:01.800: DEBUG/AsyncImageLoader(23589): Using /mnt/sdcard/PDCollector/cache/4057d5bd1cd9ba7204371ec422ea884a.jpg
07-25 02:35:01.800: DEBUG/AsyncImageLoader(23589): Using /mnt/sdcard/PDCollector/cache/4057d5bd1cd9ba7204371ec422ea884a.jpg
07-25 02:35:01.804: DEBUG/DisplayPhotoActivity(23589): - imageLoaded:
07-25 02:35:01.804: DEBUG/DisplayPhotoActivity(23589): - imageBitmap != null
07-25 02:35:01.804: DEBUG/DisplayPhotoActivity(23589): No initial image set.
07-25 02:35:01.804: DEBUG/DisplayPhotoActivity(23589): - updateInitialImage(imageBitmap)
07-25 02:35:01.804: DEBUG/DisplayPhotoActivity(23589): - setImageBitmap(imageBitmap)
07-25 02:35:02.027: DEBUG/AsyncImageLoader(23589): Using /mnt/sdcard/PDCollector/cache/4057d5bd1cd9ba7204371ec422ea884a.jpg
07-25 02:35:02.027: DEBUG/AsyncImageLoader(23589): Using /mnt/sdcard/PDCollector/cache/4057d5bd1cd9ba7204371ec422ea884a.jpg
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): - imageLoaded:
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): - imageBitmap != null
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): - updateInitialImage(imageBitmap)
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): - setImageBitmap(imageBitmap)
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): getView(): http://[...]/files/814efa535ed97cf44ea3dc3a1c15c3fb/1_1311540992715.jpg
07-25 02:35:02.082: DEBUG/AsyncImageLoader(23589): Using a previously loaded Bitmap container
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): Image was cached
07-25 02:35:02.082: DEBUG/DisplayPhotoActivity(23589): 640x480
07-25 02:35:02.257: DEBUG/DisplayPhotoActivity(23589): - imageLoaded:
07-25 02:35:02.257: DEBUG/DisplayPhotoActivity(23589): - imageBitmap != null
07-25 02:35:02.257: DEBUG/DisplayPhotoActivity(23589): - updateInitialImage(imageBitmap)
07-25 02:35:02.257: DEBUG/DisplayPhotoActivity(23589): - setImageBitmap(imageBitmap)
I hope someone can explain this strange behavior. Thanks in advance.

Categories

Resources