diff --git a/app/build.gradle b/app/build.gradle
index fb17597..cf0e993 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -24,6 +24,7 @@ repositories {
}
dependencies {
+ implementation 'com.android.support:support-annotations:28.0.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ab5cdae..7600201 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,8 +2,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/Book.java b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/Book.java
new file mode 100644
index 0000000..34ecd81
--- /dev/null
+++ b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/Book.java
@@ -0,0 +1,67 @@
+package ua.kpi.comsys.io8227.jackshen;
+
+/** An {@link Book} object contains information related to a single book. */
+
+class Book {
+
+ /** Title of the book */
+ private String mTitle;
+
+ /** Subtitle of the book */
+ private String mSubtitle;
+
+ /** ISBN of the book*/
+ private String mIsbn;
+
+ /** Retail price of the book */
+ private String mPrice;
+
+ /** Cover of the book */
+ private String mImageUrl;
+
+ /**
+ * Create book object
+ *
+ * @param title - title of the book
+ * @param subtitle - subtitle of the book
+ * @param isbn - isbn number of the book
+ * @param price - retail price of the book
+ * @param imageUrl - the URL to find cover of the book
+ */
+ Book(String title, String subtitle, String isbn, String price, String imageUrl) {
+ this.mTitle = title;
+ this.mSubtitle = subtitle;
+ this.mIsbn = isbn;
+ this.mPrice = price;
+ this.mImageUrl = imageUrl;
+ }
+
+ /**
+ * Return the title information of the book
+ *
+ * @return the title of the book
+ */
+ String getTitle() {
+ return mTitle;
+ }
+
+ /** Return the subtitle of the book */
+ String getSubtitle() {
+ return mSubtitle;
+ }
+
+ /** Return the ISBN number of the book */
+ String getISBN() {
+ return mIsbn;
+ }
+
+ /** Return the retail price of the book */
+ String getPrice() {
+ return mPrice;
+ }
+
+ /** Return the URL to find cover of the book */
+ String getImageUrl() {
+ return mImageUrl;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookActivity.java b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookActivity.java
new file mode 100644
index 0000000..dbdbedb
--- /dev/null
+++ b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookActivity.java
@@ -0,0 +1,123 @@
+package ua.kpi.comsys.io8227.jackshen;
+
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.Loader;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+public class BookActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks> {
+
+ /** URL for book data */
+ private static final String REQUEST_URL = "https://api.jsonbin.io/b/6023ffbd87173a3d2f5b37e9";
+
+ /**
+ * Constant value for the book loader ID.
+ * We can choose any int, it`s really needed for multiple loaders
+ */
+ private static final int BOOK_LOADER_ID = 1;
+
+ /** Adapter for the list of books */
+ private BookAdapter mAdapter;
+
+ /** TextView that is displayed when the list is empty */
+ private TextView mEmptyTextView;
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // Initialize activity on main thread.
+ // Bundle holds previous state when re-initialized
+ super.onCreate(savedInstanceState);
+
+ // Inflate the activity's UI
+ setContentView(R.layout.activity_book);
+
+ // Find a reference to the {@link ListView} in the layout
+ ListView bookListView = findViewById(R.id.list);
+
+ // Set empty view when there is no data
+ mEmptyTextView = findViewById(R.id.empty_view);
+ bookListView.setEmptyView(mEmptyTextView);
+
+ // Create a new adapter that takes an empty list of books as input
+ mAdapter = new BookAdapter(this, new ArrayList());
+
+ // Set the adapter on the {@link ListView} so the list can be populated in UI
+ bookListView.setAdapter(mAdapter);
+
+ // Get a reference to the ConnectivityManager to check state of network connectivity
+ ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ // Get details on the currently active default data network
+ NetworkInfo networkInfo = Objects.requireNonNull(connMgr).getActiveNetworkInfo();
+
+ // If there is a network connection -> get data
+ if (networkInfo != null && networkInfo.isConnected()) {
+ // Get a reference to the LoaderManager, in order to interact with loaders.
+ LoaderManager loaderManager = getLoaderManager();
+
+ // Get the loader initialized. Go through the above specified int ID constant
+ // and pass the bundle to null.
+ loaderManager.initLoader(BOOK_LOADER_ID, null, this);
+ } else {
+
+ // Otherwise, display error and hide loading indicator so error message will be visible
+ View loadingIndicator = findViewById(R.id.progress_bar);
+ loadingIndicator.setVisibility(View.GONE);
+
+ // Update empty state with no connection error message
+ mEmptyTextView.setText("No internet connection.");
+ }
+ }
+
+ @Override
+ public Loader> onCreateLoader(int i, Bundle bundle) {
+ // Create a new loader for the given URL
+ Uri baseUri = Uri.parse(REQUEST_URL);
+
+ return new BookLoader(this, baseUri.toString());
+ }
+
+ /**
+ * The loader requests and parses information downloader from the internet on a background
+ * thread pool, keeping the UI thread unblocked
+ */
+ @Override
+ public void onLoadFinished(Loader> loader, List books) {
+
+ // Hide progress bar
+ View loadingIndicator = findViewById(R.id.progress_bar);
+ loadingIndicator.setVisibility(View.GONE);
+
+ // Set empty state text to display "No books to display."
+ mEmptyTextView.setText("No books to display.");
+
+ // Clear the adapter of previous data
+ mAdapter.clear();
+
+ // Add valid list of books to the adapter
+ if (books != null && !books.isEmpty())
+ mAdapter.addAll(books);
+ }
+
+ @Override
+ public void onLoaderReset(Loader> loader) {
+ // Clear existing data on adapter as loader is reset
+ mAdapter.clear();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookAdapter.java b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookAdapter.java
new file mode 100644
index 0000000..53a6643
--- /dev/null
+++ b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookAdapter.java
@@ -0,0 +1,119 @@
+package ua.kpi.comsys.io8227.jackshen;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class {@link BookAdapter} is used to create a list item layout for each book
+ * in the data source (a list of {@link Book} objects).
+ *
+ * An adapter view such as ListView will be provided with these
+ * list item layouts to be presented to the user.
+ */
+
+public class BookAdapter extends ArrayAdapter {
+
+ /**
+ * Constructs a new {@link BookAdapter}.
+ *
+ * @param context of the app
+ * @param books - list of books
+ */
+
+ BookAdapter(Context context, List books) {
+ super(context, 0, books);
+ }
+
+ /**
+ * Returns the view of the list item that shows information about the book in the book list.
+ */
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @NonNull
+ @Override
+ public View getView(int position, View convertView, @NonNull ViewGroup parent) {
+ // Check if there is an existing convertView that we can reuse, otherwise,
+ // if convertView is null, then inflate a new list item layout.
+ View listItemView = convertView;
+ if (listItemView == null) {
+ listItemView = LayoutInflater.from(getContext()).inflate(R.layout.book_list_item, parent, false);
+ }
+
+ // Find the book at the given position in the list of books
+ Book currentBook = getItem(position);
+
+ // Find the TextView with view ID titleBook
+ TextView titleView = listItemView.findViewById(R.id.titleBook);
+ // Find the TextView with view ID subtitleBook
+ TextView subtitleView = listItemView.findViewById(R.id.subtitleBook);
+ // Find the TextView with view ID isbnBook
+ TextView isbnView = listItemView.findViewById(R.id.isbnBook);
+ // Find the TextView with view ID priceBook
+ TextView priceView = listItemView.findViewById(R.id.priceBook);
+ // Find the ImageView with view ID imageBook
+ ImageView imageView = listItemView.findViewById(R.id.imageBook);
+
+ // Display the title of the current book in that TextView
+ titleView.setText(Objects.requireNonNull(currentBook).getTitle());
+ // Display the subtitle of the current book in that TextView
+ subtitleView.setText(currentBook.getSubtitle());
+ // Display the ISBN of the current book in that TextView
+ isbnView.setText(currentBook.getISBN());
+ // Display the price of the current book in that TextView
+ priceView.setText(currentBook.getPrice());
+
+ // Display image in the ImageView widget
+ if (currentBook.getImageUrl().equals("")) {
+ imageView.setImageResource(R.drawable.noimage);
+ } else {
+ new DownloadImage(imageView).execute(currentBook.getImageUrl());
+ }
+
+ return listItemView;
+ }
+
+ /** Class to download an image from URL */
+ private class DownloadImage extends AsyncTask {
+ final ImageView bmImage;
+
+ DownloadImage(ImageView bmImage) {
+ this.bmImage = bmImage;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ protected Bitmap doInBackground(String... urls) {
+ String urlDisplay = urls[0];
+ Bitmap mIcon = null;
+ try {
+ InputStream in = new java.net.URL(urlDisplay).openStream();
+ mIcon = BitmapFactory.decodeStream(in);
+ } catch (Exception e) {
+ Log.e("Error", Objects.requireNonNull(e.getMessage()));
+ e.printStackTrace();
+ }
+ return mIcon;
+ }
+
+ protected void onPostExecute(Bitmap result) {
+ bmImage.setImageBitmap(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookJSONParser.java b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookJSONParser.java
new file mode 100644
index 0000000..538e8d4
--- /dev/null
+++ b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookJSONParser.java
@@ -0,0 +1,204 @@
+package ua.kpi.comsys.io8227.jackshen;
+
+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;
+
+/** Helper methods to request and retrieve book data from a specified JSON */
+final class BookJSONParser {
+
+ /** Tag for the log messages */
+ private static final String LOG_MSG = BookJSONParser.class.getSimpleName();
+
+ /**
+ * We are creating a private constructor because no one else should create
+ * the {@link BookJSONParser} object.
+ */
+ private BookJSONParser() {
+ }
+
+ /**
+ * Query given JSON and return a list of {@link String} objects.
+ *
+ * @param requestUrl - our URL as a {@link String} object
+ * @return the list of {@link Book}s
+ */
+ static List getBookData(String requestUrl) {
+ URL url = makeUrl(requestUrl);
+
+ // Perform HTTP request to the URL and receive a JSON response back
+ String jsonResponse = null;
+ try {
+ jsonResponse = makeHttpRequest(url);
+ } catch (IOException err) {
+ Log.e(LOG_MSG, "Problem making the HTTP request.", err);
+ }
+
+ // Extract relevant fields from the JSON response and create a list of {@link Book}s
+ // Then return the list of {@link Book}s
+ return extractDataFromJson(jsonResponse);
+ }
+
+ /** Returns new URL object from the given string URL. */
+ private static URL makeUrl(String strUrl) {
+ // Initialize an empty {@link URL} object to hold the parsed URL from the strUrl
+ URL url = null;
+
+ // Parse valid URL from param strUrl and handle Malformed urls
+ try {
+ url = new URL(strUrl);
+ } catch (MalformedURLException err) {
+ Log.e(LOG_MSG, "Problem building the URL!", err);
+ }
+
+ // Return valid URL
+ return url;
+ }
+
+ /** Make an HTTP request to the given URL and return a String as the response. */
+ private static String makeHttpRequest(URL url) throws IOException {
+ // Initialize variable to hold the parsed JSON response
+ String jsonResponse = "";
+
+ // Return response if URL is null
+ if (url == null) {
+ return jsonResponse;
+ }
+
+ // Initialize HTTP connection object
+ HttpURLConnection connection = null;
+
+ // Initialize {@link InputStream} to hold response from request
+ InputStream inputStream = null;
+ try {
+ // Establish connection to the URL
+ connection = (HttpURLConnection) url.openConnection();
+
+ // Set request type
+ connection.setRequestMethod("GET");
+
+ // Setting how long to wait on the request (in milliseconds)
+ connection.setReadTimeout(10000);
+ connection.setConnectTimeout(15000);
+
+ // Establish connection to the URL
+ connection.connect();
+
+ // Check for successful connection
+ if (connection.getResponseCode() == 200) {
+ inputStream = connection.getInputStream();
+ jsonResponse = readFromStream(inputStream);
+ } else {
+ Log.e(LOG_MSG, "Error response code: " + connection.getResponseCode());
+ }
+ } catch (IOException err) {
+ Log.e(LOG_MSG, "Problem retrieving the book JSON results.", err);
+ } finally {
+ if (connection != null) {
+ // Disconnect the connection after successfully making the HTTP request
+ connection.disconnect();
+ }
+
+ if (inputStream != null) {
+ // Close the stream after successfully parsing the request
+ // This also can throw an IOException
+ inputStream.close();
+ }
+ }
+
+ // Return JSON as a {@link String}
+ return jsonResponse;
+ }
+
+ /**
+ * Convert the {@link InputStream} into a String which contains the whole JSON response.
+ */
+ private static String readFromStream(InputStream inputStream) throws IOException {
+ StringBuilder output = new StringBuilder();
+ if (inputStream != null) {
+ // Decode the bits
+ InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
+
+ // Buffer the decoded characters
+ BufferedReader reader = new BufferedReader(inputStreamReader);
+
+ // Store a line of characters from the BufferedReader
+ String line = reader.readLine();
+
+ // If not end of buffered input stream, read next line and add to output
+ while (line != null) {
+ output.append(line);
+ line = reader.readLine();
+ }
+ }
+
+ // Convert chars sequence from the builder into a string and return
+ return output.toString();
+ }
+
+ private static List extractDataFromJson(String bookJSON) {
+ // Exit if no data was returned from the HTTP request
+ if (TextUtils.isEmpty(bookJSON))
+ return null;
+
+ // Initialize list of strings to hold the extracted books
+ List books = new ArrayList<>();
+
+ try {
+ // Create JSON object from response
+ JSONObject rawJSONResponse = new JSONObject(bookJSON);
+
+ // Extract the array that holds the books
+ JSONArray bookArray = rawJSONResponse.getJSONArray("books");
+
+ for (int i = 0; i < bookArray.length(); i++) {
+ // Get the current book
+ JSONObject currentBook = bookArray.getJSONObject(i);
+
+ // Get the book's title
+ String title = currentBook.getString("title");
+
+ // Get the book's subtitle
+ String subtitle = currentBook.getString("subtitle");
+
+ // Get the book's ISBN number
+ String isbn13 = currentBook.getString("isbn13");
+
+ // Get the book's price
+ String price = currentBook.getString("price");
+
+ // Get the URL of book's cover
+ String imageUrl = currentBook.getString("image");
+
+ // Create a new {@link Book} object with the title, subtitle, isbn13, price
+ // and imageUrl from the JSON response.
+ Book book = new Book(title, subtitle, isbn13, price, imageUrl);
+
+ // Add the new {@link Book} to the list of books.
+ books.add(book);
+ }
+
+ } catch (JSONException e) {
+ // Catch the exception from the 'try' block and print a log message
+ Log.e("BookJSONParser", "Problem parsing the book JSON results", e);
+ }
+
+ // Return the list of books
+ return books;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookLoader.java b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookLoader.java
new file mode 100644
index 0000000..79fb6fb
--- /dev/null
+++ b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/BookLoader.java
@@ -0,0 +1,47 @@
+package ua.kpi.comsys.io8227.jackshen;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+
+import java.util.List;
+
+
+/** Using AsyncTask to load a list of books by network request to a certain URL. */
+public class BookLoader extends AsyncTaskLoader> {
+
+ /** Query URL **/
+ final private String mUrl;
+
+
+ /**
+ * Constructs a new {@link BookLoader}.
+ *
+ * @param context of the activity
+ * @param url to load data from
+ */
+ BookLoader(Context context, String url) {
+ super(context);
+ mUrl = url;
+ }
+
+
+ /** Forcing the loader to make an HTTP request and start downloading the required data */
+ @Override
+ protected void onStartLoading() {
+ forceLoad();
+ }
+
+ /**
+ * This method is called in a background thread and takes care of the
+ * generating new data from the given JSON file
+ */
+ @Override
+ public List loadInBackground() {
+ // Check for valid string url
+ if (mUrl == null) {
+ return null;
+ }
+ // Perform the network request, parse the response, and extract a list of books.
+ return BookJSONParser.getBookData(mUrl);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/MainActivity.java b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/MainActivity.java
index 215ab1b..c6c7111 100644
--- a/app/src/main/java/ua/kpi/comsys/io8227/jackshen/MainActivity.java
+++ b/app/src/main/java/ua/kpi/comsys/io8227/jackshen/MainActivity.java
@@ -3,12 +3,10 @@
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@@ -81,14 +79,19 @@ public void onSwipeLeft() {
});
- Button button = findViewById(R.id.open_portfolio);
- button.setOnClickListener(new View.OnClickListener(){
- @Override
- public void onClick(View view) {
- Intent openSite = new Intent(Intent.ACTION_VIEW, Uri.parse("https://jackshen.herokuapp.com/"));
- startActivity(openSite);
- }
- });
+// Button button = findViewById(R.id.open_portfolio);
+// button.setOnClickListener(new View.OnClickListener(){
+// @Override
+// public void onClick(View view) {
+// Intent openSite = new Intent(Intent.ACTION_VIEW, Uri.parse("https://jackshen.herokuapp.com/"));
+// startActivity(openSite);
+// }
+// });
+ }
+
+ public void openBooks(View v) {
+ Intent intent = new Intent(this, BookActivity.class);
+ startActivity(intent);
}
public void openInfograph(View v) {
diff --git a/app/src/main/res/drawable/noimage.png b/app/src/main/res/drawable/noimage.png
new file mode 100644
index 0000000..bda4bcb
Binary files /dev/null and b/app/src/main/res/drawable/noimage.png differ
diff --git a/app/src/main/res/layout/activity_book.xml b/app/src/main/res/layout/activity_book.xml
new file mode 100644
index 0000000..c8d6b78
--- /dev/null
+++ b/app/src/main/res/layout/activity_book.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 3ab363e..9462c0c 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -100,18 +100,18 @@
android:paddingLeft="32dp"
android:paddingRight="32dp">
-
+
+
+
+
+
+
+
+
+
+
+
+
+ android:textSize="16dp"
+ android:onClick="openInfograph"/>
+ android:onClick="openBooks" />
@@ -156,7 +157,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:fontFamily="@font/calibri"
- android:text="Note: The mobile version of the site is still under development, so it is desirable to watch from a PC"
+ android:text="Author: Jack Shendrikov, IO-82, 8227"
android:textAlignment="center"
android:textColor="#96ffffff"
android:textSize="14sp" />
diff --git a/app/src/main/res/layout/book_list_item.xml b/app/src/main/res/layout/book_list_item.xml
new file mode 100644
index 0000000..dfc13b2
--- /dev/null
+++ b/app/src/main/res/layout/book_list_item.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index fbdaf54..d7c6c76 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -10,6 +10,7 @@
#262626#181818
+ #4d4d4d#FFFFFF#FF8C00#0F89CA