commit 419d233b5e96d190386c9d12c1376e6abe5bc8da Author: H Malik Date: Sat Feb 24 21:25:44 2018 +1100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39fb081 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..7ac24c7 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ba7052b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0d8d74a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..fc0556d --- /dev/null +++ b/app/build.gradle @@ -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' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..05e856b --- /dev/null +++ b/app/proguard-rules.pro @@ -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 *; +#} diff --git a/app/src/androidTest/java/com/example/h_mal/bookappudacity/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/h_mal/bookappudacity/ExampleInstrumentedTest.java new file mode 100644 index 0000000..b26988a --- /dev/null +++ b/app/src/androidTest/java/com/example/h_mal/bookappudacity/ExampleInstrumentedTest.java @@ -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 Testing documentation + */ +@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()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3e5d62c --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/h_mal/bookappudacity/Books.java b/app/src/main/java/com/example/h_mal/bookappudacity/Books.java new file mode 100644 index 0000000..db32c12 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/bookappudacity/Books.java @@ -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;} +} diff --git a/app/src/main/java/com/example/h_mal/bookappudacity/BooksAdapter.java b/app/src/main/java/com/example/h_mal/bookappudacity/BooksAdapter.java new file mode 100644 index 0000000..37c260c --- /dev/null +++ b/app/src/main/java/com/example/h_mal/bookappudacity/BooksAdapter.java @@ -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{ + + public static final String LOG_TAG = MainActivity.class.getName(); + + private static final String LOCATION_SEPARATOR = ","; + + public BooksAdapter(Activity context, ArrayList 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; + } +} diff --git a/app/src/main/java/com/example/h_mal/bookappudacity/BooksLoader.java b/app/src/main/java/com/example/h_mal/bookappudacity/BooksLoader.java new file mode 100644 index 0000000..cea3ae6 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/bookappudacity/BooksLoader.java @@ -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> { + + public String mURL; + + public BooksLoader(Context context, String url) { + super(context); + mURL = url; + } + + @Override + protected void onStartLoading() { + forceLoad(); + } + + @Override + public List loadInBackground() { + + if (mURL == null) { + return null; + } + + List result = DataSink.fetchBookData(mURL); + return result; + } +} diff --git a/app/src/main/java/com/example/h_mal/bookappudacity/DataSink.java b/app/src/main/java/com/example/h_mal/bookappudacity/DataSink.java new file mode 100644 index 0000000..1d02011 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/bookappudacity/DataSink.java @@ -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 extractFeatureFromJson(String booksJSON) { + if (TextUtils.isEmpty(booksJSON)) { + return null; + } + + List 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 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 = extractFeatureFromJson(jsonResponse); + + return books; + } +} + diff --git a/app/src/main/java/com/example/h_mal/bookappudacity/MainActivity.java b/app/src/main/java/com/example/h_mal/bookappudacity/MainActivity.java new file mode 100644 index 0000000..7c9c581 --- /dev/null +++ b/app/src/main/java/com/example/h_mal/bookappudacity/MainActivity.java @@ -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> { + + 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()); + 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> loader, List 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> loader) { + mAdapter.clear(); + } + + @Override + public Loader> onCreateLoader(int i, Bundle bundle) { + Log.e(LOG_TAG, "Loader Created"); + return new BooksLoader(this, GOOGLE_BOOKS_URL); + } + + private class BookAsyncTask extends AsyncTask> { + + @Override + protected List doInBackground(String... urls) { + + if (urls.length < 1 || urls[0] == null) { + return null; + } + + List result = DataSink.fetchBookData(urls[0]); + return result; + } + + @Override + protected void onPostExecute(List data) { + + mAdapter.clear(); + + if (data != null && !data.isEmpty()) { + mAdapter.addAll(data); + } + } + } + +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f603493 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,53 @@ + + + + + + +