Android Google map: Add weather data

Topics covered

NFC

Google map in Android

Weather tile

Add tile to a map

This post describes how to add weather layer to google map. We want to add weather information to a map so that we can have a look about clouds, temperature, pressure and so on.
In the previous post we talked about how to get weather information and how to parse the data to create a UI, now we want to add other weather tile to a map. To do it, we start creating a sample project that contains a map activity.
If we use Android Studio (as i suggest) it is very simple and fast.

Create Android Map project in Android Studio

The first step is creating a google map project in Android. As always, we select File->New project and after inserting some project information we land to this page:


Now we select Google Map Activity. Android Studio will create all the things to handle google map in our project. The last thing to do is to add the key to use the map. If you look in the files inside the project you will find the way to create your debug key. Once you have the key, you are ready to use the map.

Add weather tile to Google Map

The next step is adding weather tile to google map. To this purpose, we will use Open Weather Map as weather provider, but you could use other weather providers. We modify the setUpMap inside our MapsActivity (or the name of the class you used when you created the project) and add a new TileProvider. A Tile provider is essential an interface that we have to implement in our provider so that we fetch the image that covers the map. A tile is an image that is represented over the map. In the case of OWM, the URL we have to use to retrieve the image is:

http://tile.openweathermap.org/map/%s/%d/%d/%d.png

where %s represents the type of the tile we want to use like clouds, temperature and so on while the other three integers (%d) represent the zoom and x, y coords.
In our case, we use a UrlTileProvider that can be used to retrieve tiles from an HTTP connection. Our implementation is very simple:

    private TileProvider createTilePovider() {
        TileProvider tileProvider = new UrlTileProvider(256, 256) {
            @Override
            public URL getTileUrl(int x, int y, int zoom) {
                String fUrl = String.format(OWM_TILE_URL, tileType == null ? "clouds" : tileType, zoom, x, y);
                URL url = null;
                try {
                    url = new URL(fUrl);
                }
                catch(MalformedURLException mfe) {
                    mfe.printStackTrace();
                }

                return url;
            }
        } ;

Finally in our setUpMap() method we add our tile provider to the map:

tileOver = mMap.addTileOverlay(new TileOverlayOptions().
                tileProvider(createTransparentTileProvider()));

where tileOver is an instance of TileOverlay. Running the example we get:


Add weather data tiles

Now we want to add a Spinner over our map so that we can select the type of information we want to add on top of the map like temperature, wind, pressure and so on.
This is very simple because we can modify the activity_maps.xml and add our spinner:


<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">

  <fragment  android:id="@+id/map"
      android:name="com.google.android.gms.maps.SupportMapFragment"
      android:layout_width="match_parent" 
      android:layout_height="match_parent"
      tools:context=".MapsActivity" />

  <Spinner
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentBottom="true"
      android:layout_alignParentRight="true"
      android:layout_marginBottom="10dp"
      android:layout_marginRight="10dp"
      android:id="@+id/tileType"/>

</RelativeLayout>

Finally in the onCreate() we add the code to handle the spinner:

        spinner = (Spinner) findViewById(R.id.tileType);

        String[] tileName = new String[]{"Clouds", "Temperature", "Precipitations", "Snow", "Rain", "Wind", "Sea level press."};

        ArrayAdapter adpt = new ArrayAdapter(this, android.R.layout.simple_spinner_item, tileName);

        spinner.setAdapter(adpt);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

            @Override
            public void onNothingSelected(AdapterView parent) {

            }

            @Override
            public void onItemSelected(AdapterView parent, View view, int position, long id) {
                 // Check click
                switch (position) {
                    case 0:
                        tileType = "clouds";
                        break;
                    case 1:
                        tileType = "temp";
                        break;
                    case 2:
                        tileType = "precipitation";
                        break;
                    case 3:
                        tileType = "snow";
                        break;
                    case 4:
                        tileType = "rain";
                        break;
                    case 5:
                        tileType = "wind";
                        break;
                    case 6:
                        tileType = "pressure";
                        break;

                }

                if (mMap != null) {
                    tileOver.remove();
                    setUpMap();
                }


            }
        });

We simply list all the tile types and when the user selects one of them we show the relative images on top of the map. If we select for example the temperature map we have:


If you look the image you can notice that the tile has covered almost the map behind. This happens because we don't have an easy way to modify the transparency of the map.

Add transparent tile to Google map

The last step, if we want that the weather data doesn't cover the map that stands behind, is modifying our provider. This code shown below is derived from this post. We create, in other words, a custom provider so that we can change the opacity of the images we add over the map:
public class TransparentTileOWM implements TileProvider {
    //private String url;
    private Paint opacityPaint = new Paint();
    private String tileType;

    private static final String OWM_TILE_URL = "http://tile.openweathermap.org/map/%s/%d/%d/%d.png";

    /**
     * This constructor assumes the url parameter contains three placeholders for the x- and y-positions of
     * the tile as well as the zoom level of the tile. The placeholders are assumed to be {x},
     * {y}, and {zoom}. An example
     * for an OpenWeatherMap URL would be: http://tile.openweathermap.org/map/precipitation/{zoom}/{x}/{y}.png
     *
     */
    public TransparentTileOWM(String tileType)
    {
        this.tileType = tileType;
        setOpacity(50);
    }

    /**
     * Sets the desired opacity of map {@link Tile}s, as a percentage where 0% is invisible and 100% is completely opaque.
     * @param opacity The desired opacity of map {@link Tile}s (as a percentage between 0 and 100, inclusive)
     */
    public void setOpacity(int opacity)
    {
        int alpha = (int)Math.round(opacity * 2.55);    // 2.55 = 255 * 0.01
        opacityPaint.setAlpha(alpha);
    }

    @Override
    public Tile getTile(int x, int y, int zoom)
    {
        URL tileUrl = getTileUrl(x, y, zoom);

        Tile tile = null;
        ByteArrayOutputStream stream = null;

        try
        {
            Bitmap image = BitmapFactory.decodeStream(tileUrl.openConnection().getInputStream());
            image = adjustOpacity(image);

            stream = new ByteArrayOutputStream();
            image.compress(Bitmap.CompressFormat.PNG, 100, stream);

            byte[] byteArray = stream.toByteArray();

            tile = new Tile(256, 256, byteArray);
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if(stream != null)
            {
                try
                {
                    stream.close();
                }
                catch(IOException e) {}
            }
        }

        return tile;
    }

    /**
     * Helper method that returns the {@link URL} of the tile image for the given x/y/zoom location.
     *
     * This method assumes the URL string provided in the constructor contains three placeholders for the x-
     * and y-positions as well as the zoom level of the desired tile; {x}, {y}, and
     * {zoom}. An example for an OpenWeatherMap URL would be:
     * http://tile.openweathermap.org/map/precipitation/{zoom}/{x}/{y}.png


     *
     * @param x The x-position of the tile
     * @param y The y-position of the tile
     * @param zoom The zoom level of the tile
     *
     * @return The {@link URL} of the desired tile image
     */
    private URL getTileUrl(int x, int y, int zoom)
    {
        String tileUrl = String.format(OWM_TILE_URL, tileType, zoom, x, y);
        try
        {
            return new URL(tileUrl);
        }
        catch(MalformedURLException e)
        {
            throw new AssertionError(e);
        }
    }

    /**
     * Helper method that adjusts the given {@link Bitmap}'s opacity to the opacity previously set via
     * {@link #setOpacity(int)}. Stolen from Elysium's comment at StackOverflow.
     *
     * @param bitmap The {@link Bitmap} whose opacity to adjust
     * @return A new {@link Bitmap} with an adjusted opacity
     *
     * @see htp://stackoverflow.com/questions/14322236/making-tileoverlays-transparent#comment19934097_14329560
     */
    private Bitmap adjustOpacity(Bitmap bitmap)
    {
        Bitmap adjustedBitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(adjustedBitmap);
        canvas.drawBitmap(bitmap, 0, 0, opacityPaint);

        return adjustedBitmap;
    }

}

Now running the code and using this new provider we have:




NFC Android: Read NDEF Tag

Topics covered

NFC

NDEF

Read NFC content

NFC payload

Smart poster, text, link

In this post we want to describe how to read NFC tag. Android SDK provides a set of API that can be used to read the NFC payload, anyway if we look at the NFC spec we can notice there are several type of payload depending on the type of the tag. Android SDK doesn't provide any support to parse the payload and extract information, so in this post we will describe how we can read the NFC tag content. As we said in the previous post, explaining how to use NFC in Android, there are several type of NFC (NDEF) tag:
  • NFC Forum well-known type [NFC RTD] 
  • Media-type as defined in RFC 2046 
  • Absolute URI as defined in RFC 3986 
  • NFC Forum external type [NFC RTD]
We can know the NFC type using the last three bytes in the NFC header or simpler using:

short tnf = record.getTnf();

Comparing the tnf (Type Name Format) with all possible combination we can know the record type. In the code above record is an instance of NdefRecord.

NDEF Record Structure

Before analysing how to read NDEF content, it is important to know the NDEF record structure. The picture below shows the structure:


The most important byte (7th) is the Message Begin byte, this byte is 1 if the it is the starting message otherwise is zero. The 6th byte is the Message End, this byte is 1 if the this record is the end record otherwise is 0. SR is the Short Record and it is 1 if it is a short record. This information are important if we want to handle the NDEF tag data correctly.
We know that Android SDK provides the method getPayload() that returns an array of bytes that represent the tag content. We have to read and parse this array to extract information.
Let's start from the simplest record structure: text record.

Well known type: Text Record

This is the simplest record type and we will start from here. We know from the specification how to read the payload. The first thing we have to read is the header (payload[0]) and parse it. The most significative byte (the 7th) represent the text encoding:

byte status = payload[0];
int enc = status & 0x80; // Bit mask 7th bit 1
String encString = null;
if (enc == 0)
  encString = "UTF-8";
else
  encString = "UTF-16";

The bit from 5th to 0 represents the language length:

int ianaLength = status & 0x3F; // Bit mask bit 5..0

Now we are ready to read the "real" content:

try {
  String content = new String(payload, ianaLength + 1, payload.length - 1 - ianaLength, encString);
  record.payload = content;
}
catch(Throwable t) {
    t.printStackTrace();
}

Let us suppose we create  simple text/plain content with Surviving with Android. Now if we read the content we have:

02 65 6e 53 75 72 76 69 76 69 6e 67 20 77 69 74 68 20 61 6e 64 72 6f 69 64

This is the payload, and if we parse it we get:


NFC Forum external type

This is another simple NDEF content. This type of content is built for organization that want to create a custom name space. This type of content can be useful when we want to create a special name space to run an app for example. Reading it is very simple:

StringBuffer pCont = new StringBuffer();
for (int rn=0; rn < payload.length;rn++) {
   pCont.append(( char) payload[rn]);
}

All the payload is the content.

NDEF Smart poster

This is the most complex NDEF tag, because it can be made by several inner contents made by several types. In this case is very important to read the message header to know if the record is a Message Begin or Message end or if the record is a Short Record.
The first thing we have to do is read the header:

int[] result = getHeader(payload); // 0 = MB, 1 = ME, 2 = SR
int numLenByte = 1;
if (result[2] == 0)
   numLenByte = 4; // This is not a Short Record. Length = 4 byte

Now we know how many bytes is the payload length and then we have to get the length:

String byteLen = "";
for (int p = 2; p <= 2 + numLenByte - 1; p++)
   byteLen = byteLen + payload[p]; // We simply append the bytes

Then we read the record type to know how to handle it:

int pos = 2 + numLenByte;
int type = payload[pos];

We can parse the payload according to the record type:
if (type == 'U') {
  RDTUrl url = new RDTUrl();
  result = getHeader(payload);
  url.MB = result[0];
  url.ME = result[1];
  url.SR = result[2];
  url.prefix = payload[pos];
  Log.d("NFC", "Action:" + "0x" + Integer.toHexString(url.prefix));
  url.url = new String(payload, pos + 1, Integer.parseInt(byteLen) - 1);
  Log.d("NFC", "Content:" + url.url);
  record.records.add(url);
}
else if (type == 'T') {
   RDTTextRecord text = new RDTTextRecord();
   result = getHeader(payload);
   text.MB = result[0];
   text.ME = result[1];
   text.SR = result[2];
   int len = payload[pos];
   Log.d("Nfc", "Lan len ["+len+"]");
   text.language = "";
   for (int i = pos + 1; i <= pos + len; i++)
     text.language = text.language + (char) payload[i];
     Log.d("Nfc", "Lang ["+text.language+"]");
     text.payload = new String(payload, pos + len + 1, Integer.parseInt(byteLen) - len - 1);
     Log.d("NFC", "Content:" + text.payload);
     record.records.add(text);
   }
}

And finally we move to the next message part:

payload = Arrays.copyOfRange(payload, pos + Integer.parseInt(byteLen), payload.length);

...and of course we repeat all these things until the payload length is greater than 0. That's all.
Let us suppose we a NDEF Tag that has a link that points to this website and a text part like surviving.
The payload is:

ffffff91 1 19 55 1 73 75 72 76 69 76 69 6e 67 77 69 74 68 61 6e 64 72 6f 69 64 2e 63 6f 6d 51 1 c 54 2 65 6e 73 75 72 76 69 76 69 6e 67

Now, if we run our app we get:


We can suppose now we have a tag containing a telephone number with a label:


Source code available @github

First steps with NFC in Android

Topics covered

NFC

NDEF

NFC Foreground dispatch

NFC payload

NFC filter

This post describes how to use NFC in Android. The NFC technology stands for Near Field Communication and you can find the specification at NFC Forum. In this first post, we will analyse some basic aspects of NFC and we will describe how we can implement an app in Android that handles NFC tags.
If you want to experiment NFC, there are several web site where you can buy NFC with a few euro.
NFC can be used in different situation: we can use it to turn on our Wifi when we are at home or launch task actions and so on.


We will focus our attention on NDEF data that is a special type of NFC tag. There are some basic steps we have to follow before using the NFC.


NFC Filter

When we use NFC tag,, the first thing we want is our app is notified when we get near a NFC tag. To this purpose we use a intent filter. Android SDK provides three different filter that we can use with different level of priority:

  • ACTION_NDEF_DISCOVERED
  • ACTION_TECH_DISCOVERED
  • ACTION_TAG_DISCOVERED
We focus our attention on ACTION_NDEF_DISCOVERED, that has the highest level of priority. As said, our goal is being notified when the smart phone is near a NFC tag and, if we have only this app installed and capable to handle this NFC tag, we want that the app starts immediately. To do it, we register the filter in the Manifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.survivingwithandroid.nfc" >

    ....
     <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data  android:mimeType="text/plain"/>
     </intent-filter>
<manifest>

At line 6 we register our app so that it can be notified with ACTION_NDEF_DISCOVERED. We can use different types of filter, in this example (at line 8) we used mime type. In other word when a NFC tag NDEF is discovered and it has a mime type text/plain then our app will be started. We can filter using several mime types not only text/pain. We can, moreover, use other type of filters like android:scheme to filter using the protocol or using a string pattern.

NFC Foreground Dispatch

Filtering with intents works if our app is not in foreground. If our app is running in foreground, it won't be notified, if move our smart phone near a NFC tag. In this case we have to use a different technique called NFC Foreground dispatch. The first step is defining in our code the intent filter (as we did in the manifest.xml):
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        Intent nfcIntent = new Intent(this, getClass());
        nfcIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

        nfcPendingIntent =
                PendingIntent.getActivity(this, 0, nfcIntent, 0);

        IntentFilter tagIntentFilter =
                new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        try {
            tagIntentFilter.addDataType("text/plain");
            intentFiltersArray = new IntentFilter[]{tagIntentFilter};
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
Now we have to register our filter, and we do it in onResume method, in this way:
   protected void onResume() {
        super.onResume();
       nfcAdpt.enableForegroundDispatch(
                this,
                nfcPendingIntent,
                intentFiltersArray,
                null);
        handleIntent(getIntent());
    }
We should, also, remember to disable foreground dispatch as soon as the app gets in background and the best place to do it is in onPause method.
    @Override
    protected void onPause() {
        super.onPause();
        nfcAdpt.disableForegroundDispatch(this);
    }
where nfcAdpt is the NFC Adapter.

Handle NFC using NFCAdapter

Once we created our filters, we have to interact with the NFC component in our smart phone. For this purpose, we use NfcAdapter, provided by Android SDK. Using this class, we can check, for example, if the NFC is supported by our smart phone or if the NFC is turned on or off:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       ...
       nfcAdpt = NfcAdapter.getDefaultAdapter(this);
       // Check if the smartphone has NFC
       if (nfcAdpt == null) {
            Toast.makeText(this, "NFC not supported", Toast.LENGTH_LONG).show();
            finish();
       }
       // Check if NFC is enabled
       if (!nfcAdpt.isEnabled()) {
            Toast.makeText(this, "Enable NFC before using the app", Toast.LENGTH_LONG).show();
       }
    }

NFC Data: Payload

Once we know how to handle NFC tag, we want to read the tag content. There are several type of content defined in NFC specs:

  • NFC Forum well-known type
  • Media-type
  • Absolute URI
  • NFC Forum external type

Each type has it is own payload. Generally speaking, a NFC NDEF data is composed by a Message. A message can contain one or more records. Each record is made by an header and a payload (the real information). By now, if we want to read the data inside a NFC NDEF tag we can use:
 @Override
    public void onNewIntent(Intent intent) {
        Log.d("Nfc", "New intent");
        getTag(intent);
    }

    private void getTag(Intent i) {
        if (i == null)
            return ;

        String type = i.getType();
        String action = i.getAction();
        List dataList = new ArrayList();

        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
            Log.d("Nfc", "Action NDEF Found");
            Parcelable[] parcs = i.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

            for (Parcelable p : parcs) {
                recNumberTxt.setText(String.valueOf(numRec));
                NdefRecord[] records = msg.getRecords();
                for (NdefRecord record: records) {
                    short tnf = record.getTnf();
                    // Here we handle the payload

                }
            }
       }
    }

In the next post, we will describe how to read different NDEF types and how to extract information from the tag.
If you want to know how to read look at NFC - Read NDEF Tag.

Android Text to Speech (TTS)

Topics covered

Text to Speech (TTS)

Pitch

Speed rate

Talking app

This post describes how to use text to speech (TTS) in Android. It isn't so common to find a post explaining how to use it and it is a part of Android not much covered. In my opinion, TTS is very interesting because it can add some nice features to an app. Text to Speech is a feature of Android platform that can be used to "read" the words and make the app talking, or more in detail to synthesize text.
In this post, i want to cover how to implement TTS in Android and how we can control some interesting aspects of speech engine. We want to code an app that has a EditText widget so that we write the words that have to be read and some controls to modify the speech engine.



Text to speech Engine

The first thing we have to do to use the TTS in our app is initialise the engine. The class that controls the engine is called TextToSpeech,
engine = new TextToSpeech(this, this);

where the first parameter is the Context and the other one is the listener. The listener is used to inform our app that the engine is ready to be used. In order to be notified we have to implement TextToSpeech.OnInitListener, so we have:

public class MainActivity extends Activity implements TextToSpeech.OnInitListener {
    ....
    @Override
    public void onInit(int status) {
        Log.d(&Speech&, &OnInit - Status [&+status+&]&);

        if (status == TextToSpeech.SUCCESS) {
            Log.d(&Speech&, &Success!&);
            engine.setLanguage(Locale.UK);
        }
    }
}
We use the onInit as callback method, and when the engine is ready, we set the default language that the engine used to read our sentence.

Read the words

Now our engine is ready to be used, we need simply pass the string we want to read. To this purpose, we use an EditText so that the user can edit his string and when he clicks on the microphone the app start reading. Without detailing too much the code because is trivial we focus our attention when user clicks on the microphone button:

speechButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                speech();
            }
        });
where
   
    private void speech() {
        engine.speak(editText.getText().toString(), TextToSpeech.QUEUE_FLUSH, null, null);
    }

we simply have to call the speak method to make our app reading the text!

Control Text to Speech Engine parameters

We can have more control on how the engine read the sentence. We can modify for example the pitch and the speech rate.
In the app, for example we used two seek bars to control the pitch and rate. If we want to set the voice pitch we can use setPitch passing a float value.
On the other hand, if we want to change the speech rate we can use setSpeechRate.
In our app, we read this value from two seek bars and the speech method become:

   private void speech() {
        engine.setPitch((float) pitch);
        engine.setSpeechRate((float) speed);
        engine.speak(editText.getText().toString(), TextToSpeech.QUEUE_FLUSH, null, null);
    }


Source code available @github

A Guide to Android RecyclerView and CardView

Topics covered

RecyclerView

CardView

Android adapter

The new support library in Android L (Lollipop) introduced two new UI widgets: RecyclerView and CardView. The RecyclerView is a more advanced and more flexible version of the ListView. This new component is a big step because the ListView is one of the most used UI widgets. The CardView widget, on the other hand, is a new component that does not "upgrade" an existing component. In this tutorial, I'll explain how to use these two widgets and show how we can mix them. Let's start by diving into the RecyclerView.

RecyclerView: Introduction

As I mentioned, RecyclerView is more flexible that ListView even if it introduces some complexities. We all know how to use ListView in our app and we know if we want to increase the ListView performances we can use a pattern called ViewHolder. This pattern consists of a simple class that holds the references to the UI components for each row in the ListView. This pattern avoids looking up the UI components all the time the system shows a row in the list. Even if this pattern introduces some benefits, we can implement the ListView without using it at all. RecyclerView forces us to use the ViewHolder pattern. To show how we can use the RecyclerView, we can suppose we want to create a simple app that shows a list of contact cards. The first thing we should do is create the main layout. RecyclerView is very similar to the ListView and we can use them in the same way:
<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=".MyActivity">
   <android.support.v7.widget.RecyclerView
             android:id="@+id/cardList"
             android:layout_width="match_parent"
             android:layout_height="match_parent"/> 
</RelativeLayout>
As you'll notice from the layout shown above, the RecyclerView is available in the Android support library, so we have to modify build.gradle to include this dependency:
dependencies {
...
compile 'com.android.support:recyclerview-v7:21.0.0'

}