package com.brew.brewshop.fragments;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.support.v7.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.brew.brewshop.FragmentHandler;
import com.brew.brewshop.pdf.PdfRecipeWriter;
import com.brew.brewshop.pdf.SavePdfClickListener;
import com.brew.brewshop.pdf.WritePdfTask;
import com.brew.brewshop.settings.Settings;
import com.brew.brewshop.storage.inventory.InventoryItem;
import com.brew.brewshop.storage.inventory.InventoryList;
import com.brew.brewshop.storage.recipes.IngredientListView;
import com.brew.brewshop.R;
import com.brew.brewshop.ViewClickListener;
import com.brew.brewshop.storage.BrewStorage;
import com.brew.brewshop.storage.recipes.BeerStyle;
import com.brew.brewshop.storage.recipes.HopAddition;
import com.brew.brewshop.storage.recipes.MaltAddition;
import com.brew.brewshop.storage.recipes.Recipe;
import com.brew.brewshop.storage.recipes.Weight;
import com.brew.brewshop.storage.recipes.Yeast;
import com.brew.brewshop.util.UnitConverter;
import com.brew.brewshop.util.Util;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
public class RecipeFragment extends Fragment implements ViewClickListener,
AdapterView.OnItemClickListener,
ActionMode.Callback {
@SuppressWarnings("unused")
private static final String TAG = RecipeFragment.class.getName();
private static final int SAVE_AS_PDF_CODE = 100;
private static final String ACTION_MODE = "ActionMode";
private static final String SELECTED_INDEXES = "Selected";
private static final String RECIPE = "Recipe";
private static final String UNIT_MINUTES = " min";
private static final String UNIT_PERCENT = "%";
private static final String UNIT_PLATO = "°P";
private Recipe mRecipe;
private BrewStorage mStorage;
private FragmentHandler mFragmentHandler;
private Dialog mSelectIngredient;
private ActionMode mActionMode;
private IngredientListView mIngredientView;
private View mRootView;
private Settings mSettings;
private UnitConverter mConverter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
mSettings = new Settings(getActivity());
View root = inflater.inflate(R.layout.fragment_edit_recipe, container, false);
mRootView = root;
root.findViewById(R.id.recipe_stats_layout).setOnClickListener(this);
root.findViewById(R.id.recipe_notes_layout).setOnClickListener(this);
root.findViewById(R.id.new_ingredient_view).setOnClickListener(this);
setHasOptionsMenu(true);
mStorage = new BrewStorage(getActivity());
mConverter = new UnitConverter(getActivity());
if (state != null) {
mRecipe = state.getParcelable(RECIPE);
}
if (mRecipe == null) {
Log.d(TAG, "Loading recipe from storage");
mRecipe = mFragmentHandler.getCurrentRecipe();
mStorage.retrieveRecipe(mRecipe);
}
BeerStyle style = mRecipe.getStyle();
TextView textView;
textView = (TextView) root.findViewById(R.id.recipe_name);
textView.setText(mRecipe.getName());
ImageView iconView = (ImageView) root.findViewById(R.id.recipe_stats_icon);
//iconView.setBackgroundColor(Util.getColor(mRecipe.getSrm()));
iconView.setBackgroundColor(getResources().getColor(R.color.color_transparent));
iconView.setImageResource(style.getIconDrawable());
textView = (TextView) root.findViewById(R.id.recipe_style);
String styleName = style.getDisplayName();
if (styleName == null || styleName.length() == 0) {
styleName = getActivity().getResources().getString(R.string.select_style);
}
textView.setText(styleName);
textView = (TextView) root.findViewById(R.id.batch_volume);
textView.setText(mConverter.fromGallonsWithUnits(mRecipe.getBatchVolume(), 1));
textView = (TextView) root.findViewById(R.id.boil_volume);
textView.setText(mConverter.fromGallonsWithUnits(mRecipe.getBoilVolume(), 1));
textView = (TextView) root.findViewById(R.id.boil_time);
textView.setText(Util.fromDouble(mRecipe.getBoilTime(), 0) + UNIT_MINUTES);
textView = (TextView) root.findViewById(R.id.efficiency);
textView.setText(Util.fromDouble(mRecipe.getEfficiency(), 1) + UNIT_PERCENT);
updateStats();
textView = (TextView) root.findViewById(R.id.style_og);
if (style.getOgMin() < 0) {
textView.setText(getResources().getString(R.string.varies));
} else {
String og = Util.fromDouble(style.getOgMin(), 3, false);
if (style.getOgMax() - style.getOgMin() >= 0.001) {
og += " - " + Util.fromDouble(style.getOgMax(), 3, false);
}
textView.setText(og);
}
textView = (TextView) root.findViewById(R.id.style_ibu);
if (style.getIbuMin() < 0) {
textView.setText(getResources().getString(R.string.varies));
} else {
String ibu = Util.fromDouble(style.getIbuMin(), 1);
if (style.getIbuMax() - style.getIbuMin() >= 0.1) {
ibu += " - " + Util.fromDouble(style.getIbuMax(), 1);
}
textView.setText(ibu);
}
textView = (TextView) root.findViewById(R.id.style_srm);
if (style.getSrmMin() < 0) {
textView.setText(getResources().getString(R.string.varies));
} else {
String srm = Util.fromDouble(style.getSrmMin(), 1);
if (style.getSrmMax() - style.getSrmMin() > 0.1) {
srm += " - " + Util.fromDouble(style.getSrmMax(), 1);
}
textView.setText(srm);
}
textView = (TextView) root.findViewById(R.id.style_fg);
if (style.getFgMin() < 0) {
textView.setText(getResources().getString(R.string.varies));
} else {
String fg = Util.fromDouble(style.getFgMin(), 3, false);
if (style.getFgMax() - style.getFgMin() >= 0.001) {
fg += " - " + Util.fromDouble(style.getFgMax(), 3, false);
}
textView.setText(fg);
}
textView = (TextView) root.findViewById(R.id.style_abv);
if (style.getAbvMin() < 0) {
textView.setText(getResources().getString(R.string.varies));
} else {
String abv = Util.fromDouble(style.getAbvMin(), 1);
if (style.getAbvMax() - style.getAbvMin() >= 0.1) {
abv += " - " + Util.fromDouble(style.getAbvMax(), 1);
}
textView.setText(abv + UNIT_PERCENT);
}
mIngredientView = new IngredientListView(getActivity(), root, mRecipe, this);
mIngredientView.getAdapter().showInventory(mSettings.getShowInventoryInRecipe());
mIngredientView.drawList();
textView = (TextView) root.findViewById(R.id.recipe_notes);
textView.setText(mRecipe.getNotes());
checkResumeActionMode(state);
mFragmentHandler.setTitle(findString(R.string.edit_recipe));
return root;
}
@Override
public void onPause() {
super.onPause();
if (mActionMode != null) {
mActionMode.finish();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mStorage.close();
}
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
if (state == null) {
state = new Bundle();
}
state.putParcelable(RECIPE, mRecipe);
state.putBoolean(ACTION_MODE, mActionMode != null);
if (mActionMode != null) {
state.putIntArray(SELECTED_INDEXES, mIngredientView.getSelectedIndexes());
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.edit_recipe_menu, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
if (mRecipe.getIngredients().isEmpty()) {
menu.findItem(R.id.action_show_inventory).setVisible(false);
menu.findItem(R.id.action_hide_inventory).setVisible(false);
menu.findItem(R.id.action_remove_from_inventory).setVisible(false);
} else {
boolean showing = mSettings.getShowInventoryInRecipe();
menu.findItem(R.id.action_show_inventory).setVisible(!showing);
menu.findItem(R.id.action_hide_inventory).setVisible(showing);
menu.findItem(R.id.action_remove_from_inventory).setVisible(showing);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_show_inventory:
mSettings.setShowInventoryInRecipe(true);
getActivity().supportInvalidateOptionsMenu();
showInventory(true);
return true;
case R.id.action_hide_inventory:
mSettings.setShowInventoryInRecipe(false);
getActivity().supportInvalidateOptionsMenu();
showInventory(false);
return true;
case R.id.action_remove_from_inventory:
new AlertDialog.Builder(getActivity())
.setMessage(R.string.confirm_remove_from_inventory)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
removeFromInventory();
}
})
.setNegativeButton(R.string.no, null)
.show();
return true;
case R.id.action_save_as_pdf:
saveAsPdf();
return true;
}
return super.onOptionsItemSelected(menuItem);
}
private void saveAsPdf() {
Intent fileIntent;
fileIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
fileIntent.addCategory(Intent.CATEGORY_OPENABLE);
fileIntent.setType("*/*");
if (Util.isIntentAvailable(getActivity().getBaseContext(), fileIntent)) {
startActivityForResult(fileIntent, SAVE_AS_PDF_CODE);
} else {
manualSaveAsPdf();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == SAVE_AS_PDF_CODE && resultCode == Activity.RESULT_OK) {
if (resultData != null) {
OutputStream pdfOutputStream;
try {
Uri uri = resultData.getData();
pdfOutputStream = getActivity().getContentResolver().openOutputStream(uri);
} catch (FileNotFoundException e) {
Log.e(TAG, "Save to PDF file not found", e);
return;
}
WritePdfTask task = new WritePdfTask(getActivity(), mRecipe, pdfOutputStream);
task.execute();
}
}
}
private void manualSaveAsPdf() {
final EditText input = new EditText(getActivity());
SavePdfClickListener listener = new SavePdfClickListener(getActivity(), input, mRecipe);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getString(R.string.enter_pdf_file_name));
builder.setView(input);
builder.setPositiveButton(getString(R.string.ok), listener);
builder.setNegativeButton(getString(R.string.cancel), null);
AlertDialog dialog = builder.create();
input.requestFocus();
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
dialog.show();
}
private void showInventory(boolean show) {
mIngredientView.getAdapter().showInventory(show);
mIngredientView.drawList();
}
private void removeFromInventory() {
removeMaltsFromInventory();
removeHopsFromInventory();
removeYeastFromInventory();
mIngredientView.drawList();
Toast.makeText(getActivity(), R.string.inventory_updated, Toast.LENGTH_SHORT).show();
}
private void removeMaltsFromInventory() {
for (MaltAddition addition : mRecipe.getMalts()) {
Weight weight = new Weight(addition.getWeight());
InventoryList maltsWithSameName = mStorage.retrieveInventory().findAll(addition.getMalt());
removeWeightFromInventory(weight, maltsWithSameName);
}
}
private void removeHopsFromInventory() {
for (HopAddition addition : mRecipe.getHops()) {
Weight weight = new Weight(addition.getWeight());
InventoryList hopsWithSameName = mStorage.retrieveInventory().findAll(addition.getHop());
removeWeightFromInventory(weight, hopsWithSameName);
}
}
private void removeYeastFromInventory() {
for (Yeast yeast : mRecipe.getYeast()) {
InventoryList yeastWithSameName = mStorage.retrieveInventory().findAll(yeast);
removeQuantityFromInventory(1, yeastWithSameName);
}
}
private void removeWeightFromInventory(Weight weight, InventoryList inventory) {
for (InventoryItem item : inventory) {
if (weight.greaterThanOrEqual(item.getWeight())) {
weight.subtract(item.getWeight());
mStorage.deleteInventoryItem(item);
} else {
item.getWeight().subtract(weight);
mStorage.updateInventoryItem(item);
break;
}
}
}
private void removeQuantityFromInventory(int quantity, InventoryList inventory) {
for (InventoryItem item : inventory) {
if (quantity >= item.getCount()) {
quantity -= item.getCount();
mStorage.deleteInventoryItem(item);
} else {
item.setCount(item.getCount() - quantity);
mStorage.updateInventoryItem(item);
break;
}
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.recipe_stats_layout:
mFragmentHandler.showRecipeStatsEditor(mRecipe);
return;
case R.id.recipe_notes_layout:
mFragmentHandler.showRecipeNotesEditor(mRecipe);
return;
case R.id.new_ingredient_view:
addNewIngredient();
return;
}
Object ingredient = view.getTag(R.string.ingredients);
if (ingredient != null) {
int index = (Integer) view.getTag(R.integer.ingredient_index);
boolean selected = (Boolean) view.getTag(R.integer.is_recipe_selected);
if (mActionMode != null) {
mIngredientView.setSelected(index, !selected);
if (mIngredientView.getSelectedCount() == 0) {
mActionMode.finish();
}
updateActionBar();
} else {
editIngredient(ingredient);
}
}
}
@Override
public boolean onLongClick(View view) {
Object ingredient = view.getTag(R.string.ingredients);
if (ingredient != null) {
if (mActionMode != null) {
updateActionBar();
return false;
} else {
int index = (Integer) view.getTag(R.integer.ingredient_index);
startActionMode(new int[]{index});
}
return true;
}
return false;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mFragmentHandler = (FragmentHandler) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement " + FragmentHandler.class.getName());
}
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
mSelectIngredient.dismiss();
String ingredient = getIngredientTypes().get(position);
if (findString(R.string.malt).equals(ingredient)) {
MaltAddition malt = new MaltAddition();
mRecipe.getMalts().add(malt);
mFragmentHandler.showMaltEditor(mRecipe, malt);
} else if (findString(R.string.hops).equals(ingredient)) {
HopAddition hop = new HopAddition();
mRecipe.getHops().add(hop);
mFragmentHandler.showHopsEditor(mRecipe, hop);
} else if (findString(R.string.yeast).equals(ingredient)) {
Yeast yeast = new Yeast();
mRecipe.getYeast().add(yeast);
mFragmentHandler.showYeastEditor(mRecipe, yeast);
}
}
@Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
mActionMode = actionMode;
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
menu.clear();
int checked = mIngredientView.getSelectedCount();
mActionMode.setTitle(getResources().getString(R.string.select_ingredients));
mActionMode.setSubtitle(checked + " " + getResources().getString(R.string.selected));
MenuInflater inflater = actionMode.getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
boolean itemsChecked = (mIngredientView.getSelectedCount() > 0);
mActionMode.getMenu().findItem(R.id.action_delete).setVisible(itemsChecked);
mActionMode.getMenu().findItem(R.id.action_select_all).setVisible(!mIngredientView.areAllSelected());
return true;
}
@Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.action_select_all:
mIngredientView.setAllSelected(true);
updateActionBar();
return true;
case R.id.action_delete:
int count = mIngredientView.getSelectedCount();
String message;
if (count > 1) {
message = String.format(getActivity().getResources().getString(R.string.delete_selected_ingredients), count);
} else {
message = String.format(getActivity().getResources().getString(R.string.delete_selected_ingredient), count);
}
new AlertDialog.Builder(getActivity())
.setMessage(message)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteIngredients();
}
})
.setNegativeButton(R.string.no, null)
.show();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode actionMode) {
mIngredientView.setAllSelected(false);
mActionMode = null;
}
private void deleteIngredients() {
int deleted = deleteSelected();
mIngredientView.drawList();
mActionMode.finish();
updateStats();
toastDeleted(deleted);
}
private void checkResumeActionMode(Bundle bundle) {
if (bundle != null) {
if (bundle.getBoolean(ACTION_MODE)) {
int[] selected = bundle.getIntArray(SELECTED_INDEXES);
startActionMode(selected);
}
}
}
private void updateStats() {
TextView textView;
textView = (TextView) mRootView.findViewById(R.id.recipe_og);
switch (mSettings.getExtractUnits()) {
case SPECIFIC_GRAVITY:
textView.setText(Util.fromDouble(mRecipe.getOg(), 3, false));
break;
case DEGREES_PLATO:
textView.setText(Util.fromDouble(mRecipe.getOgPlato(), 1, false) + UNIT_PLATO);
break;
}
textView = (TextView) mRootView.findViewById(R.id.recipe_srm);
textView.setText(Util.fromDouble(mRecipe.getSrm(), 1));
textView = (TextView) mRootView.findViewById(R.id.recipe_ibu);
textView.setText(Util.fromDouble(mRecipe.getTotalIbu(), 1));
textView = (TextView) mRootView.findViewById(R.id.recipe_fg);
if (mRecipe.hasYeast()) {
switch (mSettings.getExtractUnits()) {
case SPECIFIC_GRAVITY:
textView.setText(Util.fromDouble(mRecipe.getFg(), 3, false));
break;
case DEGREES_PLATO:
textView.setText(Util.fromDouble(mRecipe.getFgPlato(), 1, false) + UNIT_PLATO);
break;
}
textView.setTextColor(getResources().getColor(R.color.text_dark_primary));
} else {
textView.setText(getResources().getString(R.string.add_yeast));
textView.setTextColor(getResources().getColor(R.color.text_dark_secondary));
}
textView = (TextView) mRootView.findViewById(R.id.recipe_abv);
if (mRecipe.hasYeast()) {
textView.setText(Util.fromDouble(mRecipe.getAbv(), 1) + UNIT_PERCENT);
textView.setTextColor(getResources().getColor(R.color.text_dark_primary));
} else {
textView.setText("-");
textView.setTextColor(getResources().getColor(R.color.text_dark_secondary));
}
textView = (TextView) mRootView.findViewById(R.id.calories_label);
switch (mSettings.getUnits()) {
case IMPERIAL:
textView.setText(R.string.imperial_calories);
break;
case METRIC:
textView.setText(R.string.metric_calories);
break;
}
textView = (TextView) mRootView.findViewById(R.id.recipe_calories);
if (mRecipe.hasYeast()) {
textView.setText(mConverter.fromCaloriesPerOz(mRecipe.getCaloriesPerOz(), 1));
textView.setTextColor(getResources().getColor(R.color.text_dark_primary));
} else {
textView.setText("-");
textView.setTextColor(getResources().getColor(R.color.text_dark_secondary));
}
}
public void setRecipe(Recipe recipe) {
mRecipe = recipe;
}
public Recipe getRecipe() { return mRecipe; }
private void toastDeleted(int deleted) {
Context context = getActivity();
String message;
if (deleted > 1) {
message = String.format(context.getResources().getString(R.string.deleted_ingredients), deleted);
} else {
message = context.getResources().getString(R.string.deleted_ingredient);
}
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
private int deleteSelected() {
int[] indexes = mIngredientView.getSelectedIndexes();
for (int i = indexes.length - 1; i >= 0; i--) {
Object ingredient = mRecipe.getIngredients().get(indexes[i]);
if (ingredient instanceof MaltAddition) {
mRecipe.getMalts().remove(ingredient);
} else if (ingredient instanceof HopAddition) {
mRecipe.getHops().remove(ingredient);
} else if (ingredient instanceof Yeast) {
mRecipe.getYeast().remove(ingredient);
}
}
mStorage.updateRecipe(mRecipe);
return indexes.length;
}
private void updateActionBar() {
if (mActionMode != null) {
mActionMode.invalidate();
}
}
private void startActionMode(int[] selectedIndexes) {
for (int i : selectedIndexes) {
mIngredientView.setSelected(i, true);
}
((ActionBarActivity) getActivity()).startSupportActionMode(this);
}
private void editIngredient(Object ingredient) {
if (ingredient instanceof MaltAddition) {
mFragmentHandler.showMaltEditor(mRecipe, (MaltAddition) ingredient);
} else if (ingredient instanceof HopAddition) {
mFragmentHandler.showHopsEditor(mRecipe, (HopAddition) ingredient);
} else if (ingredient instanceof Yeast) {
mFragmentHandler.showYeastEditor(mRecipe, (Yeast) ingredient);
}
}
private void addNewIngredient() {
int maxIngredients = getActivity().getResources().getInteger(R.integer.max_ingredients);
if (mRecipe.getIngredients().size() >= maxIngredients) {
String message = String.format(findString(R.string.max_ingredients_reached), maxIngredients);
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
return;
}
mSelectIngredient = new Dialog(getActivity());
mSelectIngredient.setContentView(R.layout.select_ingredient);
IngredientTypeAdapter adapter = new IngredientTypeAdapter(getActivity(), getIngredientTypes());
ListView listView = (ListView) mSelectIngredient.findViewById(R.id.recipe_list);
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
mSelectIngredient.setCancelable(true);
mSelectIngredient.setTitle(findString(R.string.add_ingredient));
mSelectIngredient.show();
}
private List<String> getIngredientTypes() {
String[] ingredients = getActivity().getResources().getStringArray(R.array.ingredient_types);
return Arrays.asList(ingredients);
}
private String findString(int id) {
return getActivity().getResources().getString(id);
}
}