Using Android Location API in Weather App – Search city
In this post I would like to describe how to search a city using openweathermap to get weather conditions. There are two way we can use to find a city:
- Using name pattern
- Using geographic coordinates (Android Location API)
We will introduce, moreover, some concepts about Android location API and Location-based service.
Searching city by name
In this kind of search, an user inserts a part of city name and the app will show all the possible result matching the pattern. From the UI perspective, we want to obtain something like the image shown below:
As first step, we can use EditText so that the user can insert the city name:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > .... <EditText android:id="@+id/cityEdtText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/textView1" android:layout_marginTop="5dp" android:imeOptions="actionSearch" android:inputType="text" > <requestFocus /> </EditText> .... <ListView android:id="@+id/cityList" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/txt1" android:layout_marginTop="3dp"/> </RelativeLayout>
Notice that at line 13, we used imeOptions with “actionSearch” value to specify search action when user completes inserting the city name pattern. Now in the Activity that holds this layout we have:
final EditText edt = (EditText) findViewById(R.id.cityEdtText); edt.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEARCH) { bar.setVisibility(View.VISIBLE); JSONSearchTask task = new JSONSearchTask(); String pattern = edt.getEditableText().toString(); task.execute(new String[]{pattern}); return true; } return false; } });
So when user clicks on search icon, we starts searching for matching city names and at the end we will populate a ListView (line 18) . As you can notice, we start an AsyncTask, because the search operation can require quite long time and we don’t want to have ANR problems.
Now it is time to implement the logic that calls the remote openweathermap service and retrieve the cities. We know from API that the URL to call is:
http://api.openweathermap.org/data/2.5/find?mode=json&type=like&q=...&cnt=10
where after q we will add our pattern name and cnt represents the number of items we want to get (in our case we want 10 results at maximum).
Once we have the data, we parse it. We look for list tag that holds the result and for each item in the result we look for name tag that holds the name, the id and the country. The parser is very simple:
public static List<City> getCityList(String data) throws JSONException { JSONObject jObj = new JSONObject(data); JSONArray jArr = jObj.getJSONArray("list"); List<City> cityList = new ArrayList<City>(); for (int i=0; i < jArr.length(); i++) { JSONObject obj = jArr.getJSONObject(i); String name = obj.getString("name"); String id = obj.getString("id"); JSONObject sys = obj.getJSONObject("sys"); String country = sys.getString("country"); City c = new City(id,name,country); cityList.add(c); } return cityList; }
..and finally we populate the ListView to show the results to the user. This happen in onPostExecute method of the AsyncTask. Notice that you can create a custom Adapter to show this information or you can use a standard Adapter.
Search city using geographic coordinates: Android Location API
Another way to look for the city is using the current user position. We can use Location based service to find the current device position using GPS or WI-FI or cell network. There are two main element when implementing Location based service:
- Location Manager that is the entry point to the Location based service
- Location Provider representing the location technology used to find the device position
The first step is getting the LocationManager reference. This is a system service, then we can use:
LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
once we have our Location Manager reference we can find the provider. In our case, we don’t want to specify a specific provider (like GPS for example) but let the system find the best matching the search criteria we set:
private static Criteria searchProviderCriteria = new Criteria(); // Location Criteria static { searchProviderCriteria.setPowerRequirement(Criteria.POWER_LOW); searchProviderCriteria.setAccuracy(Criteria.ACCURACY_COARSE); searchProviderCriteria.setCostAllowed(false); }
and then we are ready to get the provider:
String provider = locManager.getBestProvider(searchProviderCriteria, true);
and:
Location loc = locManager.getLastKnownLocation(provider);
Now we have to check if the location is null or is too old, in this case we get a new position:
if (loc == null || (SystemClock.elapsedRealtime() - loc.getTime()) > timeOut) { // We request another update Location Log.d("SwA", "Request location"); locManager.requestSingleUpdate(provider, locListener, null); } else { JSONSearchTask task = new JSONSearchTask(); task.execute(new Location[]{loc}); }
otherwise we simply call the service to get the city. Notice at line 4, that we need a listener that implements some callback methods, that will be called when the current position is available:
private LocationListener locListener = new LocationListener() { @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onProviderEnabled(String provider) { } @Override public void onProviderDisabled(String provider) { } @Override public void onLocationChanged(Location location) { Log.d("SwA", "Location changed!"); String sLat = "" + location.getLatitude(); String sLon = "" + location.getLongitude(); Log.d("SwA", "Lat ["+sLat+"] - sLong ["+sLon+"]"); LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); locManager.removeUpdates(locListener); JSONSearchTask task = new JSONSearchTask(); task.execute(new Location[]{location}); } };
The last step is creating the URL that will be called to retrieve the city from the geographic coordinates. In this case the url to call is:
http://api.openweathermap.org/data/2.5/find?lat=%lat%&lon=%lon%
where the %lat% and &lon% will be substituted by the real value extracted from the current position.
Using emulator to test the app
The last step is testing the app passing the coordinates. We can use DDMSDDMS and pass the coordinates:
Please notice that you should separate the decimal part according to your language and number format. Another way is using
telnet iphost port
and when connected send
geo fix <longitude value> <latitude value>
Reference: | Using Android Location API in Weather App – Search city from our JCG partner Francesco Azzola at the Surviving w/ Android blog. |