Create a currency converter application using Yahoo! API

Keywords: ConnectivityManager HttpGet XmlPullParser SAXParser SimpleCursorAdapter SQLiteDatabase ContentProvider SQLiteQueryBuilder BroadcastReceiver IntentService AChartEngine Search Dialog Animation TableLayout
Contents

- Overview
- Create a new Eclipse Android project
- Define the Data model
- The Android Manifest file
- The Application class
- Create a Preferences screen
- Implement the Init Task
- The Yahoo! Finance API
- The XmlPullParser
- The SAX Parser
- Calling RESTful service
- Implement the Data Service
- Create a splash screen
- Create the Main screen
- The Search Dialog
- Create the Info screen
- The AChartEngine library
15. The Search Dialog
The Search Dialog is a UI component provided by Android that allows you to surface your application's content in a standard manner. When activated by pressing harware search key or programmatically through onSearchRequested(), it appears at the top of the activity window.Implementing search involves the following:
- A searchable configuration XML file
- A searchable activity
To enable search dialog for a given activity you need to add a meta-data tag to the activity node in the manifest and in the value attribute specify the searchable activity. We have already done this for MainActivity in the previous section.
<meta-data android:name="android.app.default_searchable" android:value=".SearchableActivity" />First, create a configuration file searchable.xml in res/xml directory.
<?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/app_name" android:hint="@string/search_hint" android:searchSuggestAuthority="com.appsrox.forexwiz.provider" android:searchSuggestIntentAction="android.intent.action.VIEW" android:searchSuggestIntentData="content://com.appsrox.forexwiz.provider/symbol"> </searchable>Although android:label attribute is the only required attribute and it's recommended to specify android:hint attribute, we specify other attributes since we also implement custom suggestions with search.
Next, create SearchableActivity to display the search results.
public class SearchableActivity extends ListActivity { private SQLiteDatabase db; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); db = ForexWiz.db; handleIntent(); } @Override protected void onNewIntent(Intent intent) { setIntent(intent); handleIntent(); } private void handleIntent() { Intent intent = getIntent(); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String searchQuery = intent.getStringExtra(SearchManager.QUERY); doSearch(searchQuery); } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { Uri detailUri = intent.getData(); long id = Long.parseLong(detailUri.getLastPathSegment()); addSymbol(id); finish(); } } private void doSearch(String query) { Cursor c = db.rawQuery("SELECT _id, name, country FROM symbol WHERE isTracked = 1", null); startManagingCursor(c); SimpleCursorAdapter adapter = (SimpleCursorAdapter) getListAdapter(); if (adapter == null) { adapter = new SimpleCursorAdapter( this, android.R.layout.simple_list_item_2, c, new String[]{"name", "country"}, new int[]{android.R.id.text1, android.R.id.text2}); setListAdapter(adapter); } else { (adapter).changeCursor(c); } } @Override protected void onListItemClick(ListView l, View v, int position, long id) { addSymbol(id); finish(); } private void addSymbol(long id) { Symbol symbol = new Symbol(id); symbol.setTracked(true); symbol.update(db); } }When search is executed Android launches this activity with action set to Intent.ACTION_SEARCH and passes the search query as a string extra. The doSearch() method is invoked which creates an adapter and the result is displayed in the list view.

Declare the activity in the manifest as follows.
<activity android:name=".SearchableActivity" android:label="Currency Search" android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.SEARCH" /> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity>Notice that we have set android:launchMode to singleTop since we only want a single instance of the search activity. So we override onNewIntent() method in SearchableActivity to set the new intent as default.
Finally, we need to implement a provider for the search suggestions. We have already declared the authority using android:searchSuggestAuthority in searchable.xml file. The authority must match with a provider declared in the manifest.
<provider android:name=".SuggestionProvider" android:authorities="com.appsrox.forexwiz.provider"></provider>We will next implement the ContentProvider for search suggestions.
public class SuggestionProvider extends ContentProvider { public static final Uri CONTENT_URI = Uri.parse("content://com.appsrox.forexwiz.provider/symbol"); private static final int ALL = 1; private static final int ROW_ID = 2; private static final int SEARCH = 3; private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); static { matcher.addURI(CONTENT_URI.getAuthority(), "symbol", ALL); matcher.addURI(CONTENT_URI.getAuthority(), "symbol/#", ROW_ID); matcher.addURI(CONTENT_URI.getAuthority(), SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH); matcher.addURI(CONTENT_URI.getAuthority(), SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH); } private SQLiteDatabase db; @Override public boolean onCreate() { DbHelper dbHelper = new DbHelper(getContext()); db = dbHelper.getReadableDatabase(); return true; } private static final HashMap<String, String> PROJECTION_MAP = new HashMap<String, String>(); static { PROJECTION_MAP.put("_id", "_id"); PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_1, "code AS " + SearchManager.SUGGEST_COLUMN_TEXT_1); PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_2, "country || ' ' || name AS " + SearchManager.SUGGEST_COLUMN_TEXT_2); PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "_id AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); builder.setTables(Symbol.TABLE_NAME); switch(matcher.match(uri)) { case SEARCH: String query = uri.getLastPathSegment(); builder.appendWhere("isTracked = 0 AND ("); builder.appendWhere("name LIKE '" + query + "%' OR "); builder.appendWhere("country LIKE '" + query + "%' OR "); builder.appendWhere("code LIKE '" + query + "%')"); builder.setProjectionMap(PROJECTION_MAP); break; } return builder.query(db, projection, selection, selectionArgs, null, null, sortOrder); } @Override public String getType(Uri uri) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException(); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } }Search suggestions are displayed below the search dialog as user types in the search box.

We'll next develop the details screen and see how to plot historical exchange rates using AChartEngine library.