Tuesday, 29 November 2016

Download an image from the web using AsyncTask

One of the powerful things we can do with Android devices is to connect to the cloud and get information from it. A very common task is to download content from the web, like an image for example.

In order to simplify the operation, Android Studio has a built in class that allows us to perform these tasks in the background, without interrupting the user experience.

I'm talking about the AsyncTask class.

This class is the prefect tool to use whenever we need to perform background tasks. It is a simpler and more efficient way to run code in a separate thread.

Let's start by selecting an image from the web, any is fine. By typing "universe" in google, this is one of the pictures that came up:



And this is its address: https://i.ytimg.com/vi/uSJSPRQdLd8/maxresdefault.jpg.

In our project, first thing we need to do is to add internet permission in the manifest:

1
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

This is necessary of course as we are going to need to access the web to connect to the image link.

Next we create our layout. Very simple, we will only need an ImageView and a ProgressBar, which will be used to show the download progress to the user.

This is the XML file of our MainActivity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:id="@+id/progressBar"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

We can now check the DownloadImage class, which is a child of AsyncTask.

This is the class responsible of downloading the content.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class DownloadImage extends AsyncTask<String, Integer, Bitmap> {

    Activity act;
    ProgressBar bar;
    ImageView image;

    public DownloadImage(Activity act)
    {
        this.act = act;
        image = (ImageView) act.findViewById(R.id.imageView);
        bar = (ProgressBar) act.findViewById(R.id.progressBar);
    }
    
    @Override
    protected void onPostExecute(Bitmap bitmap) {   

        bar.setVisibility(View.GONE);
        image.setImageBitmap(bitmap);
        
    }

    @Override
    protected void onPreExecute() {

        Toast.makeText(act,"Downloading...",Toast.LENGTH_SHORT).show();
        bar.setVisibility(View.VISIBLE);
        bar.setMax(100);
    }

    @Override
    protected void onProgressUpdate(Integer... values) {

    bar.setProgress(values[0]);

    }

    @Override
    protected Bitmap doInBackground(String... params) {

        Bitmap bmp = null;

        int count = 0;

        ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);

        try {

            URL myUrl = new URL(params[0]);
            HttpURLConnection con = (HttpURLConnection) myUrl.openConnection();
            con.setRequestProperty("Accept-Encoding", "identity");  //Allow to know file size through getContentLength
            con.connect();

            byte[] b = new byte[1024];
            InputStream in = con.getInputStream();

            int total = con.getContentLength();

            while((count = in.read(b))!= -1)
            {
                baos.write(b, 0, count);
               
                publishProgress(100*baos.toByteArray().length/total);
            }

            bmp = BitmapFactory.decodeByteArray(baos.toByteArray(),0,baos.toByteArray().length);

        } catch (MalformedURLException e) {

            e.printStackTrace();
        } catch (IOException e) {

            e.printStackTrace();
        }
        return bmp;
    }
}

First thing we notice is that the AsyncTask class uses generics. The three parameters assigned can be read as such: AsyncTask<Parameters,Progress,Result>.


  • Parameters is what is sent to the task to work with. In my case, it's a String, as I will pass the link to the image, in String format.
  • Progress is the type of variable used as parameters in the onProgressUpdate method. This value (or values) are usually passed to a PrograssBar or a ProgressDialog.
  • Result: is the returned type of the task. This type will be the parameter of the method onPostExecute, as we will soon see.
When we extend this class we must implement at least one method: doInBackground(...).

Even though this appears to be a method like many others, it actually runs on a separate thread. Whatever code is between its brackets, it will not interfere with the UI thread. As you can see on line 38, this method accepts a parameter of type String, as I described previously when discussing generics. Also, we can see that this method is of type Bitmap as this is what we are going to return as our result.

Before exploring the funtion in details, let's have a quick look at the other two overridden methods.

The first one is onPreExecute(). This function is invoked immediately before the doInBackground and it runs on the main thread. Therefore, here we can manipulate UI elements. In my app, I simply display a Toast to let the user know that downloading has started.

The other method is onPostExecute(...). This one, not surprisingly, run immediately after the background thread has returned.  The parameters passed in the method is whatever is returned by the doInBackground(...) method and, at this point, we can process the result. this method also runs on the UI thread. In our example, I made the progress bar disappear (line 26) and I set the bitmap to our ImageView.

Lastly, the onPublishProgress method, which simply takes the values passed in and applies them to our progress bar. This parameter is calculated in the doInBackground method and passed in invoking publishProgress(...).

Obviously, the doInBackground method is where the downloading occurs. To perform such a task I used a ByteArrayOutputStream, which will hold all the bytes of the image. In order to connect to the link, we first grab the String which is passed in as a parameter and create a URL with it (line 48).
.
The HttpUrlConnection is used to create the actual connection (line 49). The next line is used to allow us to know the size of the content we are linking to, in our case the image. This is going to be used soon to calculate the download progress.

After launching the connection, we create a byte array, which acts as our buffer and we get the InputStream. Also, we have a new variable, called total, which represents the content size.

Between line 58 and 63 there's a while loop which reads the content from the InputStream. We use the read(buffer) method of the input stream to get the first set of bytes which are converted into a int (count) and we check whether the value is -1: if that's the case, we have reached the end of the content. If not, we can simply write those bytes into our ByteArrayOutputStream. 

Finally, on line 62, we invoke the method publishProgress(...) which will send the values passed in (an int in our case) to the onPublishProgress method we overrode earlier. The value passed in is nothing but a percentage of the bytes read to the total size of the content.

Once the loop has completed the task, we create a bitmap and return it.

Last thing we need to do start the whole process from the MainActivity class. To do so, we simply need 2 lines of code:


1
2
DownloadImage di = new DownloadImage(this);
        di.execute("https://i.ytimg.com/vi/uSJSPRQdLd8/maxresdefault.jpg");

Place these lines in your OnCreate method of the activity and that is all. The execute method is used to invoke doInBackground. DO NOT call the method directly otherwise it will not run in a saparate thread as described before.

Below, a gif showing the result:



Conclusion

Accessing web content is one of the most powerful things we can do in Android. Using AsyncTask greatly simplifies the process also allowing us to cancel ongoing tasks. When using this class, some important things to remember:

  • doInBackground must be implemented
  • doInBackground runs on a separate thread, no UI elements should be edited here
  • onPostExecute and onPreExecute run on the main thread
  • All the actions that should be performed one the task is done should be placed inside the onPostExecute method: placing them somewhere else in the activity class could interfere with the main thread giving you unwanted results. 


Tuesday, 22 November 2016

The "Password Keeper" app

I put together a small app following the previous tutorials on database. This is a very simple application which will store all your accounts and password, just like the one we saw in the previous posts.

It is just a small project I had fun working on.




The app will allow you to store multiple password, edit them and will require a master password to access the whole content.

The APK file is downloadable here.

Friday, 11 November 2016

Notifications

Notifications are one of the ways the app can communicate information to the user. What makes notifications different is that they appear outside of the application UI.

In this simple example, we create a button which will send a notification to the user. Once the notification is clicked on, the device will go back to the main activity.

Let's take a quick look to the layout file first:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Notified!"
        android:id="@+id/notButton"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

This is the button I was talking about earlier.

The whole experiment is in a single class, our MainActivity:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Button b;

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

        b = (Button)findViewById(R.id.notButton);

        b.setOnClickListener(this);
    }

    @SuppressLint("NewApi")
    void NotifyMe()
    {

        Intent intent = new Intent(this, MainActivity.class);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

        stackBuilder.addParentStack(MainActivity.class);

        stackBuilder.addNextIntent(intent);
        PendingIntent pIntent =stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);

        Notification not = new Notification.Builder(this)
                .setContentTitle("This is the notification title")
                .setContentText("This is the content text")
                .setSmallIcon(R.drawable.icon)
                 .setContentIntent(pIntent)
                 .setAutoCancel(true).build();

        NotificationManager nm =(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        nm.notify(0,not);

    }

    @Override
    public void onClick(View v) {

        if(v.getId() == R.id.notButton)
            NotifyMe();
    }
}

In the onCreate() I simply assign the Button reference and set its OnClickListener to the current class.

When the button is pressed, the method NotifyMe() is called.

In order to create a notification we use the Notification class and its Builder, in a similar way we create dialogs. While creating the notification we can assign other properties to it (line28 onwards), for example, a title, a message an icon, which will appear on the notification slider and a content intent. The latter is a pending intent called whenever the user clicks on the notification itself.

Observe where the title and text will appear in the notification slider:



The intent is created on line 19 and it is a standard way to spawn a new activity. In our case, the MainActivity. What happens after (line 21 - 26) is that we create an artificial stack for the activities we are launching  and we prevent the new activity to spawn on top of the other one. By doing so, once the user has pressed the notification and it's redirected to the main activity, if the back button is pressed, the application will shut down and the user will be sent to the home screen.

Finally, in order to execute the notification, we get the NotificationManager (line 35) and call the method notify(...). The parameters passed in this functions are: the notification id, which is used, for example, to cancel a notification through the notification manager, and the notification object itself, created previously.


Conclusion

Notifications are one of the most popular ways to notify the user. There are other things we can do to notifications, like apply expanded layouts or add inline reply actions, however, it is often enough to use simple notifications in order to communicate to the user, launching other activities to handle the following actions.