Skip to content

Latest commit

 

History

History
276 lines (186 loc) · 27.4 KB

README.md

File metadata and controls

276 lines (186 loc) · 27.4 KB

Books Library App

GitHub GitHub code size in bytes GitHub repo size GitHub release (latest by date) GitHub All Releases GitHub search hit counter Minimum API level

This App has been developed as part of the Udacity Android Basics Nanodegree Course for the Exercise Project "Book Listing App". App connects to the Google Books API to retrieve the list of Books for the topic searched and then displays them in a decorative BookShelf format.


App Compatibility

Android device running with Android OS 4.0.4 (API Level 15) or above. Best experienced on Android Nougat 7.1 and above.


Rubric followed for the Project

  • Contains a ListView that is populated with list items having information displayed with proper text wrapping.
  • List items displays the author and title information.
  • ListViews to be populated with properly parsed information, from the JSON Response.
  • For the user to search the desired book, the screen has a search box (SearchView) that accepts a word or phrase.
  • App should fetch the query related books via an HTTP request from Google Books API, using a class such as HttpUriRequest or HttpURLConnection.
  • All network calls should occur off the UI Thread by making use of AsyncTask or similar threading object.
  • On device rotation
    • The layout remains scrollable.
    • The app saves state and restores the list back to the previously scrolled position.
    • The UI properly adjusts so that all contents of each list item is still visible and not truncated/overlapped.
    • The Search button remains visible on the screen even after the device is rotated.
  • Checks whether connected to internet or not. Also, validates for any occurrence of bad server response or lack of response.
  • No external libraries to be used.
  • A Default message is shown when there is no data to display for the search query made.
  • A Default text is shown for guiding the user, on what to enter while searching.

Things explored/developed in addition to the above defined Rubric

  • Assisted Search Implementation with SearchView.
  • Used RecyclerView in place of ListView and GridView for its advantages in performance and easy placeholders for custom item decoration.
  • Custom RecyclerView.ItemDecoration for decorating each of the items in List/Grid with the Book shelf decoration.
  • Explored FragmentStatePagerAdapter that displays the Fragments (retaining their state) for the ViewPager.
  • Implemented android.support.v7.preference.Preference Preferences for the Settings.
  • No external libraries are used for communicating with the REST API and also for loading the images. AsyncTaskLoader has been used for downloading the data and images in the background thread. Images are downloaded using a Headless Fragment.
  • Developed BitmapImageCache utility that uses android.util.LruCache to cache the recent Bitmap Images downloaded.
  • Most layouts are designed using ConstraintLayout to flatten the layout hierachy as far as possible.
  • Indeterminate progress bar is implemented with animation-list / AnimationDrawable.
  • TextAppearanceUtility for decorating TextViews using Spannables, for strikethrough, image within text, selective text coloring and relative text resize.
  • Custom Fonts for TextViews.
  • CardView for displaying the information of a Book.

Design and Implementation of the App

Video of Complete App Flow

The Welcome screen layout

The first screen displayed when the app is launched, is the welcome page screen as shown below.

This layout is inflated by the BookSearchActivity and is included in the activity_main.xml. This is visible only for the first time when the app is launched and loaded into memory. This screen basically tells the user on how to search for a book.

Assisted Search

The SearchView in the action menu is implemented with Assisted Search. Hence this activity also becomes the Searchable activity with the searchable configuration as defined here. As seen in the Searchable configuration, a Recent Search Suggestions Provider is also implemented and is displayed when the user types in atleast 3 characters in the search to show the corresponding match, provided if there were any recent search with those 3 characters.

When the SearchView is expanded, user can opt to search by Voice as well. But this will not allow the user to make any modifications as the transcribed text is directly fed to the search box and executed in one shot. Any search that is done, is updated to the title as shown below.

One can clear the recent search history by simply going to the overflow menu at the top and select the menu option that says "Clear Search History". On success of this action, a toast will be displayed.

Displaying Results

Once the search is entered by the user, the welcome screen if active is replaced with a ViewPager and its Fragments to display the results. The ViewPager will be set to show its first Tab by default, which is the LIST Tab. In this the results are displayed like in a vertical list with list of Books related to the search. The other Tab is the GRID Tab which allows the user to view the results in a Staggered Grid View (of 2 columns).

Adapter used by ViewPager is an extension of FragmentStatePagerAdapter whose Fragments for List & Grid view is given by the RecyclerViewFragment, which uses RecyclerView instead of ListView or GridView.

  • For List View
    • The RecyclerView uses a LinearLayoutManager as its layout manager and an adapter RecyclerListAdapter to populate the List.
  • For Grid View
    • The RecyclerView uses a StaggeredGridLayoutManager as its layout manager and an adapter RecyclerGridAdapter to populate the Grid.

For both of the layout managers used, an item decoration is added defined by the BookShelfItemDecoration. This basically adds a book shelf decoration just below the item.

Both the above adapters registers an item click listener whose interface callbacks are defined by OnAdapterItemClickListener.

The RecyclerViewFragment registers an OnScrollListener to propagate the corresponding event to the BookSearchActivity. This is implemented by extending RecyclerView.OnScrollListener which is done by the class BaseRecyclerViewScrollListener. This is an abstract class which provides an event callback when the scroll reaches the last 3 items in the list, to reveal the pagination buttons for the user to navigate to different pages in the current search, as shown below. The RecyclerViewFragment registers a concrete implementation of this class which is RecyclerViewScrollListener to propagate the event to the BookSearchActivity, which reveals the hidden pagination panel (R.id.pagination_panel_id).

pagination

The Details Page

Clicking on an item in the List Tab View or the Grid Tab View, opens up the Details page activity_book_detail.xml inflated by the activity BookDetailActivity. This displays additional information such as

  • The book description
  • Sample previews of the Book, that basically takes the user to a link via an Intent to the browser.
  • Information link when no previews present and
  • A button that takes the user to a page for buying a book if the book is saleable in the user's region.

The Book Image Page

If the Image of the Book is loaded in the Details page activity_book_detail.xml, then the user can click on the image to open a larger image of the same, which is displayed by the activity BookImageActivity

Controlling the Search Results

Search Results can be controlled by varying certain parameters embedded in the search query. This is defined by the Google Books API which this application is based on and is acting as a client that receives the data for the search executed.

Most of these parameters can be altered by using the "Search Settings" menu item in the BookSearchActivity. The "Search Settings" menu item, opens up the preferences preferences.xml loaded by the activity SearchSettingsActivity. These preferences provides options to override the search parameters used in the query, and the following are the ones that can changed -

  • filter parameter which restricts the results returned to the kind of books we want like Partially viewable/Free ebooks/Paid ebooks and so on.
  • printType parameter which restricts the results returned to specific print or publication type.
  • orderBy parameter that defines the sorting order of the results.
  • Pagination parameters like
    • startIndex parameter that tells the page index to be shown.
    • maxResults parameters that tells the max number of results to be shown per page.

The search settings implements the android.support.v7.preference.Preference Preferences. As such all the values are stored in the default SharedPreferences. For the Pagination parameters, custom NumberPicker Preference has been implemented by extending the DialogPreference.

There is also another preference setting provided, which resets all the preferences (used in the search) to their default values. This is implemented by ConfirmationPreference

When the user returns to the BookSearchActivity after making the required changes in the "Search Settings", the search query will be rebuilt and the results will be loaded accordingly. For this to happen, BookSearchActivity implements the Listener SharedPreferences.OnSharedPreferenceChangeListener.

While searching, one can also filter the results by entering keyword prefixes into the search box, specifically to search only in that field of the output. Following are the parameters that cater to this category -

  • intitle - Returns results where the text following this keyword is found in the title.
  • inauthor - Returns results where the text following this keyword is found in the author.
  • inpublisher - Returns results where the text following this keyword is found in the publisher.
  • subject - Returns results where the text following this keyword is listed in the category list of the volume.

The above is NOT provided by the "Search Settings" menu item, instead it can be accessed by another menu item that says "Search Keyword Filters". This shows a dialog with a list of cards specific to each of the above mentioned prefixes, which is displayed by the DialogFragment KeywordFiltersDialogFragment. Selecting any of these cards, will insert the prefix into the search box. Any word/phrase typed after this, will be searched in that particular field of the results.

keyword_filter

Loading of Data

Loading of Data for the Book Search done is carried out in a background thread using BooksLoader that extends an AsyncTaskLoader. Request and response is carried out via a REST call to the Google Books API. The data format used for communication is the JSON format. Talking to the REST API and parsing the response is done by the utility code BookClientUtility.

As per the Rubric, no third party library is used for communicating with the REST API.

Loading of Images

Loading of Images for each of the items in the list/grid views, in the Details page and the Book Image page is carried out in a background thread through a viewless Fragment ImageDownloaderFragment. Functioning of this fragment is as follows -

  • It first checks whether the image to be loaded is present in the Bitmap Cache, implemented by BitmapImageCache
  • If present in the cache, it updates the image to the corresponding ImageView passed.
  • If not present in cache, then the image is downloaded in a background thread using ImageDownloader that extends an AsyncTaskLoader. Once successfully downloaded, it updates the image to the corresponding ImageView passed.

The identifier for the Fragment and its Loader is maintained to be unique to the item being displayed in the List/Grid View such that each item displays the correct image to be shown, without resulting in any duplication. If duplication still happens, the loader will take care of loading the correct one when the item is being displayed the second time in the screen.

As per the Rubric, no third party library is used for loading images.

Information in general, on the entire app

  • After scrolling to some extent in the List Tab View or the Grid Tab View

    • If the user swipes across the tabs, then the Fragment being displayed will scroll to the item last shown in the previous tab.
    • If the user selects the active tab again, user will be taken to the top item (0th item) in the Fragment.
  • If the search entered by the user does not yield any result, then the BookSearchActivity will display the hidden embedded page no_result_page.xml.

  • If during search, that is while initiating a request to the REST API, a network connectivity issue is encountered, then a Network Error dialog is displayed for the user to take action accordingly. The dialog is implemented by the DialogFragment NetworkErrorDialogFragment that inflates the layout network_error_dialog.xml.

  • If the response fails with any HTTP error, then this will be consumed silently and the search will not happen. Instead, the previous search results continues to be displayed.
  • During the loading of books data, a custom progress bar will be displayed in the BookSearchActivity. This is implemented using the animation-list / AnimationDrawable.

The About Page

This can be viewed by going into the menu item "About" in the overflow menu of the BookSearchActivity. This page describes in brief about the app, and has links to my bio and the course details hosted by Udacity. This is shown by the activity AboutActivity that inflates the layout activity_about.xml.


Bug Fixes and other important changes

  • Custom Preferences NumberPickerPrefence and the ConfirmationPreference have been modified to fix the Settings Screen crash issue noticed.
  • The No Result page and the Welcome page layouts had issues related ConstraintLayouts used, noticed in some devices running on Android 5. Screen elements were either getting clipped or at times used to disappear on rotation.
  • The Title Text in the details screen was not displaying the Ellipse characters for content that exceeded its MaxLines set. This was happening due to the Spannable being used for resizing the Subtitle part of the Title when it was present. This was fixed by employing a SpannableStringBuilder to do the same and finalized the text on TextView with BufferType as NORMAL, that enables content modification, eg., ellipse can be applied later. Changes can be found in the issue.
  • The Title and Author Text in the details screen can at times be very large for some books, which in turn renders the scrollable content (following the Author Text) inaccessible. This has been fixed as part of the issue. The TextViews were basically embedded in a NestedScrollView of a fixed height (managed dynamically in the BookDetailActivity code). These were enabled for expand/collapse whenever the content in them exceeded their MaxLines set, yielding to ellipsis in the end. A ViewTreeObserver was employed to monitor for large content(that exceeded MaxLines) during the layout process that enables for expand/collapse of the TextViews. As an indicator, image anchors are placed against these TextViews when they can be expanded/collapsed.

Following are the images for a Book having a long Title Text

The same in landscape

Sample image for a Book having long Title and Author Text


Branches in this Repository

  • udacity
    • Contains the code submitted for review.
    • Added Copyright info.
    • Updated Gradle version, made related changes and applied valid lint corrections.
    • Regenerated App Icons.
  • release_v1.0
    • Used ConstrainedWidth to enforce WRAP_CONTENT constraints for Item Views shown by BookSearchActivity (commit) and certain other views shown by BookDetailActivity(commit).
    • Removed unnecessary NestedScrollViews for Expandable TextViews shown by BookDetailActivity. Also, used NestedScrollView instead of ScrollView for the scrollable content following the Book Title and Author in the Portrait mode - (commit).
    • Added Click Listeners to Expand/Collapse Image Anchors shown by BookDetailActivity - (commit).
    • Moved registration of Adapter Item Click listener to the BookSearchActivity (from RecyclerViewFragment) so that we can save the position of the Item clicked, when launching the BookDetailActivity, which prevents the scroll to top when navigating back from the BookDetailActivity - (commit).
    • Modified the dispatch order of Diff updates to the Adapter by dispatching the updates to the Adapter first and then clearing the Adapter's old data to load the new list - (commit).
    • "Handling of Null Image links", and "Reading from and Writing Bitmaps to Memory cache" is now taken care by the ImageDownloader AsyncTaskLoader - (commit).
    • Modified DiffUtil to issue Payload only for the required changes, and the Adapters to bind the data changes from the Payload - (commit).
    • Bitmap Memory Cache is cleared on App Exit as well - (commit).
    • Enabled logging for debuggable build types only, through the use of custom Logger which is a wrapper to the android.util.Log - (commit).
    • Fixed the layout clipping of Page NumberPicker Dialog and NetworkError Dialog.
    • Fixes the performance issue identified, to a considerable extent.

Icon and Image credits


Review from the Reviewer (Udacity)

Review_Book_Listing_App


License

Copyright 2017 Kaushik N. Sanji

Licensed under the Apache License, Version 2.0 (the "License"); 
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0
   
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.