6

What I'm trying to do is this: I want my application to download an image from the Internet and save it to the phone's internal memory in a location that is private to the application. If there is no image available for the list item (i.e. it can't be found on the Internet), I want a default placeholder image to display. This is the image that I have defined in my list_item_row.xml file as the default.

In my ListActivity file, I am calling an instance of a CustomCursorAdapter class I have written. It is in CustomCursorAdapter where I am iterating through all the list items and defining what content needs to be mapped to the views, including the image file by trying to read it from internal memory.

I've seen several questions on this subject, but the examples either are specific to external phone memory (e.g. SDCard), involve saving strings instead of images, or involve using Bitmap.CompressFormat to reduce the resolution of the file (which is unnecessary in my case, as these images will be small thumbnails of already-small resolution). Trying to piece together code from each example has been difficult, hence my asking about my specific example.

At the moment, I believe I've written valid code, but no image is displaying for my list items, including the default placeholder image. I don't know if the problem is being caused by invalid download/save code, or invalid read code - it doesn't help that I don't know how to check internal memory to see if the image exists.

Anyways, here's my code. Any help would be greatly appreciated.

ProductUtils.java

public static String productLookup(String productID, Context c) throws IOException {
    URL url = new URL("http://www.samplewebsite.com/" + productID + ".jpg");
    URLConnection connection = url.openConnection();
    InputStream input = connection.getInputStream();
    FileOutputStream output = 
        c.openFileOutput(productID + "-thumbnail.jpg", Context.MODE_PRIVATE);
    byte[] data = new byte[1024];

    output.write(data);
    output.flush();
    output.close();
    input.close();
}

CustomCursorAdapter.java

public class CustomCursorAdapter extends CursorAdapter {
    public CustomCursorAdapter(Context context, Cursor c) {
        super(context, c);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        ImageView thumbnail = (ImageView) view.findViewById(R.id.thumbnail);

        String fileName = 
                cursor.getString(cursor.getColumnIndex(DbAdapter.KEY_IMAGE_FILE_PATH));

        Bitmap bMap = BitmapFactory.decodeFile(fileName);
        thumbnail.setImageBitmap(bMap);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View v = inflater.inflate(R.layout.list_item_row, parent, false);
        bindView(v, context, cursor);
        return v;
    }
}
Keeb13r
  • 235
  • 3
  • 8
  • 18

3 Answers3

6

it seems that some code is left out, I re-wrote it like this:

ProductUtils.java

public static String productLookup(String productID, Context c) throws IOException {

    URL url = new URL("http://www.samplewebsite.com/" + productID + ".jpg");

    InputStream input = null;
    FileOutputStream output = null;

    try {
        String outputName = productID + "-thumbnail.jpg";

        input = url.openConnection().getInputStream();
        output = c.openFileOutput(outputName, Context.MODE_PRIVATE);

        int read;
        byte[] data = new byte[1024];
        while ((read = input.read(data)) != -1)
            output.write(data, 0, read);

        return outputName;

    } finally {
        if (output != null)
            output.close();
        if (input != null)
            input.close();
    }
}
dacwe
  • 43,066
  • 12
  • 116
  • 140
  • The application won't compile with that code. It says that the input and output variables need to be initialized. I corrected to problematic lines to say "InputStream input = null" and "FileOutputStream output = null", but I'm still having the same problem with no image displaying. – Keeb13r Nov 16 '10 at 01:33
  • Looks like the error I'm getting is this: "java.io.IOException: Is a directory". I'm not sure what to think, since I thought that merely specifying the filename without introducing any intervening folders would result in the write and read path being exactly the same. – Keeb13r Nov 17 '10 at 00:06
  • Hum, does productID contain slashes (`/`)? – dacwe Nov 17 '10 at 07:56
  • Nope, it can only contain numbers and sometimes, an X to represent a 10. Is it possible I'm not reading the file from the proper location, i.e. when I call decodeFile(), I need to tell it the path where that file will be? – Keeb13r Nov 18 '10 at 15:16
  • Write to the log so you are sure that you have write and read the same file? – dacwe Nov 18 '10 at 15:26
  • Also, instead of writing the file to internal storage and storing the filename in a database, just download the file and show it directly in the view (just to be sure that you are downloading the correct file). Maybe the productID + "-thumbnail.jpg" does not exist on the server and you end up writing garbage to the internal storage! – dacwe Nov 18 '10 at 15:29
  • How would this be any different from what I'm currently doing? If I'm downloading the image to the phone's internal memory and then displaying it directly in the view, my app will still be reading the image from the exact same location it would if it was looking up the path from my DB. – Keeb13r Nov 21 '10 at 00:01
  • One thing I forgot to ask - are there any catch statements I need in the downloading image block of code? What if the URL can't have a connection opened to it? What if there is no input stream to retrieve? Don't catch statements need to – Keeb13r Nov 22 '10 at 14:19
  • Well, if `productLookup` throws an exception you need to work out what should happen.. use a default image perhaps? – dacwe Nov 22 '10 at 14:26
  • What types of exceptions should I be throwing, though? Instantiating both the "input" and "output" variables can fail, can't they? Same goes for the reading/writing portion of that code block. – Keeb13r Nov 22 '10 at 15:06
2

Looks like simply referring to the image file name when trying to read it was not enough, and I had to call getFilesDir() to get the path of the file storage. Below is the code I used:

String path = context.getFilesDir().toString();
String fileName = cursor.getString(cursor.getColumnIndex(DbAdapter.KEY_PRODUCT_ID));

if (fileName != null && !fileName.equals("")) {
    Bitmap bMap = BitmapFactory.decodeFile(path + "/" + fileName);
    if (bMap != null) {
        thumbnail.setImageBitmap(bMap);
    }
}
Keeb13r
  • 235
  • 3
  • 8
  • 18
  • Then my question was valid: "Write to the log so you are sure that you have write and read the same file?". – dacwe Nov 21 '10 at 09:03
  • Originally, I wasn't aware that anything more than the filename was needed and I thought that the application was self-aware of the specific path where the file would be. – Keeb13r Nov 21 '10 at 20:47
-1
void  writeToFile(Bitmap _bitmapScaled)
    {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        _bitmapScaled.compress(Bitmap.CompressFormat.PNG, 40, bytes);
        try{
        File f;
        //you can create a new file name "test.jpg" in sdcard folder.
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            f =new File(android.os.Environment.getExternalStorageDirectory(),"FamilyLocator/userimages/"+imageName);
        else
         f = new File(Environment.getDataDirectory(),"FamilyLocator/userimages/"+imageName);

        f.createNewFile();
        //write the bytes in file
        FileOutputStream fo = new FileOutputStream(f);
        fo.write(bytes.toByteArray());
        // remember close de FileOutput
        fo.close();
        }catch(Exception e){e.printStackTrace();}
    }
Yuvraj Kakkar
  • 1,123
  • 1
  • 11
  • 24
  • This code is not really relevant to the question and does not even compile. I do think there's value in outlining the algorithm to persist the bitmap for future use, however. – IcedDante May 08 '17 at 14:45