Android Open Source - Books Main View






From Project

Back to project page Books.

License

The source code is released under:

Apache License

If you think the Android project Books listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.contender.books;
/*w ww .  j  a  va 2s .  c  o m*/
import java.util.Calendar;
import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.Days;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CalendarContract.Events;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;

/**
 * Implements a ListView that connects to the {@link BooksStorage#BOOKS_TABLE_NAME}
 * table and displays it. 
 * <p>
 * Implements DialogListeners and onActivityResult to catch results from
 * user interaction and launch the appropriate activity/intent.
 * 
 * @author Paul Klinkenberg <pklinken@gmail.com>
 * @version {@value #BOOKS_VERSION}
 */
public class MainView extends Activity implements AdapterView.OnItemClickListener,
EditOrDeleteDialogFragment.EditOrDeleteDialogListener,
ScanOrManualDialogFragment.ScanOrManualDialogListener {
  
 
  // FIXME: Layout bug where, after scanning a barcode and immediately pressing Back, the layout becomes full screen
  // with the status bar obscuring half the actionbar.
  
  // These constants identify the different Intents and data one can send to BookActivity
  public static final String EXTRA_INDEX = "EditBookIndex";
  public static final String EXTRA_ISBN = "EditBookISBN";
  public static final String ACTION_ADD_ISBN = "AddBookIsbn";
  public static final String ACTION_ADD_MANUAL = "AddBookManual";
  public static final String ACTION_EDIT = "EditBook";

  // These contants define the SharedPreferences location and keys.
  public static final String PREFS_NAME = "BooksPreferences";
  public static final String PREFS_PERIOD_LENGTH = "BooksPeriodLength";
  public static final String PREFS_PERIOD_UNIT = "BooksPeriodUnit";
  public static final String PREFS_SORT_ORDER = "BooksSortOrder";

  public static final String BOOKS_VERSION = "0.4";

  private static final String TAG = "MainView";

  /**
   * {@link onItemClick} sets this to the {@link BookStorage#COLUMN_NAME__ID} of
   * the clicked row in our ListView.
   */
  private static int selectionIndex = -1;
  private ListView listView;
  public static BooksStorage BooksProvider;
  private Cursor mCursor;
  BaseAdapter adapter;


  /**
   * Text converter implemented by the ListView
   * <p>
   * if the processed field is {@link R.id.itemDuedateText} returns a string
   * to display time remaining in days/weeks/months or that the book is late.
   */
  private String convText(TextView v, String text) {
    if(v.getId() == R.id.itemDuedateText) {

      Date dueDate = new Date(Long.parseLong(text));
      Date today = new Date(); 
      Resources res = getResources();
      int days = Days.daysBetween(new DateTime(today), new DateTime(dueDate)).getDays();
      String remTime = new String();

      if(days == 0) {
        remTime = res.getString(R.string.conv_text_today);
        return remTime;
      }
      if(days < 0) {
        remTime = res.getQuantityString(R.plurals.plural_days_overdue, Math.abs(days), Math.abs(days));
        return remTime;
      }

      int x = days / 365;
      if(x > 0) {
        remTime = "+" + x + " " + res.getQuantityString(R.plurals.plural_years, x);
        return remTime;
      }

      x = days / 30;
      if(x > 0) {
        remTime = "+" + x + " " + res.getQuantityString(R.plurals.plural_months, x);
        return remTime;
      }
      x = days / 7;
      if(x > 0) {
        remTime = "+" + x + " " + res.getQuantityString(R.plurals.plural_weeks, x);
        return remTime;
      }
      if(days > 0) {
        remTime = days + " " + res.getQuantityString(R.plurals.plural_days, days);
        return remTime;
      }


      return remTime;
    }
    return text;
  }

  /**
   * Reloads the {@link BookStorage.#BOOKS_TABLE_NAME} table and displays it in our
   * ListView.
   * <p>
   * Also uses SharedPreferences to determine sorting order.
   */
  private void refreshOverview() {

    SharedPreferences settings = this.getSharedPreferences(MainView.PREFS_NAME, 0);
    int sortOrder = settings.getInt(PREFS_SORT_ORDER, 0);
    String sortOrderString;
    switch (sortOrder) {
    case 0:
      sortOrderString = new String(BooksStorage.COLUMN_NAME_DUEDATE);
      break;
    case 1:
      sortOrderString = new String(BooksStorage.COLUMN_NAME_BOOK);
      break;
    case 2:
      sortOrderString = new String(BooksStorage.COLUMN_NAME_CONTACT);
      break;
    default: 
      sortOrderString = null;
    }

    mCursor = BooksProvider.query(BooksStorage.BOOKS_TABLE_NAME, new String[] { BooksStorage.COLUMN_NAME__ID, BooksStorage.COLUMN_NAME_BOOK,
        BooksStorage.COLUMN_NAME_CONTACT, BooksStorage.COLUMN_NAME_DUEDATE }, null, null, sortOrderString);

    if(mCursor == null) {
      Log.e(TAG, "Received empty cursor");
      finish();
    }

    adapter = new SpecialCursorAdapter(this, R.layout.overview_item, mCursor, 
        new String[] { BooksStorage.COLUMN_NAME_BOOK, BooksStorage.COLUMN_NAME_CONTACT, BooksStorage.COLUMN_NAME_DUEDATE }, 
        new int[] { R.id.itemBookText, R.id.itemContactText, R.id.itemDuedateText }, 0) {
      @Override
      public void setViewText(TextView v, String text) {
        super.setViewText(v,  convText(v, text));;
      }
    };
    listView.setAdapter(adapter);
  }

  /**
   * Launches {@link BookActivity} with the ACTION_EDIT intent and {@link selectionIndex}
   * <p>
   * Implements {@link EditOrDeleteDialogFragment.EditOrDeleteDialogListener}
   */
  @Override
  public void onDialogEditClick(DialogFragment dialog) {
    // Edit button
    Intent intent = new Intent();
    intent.setClass(getApplicationContext(), BookActivity.class);
    intent.setAction(ACTION_EDIT);
    intent.putExtra(EXTRA_INDEX, selectionIndex);
    startActivity(intent);
  }

  /** Deletes calendar event associated with the db row where _id IS {@link row}.
   *  
   * @param row the _id of the db entry whose calendar event is to be deleted.
   */
  private void deleteCalendarEvent(int row) {

    int column;
    long eventID;
    Cursor cursor = MainView.BooksProvider.query(BooksStorage.BOOKS_TABLE_NAME, new String[] { 
        BooksStorage.COLUMN_NAME_CALENDAREVENTID }, BooksStorage.COLUMN_NAME__ID + " IS ?", 
        new String[] { new String(Integer.toString(row)) }, null);

    if(!cursor.moveToFirst()) {
      // Something went wrong
      Log.e(TAG, "Received empty cursor.");
      return;
    }

    column = cursor.getColumnIndex(BooksStorage.COLUMN_NAME_CALENDAREVENTID);
    eventID = cursor.getLong(column);

    ContentResolver cr = getContentResolver();
    Uri deleteUri = null;
    deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    int rows = cr.delete(deleteUri, null, null);
    Log.d(TAG, "Rows deleted: " + rows);  
  }

  /**
   * Deletes the row from the DB whose _id IS {@link selectionIndex}
   * <p>
   * If the row has a calendarevent it will also call {@link deleteCalendarEvent}
   * <p>
   * Implements {@link EditOrDeleteDialogFragment.EditOrDeleteDialogListener}
   * 
   * @param dialog dialog dialog that called this function
   */
  @Override
  public void onDialogDeleteClick(DialogFragment dialog) {
    // Delete button

    Cursor cursor = BooksProvider.query(BooksStorage.BOOKS_TABLE_NAME, new String[] { 
        BooksStorage.COLUMN_NAME_BOOK, BooksStorage.COLUMN_NAME_AUTHOR, 
        BooksStorage.COLUMN_NAME_CONTACT, BooksStorage.COLUMN_NAME_DUEDATE, 
        BooksStorage.COLUMN_NAME_LOANDATE, BooksStorage.COLUMN_NAME_HASREMINDER }, 
        BooksStorage.COLUMN_NAME__ID + " IS ?", new String[] { new String(Integer.toString(selectionIndex)) }, null);

    if(!cursor.moveToFirst()) {
      // Something went wrong
      Log.e(TAG,  "Received empty cursor.");
      return;
    }
    ContentValues values = new ContentValues();
    values.put(BooksStorage.COLUMN_NAME_BOOK, cursor.getString(cursor.getColumnIndex(BooksStorage.COLUMN_NAME_BOOK)));
    values.put(BooksStorage.COLUMN_NAME_AUTHOR, cursor.getString(cursor.getColumnIndex(BooksStorage.COLUMN_NAME_AUTHOR)));
    values.put(BooksStorage.COLUMN_NAME_CONTACT, cursor.getString(cursor.getColumnIndex(BooksStorage.COLUMN_NAME_CONTACT)));
    Calendar rightNow = Calendar.getInstance();
    values.put(BooksStorage.COLUMN_NAME_RETURNDATE, rightNow.getTimeInMillis());

    if(BooksProvider.insert(BooksStorage.HISTORY_TABLE_NAME, values) < 0) {
      Log.w(TAG, "Error inserting in history table.");
      return;
    }

    int column = cursor.getColumnIndex(BooksStorage.COLUMN_NAME_HASREMINDER);
    if(cursor.getInt(column) != 0)
    { // cursor has no getBool() method so we will assume any non-zero value is TRUE
      deleteCalendarEvent(selectionIndex);
    }
    if(BooksProvider.delete(BooksStorage.BOOKS_TABLE_NAME, BooksStorage.COLUMN_NAME__ID + "=" + selectionIndex, null ) < 0) {
      Log.e(TAG, "Error deleting from books table.");
      return;
    }
    refreshOverview();
  }

  /**
   * Starts the scan book intent to obtain an ISBN number.
   * <p>
   * Implements {@link ScanOrManualDialogFragment.ScanOrManualDialogListener}
   * 
   * @param dialog dialog dialog that called this function
   */
  @Override
  public void onDialogScanClick(DialogFragment dialog) {
    // Start scanner intent, catch result in this activity and launch addbook activity with ISBN data
    IntentIntegrator integrator = new IntentIntegrator(this);
    integrator.initiateScan();
  }

  /**
   * Starts {@link BookActivity} intent with {@link ACTION_ADD_MANUAL}
   * <p>
   * Implements {@link ScanOrManualDialogFragment.ScanOrManualDialogListener}
   * 
   * @param dialog dialog dialog that called this function
   */
  @Override
  public void onDialogManualClick(DialogFragment dialog) {
    // launch addbook activity with no data
    Intent intent = new Intent();
    intent.setClass(getApplicationContext(), BookActivity.class);
    intent.setAction(ACTION_ADD_MANUAL);
    startActivity(intent);
  }

  /**
   * Retrieves result and ISBN data (if present) from calling Activity and launches
   * {@link BookActivity} intent with {@link ACTION_ADD_ISBN} and ISBN payload data.
   * 
   * @param requestCode The integer request code originally supplied to 
   * startActivityForResult(), allowing you to identify who this result came from.
   * @param resultCode The integer result code returned by the child activity through its setResult() 
   * @param intent data An Intent, which can return result data to the caller 
   * (various data can be attached to Intent "extras").
   */
  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent intent) {

    if(resultCode == RESULT_CANCELED) {
      // User cancelled barcodescan, show a toast notification 
      Context context = getApplicationContext();
      Resources res = getResources();
      CharSequence text = res.getString(R.string.scan_cancelled);
      int duration = Toast.LENGTH_LONG;
      Toast toast = Toast.makeText(context, text, duration);
      toast.show();
      return;
    } else if(resultCode == RESULT_OK)
    {
      IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
      if (scanResult != null && scanResult.getContents() != null) {
        Intent newIntent = new Intent();
        newIntent.setClass(getApplicationContext(), BookActivity.class);
        newIntent.setAction(ACTION_ADD_ISBN);
        newIntent.putExtra(EXTRA_ISBN, scanResult.getContents());
        startActivity(newIntent);      
      }
    } else
      assert false : "onActivityResult() unexpected resultCode: " + resultCode;
    
    return;
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main_view);

    listView = (ListView) findViewById(R.id.listView1);
    listView.setOnItemClickListener(this);
    BooksProvider = new BooksStorage(this);
  }

  @Override
  public void onResume() {
    super.onResume();

    refreshOverview();
  }

  @Override
  public void onStop(){
    super.onStop();

    mCursor.close();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {

    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main_view, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.

    Intent intent;

    switch(item.getItemId()) {
    case R.id.action_settings:
      intent = new Intent(getApplicationContext(), SettingsActivity.class);
      startActivity(intent);
      return true;
    case R.id.action_about:
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setTitle(R.string.about_title);
      Resources res = getResources();
      String aboutMessage = res.getString(R.string.about_message, BOOKS_VERSION);
      builder.setMessage(aboutMessage);
      AlertDialog aboutDialog = builder.create();
      aboutDialog.show();
      TextView view = (TextView) aboutDialog.findViewById(android.R.id.message);
      view.setTextSize(12);
      return true;
    case R.id.action_add:
      DialogFragment dialog = new ScanOrManualDialogFragment();
      dialog.show(getFragmentManager(),  "scan_or_manual");
      return true;
    case R.id.action_history:
      intent = new Intent(getApplicationContext(), HistoryActivity.class);
      startActivity(intent);
      return true;

    }
    return super.onOptionsItemSelected(item);
  }

  public void onItemClick(AdapterView<?> parent, View v, int position, long id) {

    // map position to _id in DB
    selectionIndex = (int) adapter.getItemId(position);

    DialogFragment dialog = new EditOrDeleteDialogFragment();
    dialog.show(getFragmentManager(), "edit_or_delete");

  }
}




Java Source Code List

com.contender.books.BookActivity.java
com.contender.books.BooksStorage.java
com.contender.books.DatePickerFragment.java
com.contender.books.EditOrDeleteDialogFragment.java
com.contender.books.HistoryActivity.java
com.contender.books.MainView.java
com.contender.books.ScanOrManualDialogFragment.java
com.contender.books.SettingsActivity.java
com.contender.books.SpecialCursorAdapter.java
com.google.zxing.integration.android.IntentIntegrator.java
com.google.zxing.integration.android.IntentResult.java