Reading from an input stream

the purpose of an InputStream
Represents a stream of bytes (small chunks of data)

the purpose of an BufferedReader
Helps us read text from an InputStream

String: Immutable(can’t change once created)
StringBuilder: Mutable(Can change once created)

StringBuilder builder = new StringBuilder();
builder.append("World");
builder.deleteCharAt(3);
builder.append(" builder").append(".");
String built = builder.toString();

what exception is caught
in the makeHttpRequest() method?
IOException
in the extractFeatureFromJson() method?
JSONException

Reading from an Input

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

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>