@Override
 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
   mViewAdapter.swapCursor(new BooksCursor(data));
   // If the user queries, set the no results string. If there is no query, set the empty
   //  library string.
   TextView emptyView = (TextView) mRootView.findViewById(R.id.view_books_empty);
   if (mQueried) emptyView.setText(R.string.empty_view_books_no_results);
   else emptyView.setText(R.string.empty_view_books);
   mViewAdapter.notifyDataSetChanged();
 }
  @Override
  public View onCreateView(
      final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    mRootView = inflater.inflate(R.layout.fragment_view_books, container, false);
    View emptyView = mRootView.findViewById(R.id.view_books_empty);

    mSearchField = (EditText) mRootView.findViewById(R.id.view_books_search_field);
    mLoadingSpinner = (ProgressBar) mRootView.findViewById(R.id.view_books_progress_spinner);

    // Initialize recycler view
    mRecyclerView = (RecyclerView) mRootView.findViewById(R.id.view_books_recyclerview);
    mViewAdapter =
        new ViewAdapter(
            getContext(),
            new ViewAdapter.ViewAdapterOnClickHandler() {
              @Override
              public void onClick(String bookId, ViewAdapter.ViewHolder holder) {
                ((BookSelectionCallback) getActivity()).onBookItemSelected(bookId, holder);
              }
            },
            emptyView);
    mViewAdapter.swapCursor(null);
    if (((ViewGroup.MarginLayoutParams) mSearchField.getLayoutParams()).leftMargin > 0) {
      // Number of columns determined by sw and orientation (see res/values/integers)
      mRecyclerView.setLayoutManager(
          new StaggeredGridLayoutManager(
              getResources().getInteger(R.integer.num_card_columns),
              StaggeredGridLayoutManager.VERTICAL));
    } else {
      mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
    }
    mRecyclerView.setAdapter(mViewAdapter);

    // This is performed every time the text field value is changed
    // Use of timer to delay changed text event attributed to Marcus Pohls of futurestud.io
    //  (url: https://futurestud.io/blog/android-how-to-delay-changedtextevent-on-androids-edittext)
    mSearchField.addTextChangedListener(
        new TextWatcher() {
          private Timer timer;
          private static final int TIMER_DURATION = 600;

          // Called before text in EditText changes
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // no need
          }

          // Called while text in EditText changes
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            // User is currently typing: reset already started timer (if existing)
            if (timer != null) timer.cancel();
          }

          // Called after text in EditText changes
          @Override
          public void afterTextChanged(final Editable s) {
            // User typed: start the timer. This allows the action below to be executed only
            //  within the temporal bounds set by the timer. The timer runs until the specified
            //  duration time (in ms). If the user continues to type, the timer is reset back
            //  to 0 and starts again.
            timer = new Timer();
            timer.schedule(
                new TimerTask() {
                  @Override
                  public void run() {
                    // Restarting the loader needs to be done on the ui thread
                    mRootView.post(
                        new Runnable() {
                          @Override
                          public void run() {
                            restartLoader();
                          }
                        });
                  }
                },
                TIMER_DURATION);
          }
        });

    // Start the fetch fragment
    mRootView
        .findViewById(R.id.view_books_button_add)
        .setOnClickListener(
            new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                ((FetchButtonClickedListener) getActivity()).onFetchButtonClicked();
              }
            });

    // Save the value currently in the search field for when the activity recreates itself
    if (savedInstanceState != null) {
      mSearchField.setText(savedInstanceState.getString(SIS_QUERY));
      mSearchField.setHint("");
    }

    return mRootView;
  }
 @Override
 public void onLoaderReset(Loader<Cursor> loader) {
   mViewAdapter.swapCursor(null);
 }