MealRecipeAddActivity.java
/*
* Copyright 2016 the Cook-E development team
*
* This file is part of Cook-E.
*
* Cook-E is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cook-E is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cook-E. If not, see <http://www.gnu.org/licenses/>.
*/
package org.cook_e.cook_e;
import android.content.Intent;
import android.databinding.ObservableArrayList;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.widget.ListView;
import android.widget.SearchView;
import org.cook_e.cook_e.ui.RecipeAddListAdapter;
import org.cook_e.data.Bunch;
import org.cook_e.data.Objects;
import org.cook_e.data.Recipe;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* An activity that displays a list of recipes and allows the user to add one or more of them
* to a meal
*
* When starting this activity, the parent must provide an extra with a key of {@link #EXTRA_RECIPES}
* that contains an array of Recipe objects. The user will be able to select zero or more of
* the provided recipes.
*
* The parent should request a result with the request code {@link #REQUEST_ADD_RECIPES} to get a
* result.
*
* This activity will return a result with code {@link #REQUEST_ADD_RECIPES}. The result will contain
* an extra with the key {@link #EXTRA_RECIPES} containing an array
* of Recipe objects. The array will contain the recipes that the user selected to add.
*/
public class MealRecipeAddActivity extends AppCompatActivity {
/**
* The class tag, used for logging
*/
private static final String TAG = MealRecipeAddActivity.class.getSimpleName();
/**
* A request code used with {@link android.app.Activity#startActivityForResult(Intent, int)} to
* request that this activity return the recipes to be added
*/
public static int REQUEST_ADD_RECIPES = 84;
/**
* A key used to identify an intent extra that contains an array of Recipes that should be
* displayed
*/
public static String EXTRA_RECIPES = MealRecipeAddActivity.class.getName() + ".RECIPES";
/**
* A key used to identify an intent extra that contains an array of Recipes that are already
* in the meal
*/
public static String EXIST_RECIPES = MealRecipeAddActivity.class.getName() + ".EXIST";
/**
* The recipes the user has selected to add
*/
private List<Recipe> mSelectedRecipes;
/**
* The view used for searching
*/
private SearchView mSearchView;
/**
* Total unpackedRecipes
*/
private ObservableArrayList<Recipe> mRecipes;
/**
* Total recipes that should be shown in the list
*/
private ObservableArrayList<Recipe> mVisibleRecipes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSelectedRecipes = new ArrayList<>();
setContentView(R.layout.activity_meal_recipe_add);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setUpActionBar();
// Unpack recipes
mVisibleRecipes = new ObservableArrayList<>();
mRecipes = unpackRecipes();
mVisibleRecipes.addAll(mRecipes);
mSelectedRecipes.addAll(existRecipes());
// Initialize view
final ListView list = (ListView) findViewById(R.id.recipe_list);
final RecipeAddListAdapter adapter = new RecipeAddListAdapter(this, mVisibleRecipes, mSelectedRecipes);
adapter.setAddListener(new RecipeAddListAdapter.RecipeAddListener() {
@Override
public void recipeAddRequested(Recipe recipe) {
// Verify that the added recipe is actually in the recipe list
if (BuildConfig.DEBUG) {
boolean found = false;
for (Recipe existingRecipe : mRecipes) {
if (existingRecipe.equals(recipe)) {
found = true;
}
}
if (!found) {
throw new IllegalArgumentException(
"The recipe added by the user is not in the recipe list");
}
}
Log.d(TAG, "Added recipe " + recipe);
// Add to list of recipes to add
mSelectedRecipes.add(recipe);
updateResult();
}
});
list.setAdapter(adapter);
mSearchView = (SearchView) findViewById(R.id.search);
mSearchView.setOnQueryTextListener(new SearchHandler());
// Set initial result
updateResult();
}
/**
* Sets up the action bar for this activity
*/
private void setUpActionBar() {
final ActionBar bar = getSupportActionBar();
assert bar != null;
bar.setTitle(R.string.add_recipes);
bar.setDisplayHomeAsUpEnabled(true);
}
/**
* Updates the result of this activity to contain the contents of {@link #mSelectedRecipes}
*/
private void updateResult() {
final Intent resultIntent = new Intent();
resultIntent.putExtra(EXTRA_RECIPES,
mSelectedRecipes.toArray(new Recipe[mSelectedRecipes.size()]));
setResult(RESULT_OK, resultIntent);
}
@Override
protected void onPause() {
super.onPause();
// Save the recipes that have been added
// TODO
}
/*
* This is a workaround for inconsistent behavior.
*
* Pressing the system back button or calling finish() returns a result to the parent activity,
* as expected. However, the default action when the up button is pressed does not send a result
* to the parent. This override ensures that a result is sent when the action bar up button is
* pressed.
*/
@Override
public boolean onSupportNavigateUp() {
finish();
return true;
}
/**
* Unpacks the recipes extra from the intent that started this activity and returns an
* ObservableArrayList containing the same recipes.
*
* @return a list of recipes
*/
private ObservableArrayList<Recipe> unpackRecipes() {
final Parcelable[] parcelables = getIntent().getParcelableArrayExtra(EXTRA_RECIPES);
Objects.requireNonNull(parcelables,
"MealRecipeAddActivity must be started with a recipes extra");
final ObservableArrayList<Recipe> recipes = new ObservableArrayList<>();
recipes.ensureCapacity(parcelables.length);
for (Parcelable parcelable : parcelables) {
recipes.add((Recipe) parcelable);
}
return recipes;
}
private List<Recipe> existRecipes() {
final Parcelable[] parcelables = getIntent().getParcelableArrayExtra(MealRecipeAddActivity.EXIST_RECIPES);
Objects.requireNonNull(parcelables,
"MealRecipeAddActivity must be started with a exist recipe list");
final ObservableArrayList<Recipe> exist = new ObservableArrayList<>();
exist.ensureCapacity(parcelables.length);
for (Parcelable parcelable : parcelables) {
exist.add((Recipe) parcelable);
}
return exist;
}
private class SearchHandler implements SearchView.OnQueryTextListener {
@Override
public boolean onQueryTextSubmit(String query) {
// Do nothing more
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
updateVisibleMeals();
return true;
}
}
private void updateVisibleMeals() {
final String query = mSearchView.getQuery().toString();
// Make all meals visible
mVisibleRecipes.clear();
if (!query.isEmpty()) {
final List<Recipe> filteredRecipes = new ArrayList<>();
// Limit mVisibleMeals to the meals whose titles contain the query
// Case insensitive
final String lowerQuery = query.toLowerCase(Locale.getDefault());
for (Recipe recipe : mRecipes) {
final String lowerTitle = recipe.getTitle().toLowerCase(Locale.getDefault());
if (lowerTitle.contains(lowerQuery)) {
filteredRecipes.add(recipe);
}
}
mVisibleRecipes.addAll(filteredRecipes);
} else {
// Empty query
mVisibleRecipes.addAll(mRecipes);
}
}
}