quarta-feira, 27 de março de 2013

Image Downloader

Hello people,

This post demonstrates like to use the DownloadManager class available in Android SDK. This class is very powerful and enable us to download files from Internet to our device. Our application will have a main screen with one CheckBox to define whether our downloads will be done only if a WI-FI connection be available and one Button that starts the download. The other activity will be called when the notification area is clicked and will show the filename name and to enable cancelling the download.

You will see at this post:

  • Writing in shared preferences
  • Downloading a file
  • File System

The code used in this post can be downloaded here.

At first, is necessary to write the permissions on the manifest file.


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

I believe that these permissions are very easy to understand, because their names are very deductible.

I created a BroadcastReceiver to receiver all download notifications. Purposely, I registered the receiver on the manifest, so, even that our application is closed, it will be called when the user click on the notification area. When the user clicks on the notification area, the download id is retrieved and checked whether it was started by our application.


@Override
public void onReceive(Context context, Intent intent) {
if (!checkDownloadID(context))
return;
String action = intent.getAction();
if (action == DownloadManager.ACTION_DOWNLOAD_COMPLETE)
downloadCompletedOrCancelled(context, intent);
if (action == DownloadManager.ACTION_NOTIFICATION_CLICKED)
downloadNotificationClicked(context, intent);

}



private void downloadNotificationClicked(Context context, Intent intent) {

String extraID = DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS;
long[] references = intent.getLongArrayExtra(extraID);
for (long reference : references) {
Intent intentDownloadDetails = new Intent(context,
DownloadDetailsActivity.class);
Bundle bundle = new Bundle();
bundle.putLong(General.EXTRA_DOWNLOAD_ID_KEY, reference);
intentDownloadDetails.putExtras(bundle);
intentDownloadDetails.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentDownloadDetails);
}
}


Note that the DownloadDetailsActivity is called when the notification area is clicked by the user.

When the user starts a new download on the MainActivity, the download id is stored on the preferences. It is used when our application starts, to check if the download manager is controlling a download from our application.

The code block below is very interesting because it has a lot of important tips. I commented this code purposely.


buttonStartDownload.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if (!checkConnection())
return;

// create a downloadManager reference and sets it...
// DownloadManager downloadManager;
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new Request(Uri.parse(url));

if (checkBoxDownloadWifiOnly.isChecked())
request.setAllowedNetworkTypes(Request.NETWORK_WIFI);

// allows that the file be scanned by media scanner...
request.allowScanningByMediaScanner();

//set the details of notification area...
request.setTitle(getResources().getText(R.string.downloadImage));
request.setDescription(getResources().getText(
R.string.nasaImageForDownload));

// saves the image on the public directory of pictures...
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_PICTURES, "image.jpg");

// disables the controls...
buttonStartDownload.setEnabled(false);
checkBoxDownloadWifiOnly.setEnabled(false);

// starts the download and use the shared preferences to persist
// its key.
long reference = downloadManager.enqueue(request);
Editor editor = preferences.edit();
editor.putLong(General.PREFERENCE_DOWNLOAD_ID_KEY, reference);
editor.apply();

// finishes the activity...
finish();

}
});

I believe that you may download the code and run it.

Good luck!




terça-feira, 12 de março de 2013

Creating a currency converter application

Hello people,

There's a long time since the last post, but I have a lot of new things to show here. In the last posts, I wrote about specifics subjects, but today I'll write about a lot of subjects  while we build a application.
Like this post title said, we will build a application that will to convert a currency to other. For this we will approach:


  • Internet resources
  • Network State
  • Shared Preferences
  • Preferences Framework
  • Broadcasts and Receivers


You can download this application clicking here.

Our application will have a principal screen, where the user can see the base currency and the other currencies with yours respective conversion rates.

Furthermore, the user can modify the program settings, choosing the base currency, the currencies that will be converted and the interval between screen refreshes.

First of all, we will see the AndroidManifest.xml. There, we must have declare the permissions used by our application. If we don't declare this permissions our application can not be executed.

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

We will use the android.permission.INTERNET when our application to request a URL and we will use android.permission.ACCESS_NETWORK_STATE to receive broadcasts when network state change.

I won't explain each class contained in this project, but it's very important to know some them.

ConnectivityMonitorReceiver to MainActivity


I built this class to manage the connection state. This class is a extension from a BroadcastReceiver class and its function is receive the broadcast with the connection state and send a broadcast to application with the treated connection state information. I did the integration between this class and the application by broadcasts interchanging.

The MainActivity has a registred receiver to handle the connection state. When the MainActivity receives the broadcast automatically is showed in a textView the connection state.

CurrencyAsyncTask to MainActivity


The CurrencyAsyncTask class has been built to execute asynchronous actions on  Internet. When it is need to do a request to Internet is need execute this action in a separate thread, otherwise a exception of type NetworkOnMainThreadException will be thrown. This class extends AsyncTask and after the main action is executed, a broadcast is sent to application with all data related a specific currency.

In this class, the two most important points are the code used to access Internet information and the fact of this class be executed in a separated thread due be extended from AsyncTask.

See this code below, overriding doInBackground we can put Internet requests here:

@Override
protected String doInBackground(String... args) {

// get parameters passed...
String fromCurrency = args[0];
String toCurrency = args[1];

// set the url from service passing parameters values...
String urlstring = "http://currencies.apps.grandtrunk.net/getlatest/";
urlstring += fromCurrency + "/";
urlstring += toCurrency;

// creates URL object to retrieve data...
URL url;
try {
url = new URL(urlstring);

URLConnection connection;
connection = url.openConnection();
HttpURLConnection httpConnection = (HttpURLConnection) connection;

int responseCode = httpConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream in = httpConnection.getInputStream();

BufferedReader r = new BufferedReader(new InputStreamReader(in));
String line = r.readLine();

Intent intent = new Intent(RATE_CALCULATED_ACTION);
intent.putExtra(RATE_VALUE_KEY, 1.0/Double.parseDouble(line));
intent.putExtra(CURRENCY_FROM_KEY, fromCurrency);
intent.putExtra(CURRENCY_TO_KEY, toCurrency);

if (context != null)
context.sendBroadcast(intent);

return line;
}

} catch (NumberFormatException ex) {

} catch (NetworkOnMainThreadException ex) {

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

}

return ("");
}


I create a most friendly method to get conversion rate from this class. This method is described below:

// this method starts a request to calculate rate. The result will be
// retrieved by a broadcastReceiver...
public void getConversionRate( String fromCurrency,
String toCurrency) {

execute(fromCurrency, toCurrency);

}

When I call execute, automatically the doInBackground is called.

MainActivity


We will see the MainActivity now. At this class it's the local where all happens, further because this class is responsable by display the main screen of application and  is in this local that broadcasts are received and treated.

I won't paste the code here, but you can see that in onCreated method are created the receivers. The start point to the process of send broadcast and receive broadcasts is the method refreshRates(). When the refreshRates is called, all currencies chosen by the user are sent to CurrencyAsync by the method getConversionRate. As each one of these requests are executed, a broadcast is received by the currencyAsyncTaskReceiver and the data is showed in the MainActivity.

It's best practice register the receivers on the onResume method and unregister on the onPause method if your application don't need of these receivers when the application isn't visible.

Preferences


The preferences screen has been using the PreferenceFramework from Android. First of all, it's very important know that the layout of preference screens aren't stored in res\layout folder. The layout of preference screens are stored in res\xml folder.

I used to build the preference screen of this application two different views, ListPreference and MultiSelectListPreference. Both work similarly, but ListPreference enable user select only one option and MultiSelectListPreference enable user select various options as it to want. The class that display the preferences is SettingsActitivity.

I hope have help you!




Regards!