private String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null){
InputStreamReader inputStreamReader =
new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null){
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
HTTP Status Codes
Status code: Description
200: OK, request received, everything normal
301: moved permanently
404: Page not found
500: Internal server error
List of HTTP status codes
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
3xx Redirection
4xx Client Error
5xx Server Error
the request is successful
200
the request is unsuccessful
400
EX
Error 400: Bad Request Bad endtime value "2014-01-02asdfasdf". Valid values are ISO-8601 timestamps. Usage details are available from https://earthquake.usgs.gov/fdsnws/event/1
200 response code -> Proceed with reading input stream and parsing the JSON response
Any other response code -> Return empty string for the JSON response
Abstraction
Android System Architecture
Physical Device Hardware, Android Operating System, Framework, App
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.connect();
GET(Read):Requests data from a specified resource
POST(Write):Submits data to be processed to a specified resource
https://www.w3schools.com/tags/ref_httpmethods.asp
HTTP request method
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
Why are we using a GET instead of a POST?
1.You want to retrieve data from the server
2.You’re not posting new information to the server
url connection
https://developer.android.com/reference/java/net/URLConnection.html?utm_source=udacity&utm_medium=course&utm_campaign=android_basics#connect()
HTTP request and response
Android client
1. form http request, send request to USGS
working with URL
https://docs.oracle.com/javase/tutorial/networking/urls/
Web API Endpoint Reference
HTTPConection
https://developer.android.com/reference/java/net/HttpURLConnection.html
URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
} finally {
urlConnection.disconnect();
}
Soonami
package com.example.android.soonami;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
public class MainActivity extends AppCompatActivity {
public static final String LOG_TAG = MainActivity.class.getSimpleName();
private static final String USGS_REQUEST_URL =
"http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2012-01-01&endtime=2012-12-01&minmagnitude=6";
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TsunamiAsyncTask task = new TsunamiAsyncTask();
task.execute();
}
private void updateUi(Event earthquake){
TextView titleTextView = (TextView) findViewById(R.id.title);
titleTextView.setText(earthquake.title);
TextView dateTextView = (TextView) findViewById(R.id.date);
dateTextView.setText(getDateString(earthquake.time));
TextView tsunamiTextView = (TextView) findViewById(R.id.tsunami_alert);
titleTextView.setText(earthquake.title);
}
private String getDateString(long timeInMilliseconds){
SimpleDateFormat formatter = new SimpleDateFormat("EEE, d MMM yyy 'at' HH:mm:ss z");
return formatter.format(timeInMilliseconds);
}
private String getTsunamiAlertString(int tsunamiAlert){
switch (tsunamiAlert){
case 0:
return getString(R.string.alert_no);
case 1:
return getString(R.string.alert_yes);
default:
return getString(R.string.alert_no_available);
}
}
private class TsunamiAsyncTask extends AsyncTask<URL, Void, Event>{
@Override
protected Event doInBackground(URL... urls){
// Create URL object
URL url = createUrl(USGS_REQUEST_URL);
} catch (IOException e){
// todo handle the IOException
}
Event earthquake = extractFeatureFromJson(jsonResponse);
return earthquake;
}
@Override
protected void onPostExecute(Event earthquake){
if (earthquake == null){
return;
}
updateUi(earthquake);
}
private URL createUrl(String stringUrl){
URL url = null;
try {
url = new URL(stringUrl);
} catch (MalformedURLException exception){
Log.e(LOG_TAG, "Error with creating URL", exception);
return null;
}
return url;
}
private String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.connect();
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} catch (IOException e){
// todo
} finally {
if (urlConnection != null){
urlConnection.disconnect();
}
if (inputStream != null){
inputStream.close();
}
}
return jsonResponse;
}
private String readFromStream(InputStream inputStream) throws IOException {
StringBuilder output = new StringBuilder();
if (inputStream != null){
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(inputStreamReader);
String line = reader.readLine();
while (line != null){
output.append(line);
line = reader.readLine();
}
}
return output.toString();
}
private Event extractFeatureFromJson(String earthquakeJSON){
try {
JSONObject baseJsonResponse = new JSONObject(earthquakeJSON);
JSONArray featureArray = baseJsonResponse.getJSONArray("features");
if (featureArray.length() > 0){
JSONObject firstFeature = featureArray.getJSONObject(0);
JSONObject properties = firstFeature.getJSONObject("properties");
String title = properties.getString("title");
long time = properties.getLong("time");
int tsunamiAlert = properties.getInt("tsunami");
return new Event(title, time, tsunamiAlert);
}
} catch (JSONException e){
Log.e(LOG_TAG, "Problem parsing the earthquake JSON results", e);
}
return null;
}
}
Access to the internet
By default, an Android app does not have access to the Internet
Android Client -> USGS Web Server
Request permission to access certain functions of device
-use internet, use camer, send sms message, call phone number
-record microphone audio, get current location, network connectivity status …and more!
https://developer.android.com/training/permissions/declaring.html
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.snazzyapp">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application ...>
...
</application>
</manifest>
levels of permissions in android
-normal permissions
access the internet, vibrate the device, set the timezone, network connectivity status
automatically granted by the system
-dangerous permissions
use camera, access call log, access contacts, record audio
requested at runtime when app needs the permission pop up a dialog to ask for permission
prompt to accept all permissions at install time
prompt at runtime when user accesses feature that requires permission(no prompt at install time)
Hypertext transfer protocol
android client -> USGS web server
Client(GET,POST,PUT,DELETE)
Intro to Networking
Your phone -> REQUEST “I want earthquake data!” -> Computers at USGS
Response: “Here’s a list of earthquakes from our earthquake data set…”
protocol/scheme
host/domain/authority
resource path
query (param)
HTTP Connection
1.Form HTTP Request
2.Send the Request
3.Receive the Response and makes sense of it
4.Update the UI
Visual Polish
search fit APIs
https://www.programmableweb.com/apis/directory
https://developers.google.com/apis-explorer/#p/
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:orientation="horizontal" android:paddingEnd="16dp" android:paddingLeft="16dp" android:paddingRight="16dp" android:paddingStart="16dp"> <TextView android:id="@+id/magnitude" android:layout_width="36dp" android:layout_height="36dp" android:layout_gravity="central_vertical" android:background="@drawable/magnitude_circle" android:gravity="center" android:textColor="@android:color/white" android:textSize="16sp" tools:text="8.9" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/location_offset" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" android:fontFamily="sans-serif-medium" android:maxLines="1" android:textAllCaps="true" android:textColor="@color/textColorEarthquakeDetails" android:textSize="12sp" tools:text="30km S of" /> <TextView android:id="@+id/primary_location" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="2" android:textColor="@color/textColorEarthquakeLocation" android:textSize="16sp" tools:text="Long placeholder location that should wrap to more than 2 lines of text" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:orientation="vertical"> <TextView android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" andorid:textColor="@color/textColorEarthquakeDetails" android:textSize="12sp" tools:text="Mar 6, 2010"/> <TextView android:id="@+id/time" adoroid:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:textColor="@color/textColorEarthquakeDetails" andorid:textSize="12sp" tools:text="3:00 PM" /> </LinearLayout> </LinearLayout>
EarthquakeAdapter.java
import android.support.v4.content.ContextCompact;
private int getMagnitudeColor(double magnitude){
int magnitudeColorResourceId;
int magnitudeFloor = (int) Math.floor(magnitude);
switch(magnitudeFloor){
case 0:
case 1:
magnitudeColorResourceId = R.color.magnitude1;
break;
case 2:
magnitudeColorResourceId = R.color.magnitude2;
break;
case 3:
magnitudeColorResourceId = R.color.magnitude3;
break;
case 4:
magnitudeColorResourceId = R.color.magnitude4;
break;
case 5:
magnitudeColorResourceId = R.color.magnitude5;
break;
case 6:
magnitudeColorResourceId = R.color.magnitude6;
break;
case 7:
magnitudeColorResourceId = R.color.magnitude7;
break;
case 8:
magnitudeColorResourceId = R.color.magnitude8;
break;
case 9:
magnitudeColorResourceId = R.color.magnitude9;
break;
default:
magnitudeColorResourceId = R.color.magnitude10plus;
break;
}
return ContextCompact.getColor(getContext(), magnitudeColorResourceID);
}
}
<color name="textColorEarthquakeDetails">#B4BAC0</color> <color name="textColorEarthquakeDetails">#2B3D4D</color>
set up magnitude color
<color name="magnitude1">#4A7BA7</color> <color name="magnitude2">#04B4B3</color> <color name="magnitude3">#10CAC9</color> <color name="magnitude4">#F5A623</color> <color name="magnitude5">#FF7D50</color> <color name="magnitude6">#FC6644</color> <color name="magnitude7">#E75F40</color> <color name="magnitude8">#E13A20</color> <color name="magnitude9">#D93218</color> <color name="magnitude10plus">#C03823</color>
magnitude_circle.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmln:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@color/magnitude1" /> <size android:width="36dp" android:height="36dp" /> <corners android:radius="18dp" /> </shape>
TextView
<TextView android:id="@+id/magnitude" android:layout_width="36dp" android:layout_height="36dp" android:layout_gravity="center_vertical" android:background="@drawable/magnitude_circle" android:fontFamily="sans-serif-dedium" android:gravity="center" android:textColor="@android:color/white" android:textSize="16sp" tools:text="8.9" />
<TextView android:id="@+id/location_offset" android:layout_marginLeft="16dp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" tools:text="30km S of" />