I was approached by a dear friend of mine with an interesting problem. She is a chef and, as most of chefs do, likes to go around and try new food in new places all the time. However, she usually picks her favorite places from home, perhaps checking reviews on Google or other popular websites. The problem is that, when she happens to be around a particular place she had intention to go visit, she totally forgets about it and perhaps drive past it without noticing.
So, she asked me to make a simple app that allows her to pick places from the map and tracks her once she leaves her house and, most importantly, that notifies her whenever she is close to a placed she saved.
And that's what this application does. In addition, the app will display a place details, such as business contacts, photos and more, if required.
Here are a few screenshots:
Some information about it:
- For the sake of experimenting, I used 2 different ways to obtain the user location. To locate the user position while interacting with the app, I used the Google Location API, the so called fused location provider. This method is apparently more efficient than the classic Android SDK with the Location Manager. Nonetheless, the latter was used in the background service, in order to track the user while the app is not on foreground.
- To pick a place from the map I used the Google Place Picker, which I believe is a great tool considering that it is specifically designed to do exactly that.
- In order to retrieve information about a place, I made use of the Google Web Services API. After performing an HTTP request, the server will send information back in JSON format. All I had to do was manipulating the data and display it to the user. With this method, however, only 5 reviews and 10 photos for each places are sent back, which for this particular application is enough.
- To obtain pictures a similar process was used: from the previous step, part of the JSON response is a list of references for the pictures. Once obtained, these reference can be used to make additional requests to get the actual images (more on this here).
- The activity that displays all the detail about a selected place was another experiment, specifically with the new UI tools such as Coordinator Layout and custom behaviors. It was both challenging and rewarding getting all those views to do what I had in mind.
- To display the pictures to full screen and to add zoom in/out features I opted for a quick and easy solution: this amazing library by Chris Banes fixed all my problems in no time.
The application is downloadable at this link.
Android Canteen
For Android developer enthusiasts. This blog is set up so I can create my own "Android reference book" and to have an easily reachable place in which I can store the knowledge I gather. Hopefully, it will help you as well
Saturday, 13 May 2017
Thursday, 13 April 2017
Custom behavior for Coordinator Layouts
Using the new CoordinatorLayout for your Android app is a great way to create interesting interactions between your views in the activity.
According to the official documentation, it is a super powered FrameLayout.
It allows the developer to create dependancies among the various view that populate the layout, creating dynamic effects which ultimately result in a more immersive user experience.
Recently, I'm developing an application, which I will post on this blog, and I wanted to experiment with this tool and I ended up creating a custom behavior for my views. Creating behaviors will enable you to, quite literally, do whatever you want with your views in response to other views.
Before I post any code, this is the effect I was trying to achieve:
Let's have a look at the code:
We create a class that extends CoordinatorLayout.Behavior<CardView>, and we pass in CardView as that is the type of view we are writing the behavior for.
Although we do not call the constructor directly it is important to implement it as it is invoked automatically when the layout is being inflated.
We then override the method layoutDependsOn, which is a method that is called every time something changes in the layout and we must return the class of the view our behavior will react in response to. In our case, the NestedScrollView.
When this method returns true, OnDependentViewChanged is called immediately and this is where we write the reaction. In our case, we simply check the distance of the nested scroll being dragged from the top of the screen, we get a percentage of the current position and set it as size X and Y for our CardView.
In order to apply our behavior to the view we simply need to add a single line in the xml layout file:
The behavior is applied on line 3, directly to the CardView. It is very important to remember that every behavior we write must be applied to a direct child of the Coordinator Layout. For example, this would not work if we applied the custom behavior to the Linear Layout inside the CardView, regardless whether we modify the Java code accordingly.
According to the official documentation, it is a super powered FrameLayout.
It allows the developer to create dependancies among the various view that populate the layout, creating dynamic effects which ultimately result in a more immersive user experience.
Recently, I'm developing an application, which I will post on this blog, and I wanted to experiment with this tool and I ended up creating a custom behavior for my views. Creating behaviors will enable you to, quite literally, do whatever you want with your views in response to other views.
Before I post any code, this is the effect I was trying to achieve:
Let's have a look at the code:
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 | public class PlaceDetailsCardDisapperBehaviour extends CoordinatorLayout.Behavior<CardView> { float maximumDifference = 0; public PlaceDetailsCardDisapperBehaviour(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean layoutDependsOn(CoordinatorLayout parent, CardView child, View dependency) { return dependency instanceof NestedScrollView; } @TargetApi(Build.VERSION_CODES.M) @Override public boolean onDependentViewChanged(CoordinatorLayout parent, CardView child, View dependency) { int targetLoc[] = new int[2]; dependency.getLocationInWindow(targetLoc); int[] childLocation = new int[2]; child.getLocationInWindow(childLocation); float YThreshold = GlobalVariables.DpToPx(100); if(maximumDifference==0) maximumDifference = childLocation[1] - YThreshold; float currentDifference = childLocation[1] - YThreshold; float perc = currentDifference / maximumDifference; if(perc>1.0f) perc = 1.0f; else if(perc<0) perc = 0; child.setScaleX(perc); child.setScaleY(perc); return false; } } |
We create a class that extends CoordinatorLayout.Behavior<CardView>, and we pass in CardView as that is the type of view we are writing the behavior for.
Although we do not call the constructor directly it is important to implement it as it is invoked automatically when the layout is being inflated.
We then override the method layoutDependsOn, which is a method that is called every time something changes in the layout and we must return the class of the view our behavior will react in response to. In our case, the NestedScrollView.
When this method returns true, OnDependentViewChanged is called immediately and this is where we write the reaction. In our case, we simply check the distance of the nested scroll being dragged from the top of the screen, we get a percentage of the current position and set it as size X and Y for our CardView.
In order to apply our behavior to the view we simply need to add a single line in the xml layout file:
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 | <android.support.v7.widget.CardView android:id="@+id/placeDetailsCard" app:layout_behavior="com.blogspot.androidcanteen.interestpoints.PlaceDetailsCardDisapperBehaviour" app:layout_anchor="@+id/appbar" app:layout_anchorGravity="bottom|center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingLeft="6dp" android:paddingRight="6dp"> <me.grantland.widget.AutofitTextView android:text="Title" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/theTitle" android:textAppearance="@style/Base.TextAppearance.AppCompat.Large" android:gravity="center_horizontal" android:textStyle="normal|bold" tools:textSize="18sp" android:layout_weight="0.5" android:padding="2dp" /> <me.grantland.widget.AutofitTextView android:text="" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/theType" android:textAppearance="@style/Base.TextAppearance.AppCompat.Small" android:gravity="center_horizontal" android:textStyle="italic" tools:textSize="12sp" android:layout_weight="0.5" android:padding="2dp" /> </LinearLayout> </android.support.v7.widget.CardView> |
The behavior is applied on line 3, directly to the CardView. It is very important to remember that every behavior we write must be applied to a direct child of the Coordinator Layout. For example, this would not work if we applied the custom behavior to the Linear Layout inside the CardView, regardless whether we modify the Java code accordingly.
Tuesday, 7 February 2017
The Stocktake app
My latest project.
This application serves as an inventory manager and it is currently used by some friends of mine who works in bars. As they need to perform a stock count every week of the current amount of drinks and food in the premise, I provided them with this simple piece of software that facilitates their job.
The project if fully downloadable on this post, at the bottom of the page.
Description:
The app will allow the user to create multiple databases which can contain multiple inventories. These databases are exportable (as ".stock" files) and can be shared. Of course, the app will allow users to import databases with that extension. There is also one separate databases which contains all the items that are put in in order to let the user pick from the database whenever possible rather then type each and every time.
In order to quickly pre load the database of items, it is also possible to load all the information using a .txt file, which you will be able to also download and try out.
Once all the items have been put int the inventory, the application will calculate the total amount of each item for each location. For example, if we have 2 ltrs of Coke in the Restaurant and 3 in the Bar the app will display a total of 5 ltrs of Coke in the venue, and also show the correct amount for each location.
All this information is visible within the app or, more usefully, as a pdf file. The app will allow to export all the information regarding the inventory as a pdf file, with name of the person who performed the task, date and time and all the items listed alphabetically.
Also, a calculation of the amount of drinks to order is also displayed, based on the Top Stock value of each item.
Finally, since I wanted to experiment with cloud computing, I added an extra feature for authorization checking: once the app is started, an id is sent to an external server (I used Amazon Web Services) in order to check if the current user has permission to use the app. This is just something I wanted to try, and it's currently implemented in the downloadable version on this post.
I modified the logos which I replaced with the default android icon.
Here are some screenshots:
Please report any bug or problems.Also, if interested, I'll gladly create tutorial posts on how to create the app itself, let me know.
This application serves as an inventory manager and it is currently used by some friends of mine who works in bars. As they need to perform a stock count every week of the current amount of drinks and food in the premise, I provided them with this simple piece of software that facilitates their job.
The project if fully downloadable on this post, at the bottom of the page.
Description:
The app will allow the user to create multiple databases which can contain multiple inventories. These databases are exportable (as ".stock" files) and can be shared. Of course, the app will allow users to import databases with that extension. There is also one separate databases which contains all the items that are put in in order to let the user pick from the database whenever possible rather then type each and every time.
In order to quickly pre load the database of items, it is also possible to load all the information using a .txt file, which you will be able to also download and try out.
Once all the items have been put int the inventory, the application will calculate the total amount of each item for each location. For example, if we have 2 ltrs of Coke in the Restaurant and 3 in the Bar the app will display a total of 5 ltrs of Coke in the venue, and also show the correct amount for each location.
All this information is visible within the app or, more usefully, as a pdf file. The app will allow to export all the information regarding the inventory as a pdf file, with name of the person who performed the task, date and time and all the items listed alphabetically.
Also, a calculation of the amount of drinks to order is also displayed, based on the Top Stock value of each item.
Finally, since I wanted to experiment with cloud computing, I added an extra feature for authorization checking: once the app is started, an id is sent to an external server (I used Amazon Web Services) in order to check if the current user has permission to use the app. This is just something I wanted to try, and it's currently implemented in the downloadable version on this post.
I modified the logos which I replaced with the default android icon.
Here are some screenshots:
Conclusion
The application is downloadable for free here and you will also find a txt file which contains a list of items that can be used for the inventory.Please report any bug or problems.Also, if interested, I'll gladly create tutorial posts on how to create the app itself, let me know.
Friday, 30 December 2016
Transferring data with JSON
Javascript Object Notation, in short JSON is likely the most popular way to transfer data across different systems. Its syntax, based on JavaScript, makes it easy for humans to read it and it simplifies the way we can extract information from data
In order to demonstrate what we can do with JSON we need a server to query. I found this website which is set up exactly for our purpose. We will use the date and time service and we will need to send our request to this address: http://date.jsontest.com.
The server will respond with the information requested in JSON format, like so:
This is pretty much what objects look like in JavaScript. Also, we can visualize this like an HashMap, where, for example, on line 2, the key "time" will return the value "03:53:25 AM".
In our application we simply want to display the time and the date using two TextViews.
All can be achieved with a single class which, following our last tutorial, will extend AsyncTask once again in order to make our request asynchronous
Let's have a look at the class itself, called JSONDownloader:
First thing first, the constructor. We simply pass in the Activity which we use to get our TextViews references.
In the doInBackground method we send the request: first we create an URL object passing the first paramter of our var args which is going to be the address I linked above, you will see it in a minute in the MainActivity class. Calling the method open() form this object we get our HttpURLConnection object, which is used to get our InputStream and, consequently, our InputStreamReader. All the data collected is ultimately put into a String variable, called result which is the value returned by the function.
Our String now contains the information we wanted in JSON, so we need to find a way to extract what we need which, in our case, is a String containing the time information and one for the date.
Thankfully for us, Java has a very easy way to work with JSON with a built-in class of the same name.
As you can see in the onPostExecute method, we can create JSON object and pass in the constructor the String we just filled with the information.
Once we have the JSON object ready we can extrapolate values using the method get(..). You can see on lines 21 and 22 that, in order to get our date and time, we call that method passing in the keys "time" and "date", which are the 2 keys that need to be used to get our data as we saw on the website. At this point we are returned String values which we can place as texts in our TextViews.
Finally, in the MainActivity, we create the class object and execute the task:
In order to demonstrate what we can do with JSON we need a server to query. I found this website which is set up exactly for our purpose. We will use the date and time service and we will need to send our request to this address: http://date.jsontest.com.
The server will respond with the information requested in JSON format, like so:
1 2 3 4 5 | { "time": "03:53:25 AM", "milliseconds_since_epoch": 1362196405309, "date": "03-02-2013" } |
This is pretty much what objects look like in JavaScript. Also, we can visualize this like an HashMap, where, for example, on line 2, the key "time" will return the value "03:53:25 AM".
In our application we simply want to display the time and the date using two TextViews.
All can be achieved with a single class which, following our last tutorial, will extend AsyncTask once again in order to make our request asynchronous
Let's have a look at the class itself, called JSONDownloader:
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 | public class JSONDownloader extends AsyncTask<String, Void, String> { Activity act; TextView time,date; public JSONDownloader(Activity act) { this.act = act; time = (TextView) act.findViewById(R.id.textTime); date = (TextView) act.findViewById(R.id.textDate); } @Override protected void onPostExecute(String s) { super.onPostExecute(s); try { JSONObject j = new JSONObject(s); time.setText(j.get("time").toString()); date.setText(j.get("date").toString()); } catch (JSONException e) { e.printStackTrace(); } } @Override protected String doInBackground(String... params) { String result = ""; try { URL url = new URL(params[0]); HttpURLConnection connection; connection = (HttpURLConnection) url.openConnection(); InputStream in = connection.getInputStream(); InputStreamReader reader = new InputStreamReader(in); int data = reader.read(); while(data!=-1) { char c = (char)data; result += c; data = reader.read(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } } |
First thing first, the constructor. We simply pass in the Activity which we use to get our TextViews references.
In the doInBackground method we send the request: first we create an URL object passing the first paramter of our var args which is going to be the address I linked above, you will see it in a minute in the MainActivity class. Calling the method open() form this object we get our HttpURLConnection object, which is used to get our InputStream and, consequently, our InputStreamReader. All the data collected is ultimately put into a String variable, called result which is the value returned by the function.
Our String now contains the information we wanted in JSON, so we need to find a way to extract what we need which, in our case, is a String containing the time information and one for the date.
Thankfully for us, Java has a very easy way to work with JSON with a built-in class of the same name.
As you can see in the onPostExecute method, we can create JSON object and pass in the constructor the String we just filled with the information.
Once we have the JSON object ready we can extrapolate values using the method get(..). You can see on lines 21 and 22 that, in order to get our date and time, we call that method passing in the keys "time" and "date", which are the 2 keys that need to be used to get our data as we saw on the website. At this point we are returned String values which we can place as texts in our TextViews.
Finally, in the MainActivity, we create the class object and execute the task:
1 2 3 4 5 6 7 8 9 10 11 12 | public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); JSONDownloader d = new JSONDownloader(this); d.execute("http://date.jsontest.com/"); } } |
Conclusion
A very simple and effective example on how to use JSON to obtain data from the internet. We can of course send data as well and that is something that I will perhaps show later. JSON is rapidly becoming the standard used to send data across different systems and you can already find many websites that have APIs and they offer a wide variety of services (like news, weather information and more) that you can use in your app simply by sending http request and use JSON to process the data.
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:
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:
We can now check the DownloadImage class, which is a child of AsyncTask.
This is the class responsible of downloading the content.
First thing we notice is that the AsyncTask class uses generics. The three parameters assigned can be read as such: AsyncTask<Parameters,Progress,Result>.
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).
.
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:
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:
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.
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:
This is the button I was talking about earlier.
The whole experiment is in a single class, our MainActivity:
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.
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.
Subscribe to:
Posts (Atom)