A SQLite database has fields to store Latitude and Longitude values in a table. A mailing address is input from users in an Add activity.
In the AsyncTask that fetches the LatLng object for the input address, the database write operation is the last statement of its doInBackground method. However, contrary to expectation, the write is happening BEFORE the JSON parsing of the response, writing NULL values for the lat and long initially.
The ListView that front-ends the table shows null Lat and Long values, but the right values after a refresh. The focus rushes back from the Add activity before the JSON parsing and all, which is not the desired behaviour. The ListView must return only after the right LatLng are populated in the table, until which an indeterminate progress bar should keep showing. This IS all working fine, except that
the progress bar appears and vanishes too fast
The ListView is back with other components of its list items properly populated, except the LatLong values.
The question is, how can the database write be forced to wait till the JSON parsing is complete?
Hope this verbose explanation of the situation will suffice. Please mention if more elaboration or code is required.
Many thanks in advance!
Relevant pseudo-code for your kind investigation:
The AsyncTask extension:
private class Geocode extends AsyncTask<String, Void, LatLng>
{
#Override
protected LatLng doInBackground(String...addressToGeocode)
{
int DELAY = 9000;
URL geoCodeURL;
String jsonResponse = null;
String geoCodeRequestURL = Contract.HTTPS_GOOGLE_GEOCODE_PRE_ADDRESS_URL
+ Uri.encode(addressToGeocode[0]);
// Get JSON response
try
{
geoCodeURL = new URL(geoCodeRequestURL);
URLConnection geoCodeURLConnection = geoCodeURL.openConnection();
BufferedReader responseReader = new BufferedReader(new InputStreamReader (geoCodeURLConnection.getInputStream()));
StringBuilder response = new StringBuilder();
int cp;
while((cp = responseReader.read()) != -1)
{
response.append((char)(cp));
}
jsonResponse = response.toString();
Thread.sleep(DELAY); // Force a delay, apparently not enough...
}
catch (relevantExceptions e)
{
Log.i(LOG_TAG, e.toString());
e.printStackTrace();
}
// Parse the LatLng from the JSON response
try
{
JSONObject responseObject = new JSONObject(jsonResponse);
double lat = ((JSONArray) responseObject
.get("results"))
.getJSONObject(0)
.getJSONObject("geometry")
.getJSONObject("location")
.getDouble("lat");
double lng = ((JSONArray) responseObject
.get("results"))
.getJSONObject(0)
.getJSONObject("geometry")
.getJSONObject("location")
.getDouble("lng");
addressLatLng = new LatLng(lat, lng);
object.setLat(lat);
objet.setLng(lng);
}
catch (JSONException e)
{
e.printStackTrace();
}
if (addressLatLng != null)
{
....
}
else
{
Toast.makeText(getApplicationContext(), "Unable to Geocode", Toast.LENGTH_SHORT).show();
}
// Write to the database
dbResult = dbHelper.commitRow(object);
return addressLatLng;
}
#Override
protected void onPreExecute()
{
setProgressBarIndeterminate(true);
setProgressBarIndeterminateVisibility(true);
}
#Override
protected void onPostExecute(LatLng addressLatLng)
{
setProgressBarIndeterminateVisibility(false);
}
} // of getLatLong()
Relevant code in the Add activity that instantiates the AsyncTask:
String addressToGeocode = address +
" " +
city +
country;
AsyncTask<String, Void, LatLng> geocodeTask = new Geocode();
geocodeTask.execute(addressToGeocode);
The Add activity is invoked by a ListView activity like so:
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
int itemId = item.getItemId();
switch (itemId)
{
case (R.id.action_menu_add):
{
Intent addIntent = new Intent(this, AddActivity.class);
startActivityForResult(addIntent, Contract.ADD_REQUEST_CODE);
return true;
}
default:
return false;
}
} // of onOptionsItemSelected()
The ListView's onActivityResult
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (resultCode == RESULT_OK)
{
Bundle b = data.getBundleExtra(GlobeShopperContract.NEW_BUNDLE);
MyObject d = (MyObject) b.get(Contract.NEW_BUNDLE_NAME);
collection.add(d);
dViewAdapter.setNotifyOnChange(true);
dViewAdapter.notifyDataSetChanged();
}
else
...
}
Related
I have written a program in which on button click i am getting the nearby atms from json . This is the link
https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=19.052696,72.8713694&radius=1000&types=atm&sensor=true&key=AIzaSyA8szrI9Ue4EwyUwTgz7Nk0c39qMal0pN4
I want to plot the atms on google map but the problem is only the last atm is being displayed on the map
Code : Method to get the atm names , latitude , longitude and vicinity
public void showAtm(){
String getAtmUrl =
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?
location="+lat+","+lng+"&radius=1000&types=atm&sensor=true
&key=AIzaSyA8szrI9Ue4EwyUwTgz7Nk0c39qMal0pN4";
try{
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(getAtmUrl).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
Map_Activity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getBaseContext(), "Request to atm
locations failed", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onResponse(Call call, Response response) throws
IOException {
Log.i("response ", "onResponse(): " + response);
String result = response.body().string();
Log.i("result",result);
try{
JSONObject jsonObject = new JSONObject(result);
String resultData = jsonObject.getString("results");
JSONArray urlDetails = new JSONArray(resultData);
for (int i = 0 ; i < urlDetails.length(); i++){
JSONObject json = urlDetails.getJSONObject(i);
geometry = json.getString(GOEMETRY);
vicinity = json.getString(VICINITY);
JSONObject jsonGeometry = new JSONObject(geometry);
String geoLocation =
jsonGeometry.getString(LOCATION);
JSONObject jsonLatLng = new JSONObject(geoLocation);
atmLat = jsonLatLng.getDouble(LATITUDE);
atmLong = jsonLatLng.getDouble(LONGITUDE);
atmName = json.getString(ATM_NAME);
Log.i("JsonArrayAtm", "" + atmName);
Log.i("JsonArrayGeometry",geometry);
Log.i("LatLong",""+atmLat+" , "+atmLong);
Log.i("Vicinity", vicinity);
runOnUiThread(new Runnable() {
#Override
public void run() {
moveAtmMap(atmLat ,atmLong );
}
});
}
}catch (Exception e){
e.printStackTrace();
}
}
});
}catch (Exception e){
e.printStackTrace();
}
}
///////////////////////////// atm locations map ///////////////////
private void moveAtmMap(Double amtLatitude,Double atmLongitude){
fragment.getMap().clear();
CameraPosition position = CameraPosition.builder()
.target(new LatLng(amtLatitude, atmLongitude))
.zoom(16f)
.bearing(0.0f)
.tilt(0.0f)
.build();
String msg = amtLatitude+ ", " + atmLongitude;
LatLng latLng = new LatLng(amtLatitude, atmLongitude);
fragment.getMap().addMarker(new MarkerOptions()
.position(latLng));
fragment.getMap().setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter()
{
#Override
public View getInfoWindow(Marker marker) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
View v = getLayoutInflater().inflate(R.layout.atm_custom_window,
null);
TextView atmHeader = (TextView) v.findViewById(R.id.atmName);
TextView atmLocation = (TextView)
v.findViewById(R.id.atmLocation);
atmHeader.setText(atmName);
atmLocation.setText(vicinity);
return v;
}
});
fragment.getMap().setMapType(GoogleMap.MAP_TYPE_NORMAL);
fragment.getMap().setTrafficEnabled(true);
fragment.getMap().setMyLocationEnabled(true);
fragment.getMap().animateCamera(CameraUpdateFactory
.newCameraPosition(position), null);
}
How do i achieve the above , can anyone suggest me ?
Thanks
You have written the method as,
private void moveAtmMap(Double amtLatitude,Double atmLongitude){
fragment.getMap().clear();
...
}
so every time this method will be called, it will clear all previous markers and you will end up having only the last marker.
Edit
for (int i = 0 ; i < urlDetails.length(); i++){
JSONObject json = urlDetails.getJSONObject(i);
String geometry = json.getString(GOEMETRY);
String vicinity = json.getString(VICINITY);
JSONObject jsonGeometry = new JSONObject(geometry);
String geoLocation = jsonGeometry.getString(LOCATION);
JSONObject jsonLatLng = new JSONObject(geoLocation);
double atmLat = jsonLatLng.getDouble(LATITUDE);
double atmLong = jsonLatLng.getDouble(LONGITUDE);
String atmName = json.getString(ATM_NAME);
runOnUiThread(new Runnable() {
#Override
public void run() {
moveAtmMap(atmLat, atmLong, atmName, vicinity, geometry);
}
});
}
and change method like,
private void moveAtmMap(Double amtLatitude,Double atmLongitude, String name, String vicinity, String geometry)
If you want to clear markers from previous web service hit, then do it before you start adding markers for new service hit, like before the for loop.
Remove this line from the method moveAtmMap:
fragment.getMap().clear();
For the first time, write it before for loop in onResponse.
Try this simple code,
for(int i=0;i<jsonArray.length();i++){
MarkerOptions markerOptions;
markerOptions = new MarkerOptions().position(new LatLng(lattitude,
longitude)
).title("Title").snippet("This is snippet");
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_icon));
marker = googleMap.addMarker(markerOptions);
}
Just replace lattitude,longitude with your values.
If you want to have instance of each marker,then you can put each "marker" object into hashmap with key as marker id. Let me know your feedback.
Note: remove this line - fragment.getMap().clear(); because it will clear map everytime when compiler comes into loop and it will take only last object. this is what happenng right now.
i am working on an app, where i am getting response(Json) from my remote server.I am also able to parse and place markers based on that response.But i am failing in parsing ph data from response and passing it into another activity.
It is sending only phone number of last json object data in setOnInfoWindowClickListener event.
I know some minor modifications i have to made. Please suggest me in this.
This is the Json response i am getting.
[
{
id: 965,
distance: "1.47",
ph: "33441111",
name: "XYZ",
LS: " abcdef",
point: "77.588018,12.959282"
},
{
id: 965,
distance: "1.47",
ph: "33441111",
name: "XYZ",
LS: " abcdef",
point: "77.588018,12.959282"
},
.
.
]
I tried this way to parse
private class HttpGetTask extends AsyncTask<Void, Void, String> {
// Showing progress dialog
// Passing URL
#Override
protected String doInBackground(Void... params) {
// http stuff
}
#Override
protected void onPostExecute(String result) {
if (pDialog.isShowing())
pDialog.dismiss();
try {
JSONArray json = new JSONArray(result);
for (int i = 0; i < json.length(); i++) {
Log.v("Response", result);
final JSONObject e = json.getJSONObject(i);
String point = e.getString("point");
final String phone = e.getString("ph");
String[] point2 = point.split(",");
double lat1 = Double.parseDouble(point2[0]);
double lng1 = Double.parseDouble(point2[1]);
gMap.addMarker(new MarkerOptions()
.title(e.getString("name"))
.snippet(
e.getString("LS") + "*" + e.getString("ph"))
.position(new LatLng(lng1, lat1))
.icon(BitmapDescriptorFactory
.fromResource(R.drawable.pmr)));
gMap.setInfoWindowAdapter(new InfoWindowAdapter() {
#Override
public View getInfoWindow(Marker arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public View getInfoContents(Marker mrkr) {
// TODO Auto-generated method stub
String name = mrkr.getTitle();
String detail = mrkr.getSnippet();
String trimmedDetail = detail.substring(0, 60);
Log.v("Info", name + " " + detail);
View v = getLayoutInflater().inflate(
R.layout.infowindow, null);
TextView title = (TextView) v
.findViewById(R.id.titleTV);
TextView snippet = (TextView) v
.findViewById(R.id.snippetTV);
title.setText("" + name);
snippet.setText("" + trimmedDetail);
return v;
}
});
gMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker arg0) {
Intent myIntent = new Intent(getBaseContext(),
DtActivity.class);
myIntent.putExtra("title", arg0.getTitle());
myIntent.putExtra("detail", arg0.getSnippet());
myIntent.putExtra("ph1", phone);
// How to access and send ph here.
try {
String ph = e.getString("ph");
myIntent.putExtra("ph", ph);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
startActivity(myIntent);
}
});
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (null != mClient)
mClient.close();
}
}
You need to Initialize the gMap Globaly and add the marker inside the for Loop. Remove the adapter and infowindow click listener codes from form loop and paste it outside. Kindly check the below sample.
public class Example extends FragmentActivity implements OnInfoWindowClickListener {
public static GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview);
mMap = ((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map)).getMap();
mMap.setOnInfoWindowClickListener(this);
// Inside the Loop
mMap.addMarker(new MarkerOptions().title(e.getString("name"))
.snippet(e.getString("LS") + "*" + e.getString("ph"))
.position(new LatLng(lng1, lat1))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.pmr)));
// Close the loop
}
#Override
public void onInfoWindowClick(Marker marker) {
// here you can get title, snippet, lat,lng of selected marker. then you
// can start activity.
}
}
Kindly paste the for loop inside the onPostExecute of your Asyntask Class.
Add this code snippet to get the phone number.I have marked it with you will get your phone number here
gMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker arg0) {
///////////////you will get your phone number here////////////////////////
for (int j = 0; j < json.length(); j++) {
JSONObject jo = json.getJSONObject(j);
if(arg0.getTitle().equals(jo.getString("title"))){
String phone= jo.getString("ph");
}
///////////////you will get your phone number here////////////////////////
Intent myIntent = new Intent(getBaseContext(),
DtActivity.class);
myIntent.putExtra("title", arg0.getTitle());
myIntent.putExtra("detail", arg0.getSnippet());
myIntent.putExtra("ph1", phone);
startActivity(myIntent);
}
});
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The problem is that you are setting a new setInfoWindowAdapter and setOnInfoWindowClickListener
each time you iterate to ur for loop and the last iterate or the last json object will be the InfoWindowAdapter and InfoWindowClickListener that why you are getting the last json, you can only set it once not multiple times except for the add marker.
Dont put the setInfoWindowAdapter and setOnInfoWindowClickListener in the for loop
I have a splash screen that loads URLs from the Internal Storage and downloads their content from the Web (with an AsynkTask). It puts the downloaded data into an ArrayList, calls the main Activity and finishes. The main activity adapter manages the ArrayList and sets a ListView containing its data.
While I'm in the main Activity, if I press the back button the application exits (I set the android:nohistory="true" for the splash screen activity), but when I return to the app, the splash screen gets loaded and downloads the data again, "doubling" the list view.
How can I prevent the splash screen to be loaded when I return to the app?
Splash screen code:
Context mContext;
ProgressBar progress = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_launcher);
progress = (ProgressBar)findViewById(R.id.progress);
progress.setIndeterminate(true);
if(canWriteOnExternalStorage()) {
try {
setupStorage();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
//dialog appears
}
AsynkTask code:
private class LoadGames extends
AsyncTask<String, Integer, Boolean> {
private ProgressDialog mProgressDialog = null;
private String remoteUrl = null;
#Override
protected void onCancelled() {
Log.e(com.example.ludos2_0.MainActivity.TAG,
"AsyncTask->LoadGames: onCancelled !");
super.onCancelled();
}
#Override
protected void onPreExecute() {
Log.d(com.example.ludos2_0.MainActivity.TAG,
"AsyncTask->LoadGames: onPreExecute !");
}
#Override
protected Boolean doInBackground(String... params) {
if (params.length == 0)
return false;
else
for (int k = 0; k < (params.length)/2; ++k)
{
this.remoteUrl = params[k*2];
Log.d(com.example.ludos2_0.MainActivity.TAG,
"AsyncTask->LoadGames: doInBackground ! ("
+ this.remoteUrl + ")");
// HTTP Request to retrieve the videogames list in JSON format
try {
// Creates the remote request
Log.d(com.example.ludos2_0.MainActivity.TAG,
this.remoteUrl);
RESTRequest request = new RESTRequest(this.remoteUrl);
request.isMethodGET(true);
// Executes the request and print the received response
String response = RESTRequestExecutor.execute(request);
// Custom/Manual parsing using GSON
JsonParser parser = new JsonParser();
if (response != null && response.length() > 0) {
Log.d(com.example.ludos2_0.MainActivity.TAG, "Response: "
+ response);
JsonObject jsonObject = (JsonObject) parser.parse(response);
JsonObject itemObj = jsonObject.getAsJsonObject("results");
String id = null;
String title = null;
String thumbnail = null;
String description = null;
String image = null;
String platform = null;
id = itemObj.get("id").getAsString();
title = itemObj.get("name").getAsString();
if (!(itemObj.get("image").isJsonNull()))
{
thumbnail = ((JsonObject)itemObj.get("image")).get("tiny_url").getAsString();
image = ((JsonObject)itemObj.get("image")).get("small_url").getAsString();
}
else
{
thumbnail = "http://www.persicetometeo.com/images/not_available.jpg";
image = "http://www.persicetometeo.com/images/not_available.jpg";
}
description = itemObj.get("deck").getAsString();
platform = params[k*2 + 1];
Log.d(com.example.ludos2_0.MainActivity.TAG,
title);
ListsManager.getInstance().addVideogame(new Videogame(id, title, thumbnail, image, description, platform));
} else {
Log.d(com.example.ludos2_0.MainActivity.TAG,
"Error getting response ...");
}
} catch (Exception e) {
e.printStackTrace();
Log.e(com.example.ludos2_0.MainActivity.TAG,
"Exception: " + e.getLocalizedMessage());
}
}
return true;
}
#Override
protected void onPostExecute(Boolean result) {
Log.d(com.example.ludos2_0.MainActivity.TAG,
"AsyncTask->LoadGames: onPostExecute !");
progress.setVisibility(View.GONE);
if (result == false) {
Log.e(com.example.ludos2_0.MainActivity.TAG,
"AsyncTask->LoadGames: Error Downloading Data !");
} else {
Log.d(com.example.ludos2_0.MainActivity.TAG,
"AsyncTask->LoadGames: Data Correctly Downloaded !");
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
finish();
}
super.onPostExecute(result);
}
}
The setupStorage() method loads the file from the Storage and executes the AsynkTask.
Maybe could the overriding of the onRestart() method be a solution?
Or should I prevent the AsyncTask from loading the data already downloaded?
Thanks!
It would be better to prevent AsynkTask to download it again. Or better to clear your listview data. Means if use ArrayList with your List adapter then just clear it before storing putting new data.
I'd like to set a ListView to data I get from a web service. I get the data in a AsyncTask instance, but when I try to set some of my ListView attributes, it crashes (on line "lv.setVisibility(View.VISIBLE);"). Anybody can help?
thanks
public class Atable extends Activity {
private EditText mSearch;
private static final int ACTIVITY_EDIT=0;
private Button mSearchButton;
private TextView mNoresults;
private ListView lv;
private CheckBox checkBox;
private LocationManager locationManager;
private RestaurantList restaurantList;
private Criteria criteria;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv= (ListView)findViewById(R.id.listview);
mNoresults = (TextView) findViewById(R.id.noresults);
mNoresults.setVisibility(View.GONE);
mSearchButton = (Button)this.findViewById(R.id.button);
checkBox = (CheckBox) findViewById(R.id.local_check);
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
mSearchButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mNoresults.setVisibility(View.GONE);
mSearch = (EditText) findViewById(R.id.search);
String tmp_str = mSearch.getText().toString().replace(" ","+");
String url = "http://www.atable.org/getRestaurantByQuery/?query=" + tmp_str;
if (checkBox.isChecked()) {
//get location
String provider = locationManager.getBestProvider(criteria, true);
Location location = locationManager.getLastKnownLocation(provider);
if (location!=null) {
String lat = String.valueOf(location.getLatitude());
String lng = String.valueOf(location.getLongitude());
url += "&lat="+lat+"&lng="+lng;
}
}
new GetRestaurantData().execute(url);
}
});
};
private class GetRestaurantData extends AsyncTask<String, Boolean, RestaurantList> {
private HttpClient httpclient = new DefaultHttpClient();
#Override
protected RestaurantList doInBackground(String... url) {
publishProgress(true);
HttpGet httpget = new HttpGet(url[0]);
// Execute the request
HttpResponse response;
try {
response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
Reader r = new InputStreamReader(instream);
Gson gson = new Gson();
restaurantList = gson.fromJson(r, RestaurantList.class);
int nResults = restaurantList.getSize();
if (nResults>0) {
lv.setVisibility(View.VISIBLE); //app crashes here
lv.setAdapter( new ArrayAdapter<String>(Atable.this ,android.R.layout.simple_list_item_1,restaurantList.getRestaurantNames()));
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(Atable.this, RestaurantDescription.class);
Restaurant tmp_resto = restaurantList.getRestaurant((int)id);
String tmp_categories = tmp_resto.getCategories().get(0);
for (int i=1; i<tmp_resto.getCategories().size(); i++) {
tmp_categories+=", "+tmp_resto.getCategories().get(i);
}
String address = tmp_resto.getStreet()+", "+tmp_resto.getStreetNumber()+"\n"+tmp_resto.getCity()+
" "+tmp_resto.getPostalCode()+"\n"+tmp_resto.getCountry();
intent.putExtra("name", tmp_resto.getName());
intent.putExtra("address", address);
intent.putExtra("rating", tmp_resto.getRating());
intent.putExtra("price_range", tmp_resto.getPriceRange());
intent.putExtra("categories", tmp_categories);
intent.putExtra("latitude", tmp_resto.getLatitude());
intent.putExtra("longitude", tmp_resto.getLongitude());
startActivityForResult(intent, ACTIVITY_EDIT);
}
});
}
else {
lv.setVisibility(View.GONE);
mNoresults.setVisibility(View.VISIBLE);
}
//Closing the input stream will trigger connection release
instream.close();
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return restaurantList;
}
#Override
protected void onProgressUpdate(Boolean... progress) {
// line below coupled with
// getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS)
// before setContentView
// will show the wait animation on the top-right corner
Atable.this.setProgressBarIndeterminateVisibility(progress[0]);
}
#Override
protected void onPostExecute(RestaurantList result) {
publishProgress(false);
// Do something with result in your activity
}
}
In Android, all UI updates are done on the main thread (also called UI thread). It's good when you spawn a new thread to do time consuming tasks in order not to block the UI.
But in order to avoid all sorts of indeterminate behaviors, UI updates always have to be done on the UI thread. If you ever attempt to do so from a different thread, an exception will be thrown.
In your example, I see you're performing different updatings to ListView based on the value of nResults. You can try returning nResults for doInBackground(). It will be passed to onPostExecute(), which will be executed on the UI thread.
You wanna check out this article for some useful information about threadings in Android: http://android-developers.blogspot.com/2009/05/painless-threading.html
HTH
You can't access the ui thread from the doinbachground method.. you need to do that from the post execute, pre execute or on progress update methods...
I want to create an application for Android that enables me to get the geolocation of a user. This has to be made as a client-server app and for the server side I'm using OpenFire.
For getting the user's location I would have to use XEP-0080, right? And SmackAPI also?
I'm completely new to XMPP and Smack, so if anyone could get me a few pointers or maybe examples or any kind of documentation about this I'd be very grateful.
Thanks in advance for any help.
An Android project I’m currently working on required periodically publishing a user’s location to their XMPP roster friends using aSmack & XEP-0080.
It turned out trickier than I would have liked so I documented my solution here: http://www.dbotha.com/2014/11/02/xep-0080-user-location-on-android-using-pep-with-smack/
For completeness I'll cover the important parts here. In the interest of brevity the only XML child elements from the XEP-0080 specification that I’ll cover are those relating to latitude and longitude.
A PEPItem to hold the user location and transform it into the appropriate XML:
public class UserLocation extends PEPItem {
public static final String NODE =
"http://jabber.org/protocol/geoloc";
public final double latitude, longitude;
public UserLocation(double latitude, double longitude) {
this(StringUtils.randomString(16), latitude, longitude);
}
public UserLocation(double latitude, double longitude,
String id) {
super(id);
this.latitude = latitude;
this.longitude = longitude;
}
#Override
java.lang.String getNode() {
return NODE;
}
// return an XML element approximately inline
// with the XEP-0080 spec
#Override
java.lang.String getItemDetailsXML() {
return String.format(
"<geoloc xmlns='%s'><lat>%f</lat>" +
"<lon>%f</lon></geoloc>",
NODE, latitude, longitude);
}
}
A mostly boilerplate PEPEvent to hold the UserLocation PEPItem:
public class UserLocationEvent extends PEPEvent {
private final UserLocation location;
public UserLocationEvent(UserLocation location) {
this.location = location;
}
public UserLocation getLocation() {
return location;
}
#Override
public String getNamespace() {
return "http://jabber.org/protocol/pubsub#event";
}
#Override
public String toXML() {
return String.format("<event xmlns=" +
"'http://jabber.org/protocol/pubsub#event' >" +
"<items node='%s' >%s</items></event>",
UserLocation.NODE, location.toXML());
}
}
A custom PacketExtensionProvider to parse out the UserLocationEvent's from incoming packets where present.
public class UserLocationProvider
implements PacketExtensionProvider {
// This method will get called whenever aSmack discovers a
// packet extension containing a publish element with the
// attribute node='http://jabber.org/protocol/geoloc'
#Override
public PacketExtension parseExtension(XmlPullParser parser)
throws Exception {
boolean stop = false;
String id = null;
double latitude = 0;
double longitude = 0;
String openTag = null;
while (!stop) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
openTag = parser.getName();
if ("item".equals(openTag)) {
id = parser.getAttributeValue("", "id");
}
break;
case XmlPullParser.TEXT:
if ("lat".equals(openTag)) {
try {
latitude = Double.parseDouble(
parser.getText());
} catch (NumberFormatException ex) {
/* ignore */
}
} else if ("lon".equals(openTag)) {
try {
longitude = Double.parseDouble(
parser.getText());
} catch (NumberFormatException ex) {
/* ignore */
}
}
break;
case XmlPullParser.END_TAG:
// Stop parsing when we hit </item>
stop = "item".equals(parser.getName());
openTag = null;
break;
}
}
return new UserLocationEvent(
new UserLocation(id, latitude, longitude));
}
}
Now tying it all together:
XMPPTCPConnection connection = new XMPPTCPConnection();
ServiceDiscoveryManager sdm = ServiceDiscoveryManager
.getInstanceFor(connection);
sdm.addFeature("http://jabber.org/protocol/geoloc");
sdm.addFeature("http://jabber.org/protocol/geoloc+notify");
EntityCapsManager capsManager = EntityCapsManager
.getInstanceFor(connection);
capsManager.enableEntityCaps();
PEPProvider pepProvider = new PEPProvider();
pepProvider.registerPEPParserExtension(
"http://jabber.org/protocol/geoloc",
new UserLocationProvider());
ProviderManager.addExtensionProvider("event",
"http://jabber.org/protocol/pubsub#event", pepProvider);
PEPManager pepManager = new PEPManager(connection);
pepManager.addPEPListener(PEP_LISTENER);
connection.connect();
connection.login(username, password);
And finally a listener for incoming LocationEvent's:
PEPListener PEP_LISTENER = new PEPListener() {
#Override
public void eventReceived(String from, PEPEvent event) {
if (event instanceof UserLocationEvent) {
// do something interesting
}
}
};
I believe this is close to what you are trying to accomplish.
XEP-0080 User Location in Smack Library