Last active
April 4, 2017 14:20
-
-
Save nissivm/eb8629cf02b3740790c8037705209995 to your computer and use it in GitHub Desktop.
Android - Looking for places with Yelp using Volley
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- In your AndroidManifest.xml --> | |
<?xml version="1.0" encoding="utf-8"?> | |
<manifest | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
package="your.app.package"> | |
<uses-permission android:name="android.permission.INTERNET" /> | |
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
dependencies { | |
... | |
compile 'com.android.volley:volley:1.0.0' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.app.package; | |
import android.os.Parcel; | |
import android.os.Parcelable; | |
import org.json.JSONArray; | |
import org.json.JSONException; | |
import org.json.JSONObject; | |
import java.util.UUID; | |
@SuppressWarnings("SpellCheckingInspection") | |
class Business implements Parcelable | |
{ | |
private JSONObject obj; | |
private String placeId = ""; | |
private String placeName = ""; | |
private String placeAddr = ""; | |
private String placePhone = ""; | |
private String placePriceLevel = ""; | |
private double placeRating = 0; | |
private int placeReviewCount = 0; | |
private String placeImageUrl = ""; | |
private String placeUrl = ""; | |
private boolean placeClosed = true; | |
private double distance = 0; | |
private double placeLatitude = 0; | |
private double placeLongitude = 0; | |
Business(JSONObject obj) | |
{ | |
this.obj = obj; | |
setProperties(); | |
} | |
//----------------------------------------------------------------------------------------// | |
// Setters | |
//----------------------------------------------------------------------------------------// | |
private void setProperties() | |
{ | |
UUID uuid = UUID.randomUUID(); | |
placeId = uuid.toString(); | |
String str = extractString("name"); | |
if (str != null) | |
{ | |
placeName = str; | |
} | |
str = extractAddress("location"); | |
if (str != null) | |
{ | |
placeAddr = str; | |
} | |
str = extractString("display_phone"); | |
if (str != null) | |
{ | |
placePhone = str; | |
} | |
str = extractString("price"); | |
if (str != null) | |
{ | |
placePriceLevel = str; | |
} | |
placeRating = extractDouble("rating"); | |
placeReviewCount = extractInt("review_count"); | |
str = extractString("image_url"); | |
if (str != null) | |
{ | |
placeImageUrl = str.replace("\\", ""); | |
} | |
str = extractString("url"); | |
if (str != null) | |
{ | |
placeUrl = str.replace("\\", ""); | |
} | |
placeClosed = extractPlaceCloseStatus(); | |
placeLatitude = extractLatitude(); | |
placeLongitude = extractLongitude(); | |
} | |
void setDistance(double value) | |
{ | |
distance = value; | |
} | |
//----------------------------------------------------------------------------------------// | |
// Getters | |
//----------------------------------------------------------------------------------------// | |
String getPlaceId() | |
{ | |
return placeId; | |
} | |
String getPlaceName() | |
{ | |
return placeName; | |
} | |
String getPlaceAddr() | |
{ | |
return placeAddr; | |
} | |
String getPlacePhone() | |
{ | |
return placePhone; | |
} | |
String getPlacePriceLevel() | |
{ | |
return placePriceLevel; | |
} | |
double getPlaceRating() | |
{ | |
return placeRating; | |
} | |
int getPlaceReviewCount() | |
{ | |
return placeReviewCount; | |
} | |
String getPlaceImageUrl() | |
{ | |
return placeImageUrl; | |
} | |
String getPlaceUrl() | |
{ | |
return placeUrl; | |
} | |
boolean isPlaceClosed() | |
{ | |
return placeClosed; | |
} | |
double getDistance() | |
{ | |
return distance; | |
} | |
double getPlaceLatitude() | |
{ | |
return placeLatitude; | |
} | |
double getPlaceLongitude() | |
{ | |
return placeLongitude; | |
} | |
//----------------------------------------------------------------------------------------// | |
// Extraction functions | |
//----------------------------------------------------------------------------------------// | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private String extractString(String key) | |
{ | |
try | |
{ | |
String str = obj.getString(key); | |
return str; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return null; | |
} | |
} | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private String extractAddress(String key) | |
{ | |
try | |
{ | |
JSONObject location = obj.getJSONObject(key); | |
JSONArray displayAddr = location.getJSONArray("display_address"); | |
String addr = ""; | |
if (displayAddr.length() > 0) | |
{ | |
for (int i = 0; i < displayAddr.length(); i++) | |
{ | |
addr += displayAddr.getString(i); | |
if (i < (displayAddr.length() - 1)) | |
{ | |
addr += ", "; | |
} | |
} | |
} | |
return addr; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return null; | |
} | |
} | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private double extractDouble(String key) | |
{ | |
try | |
{ | |
double dbl = obj.getDouble(key); | |
return dbl; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return 0; | |
} | |
} | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private int extractInt(String key) | |
{ | |
try | |
{ | |
int i = obj.getInt(key); | |
return i; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return 0; | |
} | |
} | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private boolean extractPlaceCloseStatus() | |
{ | |
try | |
{ | |
boolean closed = obj.getBoolean("is_closed"); | |
return closed; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return true; | |
} | |
} | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private double extractLatitude() | |
{ | |
try | |
{ | |
JSONObject coordinates = obj.getJSONObject("coordinates"); | |
double lat = coordinates.getDouble("latitude"); | |
return lat; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return 0; | |
} | |
} | |
@SuppressWarnings("UnnecessaryLocalVariable") | |
private double extractLongitude() | |
{ | |
try | |
{ | |
JSONObject coordinates = obj.getJSONObject("coordinates"); | |
double lng = coordinates.getDouble("longitude"); | |
return lng; | |
} | |
catch (JSONException e) | |
{ | |
//e.printStackTrace(); | |
return 0; | |
} | |
} | |
//----------------------------------------------------------------------------------------// | |
// Parcelable | |
//----------------------------------------------------------------------------------------// | |
// Font: https://newfivefour.com/android-parcelable.html | |
public static final Parcelable.Creator<Business> CREATOR = new Parcelable.Creator<Business>() | |
{ | |
public Business createFromParcel(Parcel in) | |
{ | |
return new Business(in); | |
} | |
public Business[] newArray(int size) | |
{ | |
return new Business[size]; | |
} | |
}; | |
private Business(Parcel in) | |
{ | |
placeId = in.readString(); | |
placeName = in.readString(); | |
placeAddr = in.readString(); | |
placePhone = in.readString(); | |
placePriceLevel = in.readString(); | |
placeRating = in.readDouble(); | |
placeReviewCount = in.readInt(); | |
placeImageUrl = in.readString(); | |
placeUrl = in.readString(); | |
boolean[] myBooleanArr = new boolean[1]; | |
in.readBooleanArray(myBooleanArr); | |
placeClosed = myBooleanArr[0]; | |
distance = in.readDouble(); | |
placeLatitude = in.readDouble(); | |
placeLongitude = in.readDouble(); | |
} | |
@Override | |
public void writeToParcel(Parcel dest, int flags) | |
{ | |
dest.writeString(placeId); | |
dest.writeString(placeName); | |
dest.writeString(placeAddr); | |
dest.writeString(placePhone); | |
dest.writeString(placePriceLevel); | |
dest.writeDouble(placeRating); | |
dest.writeInt(placeReviewCount); | |
dest.writeString(placeImageUrl); | |
dest.writeString(placeUrl); | |
dest.writeBooleanArray(new boolean[]{placeClosed}); | |
dest.writeDouble(distance); | |
dest.writeDouble(placeLatitude); | |
dest.writeDouble(placeLongitude); | |
} | |
@Override | |
public int describeContents() | |
{ | |
return 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.app.package; | |
public class MainActivity extends Activity implements LocationListener, ServerRequestInterface | |
{ | |
private ServerRequest serverRequest; | |
private String yelpAccessToken = ""; | |
private double userLat = 0; | |
private double userLong = 0; | |
private String[] placesNamesArray = { | |
"Restaurants", "Cafes", "Bakeries", "Bars", "Supermarkets", "Bookstores", | |
"Shopping", "Home goods", "Pets", "Museums", "Theaters", "Movie theaters", | |
"Churches", "Parks, Plazas & Zoos", "Bus stations", "Subway stations", | |
"Beauty salons", "Gym", "Pharmacies", "Hospitals" | |
}; | |
// Font: https://www.yelp.com/developers/documentation/v3/all_category_list | |
private String[] placeCategoriesArray = { | |
"foodtrucks,restaurants,kiosk,dancerestaurants", | |
"acaibowls,bubbletea,churros,coffee,convenience,cupcakes,delicatessen,empanadas,friterie,gelato,icecream,internetcafe,milkshakebars,cakeshop,pretzels,tea,tortillas,coffeeshops", | |
"bakeries,bagels,donuts", | |
"barcrawl,bars,beergardens,jazzandblues,pianobars,poolhalls", | |
"butcher,ethicgrocery,farmersmarket,fishmonger,grocery,intlgrocery,organic_stores,markets,seafoodmarkets", | |
"bookstores,comicbooks,musicvideo,mags,usedbooks", | |
"childcloth,deptstores,hats,lingerie,maternity,menscloth,plus_size_fashion,shoes,sleepwear,sportswear,swimwear,vintage,womenscloth,shoppingcenters,thrift_stores,watches,wigs", | |
"furniture,homedecor,mattresses,gardening,outdoorfurniture,paintstores,tableware", | |
"emergencypethospital,groomer,vet", | |
"galleries,museums,planetarium", | |
"opera,theater", | |
"movietheaters", | |
"churches,synagogues", | |
"gardens,parks,playgrounds,publicplazas,zoos", | |
"busstations", | |
"metrostations", | |
"barbers,eyebrowservices,hair_extensions,waxing,hair,makeupartists,othersalons", | |
"cardioclasses,dancestudio,gyms,pilates,swimminglessons,taichi,yoga,gymnastics", | |
"drugstores,herbalshops,pharmacy,vitaminssupplements", | |
"emergencyrooms,hospitals,medcenters,urgent_care" | |
}; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) | |
{ | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
serverRequest = new ServerRequest(this); | |
if (savedInstanceState != null) | |
{ | |
yelpAccessToken = savedInstanceState.getString("yelpAccessToken"); | |
} | |
} | |
@Override | |
public void onSaveInstanceState(Bundle outState) | |
{ | |
outState.putString("yelpAccessToken", yelpAccessToken); | |
super.onSaveInstanceState(outState); | |
} | |
//----------------------------------------------------------------------------------------// | |
// LocationListener | |
//----------------------------------------------------------------------------------------// | |
@Override | |
public void onLocationChanged(Location location) // Not called in simulator | |
{ | |
// Note: See my gist -> https://gist.github.com/nissivm/b3832ecaaa5d46af2e53c60626a5efee | |
userLat = location.getLatitude(); | |
userLong = location.getLongitude(); | |
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this); | |
startPlacesFetching(); | |
} | |
//----------------------------------------------------------------------------------------// | |
// Start places fetching | |
//----------------------------------------------------------------------------------------// | |
private void startPlacesFetching() | |
{ | |
if (yelpAccessToken.equals("")) | |
{ | |
serverRequest.getYelpAccessToken(); | |
} | |
else | |
{ | |
serverRequest.fetchPlaces(userLat, userLong, yelpAccessToken, placeCategoriesArray[0]); | |
} | |
} | |
//----------------------------------------------------------------------------------------// | |
// ServerRequestInterface | |
//----------------------------------------------------------------------------------------// | |
@Override | |
public void yelpAccessTokenRequestResult(String result, String acessToken) | |
{ | |
if (result.equals("Success")) | |
{ | |
yelpAccessToken = acessToken; | |
serverRequest.fetchPlaces(userLat, userLong, yelpAccessToken, placeCategoriesArray[0]); | |
} | |
else | |
{ | |
Context context = getApplicationContext(); | |
int duration = Toast.LENGTH_LONG; | |
Toast toast = Toast.makeText(context, result, duration); | |
toast.show(); | |
} | |
} | |
@Override | |
public void searchPlacesResult(String result, ArrayList<Business> businesses) | |
{ | |
if (result.equals("Success")) | |
{ | |
// Use businesses objcets somehow | |
} | |
else | |
{ | |
Context context = getApplicationContext(); | |
int duration = Toast.LENGTH_LONG; | |
Toast toast = Toast.makeText(context, result, duration); | |
toast.show(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.app.package; | |
import android.app.Application; | |
import com.android.volley.toolbox.Volley; | |
import com.android.volley.toolbox.JsonObjectRequest; | |
import com.android.volley.RequestQueue; | |
// Note: https://www.yelp.com/developers/documentation/v3/get_started | |
public class MyApp extends Application | |
{ | |
static final String YELP_APP_ID = "your-yelp-app-id"; | |
static final String YELP_APP_SECRET = "your-yelp-app-secret"; | |
private static final String TAG = "MyApp"; | |
private static MyApp mInstance; | |
private RequestQueue mRequestQueue; | |
@Override | |
public void onCreate() | |
{ | |
super.onCreate(); | |
mInstance = this; | |
mRequestQueue = Volley.newRequestQueue(this); | |
} | |
//----------------------------------------------------------------------------------------// | |
// Get instance singleton | |
//----------------------------------------------------------------------------------------// | |
public static synchronized MyApp getInstance() | |
{ | |
return mInstance; | |
} | |
//----------------------------------------------------------------------------------------// | |
// Volley | |
//----------------------------------------------------------------------------------------// | |
public void addJsonRequestToQueue(JsonObjectRequest jsObjRequest) | |
{ | |
jsObjRequest.setTag(TAG); | |
mRequestQueue.add(jsObjRequest); | |
} | |
private void cancelPendingRequests() | |
{ | |
mRequestQueue.cancelAll(TAG); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.app.package; | |
import com.android.volley.AuthFailureError; | |
import com.android.volley.Response; | |
import com.android.volley.toolbox.JsonObjectRequest; | |
import org.json.JSONObject; | |
import java.util.HashMap; | |
import java.util.Map; | |
class PlacesRequest extends JsonObjectRequest | |
{ | |
static String yelpAccessToken; | |
PlacesRequest(int method, String url, JSONObject jsonRequest, | |
Response.Listener<JSONObject> listener, | |
Response.ErrorListener errorListener) | |
{ | |
super(method, url, jsonRequest, listener, errorListener); | |
} | |
PlacesRequest(String url, JSONObject jsonRequest, | |
Response.Listener<JSONObject> listener, | |
Response.ErrorListener errorListener) | |
{ | |
super(url, jsonRequest, listener, errorListener); | |
} | |
@Override | |
public Map<String, String> getHeaders() throws AuthFailureError | |
{ | |
Map<String, String> headers = new HashMap<>(); | |
headers.put("Content-Type", "application/json"); | |
headers.put("Authorization", "Bearer " + yelpAccessToken); | |
return headers; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.app.package; | |
import android.util.Log; | |
import com.android.volley.Request; | |
import com.android.volley.Response; | |
import com.android.volley.VolleyError; | |
import com.android.volley.toolbox.JsonObjectRequest; | |
import org.json.JSONArray; | |
import org.json.JSONException; | |
import org.json.JSONObject; | |
import java.util.ArrayList; | |
@SuppressWarnings("SpellCheckingInspection") | |
class ServerRequest | |
{ | |
private final String TAG = "ServerRequest"; | |
private ServerRequestInterface interf; | |
private int index = 0; | |
private ArrayList<Business> businesses = new ArrayList<>(); | |
ServerRequest(ServerRequestInterface i) | |
{ | |
interf = i; | |
} | |
//----------------------------------------------------------------------------------------// | |
// Get yelp access token | |
//----------------------------------------------------------------------------------------// | |
void getYelpAccessToken() | |
{ | |
String url = "https://api.yelp.com/oauth2/token?" + "grant_type=client_credentials" + | |
"&client_id=" + MyApp.YELP_APP_ID + "&client_secret=" + MyApp.YELP_APP_SECRET; | |
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, url, null, | |
new Response.Listener<JSONObject>() | |
{ | |
@Override | |
public void onResponse(JSONObject response) | |
{ | |
try | |
{ | |
String yelpAccessToken = response.getString("access_token"); | |
interf.yelpAccessTokenRequestResult("Success", yelpAccessToken); | |
} | |
catch (JSONException e) | |
{ | |
e.printStackTrace(); | |
interf.yelpAccessTokenRequestResult("An error occurred, please try again.", null); | |
} | |
} | |
}, | |
new Response.ErrorListener() | |
{ | |
@Override | |
public void onErrorResponse(VolleyError error) | |
{ | |
Log.e(TAG, "Error while getting Yelp Access Token: " + error.toString()); | |
interf.yelpAccessTokenRequestResult("An error occurred, please try again.", null); | |
} | |
}); | |
MyApp app = MyApp.getInstance(); | |
app.addJsonRequestToQueue(jsObjRequest); | |
} | |
//----------------------------------------------------------------------------------------// | |
// Fetch places | |
//----------------------------------------------------------------------------------------// | |
void fetchPlaces(double userLat, double userLong, String yelpAccessToken, String placeCategory) | |
{ | |
String url = "https://api.yelp.com/v3/businesses/search?" + | |
"latitude=" + userLat + "&longitude=" + userLong + | |
"&categories=" + placeCategory + "&locale=en_US" + | |
"&limit=" + 50 + "&sort_by=distance"; | |
PlacesRequest.yelpAccessToken = yelpAccessToken; | |
PlacesRequest placesRequest = new PlacesRequest(Request.Method.GET, url, null, | |
new Response.Listener<JSONObject>() | |
{ | |
@Override | |
public void onResponse(JSONObject response) | |
{ | |
try | |
{ | |
JSONArray businessesArray = response.getJSONArray("businesses"); | |
if (businessesArray.length() > 0) | |
{ | |
if (businesses.size() > 0) | |
{ | |
businesses.clear(); | |
} | |
createBusinessObjects(businessesArray); | |
} | |
else | |
{ | |
interf.searchPlacesResult("Search returned no results for your surroundings.", null); | |
} | |
} | |
catch (JSONException e) | |
{ | |
e.printStackTrace(); | |
interf.searchPlacesResult("An error occurred, please try again.", null); | |
} | |
} | |
}, | |
new Response.ErrorListener() | |
{ | |
@Override | |
public void onErrorResponse(VolleyError error) | |
{ | |
interf.searchPlacesResult("An error occurred or this app has reached it's daily search quota.", null); | |
} | |
}); | |
MyApp app = MyApp.getInstance(); | |
app.addJsonRequestToQueue(placesRequest); | |
} | |
//----------------------------------------------------------------------------------------// | |
// Create Business objects | |
//----------------------------------------------------------------------------------------// | |
private void createBusinessObjects(JSONArray businessesArray) | |
{ | |
try | |
{ | |
JSONObject obj = businessesArray.getJSONObject(index); | |
Business business = new Business(obj); | |
if (!business.isPlaceClosed()) | |
{ | |
if (!business.getPlaceName().equals("") && !business.getPlaceAddr().equals("") && | |
(business.getPlaceLatitude() != 0) && (business.getPlaceLongitude() != 0)) | |
{ | |
businesses.add(business); | |
} | |
} | |
if (index < (businessesArray.length() - 1)) | |
{ | |
index++; | |
createBusinessObjects(businessesArray); | |
} | |
else | |
{ | |
index = 0; | |
if (businesses.size() > 0) | |
{ | |
interf.searchPlacesResult("Success", businesses); | |
} | |
else | |
{ | |
interf.searchPlacesResult("Search returned no results for your surroundings.", null); | |
} | |
} | |
} | |
catch (JSONException e) | |
{ | |
e.printStackTrace(); | |
if (index < (businessesArray.length() - 1)) | |
{ | |
index++; | |
createBusinessObjects(businessesArray); | |
} | |
else | |
{ | |
index = 0; | |
if (businesses.size() > 0) | |
{ | |
interf.searchPlacesResult("Success", businesses); | |
} | |
else | |
{ | |
interf.searchPlacesResult("Search returned no results for your surroundings.", null); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package your.app.package; | |
import java.util.ArrayList; | |
interface ServerRequestInterface | |
{ | |
void yelpAccessTokenRequestResult(String result, String accessToken); | |
void searchPlacesResult(String result, ArrayList<Business> businesses); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment