Download image and save it to sdcard(phone storage) without any library in Android

In this post, we are going to download image from server to your phone’s local storage. Here we are not using any third party library but use “HttpURLConnection” from java.net package. So let’s get started.

download and save image sdcard without library
download and save image sdcard without library

First we will create progress dialog to display the progress of image being downloaded. In this dialog we will show percentage and total as well as remaining size of the image being downloaded, so the user will get the idea of how much time remaining and how big is the image.

So here is the code for the ProgressDialog.

ProgressDialog pd = new ProgressDialog(DownloadImageActivity.this);
pd.setMessage("Downloading image, please wait ...");
pd.setIndeterminate(true);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setCancelable(false);
pd.setProgressNumberFormat("%1d KB/%2d KB");

Here first we set the message “Downloading image, please wait …”, then we set the indeterminate flag to true because we do not know the size of the image at first. Then comes the progress style, it will be ProgressDialog.STYLE_HORIZONTAL. It will display the horizontal progress bar. The we set the cancelable flag to false so that user can not cancel the dialog while image is downloading. Then we set the progress number format, which will display the total size of the image and remaining size of the image. Here we display the data in kilobyte(KB) so we use the format “%1d KB/%2d KB”.

Now we will create the AsyncTask which will download the image. Here we use HttpURLConnection which is from java.net package and does not require any third party library to download image.

private class DownloadImage extends AsyncTask<String, Integer, String> {

  private Context c;

  private DownloadImage(Context c) {
    this.c = c;
  }

  @Override
  protected String doInBackground(String... sUrl) {
    InputStream is = null;
    OutputStream os = null;
    HttpURLConnection con = null;
    int length;
    try {
      URL url = new URL(sUrl[0]);
      con = (HttpURLConnection) url.openConnection();
      con.connect();

      if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
        return "HTTP CODE: " + con.getResponseCode() + " " + con.getResponseMessage();
      }

      length = con.getContentLength();

      pd.setMax(length / (1000));

      is = con.getInputStream();
      os = new FileOutputStream(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer.jpg");

      byte data[] = new byte[4096];
      long total = 0;
      int count;
      while ((count = is.read(data)) != -1) {
        if (isCancelled()) {
          is.close();
          return null;
        }
        total += count;
        if (length > 0) {
          publishProgress((int) total);
        }
        os.write(data, 0, count);
      }
    } catch (Exception e) {
      return e.toString();
    } finally {
      try {
        if (os != null)
          os.close();
        if (is != null)
          is.close();
      } catch (IOException ioe) {
      }

      if (con != null)
        con.disconnect();
    }
    return null;
  }

  @Override
  protected void onPreExecute() {
    super.onPreExecute();
    pd.show();
  }

  @Override
  protected void onProgressUpdate(Integer... progress) {
    super.onProgressUpdate(progress);
    pd.setIndeterminate(false);
    pd.setProgress(progress[0] / 1000);
  }

  @Override
  protected void onPostExecute(String result) {
    pd.dismiss();
    if (result != null) {
      Toast.makeText(c, "Download error: " + result, Toast.LENGTH_LONG).show();
    } else {
      Toast.makeText(c, "Image downloaded successfully!", Toast.LENGTH_SHORT).show();
      Bitmap b = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer.jpg");
      iv.setImageBitmap(b);
    }
  }
}

Here we open the connection of the image url and then get the input stream and content length of the image. Then we create file on our device’s storage and then write the data to its output stream. Here we also calculate how much image is downloaded and how much is remaining and update the same in the progress dialog.

We use “publishProgress” in doInBackground that invokes the method “onProgressUpdate” with the progress value and in that method we will update the progress dialog. Here we get the content length of the image using “getContentLength()” method of HttpURLConnection class which provides data in bytes. Then we divide it with 1000 to get the image size in KB. And then we start reading the data from input stream and put a counter to calculate how much data is downloaded and update it in the UI.

At the end we displayed the toast with the success message. Please make sure that you have gain the storage permission before executing the above AsyncTask.

Here is the full code for above functionality.

public class DownloadImageActivity extends AppCompatActivity {

  private ProgressDialog pd;
  private ImageView iv;

  private DownloadImage di;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_download_image);

    iv = findViewById(R.id.iv);

    pd = new ProgressDialog(DownloadImageActivity.this);
    pd.setMessage("Downloading image, please wait ...");
    pd.setIndeterminate(true);
    pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    pd.setCancelable(false);
    pd.setProgressNumberFormat("%1d KB/%2d KB");

    di = new DownloadImage(DownloadImageActivity.this);
    di.execute("https://images.unsplash.com/photo-1536998533868-95cde0d71742?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=69a455127db97a5cc05e2d3c9c9ef245&auto=format&fit=crop&w=4000&q=80");

    pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
      @Override
      public void onCancel(DialogInterface dialog) {
        di.cancel(true);
      }
    });
  }

  private class DownloadImage extends AsyncTask<String, Integer, String> {

    private Context c;

    private DownloadImage(Context c) {
      this.c = c;
    }

    @Override
    protected String doInBackground(String... sUrl) {
      InputStream is = null;
      OutputStream os = null;
      HttpURLConnection con = null;
      int length;
      try {
        URL url = new URL(sUrl[0]);
        con = (HttpURLConnection) url.openConnection();
        con.connect();

        if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
          return "HTTP CODE: " + con.getResponseCode() + " " + con.getResponseMessage();
        }

        length = con.getContentLength();

        pd.setMax(length / (1000));

        is = con.getInputStream();
        os = new FileOutputStream(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer.jpg");

        byte data[] = new byte[4096];
        long total = 0;
        int count;
        while ((count = is.read(data)) != -1) {
          if (isCancelled()) {
            is.close();
            return null;
          }
          total += count;
          if (length > 0) {
            publishProgress((int) total);
          }
          os.write(data, 0, count);
        }
      } catch (Exception e) {
        return e.toString();
      } finally {
        try {
          if (os != null)
            os.close();
          if (is != null)
            is.close();
        } catch (IOException ioe) {
        }

        if (con != null)
          con.disconnect();
      }
      return null;
    }

    @Override
    protected void onPreExecute() {
      super.onPreExecute();
      pd.show();
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
      super.onProgressUpdate(progress);
      pd.setIndeterminate(false);
      pd.setProgress(progress[0] / 1000);
    }

    @Override
    protected void onPostExecute(String result) {
      pd.dismiss();
      if (result != null) {
        Toast.makeText(c, "Download error: " + result, Toast.LENGTH_LONG).show();
      } else {
        Toast.makeText(c, "Image downloaded successfully!", Toast.LENGTH_SHORT).show();
        Bitmap b = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer.jpg");
        iv.setImageBitmap(b);
      }
    }
  }
}

If you find any problem or doubt, please mention in comments. Do not forget to share!

Download Source Code: https://github.com/dakshbhatt21/a-computer-engineer

Create PDF and save to the sdcard in Android

Create PDF file and save it to sdcard in Android
Create PDF file and save it to sdcard in Android

In this article, we will create PDF file of the current view and then save it to the sdcard(internal storage) as pdf file. Here we use PdfDocument (https://developer.android.com/reference/android/graphics/pdf/PdfDocument) which added from API 19(Android KitKat). So you can use it above KitKat version of Android.

First of all we will create Bitmap of the view which we want to save on the PDF file. So for that we need to get the heigh and width of the view, so here we do all the operation in the ‘onWindowFocusChanged’ method instead of ‘onCreate’ method. So the basic structure of the file will look like this.

private RelativeLayout rlContainer;

private int width, height;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_pdf);

  rlContainer = findViewById(R.id.rl_container);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);

  width = rlContainer.getWidth();
  height = rlContainer.getHeight();
}

Here we have on RelativeLayout which contains all the elements we need to print on the PDF file. So we will initialise it in onCreate and then get its width and height in onWindowFocusChanged. If you try to get the width and height in onCreate, it will be 0 because at that time the view did not rendered on the screen. So we will use the above method for that.

Now we will create a Bitmap of that relative layout using following code. Later we will draw this Bitmap on PDF file using Canvas of the PDF file.

Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c1 = new Canvas(b);
rlContainer.draw(c1);

Now we initialise PdfDocument and create pages in that PDF file. For that we use PdfDocument.Page class. If we have 5 pages in our PDF file then we have to create 5 object of PdfDocument.Page and add them to PdfDocument. Then we fetch the canvas of the PDF page and draw our Bitmap on it. Please check the code below.

PdfDocument pd = new PdfDocument();

PdfDocument.PageInfo pi = new PdfDocument.PageInfo.Builder(width, height, 1).create();
PdfDocument.Page p = pd.startPage(pi);
Canvas c = p.getCanvas();
c.drawBitmap(b, 0, 0, new Paint());
pd.finishPage(p);

pd.close();

Here we can iterate the code from line 3 to line 7 to add more pages to the PDF files. After that we need to close the PdfDocument. This will draw our bitmap to the PDF file. Now to write this PdfDocument to the file on sdcard(internal storage), we need to create a File and write PdfDocument to its OutputStream. But make sure that you write this code before closing the PdfDocument pd.close(). Here is the code for that.

try {
  //make sure you have asked for storage permission before this
  File f = new File(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer-pdf-test.pdf");
  pd.writeTo(new FileOutputStream(f));
} catch (FileNotFoundException fnfe) {
  fnfe.printStackTrace();
} catch (IOException ioe) {
  ioe.printStackTrace();
}

pd.close();

Please make sure that before running above code, you have the storage permission granted by user otherwise you will get error. Please check for the runtime Android permission for that. Now your PDF file is saved at your provided location on your sdcard(internal storage).

Here is the full code.

private RelativeLayout rlContainer;

private int width, height;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_pdf);

  rlContainer = findViewById(R.id.rl_container);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
  super.onWindowFocusChanged(hasFocus);

  width = rlContainer.getWidth();
  height = rlContainer.getHeight();

  Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  Canvas c1 = new Canvas(b);
  rlContainer.draw(c1);

  PdfDocument pd = new PdfDocument();

  PdfDocument.PageInfo pi = new PdfDocument.PageInfo.Builder(width, height, 1).create();
  PdfDocument.Page p = pd.startPage(pi);
  Canvas c = p.getCanvas();
  c.drawBitmap(b, 0, 0, new Paint());
  pd.finishPage(p);

  try {
    //make sure you have asked for storage permission before this
    File f = new File(Environment.getExternalStorageDirectory() + File.separator + "a-computer-engineer-pdf-test.pdf");
    pd.writeTo(new FileOutputStream(f));
  } catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
  } catch (IOException ioe) {
    ioe.printStackTrace();
  }

  pd.close();
}

If you find any problem or doubt, please mention in comments. Do not forget to share!

Download Source Code: https://github.com/dakshbhatt21/a-computer-engineer

Various file operations(create, rename, delete, list files) in Android

You can perform many file operation in Android. I described few of them here.

  • Create directory
  • Create directory and sub-directories in one instance
  • Rename directory
  • Delete directory
  • List the contain of that directory

The code for the first one, create a single directory at the root of your external storage directory.

File dirMain = new File(extdir + File.separator + "com.acomputerengineer");
if(!dirMain.exists())   {
  boolean mkdir = dirMain.mkdir();
  if(mkdir)   {
    Log.e("test", "Directory " + dirMain.getPath() + " created successfully");
  }
  else if(!mkdir) {
    Log.e("test", "Directory " + dirMain.getPath() + " not created");
  }
}

Result:

Screenshot of directory created
directory created

 

Now creating directory and sub-directories at the same time.

//example 1
File dirWithSubdir1 = new File(extdir + File.separator + "com.acomputerengineer" + File.separator + "testDir1" + File.separator + "testSubdir1");
if(!dirWithSubdir1.exists()) {
  dirWithSubdir1.mkdirs();
}

//example 2
File dirWithSubdir2 = new File(extdir + File.separator + "com.acomputerengineer" + File.separator + "testDir2" + File.separator + "testSubdir2" + File.separator + "testSubSubdir2");
if(!dirWithSubdir2.exists()) {
  dirWithSubdir2.mkdirs();
}

Result:

Screenshot of subdirectory created
subdirectory created

 

Now rename the directory ‘dirWithSubdir1’ from the above example.

File renameDir = new File(extdir + File.separator + "com.acomputerengineer" + File.separator + "newTestDir1");
dirWithSubdir1.renameTo(renameDir);

Result:

Screenshot of rename directory
rename directory

 

Now deleting the directory.

dirWithSubdir1.delete();

Result:

Screenshot of delete directory
delete directory

 

Now listing the content of the directory by logging them into logcat.

String[] dirMainFiles = dirMain.list();
for (int i = 0; i < dirMainFiles.length; i++)   {
  Log.e("test", "file " + i + " :" + dirMainFiles[i]);
}

Result:

Screenshot of list content of directory
list content of directory

 

Screen Shot of list content of directory
list content of directory

Resize image during decode from file to Bitmap in android

java.lang.OutOfMemoryError is the nightmare for any android developer. During image operation we may get this out of memory exception any time because our app uses all the memory allocated by android system to that app so it will give you out of memory error.

We encounter this error when we try to decode larger image using its path of sdcard to Bitmap. So we can skip this error by decoding image to smaller size.

We can avoid this problem or better to say restrict it to a level by creating Bitmap of required size and not the actual size.

Images taken from camera now a days are of more than 3000 and 4000 in resolution. So if we want to display them in GridView or ListView, we have to sample them down so it won’t create OOM or does not hang the UI.

For that we will use sample size to BitmapFactory.Options while decoding the image. It will load your image in smaller size compare to the original image and save your memory.

Google suggest that sample size should be in power of 2 so the below code contains the logic of creating sample size according to the maximum size you provide and then it will load your image with the resolution near to that maximum size.

Here is the direct function I create that you can use in your project by providing the file path of your image. You can find out how get the file path of any image from this article: Pick image from gallery before and after Kitkat version in Android

private Bitmap decodeFile(String imgPath)
{
  Bitmap b = null;
  int max_size = 1000;
  File f = new File(imgPath);
  try {
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();
    int scale = 1;
    if (o.outHeight > max_size || o.outWidth > max_size)
    {
      scale = (int) Math.pow(2, (int) Math.ceil(Math.log(max_size / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();
  }
  catch (Exception e)
  {
  }
  return b;
}

Share your views in comments!