Initial commit

This commit is contained in:
2018-02-24 21:25:44 +11:00
commit 419d233b5e
37 changed files with 1060 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

29
app/build.gradle Normal file
View File

@@ -0,0 +1,29 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "com.example.h_mal.bookappudacity"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.0'
testCompile 'junit:junit:4.12'
}

17
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\h_mal\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,26 @@
package com.example.h_mal.bookappudacity;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.example.h_mal.bookappudacity", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.h_mal.bookappudacity">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,27 @@
package com.example.h_mal.bookappudacity;
/**
* Created by h_mal on 08/03/2017.
*/
public class Books {
private String mTitle;
private String mAuthor;
private String mURLaddress;
public Books (String title, String author, String URLaddress){
mTitle = title;
mAuthor = author;
mURLaddress = URLaddress;
}
public String getTitle() { return mTitle;}
public String getAuthor() {return mAuthor;}
public String getURLaddress() {return mURLaddress;}
}

View File

@@ -0,0 +1,62 @@
package com.example.h_mal.bookappudacity;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.ArrayList;
/**
* Created by h_mal on 08/03/2017.
*/
public class BooksAdapter extends ArrayAdapter<Books>{
public static final String LOG_TAG = MainActivity.class.getName();
private static final String LOCATION_SEPARATOR = ",";
public BooksAdapter(Activity context, ArrayList<Books> books){
super(context, 0, books);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View listItemView = convertView;
if (listItemView == null) {
listItemView = LayoutInflater.from(getContext()).inflate(
R.layout.list_item, parent, false);
}
Books currentBook = getItem(position);
TextView titleTextView = (TextView) listItemView.findViewById(R.id.title);
titleTextView.setText(currentBook.getTitle());
String authors = currentBook.getAuthor();
authors = authors.replace("[","");
authors = authors.replace("]","");
String primaryAuthor;
String secondaryAuthor;
if (authors.contains(LOCATION_SEPARATOR)) {
String[] parts = authors.split(LOCATION_SEPARATOR);
primaryAuthor = parts[0] + "";
secondaryAuthor = parts[1];
} else {
secondaryAuthor = null;
primaryAuthor = authors;
}
TextView primaryAuthorTextView = (TextView) listItemView.findViewById(R.id.author);
primaryAuthorTextView.setText(primaryAuthor);
TextView secondaryAuthorTextView = (TextView) listItemView.findViewById(R.id.author_secondary);
secondaryAuthorTextView.setText(secondaryAuthor);
return listItemView;
}
}

View File

@@ -0,0 +1,36 @@
package com.example.h_mal.bookappudacity;
import android.content.AsyncTaskLoader;
import android.content.Context;
import java.util.List;
/**
* Created by h_mal on 10/03/2017.
*/
public class BooksLoader extends AsyncTaskLoader<List<Books>> {
public String mURL;
public BooksLoader(Context context, String url) {
super(context);
mURL = url;
}
@Override
protected void onStartLoading() {
forceLoad();
}
@Override
public List<Books> loadInBackground() {
if (mURL == null) {
return null;
}
List<Books> result = DataSink.fetchBookData(mURL);
return result;
}
}

View File

@@ -0,0 +1,143 @@
package com.example.h_mal.bookappudacity;
import android.text.TextUtils;
import android.util.Log;
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.util.ArrayList;
import java.util.List;
import static com.example.h_mal.bookappudacity.BooksAdapter.LOG_TAG;
/**
* Created by h_mal on 09/03/2017.
*/
public class DataSink {
private DataSink() {}
private static URL createURL(String stringURL){
URL url = null;
try{
url = new URL(stringURL);
} catch (MalformedURLException e){
Log.e(LOG_TAG, "Error when creating URL", e);
}
return url;
}
private static String makeHTTPRequest(URL url) throws IOException {
String jsonResponse = "";
if (url == null){
return jsonResponse;
}
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000);
urlConnection.setConnectTimeout(15000);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
if (urlConnection.getResponseCode() == 200){
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
}else{
Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode());
}
}catch (IOException e){
Log.e(LOG_TAG, "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
}
return jsonResponse;
}
private static 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 static List<Books> extractFeatureFromJson(String booksJSON) {
if (TextUtils.isEmpty(booksJSON)) {
return null;
}
List<Books> books = new ArrayList<>();
try {
JSONObject baseJsonResponse = new JSONObject(booksJSON);
JSONArray booksArray = baseJsonResponse.getJSONArray("items");
for (int i = 0; i < booksArray.length(); i++) {
JSONObject currentBook = booksArray.getJSONObject(i);
JSONObject properties = currentBook.getJSONObject("volumeInfo");
if(properties.has("authors")) {
String author = properties.getString("authors");
String title = properties.getString("title");
String url = properties.getString("infoLink");
Books book = new Books(title, author, url);
books.add(book);
}
}
} catch (JSONException e) {
Log.e("DataSink", "Problem parsing the book JSON results", e);
}
return books;
}
public static List<Books> fetchBookData(String requestUrl) {
URL url = createURL(requestUrl);
String jsonResponse = null;
try {
jsonResponse = makeHTTPRequest(url);
} catch (IOException e) {
Log.e(LOG_TAG, "Problem making the HTTP request.", e);
}
List<Books> books = extractFeatureFromJson(jsonResponse);
return books;
}
}

View File

@@ -0,0 +1,147 @@
package com.example.h_mal.bookappudacity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Books>> {
private static final int BOOK_LOADER_ID = 1;
private static final String LOG_TAG = MainActivity.class.getName();
private static String GOOGLE_BOOKS_URL = "https://www.googleapis.com/books/v1/volumes?q=android&maxResults=40";
private BooksAdapter mAdapter;
private TextView mEmptyStateTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(LOG_TAG, "initiate loader");
ListView booksListView = (ListView) findViewById(R.id.book_list);
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
booksListView.setEmptyView(mEmptyStateTextView);
mAdapter = new BooksAdapter(this, new ArrayList<Books>());
booksListView.setAdapter(mAdapter);
booksListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
Books currentBook = mAdapter.getItem(position);
Uri bookUri = Uri.parse(currentBook.getURLaddress());
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, bookUri);
startActivity(websiteIntent);
}
});
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo != null && networkInfo.isConnected()){
LoaderManager loaderManager = getLoaderManager();
loaderManager.initLoader(BOOK_LOADER_ID, null, this);
}else{
mEmptyStateTextView.setText("no internet connection");
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.GONE);
}
BookAsyncTask task = new BookAsyncTask();
task.execute(GOOGLE_BOOKS_URL);
}
public void ButtonClick(View view){
EditText enteredText = (EditText) findViewById(R.id.edit_text);
String dataStructure = enteredText.getText().toString();
Uri.Builder builder = new Uri.Builder();
builder.scheme("https://www.googleapis.com/books/v1/volumes?q=").appendPath(dataStructure).fragment("&maxResults=40");
String newUrl = builder.build().toString();
GOOGLE_BOOKS_URL = newUrl;
BookAsyncTask task = new BookAsyncTask();
task.execute(GOOGLE_BOOKS_URL);
}
public void ButtonClear(View view){
EditText enteredText = (EditText) findViewById(R.id.edit_text);
enteredText.setText("");
GOOGLE_BOOKS_URL = "https://www.googleapis.com/books/v1/volumes?q=android&maxResults=40";
BookAsyncTask task = new BookAsyncTask();
task.execute(GOOGLE_BOOKS_URL);
}
@Override
public void onLoadFinished(Loader<List<Books>> loader, List<Books> books) {
mEmptyStateTextView.setText("no books found");
mAdapter.clear();
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.GONE);
if (books != null && !books.isEmpty()) {
mAdapter.addAll(books);
}
}
@Override
public void onLoaderReset(Loader<List<Books>> loader) {
mAdapter.clear();
}
@Override
public Loader<List<Books>> onCreateLoader(int i, Bundle bundle) {
Log.e(LOG_TAG, "Loader Created");
return new BooksLoader(this, GOOGLE_BOOKS_URL);
}
private class BookAsyncTask extends AsyncTask<String, Void, List<Books>> {
@Override
protected List<Books> doInBackground(String... urls) {
if (urls.length < 1 || urls[0] == null) {
return null;
}
List<Books> result = DataSink.fetchBookData(urls[0]);
return result;
}
@Override
protected void onPostExecute(List<Books> data) {
mAdapter.clear();
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);
}
}
}
}

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/Layout1">
<EditText
android:id="@+id/edit_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="Search for Book" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:text="Search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:onClick="ButtonClick"/>
<Button
android:text="Clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/buttonClear"
android:onClick="ButtonClear"/>
</LinearLayout>
</LinearLayout>
<ListView
android:id="@+id/book_list"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/Layout1" />
<TextView
android:id="@+id/empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:textAppearanceMedium" />
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:textAppearanceMedium"/>
</RelativeLayout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:paddingEnd="6dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingStart="6dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="16sp"
android:textColor="@android:color/black"
android:gravity="top"
android:maxLines="2"
android:text="Title ...once upon a time there was a long book title"/>
<LinearLayout
android:gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="6dp"
android:layout_marginBottom="8dp">
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="italic"
android:textSize="12sp"
android:text="Author1"
android:layout_marginRight="8dp"/>
<TextView
android:id="@+id/author_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="italic"
android:textSize="12sp"
android:text="Author2"/>
</LinearLayout>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">BookAppUdacity</string>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -0,0 +1,17 @@
package com.example.h_mal.bookappudacity;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}