//
// Copyright 2011 Thomas Gumprecht, Robert Jacob, Thomas Pieronczyk
//
// 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.
//
package net.sourcewalker.garanbot;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import net.sourcewalker.garanbot.api.BarcodeResult;
import net.sourcewalker.garanbot.api.ClientException;
import net.sourcewalker.garanbot.api.GaranboClient;
import net.sourcewalker.garanbot.api.Item;
import net.sourcewalker.garanbot.data.GaranboItemsProvider;
import net.sourcewalker.garanbot.data.GaranbotDBMetaData;
import net.sourcewalker.garanbot.data.ImageCache;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentUris;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
/**
* This activity should enable the user to create and edit items in the local
* database. When the user chooses to save the (new) item he will be brought
* back to the list activity and a synchronization with the servers should be
* scheduled.
*
* @author Xperimental
*/
public class EditItemActivity extends Activity {
private static final String TAG = "EditItemActivity";
private static final int DIALOG_PURCHASE_DATE = 10;
private static final int DIALOG_WARRANTY_DATE = 11;
private static final int DIALOG_PROGRESS_BARCODE = 12;
private static final int DIALOG_CONFIRM_BARCODE = 13;
private static final int DIALOG_RESULT_BARCODE = 14;
private static final DateFormat DATE_FORMAT = DateFormat
.getDateInstance(DateFormat.SHORT);
private static final String EXTRA_BARCODE = "barcode";
private static final String EXTRA_RESULT = "result";
protected static final int SELECT_PICTURE = 1;
private EditText nameField;
private EditText manufacturerField;
private EditText modelField;
private EditText locationField;
private EditText notesField;
private EditText vendorField;
private Button purchaseDateField;
private Button endWarrentyDateField;
private Item item;
private BarcodeSearchTask barcodeTask;
private ImageView image;
/**
* If true, item already exists in database and is only edited.
*/
private boolean editItem = false;
/**
* Callback method, which is executed when the user sets the date in the
* date picker dialog. The callback updates the text display of the date
* picker button to the new date.
*/
private final DatePickerDialog.OnDateSetListener setPurchaseDateListener = new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(final DatePicker view, final int year,
final int monthOfYear, final int dayOfMonth) {
removeDialog(DIALOG_PURCHASE_DATE);
final Calendar cal = Calendar.getInstance();
cal.setTime(item.getPurchaseDate());
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, monthOfYear);
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
item.setPurchaseDate(cal.getTime());
updateDisplay();
}
};
/**
* Callback method, which is executed when the user sets the date in the
* date picker dialog. The callback updates the text display of the date
* picker button to the new date.
*/
private final DatePickerDialog.OnDateSetListener setWarrantyDateListener = new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(final DatePicker view, final int year,
final int monthOfYear, final int dayOfMonth) {
removeDialog(DIALOG_PURCHASE_DATE);
final Calendar cal = Calendar.getInstance();
cal.setTime(item.getEndOfWarranty());
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, monthOfYear);
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
item.setEndOfWarranty(cal.getTime());
updateDisplay();
}
};
private final OnClickListener imageClickListener = new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
SELECT_PICTURE);
}
};
/**
*
*/
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.item_edit);
// capture our View elements
nameField = (EditText) findViewById(R.id.item_edit_name);
manufacturerField = (EditText) findViewById(R.id.item_edit_manufacturer);
modelField = (EditText) findViewById(R.id.item_edit_model);
locationField = (EditText) findViewById(R.id.item_edit_location);
notesField = (EditText) findViewById(R.id.item_edit_notes);
vendorField = (EditText) findViewById(R.id.item_edit_vendor);
purchaseDateField = (Button) findViewById(R.id.item_edit_purchase);
endWarrentyDateField = (Button) findViewById(R.id.item_edit_endwarranty);
image = (ImageView) findViewById(R.id.item_edit_image);
// add a click listeners
purchaseDateField.setOnClickListener(new DateDialogButtonListener(
DIALOG_PURCHASE_DATE));
endWarrentyDateField.setOnClickListener(new DateDialogButtonListener(
DIALOG_WARRANTY_DATE));
image.setOnClickListener(imageClickListener);
item = new Item(Item.UNKNOWN_ID);
final String action = getIntent().getAction();
if (action != null && action.length() > 0) {
final Uri itemUri = Uri.parse(action);
loadItemData(itemUri);
}
barcodeTask = new BarcodeSearchTask();
// display the current date
updateDisplay();
}
/**
* Load the item from the database into the local variable.
*
* @param itemUri
* Content URI of database item.
*/
private void loadItemData(final Uri itemUri) {
final Cursor cursor = managedQuery(itemUri,
GaranbotDBMetaData.DEFAULT_PROJECTION, null, null, null);
if (cursor.moveToFirst()) {
try {
item = Item.fromCursor(cursor);
editItem = true;
} catch (ClientException e) {
Log.e(TAG, "Can't parse item: " + e.toString());
}
}
}
/**
* Updates the date on the date picker button.
*/
private void updateDisplay() {
nameField.setText(item.getName());
manufacturerField.setText(item.getManufacturer());
modelField.setText(item.getItemType());
locationField.setText(item.getLocation());
notesField.setText(item.getNotes());
vendorField.setText(item.getVendor());
purchaseDateField.setText(formatDate(item.getPurchaseDate()));
endWarrentyDateField.setText(formatDate(item.getEndOfWarranty()));
image.setImageURI(ContentUris.withAppendedId(
GaranboItemsProvider.CONTENT_URI_IMAGES, item.getLocalId()));
}
private String formatDate(final Date date) {
synchronized (DATE_FORMAT) {
return DATE_FORMAT.format(date);
}
}
/*
* (non-Javadoc)
* @see android.app.Activity#onCreateDialog(int, android.os.Bundle)
*/
@Override
protected Dialog onCreateDialog(final int id, final Bundle bundle) {
Dialog dialog;
switch (id) {
case DIALOG_PURCHASE_DATE:
dialog = createDateDialog(id, item.getPurchaseDate(),
setPurchaseDateListener);
break;
case DIALOG_WARRANTY_DATE:
dialog = createDateDialog(id, item.getEndOfWarranty(),
setWarrantyDateListener);
break;
case DIALOG_PROGRESS_BARCODE:
dialog = createProgressDialog();
break;
case DIALOG_CONFIRM_BARCODE:
dialog = new AlertDialog.Builder(this)
.setTitle(R.string.edit_barcode_dialog_title)
.setMessage(R.string.edit_barcode_dialog_confirm)
.setCancelable(false)
.setPositiveButton(android.R.string.yes,
new BarcodeConfirmListener(bundle))
.setNegativeButton(android.R.string.no, null).create();
break;
case DIALOG_RESULT_BARCODE:
dialog = new AlertDialog.Builder(this)
.setTitle(R.string.edit_barcode_dialog_title)
.setMessage(R.string.edit_barcode_dialog_result)
.setCancelable(false)
.setPositiveButton(android.R.string.yes,
new BarcodeApplyListener(bundle))
.setNegativeButton(android.R.string.no, null).create();
break;
default:
throw new IllegalArgumentException("Unknown dialog: " + id);
}
return dialog;
}
private ProgressDialog createProgressDialog() {
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle(getString(R.string.edit_barcode_dialog_title));
dialog.setMessage(getString(R.string.edit_barcode_dialog_progress));
dialog.setCancelable(true);
dialog.setOnCancelListener(barcodeTask);
return dialog;
}
private DatePickerDialog createDateDialog(final int dialogId,
final Date date, final OnDateSetListener listener) {
final Calendar cal = Calendar.getInstance();
cal.setTime(date);
final DatePickerDialog dialog = new DatePickerDialog(this, listener,
cal.get(Calendar.YEAR), cal.get(Calendar.MONDAY),
cal.get(Calendar.DAY_OF_MONTH));
dialog.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(final DialogInterface arg0) {
removeDialog(dialogId);
}
});
return dialog;
}
/*
* (non-Javadoc)
* @see android.app.Activity#onPrepareDialog(int, android.app.Dialog)
*/
@Override
protected void onPrepareDialog(final int id, final Dialog dialog,
final Bundle extras) {
final String messageFormat, barcode;
switch (id) {
case DIALOG_CONFIRM_BARCODE:
final AlertDialog confirmDialog = (AlertDialog) dialog;
messageFormat = getString(R.string.edit_barcode_dialog_confirm);
barcode = extras.getString(EXTRA_BARCODE);
confirmDialog.setMessage(String.format(messageFormat, barcode));
break;
case DIALOG_PROGRESS_BARCODE:
final ProgressDialog progressDialog = (ProgressDialog) dialog;
messageFormat = getString(R.string.edit_barcode_dialog_progress);
barcode = extras.getString(EXTRA_BARCODE);
progressDialog.setMessage(String.format(messageFormat, barcode));
break;
case DIALOG_RESULT_BARCODE:
final AlertDialog applyDialog = (AlertDialog) dialog;
messageFormat = getString(R.string.edit_barcode_dialog_result);
final BarcodeResult result = extras.getParcelable(EXTRA_RESULT);
applyDialog.setMessage(String.format(messageFormat,
result.getName()));
break;
case DIALOG_PURCHASE_DATE:
case DIALOG_WARRANTY_DATE:
break;
default:
throw new IllegalArgumentException("Unknown dialog id: " + id);
}
}
/*
* (non-Javadoc)
* @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
*/
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.edit_options, menu);
return true;
}
/*
* (non-Javadoc)
* @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.edit_save:
saveItem();
break;
case R.id.edit_cancel:
setResult(RESULT_CANCELED);
finish();
break;
case R.id.edit_scan:
IntentIntegrator.initiateScan(this,
getString(R.string.scan_install_title),
getString(R.string.scan_install_message),
getString(android.R.string.yes),
getString(android.R.string.no),
IntentIntegrator.PRODUCT_CODE_TYPES);
break;
default:
throw new IllegalArgumentException("Unknown menu item: " + item);
}
return true;
}
/**
* Creates a new item in the database.
*/
private void saveItem() {
item.setName(nameField.getText().toString());
item.setManufacturer(manufacturerField.getText().toString());
item.setItemType(modelField.getText().toString());
item.setLocation(locationField.getText().toString());
item.setNotes(notesField.getText().toString());
item.setVendor(vendorField.getText().toString());
item.getLocalState().setDetailsChanged();
if (editItem) {
getContentResolver().update(
ContentUris.withAppendedId(
GaranboItemsProvider.CONTENT_URI_ITEMS,
item.getLocalId()), item.toContentValues(), null,
null);
} else {
getContentResolver().insert(GaranboItemsProvider.CONTENT_URI_ITEMS,
item.toContentValues());
}
setResult(RESULT_OK);
finish();
}
/*
* (non-Javadoc)
* @see android.app.Activity#onActivityResult(int, int,
* android.content.Intent)
*/
@Override
protected void onActivityResult(final int requestCode,
final int resultCode, final Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case SELECT_PICTURE:
onSelectPictureResult(data);
break;
case IntentIntegrator.REQUEST_CODE:
onBarcodeResult(requestCode, resultCode, data);
}
}
}
private void onSelectPictureResult(final Intent data) {
Bitmap bMap = getBitmap(data);
try {
// save bitmap to ImageCache
ImageCache.saveImage(this, item.getLocalId(), bMap);
if (!item.hasPicture()) {
item.setHasPicture(true);
}
} catch (IOException e) {
Log.e(TAG, "Error saving picture: " + e);
}
}
private Bitmap getBitmap(final Intent data) {
String selectedImagePath;
Uri selectedImageUri = data.getData();
selectedImagePath = getPath(selectedImageUri);
Bitmap bMap = BitmapFactory.decodeFile(selectedImagePath);
return bMap;
}
public String getPath(Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(uri, projection, null, null, null);
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
private void onBarcodeResult(final int requestCode, final int resultCode,
final Intent data) {
final IntentResult scanResult = IntentIntegrator.parseActivityResult(
requestCode, resultCode, data);
if (scanResult != null) {
final String contents = scanResult.getContents();
final String formatName = scanResult.getFormatName();
if (contents == null || formatName == null) {
Toast.makeText(this, R.string.scan_cancelled, Toast.LENGTH_LONG);
} else {
startParseBarcode(contents);
}
}
}
/**
* Show a progress dialog while the barcode is sent to the Garanbo server to
* search for product details.
*
* @param contents
* Barcode as String
*/
private void startParseBarcode(final String contents) {
final Bundle extras = new Bundle();
extras.putString(EXTRA_BARCODE, contents);
showDialog(DIALOG_CONFIRM_BARCODE, extras);
}
private class DateDialogButtonListener implements OnClickListener {
private final int dialogId;
public DateDialogButtonListener(final int dialogId) {
this.dialogId = dialogId;
}
@Override
public void onClick(final View v) {
showDialog(dialogId);
}
}
public class BarcodeConfirmListener implements
android.content.DialogInterface.OnClickListener {
private final Bundle extras;
public BarcodeConfirmListener(final Bundle extras) {
this.extras = extras;
}
/*
* (non-Javadoc)
* @see
* android.content.DialogInterface.OnClickListener#onClick(android.content
* .DialogInterface, int)
*/
@Override
public void onClick(final DialogInterface dialog, final int which) {
showDialog(DIALOG_PROGRESS_BARCODE, extras);
final String barcode = extras.getString(EXTRA_BARCODE);
barcodeTask.execute(barcode);
}
}
private class BarcodeApplyListener implements
android.content.DialogInterface.OnClickListener {
private final Bundle extras;
public BarcodeApplyListener(final Bundle extras) {
this.extras = extras;
}
/*
* (non-Javadoc)
* @see
* android.content.DialogInterface.OnClickListener#onClick(android.content
* .DialogInterface, int)
*/
@Override
public void onClick(final DialogInterface dialog, final int which) {
final BarcodeResult result = extras.getParcelable(EXTRA_RESULT);
item.setName(result.getName());
item.setManufacturer(result.getManufacturer());
item.setItemType(result.getType());
updateDisplay();
}
}
private class BarcodeSearchTask extends
AsyncTask<String, Void, BarcodeResult> implements OnCancelListener {
/*
* (non-Javadoc)
* @see android.os.AsyncTask#doInBackground(Params[])
*/
@Override
protected BarcodeResult doInBackground(final String... params) {
final String barcode = params[0];
final AccountManager accountManager = AccountManager
.get(EditItemActivity.this);
final Account[] accounts = accountManager
.getAccountsByType(getString(R.string.account_type));
if (accounts.length == 1) {
final Account account = accounts[0];
final String password = accountManager.getPassword(account);
final GaranboClient client = new GaranboClient(account.name,
password);
try {
return client.searchEan(barcode);
} catch (ClientException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
/*
* (non-Javadoc)
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute(BarcodeResult result) {
dismissDialog(DIALOG_PROGRESS_BARCODE);
if (result == null) {
Toast.makeText(EditItemActivity.this,
R.string.toast_scan_not_found, Toast.LENGTH_LONG)
.show();
} else {
Bundle extras = new Bundle();
extras.putParcelable(EXTRA_RESULT, result);
showDialog(DIALOG_RESULT_BARCODE, extras);
}
}
/*
* (non-Javadoc)
* @see
* android.content.DialogInterface.OnCancelListener#onCancel(android
* .content.DialogInterface)
*/
@Override
public void onCancel(DialogInterface dialog) {
this.cancel(true);
}
}
}