Skip to content

04 Searching

Overview

This tutorial will teach you how to search for locations with adresses or parts of it.

Base

You can use your results from Tutorial03 as a base for our new project.

Project requirements

We will use the Android FloatingActionButton from the android support library in this tutorial. So please add the dependency to your build.gradle.

...

dependencies {
    ...
    implementation "com.android.support:design:28.0.3"
}

Search basics

Important

Every search besides the Point Of Interest (POI) search will only take place in the currently open map. To search in any other map, you first have to switch to the map to search in!

The NavigationSDK provides multiple possibilities for searching towns, streets in towns, house numbers in streets, point of interests and much more:

Search method Description Depends on
searchTown() Search a town by a given name or name fragment -
searchStreet() Search a street in a given town or postcode result by a name or name fragment of the street town result
searchHouseNr() Search a house number in a given town and street result town, street result
searchCrossing() Search crossings in a given street result street result
searchPOI() Search POIs with a given name around a position or globally -
searchPostcode() Search a town by postcode -
simpleGeoCode() Search a complete given address and returns its position -
simpleInvGeoCode() Search an address of a given position -

As you can see, some of the searches like street search depend on previous search results. You can for example only search a street in a previous found town and you can only search house numbers in a known street of a known town.

Every search is built up by the following steps:

  • Fill a search request with your search criteria
  • Call the appropriate search function with the request as a parameter
  • Get a single generic result by calling SearchResult result = results.get(index) with the desired index

First, let's add a floating action button to our MainActivity. For now, we use it to start the search. Later on, we will use it to switch to a search activity. This will be explained in one of the next tutorials. Add the following xml snippet to the activity_main.xml right after the MapView section:

<?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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ptvag.navigation.tutorial.MainActivity">

    <com.ptvag.navigation.tutorial.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_margin="16dp"
        android:src="@android:drawable/ic_dialog_map"
        android:onClick="onFloatingButtonClicked"/>

</RelativeLayout>

As you can see, we have connected the button with the method onFloatingButtonClicked, which we are going to implement in our MainActivity. We also integrate a simple town search example. Add the following code to your MainActivity:

public void onFloatingButtonClicked(View view)
{
    SearchRequest request = new SearchRequest("karls", null, (short)0,  SearchKind.SDK_SK_TOWNBYNAME_SORTED);
    int resultCount = 0;

    try
    {
        SearchResults results = NavigationSDK.searchTown(request, 100, true);

        for(SearchResult result: results)
        {
            float latitude = (float) result.getPosition().toGeoPosition().getLatitude();
            latitude = latitude / NavigationSDK.SDK_GEOCONVERSION_FACTOR;

            float longitude = (float) result.getPosition().toGeoPosition().getLongitude();
            longitude = longitude / NavigationSDK.SDK_GEOCONVERSION_FACTOR;

            Log.e("Results:", result.getName() +
                    ", Latitude: " + latitude +
                    ", Longitude: " + longitude + "\n");
        }
    }
    catch (NavigationException e)
    {
        e.printStackTrace();
    }
}

Start the program and take a look into the logcat to get the result output. We get a bunch of cities which all have a name beginning with the string "karls".
Now we take a more detailed view of the previous code:

First, we fill out the SearchRequest "request" with the needed parameters:

SearchRequest request = new SearchRequest("karls", null, (short)0,  SearchKind.SDK_SK_TOWNBYNAME_SORTED);

The SearchRequest class has the following members:

  • Name: The name (or name fragment) of the town we would like to search, "karls" in our example.
  • Position: An optional position around which the search should take place. If set to "null" we search alphabetically, not in a radius around the position.
  • Country Code: An optional country code to filter the result (useful for maps that consists of multiple countries). If set to 0, all contries of the currently open map will be searched.
  • SearchKind: The kind of search. We want e.g. a town search result which orders the towns in a reasonable way (biggest towns first, then districts of towns) so we set it to SearchKind.SDK_SK_TOWNBYNAME_SORTED.

The search itself is called by:

SearchResults results = NavigationSDK.searchTown(request, 100, true);

  • The first parameter is the above request
  • The second parameter declares the maxium number of results that should be returned
  • The third parameter means "one entry per town". When set, towns which have the same name but different postcodes will be filtered out.

The return value of the search is a collection of SearchResult objects.
To get a single result, we call:

SearchResult result = results.get(index);

This SearchResult object holds all data of a single result:

  • SearchKind: The kind of the result, see SearchKind for details
  • Name: The name of the result item
  • Country Code: The country code of the item
  • Category: The category of the result, not used for house numbers
  • Type: The returned kind of the result item (same type as SearchKind)
  • Position: The geographical position of the result item

The logcat output is generated by:

Log.e("Results:", "Res("+i+"):" + result.getName() + ",
   Latitude: " + (float)result.getPosition().toGeoPosition().getLatitude()/NavigationSDK.SDK_GEOCONVERSION_FACTOR + ",
   Longitude: " + (float)result.getPosition().toGeoPosition().getLongitude()/NavigationSDK.SDK_GEOCONVERSION_FACTOR + "\n");
As you can see in the code above, we print out the results name and the geodecimal position of the result.

Because the geodecimal position is held internally as an integer value, we have to cast the result latitude and longitude values to float and divide it with the constant value NavigationSDK.SDK_GEOCONVERSION_FACTOR.

Important

You have to clear search results after use by calling SearchResults.destroy() to avoid memory leaks!

The next thing we do is to implement an interactive address search. For this, we have to add and change some code in our project:

Change the onFloatingButtonClicked() method in the MainActivity:

To be able to switch to our new activity, we remove the sample search code from the onFloatingButtonClicked() method and replace it. The onFloatingButtonClicked() method should look like this:

public void onFloatingButtonClicked(View view)
{
    Intent intent = new Intent(this, SearchActivity.class);
    startActivity(intent);
}

Add the search activity:

We add two new activities to our project. A search overview and a result activity. Right click on the project and add an empty activity called SearchActivity. This SearchActivity will be the starting point for all the searches we implement in this chapter. Do the same again and add a new activity called ResultListActivity. To feed the ResultListActivity with data, we add a SearchAdapter and a SearchModel. The model will execute the current search and the adapter provides the result to the ListView.

SearchModel

The SearchModel provides methods to search towns, streets and house numbers, to get the number of results and the results itself. The search methods look very similar to the first search we created in the MainActivity. As you can see, the street search needs a former town search result, and the house number search needs a town and a street search result. For convenience reasons, the SearchModel object will be created once at startup in the Application class. This is not mandatory, but for this tutorial, it is the simplest way to access the model from all activities and the SearchAdapter. Let's add the following code to the Application class:

private static SearchModel mModel = new SearchModel();
...
public static SearchModel getSearchModel()
{
    return mModel;
}

The SearchModel class should look like this:

public class SearchModel
{
    private static final int MAXTOWNHITS = 500;
    private static final int MAXSTREETHITS = 500;
    private static final int MAXHNRHITS = 20;

    private SearchResults mTownResults = null;
    private SearchResults mStreetResults = null;
    private SearchResults mHNRResults = null;


    public SearchModel()
    {
    }

    public void searchTown(String name, int cc)
    {
        resetTownSearch();

        SearchRequest request = new SearchRequest(name, null, (short) cc, SearchKind.SDK_SK_TOWNBYNAME_SORTED);
        try
        {
            mTownResults = NavigationSDK.searchTown(request, MAXTOWNHITS, true);
        }
        catch (NavigationException e)
        {
            e.printStackTrace();
        }
    }

    public void searchStreet(String name, SearchResult formerTownResult)
    {
        resetStreetSearch();

        SearchRequest request = new SearchRequest(name, null, (short) 0, SearchKind.SDK_SK_STREETBYNAME);
        try
        {
            mStreetResults = NavigationSDK.searchStreet(request, MAXSTREETHITS, formerTownResult);
        }
        catch (NavigationException e)
        {
            e.printStackTrace();
        }
    }

    public void searchHNR(String name, SearchResult formerTownResult, SearchResult formerStreetResult)
    {
        resetHNRSearch();

        SearchRequest request = new SearchRequest(name, null, (short) 0, SearchKind.SDK_SK_HNR);
        try
        {
            mHNRResults = NavigationSDK.searchHouseNr(request, MAXHNRHITS, formerTownResult, formerStreetResult);
        }
        catch (NavigationException e)
        {
            e.printStackTrace();
        }
    }

    public int count(SearchKind type)
    {
        if (type == SearchKind.SDK_SK_TOWNBYNAME || type == SearchKind.SDK_SK_TOWNBYNAME_SORTED && mTownResults != null)
            return mTownResults.size();
        else if (type == SearchKind.SDK_SK_STREETBYNAME && mStreetResults != null)
            return mStreetResults.size();
        else if (type == SearchKind.SDK_SK_HNR && mHNRResults != null)
            return mHNRResults.size();
        else
            return 0;
    }

    public SearchResult getResult(int index, SearchKind type)
    {
        SearchResult result = new SearchResult();

        if (mTownResults != null && (type == SearchKind.SDK_SK_TOWNBYNAME || type == SearchKind.SDK_SK_TOWNBYNAME_SORTED))
        {
            if (index >= 0 && index < mTownResults.size())
                result = mTownResults.get(index);
        }
        else if (mStreetResults != null && (type == SearchKind.SDK_SK_STREETBYNAME))
        {
            if (index >= 0 && index < mStreetResults.size())
                result = mStreetResults.get(index);
        }
        else if (mHNRResults != null && (type == SearchKind.SDK_SK_HNR))
        {
            if (index >= 0 && index < mHNRResults.size())
                result = mHNRResults.get(index);
        }

        return result;
    }

    public void resetTownSearch()
    {
        mTownResults = null;
    }

    public void resetStreetSearch()
    {
        mStreetResults = null;
    }

    public void resetHNRSearch()
    {
        mHNRResults = null;
    }
}

ResultListActivity

This activity has a text input field at the top and will show search results in a listview. The SearchActivity will send the expected type of search in a bundle and the ResultListActivity will call the models appropriate search when the edit text will get changed. When a result is clicked in the listview, the chosen result is packaged into a bundle and sent back to the SearchActivity.

package com.ptvag.navigation.tutorial;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;

import com.ptvag.navigation.sdk.SearchResult;
import com.ptvag.navigation.sdk.SearchKind;

public class ResultListActivity extends AppCompatActivity
{

    private SearchKind mType;
    private int mCc;
    private SearchAdapter mItemsAdapter;

    private SearchResult mTownResult;
    private SearchResult mStreetResult;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_result_list);

        ListView listView = (ListView) findViewById(R.id.listView);

        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        if(bundle != null)
        {
            int type = bundle.getInt("type");
            mType = SearchKind.values()[type];
            mCc = bundle.getInt("cc", 0);

            // get the last clicked result from the SearchActivity (may be null pointers)
            mTownResult = bundle.getParcelable("town_result");
            mStreetResult = bundle.getParcelable("street_result");

            // reset all former searches
            Application.getSearchModel().resetTownSearch();
            Application.getSearchModel().resetStreetSearch();
            Application.getSearchModel().resetHNRSearch();

            mItemsAdapter = new SearchAdapter(this, android.R.layout.simple_list_item_1, Application.getSearchModel(), mType);

            listView.setAdapter(mItemsAdapter);
        }

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
        {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l)
            {
                Intent returnIntent = new Intent();
                Bundle bundle = new Bundle();

                // when an item was clicked, we retrieve the corresponding result and send it back to
                // the SearchActivity to allow showing the name of the found result on the search button
                SearchResult result = Application.getSearchModel().getResult(i, mType);

                bundle.putParcelable("result", result);
                returnIntent.putExtras(bundle);
                setResult(Activity.RESULT_OK, returnIntent);
                finish();
            }
        });

        EditText editText = (EditText) findViewById(R.id.editText);
        editText.addTextChangedListener(new TextWatcher()
        {

            public void afterTextChanged(Editable s) {}

            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            public void onTextChanged(CharSequence s, int start, int before, int count)
            {
                onSearch(s.toString());
            }
        });


    }

    private void onSearch(String name)
    {
        if(mType == SearchKind.SDK_SK_TOWNBYNAME || mType == SearchKind.SDK_SK_TOWNBYNAME_SORTED)
        {
            Application.getSearchModel().searchTown(name, mCc);
        }
        else if(mType == SearchKind.SDK_SK_STREETBYNAME && mTownResult != null)
        {
            Application.getSearchModel().searchStreet(name, mTownResult);
        }
        else if(mType == SearchKind.SDK_SK_HNR && mTownResult != null && mStreetResult != null)
        {
            Application.getSearchModel().searchHNR(name, mTownResult, mStreetResult);
        }

        mItemsAdapter.notifyDataSetChanged();
    }

}
Add this to the created activity_result_list.xml:

 <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:text=""
        android:ems="10"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/editText"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"/>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/editText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/listView"
        android:layout_marginTop="12dp"/>

SearchAdapter

The SearchAdapter uses the SearchModels count() and getResult() methods to feed the Listview with the search results.

public class SearchAdapter extends ArrayAdapter<SearchResult>
{
    protected final Activity mContext;
    protected SearchModel mModel;
    protected SearchKind mType;

    public SearchAdapter(Context context, int textViewResourceId, SearchModel model, SearchKind type)
    {
        super(context, textViewResourceId);
        this.mContext = (Activity) context;
        this.mModel = model;
        this.mType = type;
    }

    @Override
    public int getCount()
    {
        int cnt = mModel.count(mType);
        return cnt;
    }

    @Override
    public SearchResult getItem(int position)
    {
        return mModel.getResult(position, mType);
    }

    @Override
    public long getItemId(int arg0)
    {
        if(mModel.count(mType) > 0)
            return arg0;
        else
            return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup)
    {
        if(mModel.count(mType) < 1)
            return null;

        View v = convertView;
        if (v == null) {
            LayoutInflater layoutInflater = (LayoutInflater)mContext.getLayoutInflater();
            v = layoutInflater.inflate(R.layout.row_search_result, null, true);
        }

        SearchResult result;
        result = mModel.getResult(position, mType);

        String name = result.getName();

        TextView text = (TextView) v.findViewById(R.id.searchResultItem);
        text.setText(name);

        return v;
    }

}

The SearchAdapter needs a layout for the returned listview row, so create an xml file called row_search_result.xml in the res directory and put the following code in it:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/searchResultItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:padding="15dp"
        android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium"
        android:singleLine="true"/>

</LinearLayout>

CountryAdapter

To allow searching in different countries, we implement a CountryAdapter consisting of all countries in all installed maps. It iterates over all available maps and extracts all countries in them. If a map has a DetailLevel of 1, it consist only of major roads and will be sorted to the end of the countries. Countries can be federal states in some situations and are represented by PtvAlpha country codes. The town search needs the country code as a numerical ptvCode. We use NavigationSDK.transformCountryCodeToPtvCode to convert the code.

package com.ptvag.navigation.tutorial;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.ptvag.navigation.sdk.CountryCodeFormat;
import com.ptvag.navigation.sdk.MapInformation;
import com.ptvag.navigation.sdk.NavigationSDK;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CountryAdapter extends ArrayAdapter<CountryAdapter.Country>
{
    public class Country {
        public Country(int ptvCode, boolean isMajorRoad, int resId, int mapId) {
            this.ptvCode = ptvCode;
            this.isMajorRoad = isMajorRoad;
            this.resId = resId;
            this.mapId = mapId;
        }

        public int getPtvCode() {
            return ptvCode;
        }

        public boolean isMajorRoad() {
            return isMajorRoad;
        }

        public int getResId() {
            return resId;
        }

        public int getMapId() {
            return mapId;
        }

        private int ptvCode;
        private boolean isMajorRoad;
        private int resId;
        private int mapId;
    }

    List<Country> countries = new ArrayList<>();

    Context context;

    public CountryAdapter(final Context context, int textViewResourceId)
    {
        super(context, textViewResourceId);

        this.context = context.getApplicationContext();

        int i = 0;
        for (MapInformation info : NavigationSDK.getAvailableMaps()) {
            for (String ptvAlpha : info.getCountries()) {
                int ptvCode = NavigationSDK.transformCountryCodeToPtvCode(CountryCodeFormat.PtvAlpha, ptvAlpha);

                String resName = "country_" + ptvCode;
                int stringId = context.getResources().getIdentifier(resName, "string", context.getPackageName());

                countries.add(new Country(ptvCode, info.getDetailLevel() == 1, stringId, i));
            }
            ++i;
        }

        Collections.sort(countries, new Comparator<Country>() {
            @Override
            public int compare(Country o1, Country o2) {
                if (o1.isMajorRoad() == o2.isMajorRoad()) {
                    return context.getString(o1.getResId()).compareTo(context.getString(o2.getResId()));
                } else {
                    if (o1.isMajorRoad()) {
                        return 1;
                    } else {
                        return -1;
                    }
                }
            }
        });
    }

    @Override
    public int getCount()
    {
        return countries.size();
    }

    @Override
    public Country getItem(int position)
    {
        return countries.get(position);
    }

    @Override
    public long getItemId(int arg0)
    {
        if(getCount() > 0)
            return arg0;
        else
            return 0;
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getCustomView(position, convertView, parent);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return getCustomView(position, convertView, parent);
    }

    public View getCustomView(int position, View convertView, ViewGroup parent)
    {
        if(getCount() < 1)
            return null;

        View v = convertView;
        if (v == null) {
            LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
            v = layoutInflater.inflate(R.layout.row_search_country, null, true);
        }

        TextView text = (TextView) v.findViewById(R.id.countries_row_text);

        Country country = getItem(position);

        text.setText(country.getResId());

        int drawableId = R.drawable.flag_motorway;

        if (!country.isMajorRoad()) {
            String isoCode = NavigationSDK.transformPtvCodeToCountryCode(CountryCodeFormat.Iso2, country.getPtvCode()).toLowerCase();
            drawableId = context.getResources().getIdentifier("flag_" + isoCode, "drawable", context.getPackageName());
        }

        ImageView image = (ImageView) v.findViewById(R.id.countries_row_image);
        image.setImageResource(drawableId);

        return v;
    }
}

The CountryAdapter needs a layout for the returned listview row, so create an xml file called row_search_country.xml in the res directory and put the following code in it:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/countries_row_image"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:minWidth="30dp"
        android:src="@drawable/flag_de"
        android:scaleType="centerInside"
        android:paddingLeft="16dp" />

    <TextView
        android:id="@+id/countries_row_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:padding="16dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        android:textColor="#fff" />

</LinearLayout>

SearchActivity

The SearchActivity is the central place for our search. When clicking one of the buttons, the ResultListActivity will be started in the appropriate search mode (town, street or house number). The CountrySpinner allows us to change the map by country to search in the according country. It shows the values from the implemented CountryAdapter and automatically selects the last chosen country. The ResultListActivity will be started with startActivityForResult(). When the ResultListActivity sends back the corresponding result, the onActivityResult() will be called and the SearchActivity modifies the text of the buttons out of the result. The current result will be shown on the MapView. The zoom factor depends on the search type. You also can pan and zoom the MapView manually.

Add the following to the created activity_search.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_search"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    tools:context="com.ptvag.navigation.tutorial.SearchActivity">

    <LinearLayout
        android:id="@+id/search"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Spinner
            android:id="@+id/countrySpinner"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"/>

        <Button
            android:text="@string/town"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/buttonTown"
            android:onClick="onTownClicked"/>

        <Button
            android:text="@string/street"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/buttonStreet"
            android:onClick="onStreetClicked"/>

        <Button
            android:text="@string/house_number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/buttonHNR"
            android:onClick="onHNRClicked"/>

        <com.ptvag.navigation.tutorial.MapView
            android:id="@+id/map"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"/>
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

Add the strings to the strings.xml (which can be found in the value directory):

    <string name="town">Town</string>
    <string name="street">Street</string>
    <string name="house_number">House number</string>

Also add a list of countries for the CountrySpinner to your strings.xml. You notice that countries can also be federal states in some situations and are represented by a numerical PTV country code.

<string name="country_1">Czech Republic</string>
<string name="country_101">Alabama</string>
<string name="country_102">Alaska</string>
<string name="country_104">Arizona</string>
<string name="country_105">Arkansas</string>
<string name="country_106">California</string>
<string name="country_108">Colorado</string>
<string name="country_109">Connecticut</string>
<string name="country_11">Estonia</string>
<string name="country_110">Delaware</string>
<string name="country_111">District of Columbia</string>
<string name="country_112">Florida</string>
<string name="country_113">Georgia</string>
<string name="country_115">Hawaii</string>
<string name="country_116">Idaho</string>
<string name="country_117">Illinois</string>
<string name="country_118">Indiana</string>
<string name="country_119">Iowa</string>
<string name="country_120">Kansas</string>
<string name="country_121">Kentucky</string>
<string name="country_122">Louisiana</string>
<string name="country_123">Maine</string>
<string name="country_124">Maryland</string>
<string name="country_125">Massachusetts</string>
<string name="country_126">Michigan</string>
<string name="country_127">Minnesota</string>
<string name="country_128">Mississippi</string>
<string name="country_129">Missouri</string>
<string name="country_13">Latvia</string>
<string name="country_130">Montana</string>
<string name="country_131">Nebraska</string>
<string name="country_132">Nevada</string>
<string name="country_133">New Hampshire</string>
<string name="country_134">New Jersey</string>
<string name="country_135">New Mexico</string>
<string name="country_136">New York</string>
<string name="country_137">North Carolina</string>
<string name="country_138">North Dakota</string>
<string name="country_139">Ohio</string>
<string name="country_14">Lithuania</string>
<string name="country_140">Oklahoma</string>
<string name="country_141">Oregon</string>
<string name="country_142">Pennsylvania</string>
<string name="country_144">Rhode Island</string>
<string name="country_145">South Carolina</string>
<string name="country_146">South Dakota</string>
<string name="country_147">Tennessee</string>
<string name="country_148">Texas</string>
<string name="country_149">Utah</string>
<string name="country_15">Moldova, Republic of</string>
<string name="country_150">Vermont</string>
<string name="country_151">Virginia</string>
<string name="country_153">Washington</string>
<string name="country_154">West Virginia</string>
<string name="country_155">Wisconsin</string>
<string name="country_156">Wyoming</string>
<string name="country_16">Russia</string>
<string name="country_17">Ukraine</string>
<string name="country_18">Belarus</string>
<string name="country_2">Slovakia</string>
<string name="country_200">Canada</string>
<string name="country_299">Greenland</string>
<string name="country_30">Greece</string>
<string name="country_31">Netherlands</string>
<string name="country_32">Belgium</string>
<string name="country_33">France</string>
<string name="country_34">Spain</string>
<string name="country_36">Hungary</string>
<string name="country_382">Montenegro</string>
<string name="country_39">Italy</string>
<string name="country_40">Romania</string>
<string name="country_41">Switzerland</string>
<string name="country_43">Austria</string>
<string name="country_44">Great Britain</string>
<string name="country_45">Denmark</string>
<string name="country_46">Sweden</string>
<string name="country_47">Norway</string>
<string name="country_48">Poland</string>
<string name="country_49">Germany</string>
<string name="country_50">Gibraltar</string>
<string name="country_51">Portugal</string>
<string name="country_52">Luxembourg</string>
<string name="country_53">Ireland</string>
<string name="country_54">Iceland</string>
<string name="country_55">Albania</string>
<string name="country_56">Malta</string>
<string name="country_57">Cyprus</string>
<string name="country_58">Finland</string>
<string name="country_59">Bulgaria</string>
<string name="country_62">Andorra</string>
<string name="country_64">New Zealand</string>
<string name="country_7">San Marino</string>
<string name="country_75">Liechtenstein</string>
<string name="country_741">Western Australia</string>
<string name="country_742">Queensland</string>
<string name="country_743">New South Wales</string>
<string name="country_744">Victoria</string>
<string name="country_745">South Australia</string>
<string name="country_746">Tasmania</string>
<string name="country_747">Northern Territory</string>
<string name="country_748">Capital Territory</string>
<string name="country_752">Mexico</string>
<string name="country_755">Brazil</string>
<string name="country_761">Australia</string>
<string name="country_762">Indonesia</string>
<string name="country_81">Serbia</string>
<string name="country_85">Croatia</string>
<string name="country_86">Slovenia</string>
<string name="country_87">Bosnia</string>
<string name="country_89">Macedonia</string>
<string name="country_90">Turkey</string>
<string name="country_93">Monaco</string>
<string name="country_96">Vatican City</string>
<string name="country_98">Faroe Islands</string>

Implement the SearchActivity:

package com.ptvag.navigation.tutorial;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.Spinner;

import com.ptvag.navigation.helper.DrawerActivity;
import com.ptvag.navigation.sdk.GeoPosition;
import com.ptvag.navigation.sdk.NavigationSDK;
import com.ptvag.navigation.sdk.Position;
import com.ptvag.navigation.sdk.SearchResult;
import com.ptvag.navigation.sdk.SearchKind;

public class SearchActivity extends AppCompatActivity
{
    public static final String PREFERENCE_COUNTRY = "last_searched_country";
    private MapView mapView;

    private Button townButton;
    private Button streetButton;
    private Button hnrButton;

    private SearchResult mCurrentTownResult;
    private SearchResult mCurrentStreetResult;
    private SearchResult mCurrentHNRResult;
    private Spinner mCountrySpinner;

    private int pinImageID;
    private CountryAdapter mAdapter;
    private CountryAdapter.Country mSelectedCountry;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        pinImageID = -1;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);

        mapView = (MapView)findViewById(R.id.map);
        townButton = (Button) findViewById(R.id.buttonTown);
        streetButton = (Button) findViewById(R.id.buttonStreet);
        hnrButton = (Button) findViewById(R.id.buttonHNR);
        mCountrySpinner = (Spinner)findViewById( R.id.countrySpinner );

        GeoPosition mapViewPosition = new Position(934177, 6268747).toGeoPosition();
        int mapViewScale = 5000;
        mapView.setCenter(mapViewPosition); // Karlsruhe/Germany
        mapView.setScale(mapViewScale);

        mAdapter = new CountryAdapter( this, R.layout.row_search_country);

        mCountrySpinner.setAdapter( mAdapter );
        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        int selectedCountry = preferences.getInt(PREFERENCE_COUNTRY, 0);
        mCountrySpinner.setSelection(selectedCountry);

        mCountrySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                SharedPreferences.Editor edit = preferences.edit();
                edit.putInt(PREFERENCE_COUNTRY, position);
                edit.commit();

                CountryAdapter.Country country = mAdapter.getItem(position);
                NavigationSDK.changeMap(country.getMapId());
                mSelectedCountry = country;
                resetAllSearches();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
    }

    private void resetAllSearches() {
        Application.getSearchModel().resetTownSearch();
        Application.getSearchModel().resetStreetSearch();
        Application.getSearchModel().resetHNRSearch();

        townButton.setText(R.string.town);
        streetButton.setText(R.string.street);
        hnrButton.setText(R.string.house_number);

        streetButton.setEnabled(false);
        hnrButton.setEnabled(false);
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        mapView.resume(); // tell the MapView the activity is resumed
    }

    @Override
    protected void onPause() {
        super.onPause();
        mapView.finish();
    }

    public void onTownClicked(View view)
    {
        Intent intent = new Intent(this, ResultListActivity.class);
        Bundle bundle = new Bundle();
        SearchKind type = SearchKind.SDK_SK_TOWNBYNAME_SORTED;
        bundle.putInt("type", type.getId());
        bundle.putInt("cc", mSelectedCountry.getPtvCode());

        intent.putExtras(bundle);
        startActivityForResult(intent, 1);
    }

    public void onStreetClicked(View view)
    {
        Intent intent = new Intent(this, ResultListActivity.class);
        Bundle bundle = new Bundle();
        SearchKind type = SearchKind.SDK_SK_STREETBYNAME;
        bundle.putInt("type", type.getId());
        bundle.putParcelable("town_result", mCurrentTownResult);
        intent.putExtras(bundle);
        startActivityForResult(intent, 2);
    }

    public void onHNRClicked(View view)
    {
        Intent intent = new Intent(this, ResultListActivity.class);
        Bundle bundle = new Bundle();
        SearchKind type = SearchKind.SDK_SK_HNR;
        bundle.putInt("type", type.getId());
        bundle.putParcelable("town_result", mCurrentTownResult);
        bundle.putParcelable("street_result", mCurrentStreetResult);
        intent.putExtras(bundle);
        startActivityForResult(intent, 3);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        String name;
        int mapViewScale = 5000;
        if (resultCode == Activity.RESULT_OK)
        {
            Bundle res = data.getExtras();
            if(res != null)
            {
                SearchResult result = res.getParcelable("result");
                if (result != null) {
                    name = result.getName();

                    switch (requestCode) {
                        case 1: // town search
                            townButton.setText(name);
                            streetButton.setText(R.string.street);
                            hnrButton.setText(R.string.house_number);
                            streetButton.setEnabled(true);
                            hnrButton.setEnabled(false);
                            mCurrentTownResult = result;
                            mCurrentStreetResult = null;
                            mCurrentHNRResult = null;
                            mapViewScale = 2500;
                            break;
                        case 2: // street search
                            streetButton.setText(name);
                            hnrButton.setText(R.string.house_number);
                            hnrButton.setEnabled(true);
                            mCurrentStreetResult = result;
                            mCurrentHNRResult = null;
                            mapViewScale = 200;
                            break;
                        case 3: // house number search
                            hnrButton.setText(name);
                            mCurrentHNRResult = result;
                            mapViewScale = 100;
                            break;
                        default:
                    }
                    if (pinImageID < 0)
                        pinImageID = NavigationSDK.addImage("pin.png", result.getPosition());
                    else
                        NavigationSDK.positionImage(pinImageID, result.getPosition());

                    mapView.setCenter(result.getPosition().toGeoPosition());
                    mapView.setScale(mapViewScale);
                }
            }
        }
    }
}