/* * Copyright 2014 Google Inc. All Rights Reserved. * * 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 com.recipe_app.client; import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.ContentValues; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v13.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import android.widget.ToggleButton; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; import com.google.firebase.appindexing.Action; import com.google.firebase.appindexing.FirebaseAppIndex; import com.google.firebase.appindexing.FirebaseUserActions; import com.google.firebase.appindexing.Indexable; import com.google.firebase.appindexing.builders.Actions; import com.google.firebase.appindexing.builders.Indexables; import com.recipe_app.R; import com.recipe_app.client.Recipe.Note; import com.recipe_app.client.content_provider.RecipeContentProvider; import com.recipe_app.client.database.RecipeNoteTable; import com.recipe_app.client.database.RecipeTable; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; /** * This Activity class is used to display a {@link com.recipe_app.client.Recipe} object */ public class RecipeActivity extends Activity { private static final String TAG = RecipeActivity.class.getName(); /** * The {@link android.support.v4.view.PagerAdapter} that will provide * fragments for each of the sections. We use a * {@link FragmentPagerAdapter} derivative, which will keep every * loaded fragment in memory. If this becomes too memory intensive, it * may be best to switch to a * {@link android.support.v13.app.FragmentStatePagerAdapter}. */ private SectionsPagerAdapter mSectionsPagerAdapter; /** * The {@link ViewPager} that will host the section contents. */ private ViewPager mViewPager; private Recipe mRecipe; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recipe); onNewIntent(getIntent()); } // [START on_start] @Override public void onStart() { super.onStart(); if (mRecipe != null) { // If you’re logging an action on content that hasn’t been added to the index yet, // add it first with an update call; indexRecipe(); FirebaseUserActions.getInstance().start(getRecipeViewAction()); } } @Override public void onStop() { if (mRecipe != null) { FirebaseUserActions.getInstance().end(getRecipeViewAction()); } super.onStop(); } // [END on_start] protected void onNewIntent(Intent intent) { String action = intent.getAction(); String data = intent.getDataString(); if (Intent.ACTION_VIEW.equals(action) && data != null) { if (data.endsWith("note")) { data = data.substring(0, data.lastIndexOf("/")); } String recipeId = data.substring(data.lastIndexOf("/") + 1); Uri contentUri = RecipeContentProvider.CONTENT_URI.buildUpon() .appendPath(recipeId).build(); showRecipe(contentUri); } } // [START index_note] private void indexNote() { Note note = mRecipe.getNote(); Indexable noteToIndex = Indexables.noteDigitalDocumentBuilder() .setName(mRecipe.getTitle() + " Note") .setText(note.getText()) .setUrl(mRecipe.getNoteUrl()) .build(); Task<Void> task = FirebaseAppIndex.getInstance().update(noteToIndex); // [START_EXCLUDE] task.addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { Log.d(TAG, "App Indexing API: Successfully added note to index"); } }); task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { Log.e(TAG, "App Indexing API: Failed to add note to index. " + exception .getMessage()); } }); // [END_EXCLUDE] } // [END index_note] private void indexRecipe() { Indexable recipeToIndex = new Indexable.Builder() .setName(mRecipe.getTitle()) .setUrl(mRecipe.getRecipeUrl()) .setImage(mRecipe.getPhoto()) .setDescription(mRecipe.getDescription()) .build(); Task<Void> task = FirebaseAppIndex.getInstance().update(recipeToIndex); task.addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { Log.d(TAG, "App Indexing API: Successfully added " + mRecipe.getTitle() + " to " + "index"); } }); task.addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { Log.e(TAG, "App Indexing API: Failed to add " + mRecipe.getTitle() + " to index. " + "" + exception.getMessage()); } }); } private void showRecipe(Uri recipeUri) { Log.d("Recipe Uri", recipeUri.toString()); String[] projection = {RecipeTable.ID, RecipeTable.TITLE, RecipeTable.DESCRIPTION, RecipeTable.PHOTO, RecipeTable.PREP_TIME}; Cursor cursor = getContentResolver().query(recipeUri, projection, null, null, null); if (cursor != null && cursor.moveToFirst()) { mRecipe = Recipe.fromCursor(cursor); Uri ingredientsUri = RecipeContentProvider.CONTENT_URI.buildUpon().appendPath ("ingredients").appendPath(mRecipe.getId()).build(); Cursor ingredientsCursor = getContentResolver().query(ingredientsUri, projection, null, null, null); if (ingredientsCursor != null && ingredientsCursor.moveToFirst()) { do { Recipe.Ingredient ingredient = new Recipe.Ingredient(); ingredient.setAmount(ingredientsCursor.getString(0)); ingredient.setDescription(ingredientsCursor.getString(1)); mRecipe.addIngredient(ingredient); ingredientsCursor.moveToNext(); } while (!ingredientsCursor.isAfterLast()); ingredientsCursor.close(); } Uri instructionsUri = RecipeContentProvider.CONTENT_URI.buildUpon().appendPath ("instructions").appendPath(mRecipe.getId()).build(); Cursor instructionsCursor = getContentResolver().query(instructionsUri, projection, null, null, null); if (instructionsCursor != null && instructionsCursor.moveToFirst()) { do { Recipe.Step step = new Recipe.Step(); step.setDescription(instructionsCursor.getString(1)); step.setPhoto(instructionsCursor.getString(2)); mRecipe.addStep(step); instructionsCursor.moveToNext(); } while (!instructionsCursor.isAfterLast()); instructionsCursor.close(); } Uri noteUri = RecipeContentProvider.CONTENT_URI.buildUpon().appendPath("notes") .appendPath(mRecipe.getId()).build(); Cursor noteCursor = getContentResolver().query(noteUri, projection, null, null, null); if (noteCursor != null && noteCursor.moveToFirst()) { Note note = Note.fromCursor(noteCursor); mRecipe.setNote(note); noteCursor.close(); } // always close the cursor cursor.close(); } else { Toast toast = Toast.makeText(getApplicationContext(), "No match for deep link " + recipeUri.toString(), Toast.LENGTH_SHORT); toast.show(); } if (mRecipe != null) { // Create the adapter that will return a fragment for each of the steps of the recipe. mSectionsPagerAdapter = new SectionsPagerAdapter(getFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mSectionsPagerAdapter); // Set the recipe title TextView recipeTitle = (TextView) findViewById(R.id.recipeTitle); recipeTitle.setText(mRecipe.getTitle()); // Set the recipe prep time TextView recipeTime = (TextView) findViewById(R.id.recipeTime); recipeTime.setText(" " + mRecipe.getPrepTime()); //Set the note button toggle ToggleButton addNoteToggle = (ToggleButton) findViewById(R.id.addNoteToggle); addNoteToggle.setChecked(mRecipe.getNote() != null); addNoteToggle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mRecipe.getNote() != null) { displayNoteDialog(getString(R.string.dialog_update_note), getString(R .string.dialog_delete_note)); } else { displayNoteDialog(getString(R.string.dialog_add_note), getString(R.string .dialog_cancel_note)); } } }); } } // [START end_note_view_action] private void displayNoteDialog(final String positiveText, final String negativeText) { final AlertDialog.Builder addNoteDialog = new AlertDialog.Builder(this); final EditText edittext = new EditText(this); // [START_EXCLUDE] addNoteDialog.setTitle("Enter a note"); addNoteDialog.setView(edittext); if (mRecipe.getNote() != null) { edittext.setText(mRecipe.getNote().getText()); } addNoteDialog.setPositiveButton(positiveText, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // [END_EXCLUDE] FirebaseUserActions.getInstance().end(getNoteCommentAction()); // [START_EXCLUDE] String noteText = edittext.getText().toString().trim(); ContentValues values = new ContentValues(); values.put(RecipeNoteTable.TEXT_COLUMN, noteText); Uri noteUri = RecipeContentProvider.CONTENT_URI.buildUpon().appendPath("notes") .appendPath(mRecipe.getId()).build(); if (getString(R.string.dialog_add_note).equalsIgnoreCase(positiveText)) { getContentResolver().insert(noteUri, values); } else { getContentResolver().update(noteUri, values, null, null); } Note note = new Note(); note.setText(noteText); mRecipe.setNote(note); ((ToggleButton) findViewById(R.id.addNoteToggle)).setChecked(true); indexNote(); } }); addNoteDialog.setNegativeButton(negativeText, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { FirebaseUserActions.getInstance().end(getNoteCommentAction()); if (getString(R.string.dialog_delete_note).equalsIgnoreCase(negativeText)) { Uri noteUri = RecipeContentProvider.CONTENT_URI.buildUpon().appendPath("notes") .appendPath(mRecipe.getId()).build(); getContentResolver().delete(noteUri, null, null); mRecipe.setNote(null); ((ToggleButton) findViewById(R.id.addNoteToggle)).setChecked(false); // [START remove_note] // Deletes or removes the corresponding notes from index. String noteUrl = mRecipe.getNoteUrl(); FirebaseAppIndex.getInstance().remove(noteUrl); // [END remove_note] } else { dialog.dismiss(); ((ToggleButton) findViewById(R.id.addNoteToggle)).setChecked(mRecipe.getNote () != null); } } }); addNoteDialog.show(); FirebaseUserActions.getInstance().start(getNoteCommentAction()); // [END_EXCLUDE] } private Action getNoteCommentAction() { return new Action.Builder(Action.Builder.COMMENT_ACTION) .setObject(mRecipe.getTitle() + " Note", mRecipe.getNoteUrl()) .setMetadata(new Action.Metadata.Builder().setUpload(false)) .build(); } // [END end_note_view_action] private Action getRecipeViewAction() { return Actions.newView(mRecipe.getTitle(), mRecipe.getRecipeUrl()); } /** * A placeholder fragment containing a simple view. */ public static class RecipeFragment extends Fragment { /** * The fragment argument representing the section number for this * fragment. */ private static final String ARG_SECTION_NUMBER = "section_number"; private Recipe recipe; private ProgressBar progressBar; private ImageView recipeImage; public RecipeFragment() { } /** * Returns a new instance of this fragment for the given section * number. */ public static RecipeFragment newInstance(Recipe recipe, int sectionNumber) { RecipeFragment fragment = new RecipeFragment(); fragment.recipe = recipe; Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_recipe, container, false); this.recipe = ((RecipeActivity) getActivity()).mRecipe; progressBar = (ProgressBar) rootView.findViewById(R.id.loading); recipeImage = (ImageView) rootView.findViewById(R.id.recipe_image); String photoUrl = recipe.getPhoto(); int sectionNumber = this.getArguments().getInt(ARG_SECTION_NUMBER); if (sectionNumber > 1) { Recipe.Step step = recipe.getInstructions().get(sectionNumber - 2); if (step.getPhoto() != null) { photoUrl = step.getPhoto(); } } Picasso.with(rootView.getContext()) .load(photoUrl) .into(recipeImage, new Callback.EmptyCallback() { @Override public void onSuccess() { progressBar.setVisibility(View.GONE); Log.d("Picasso", "Image loaded successfully"); } @Override public void onError() { progressBar.setVisibility(View.GONE); Log.d("Picasso", "Failed to load image"); } }); FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); if (sectionNumber == 1) { Fragment ingredientsFragment = IngredientsFragment.newInstance(recipe, sectionNumber); transaction.replace(R.id.ingredients_fragment, ingredientsFragment).commit(); } else { Fragment instructionFragment = InstructionFragment.newInstance(recipe, sectionNumber); transaction.replace(R.id.instruction_fragment, instructionFragment).commit(); } return rootView; } } /** * A placeholder fragment containing a simple view. */ public static class IngredientsFragment extends Fragment { /** * The fragment argument representing the section number for this * fragment. */ private static final String ARG_SECTION_NUMBER = "section_number"; private Recipe recipe; public IngredientsFragment() { } /** * Returns a new instance of this fragment for the given section * number. */ public static IngredientsFragment newInstance(Recipe recipe, int sectionNumber) { IngredientsFragment fragment = new IngredientsFragment(); fragment.recipe = recipe; Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.ingredients_fragment, container, false); this.recipe = ((RecipeActivity) getActivity()).mRecipe; TableLayout table = (TableLayout) rootView.findViewById(R.id.ingredientsTable); for (Recipe.Ingredient ingredient : recipe.getIngredients()) { TableRow row = (TableRow) inflater.inflate(R.layout.ingredients_row, null); ((TextView) row.findViewById(R.id.attrib_name)).setText(ingredient.getAmount()); ((TextView) row.findViewById(R.id.attrib_value)).setText(ingredient .getDescription()); table.addView(row); } return rootView; } } /** * A placeholder fragment containing a simple view. */ public static class InstructionFragment extends Fragment { /** * The fragment argument representing the section number for this * fragment. */ private static final String ARG_SECTION_NUMBER = "section_number"; private Recipe recipe; public InstructionFragment() { } /** * Returns a new instance of this fragment for the given section * number. */ public static InstructionFragment newInstance(Recipe recipe, int sectionNumber) { InstructionFragment fragment = new InstructionFragment(); fragment.recipe = recipe; Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.instructions_fragment, container, false); int sectionNumber = this.getArguments().getInt(ARG_SECTION_NUMBER); this.recipe = ((RecipeActivity) getActivity()).mRecipe; TextView instructionTitle = (TextView) rootView.findViewById(R.id.instructionTitle); instructionTitle.setText("Step " + Integer.toString(sectionNumber - 1)); // Section 1 is ingredients list, Section 2 is start of instructions // So the first instruction [instructions.get(0)] will be on Section 2. Second // instruction [instructions.get(1)] on will be on Section 3... Recipe.Step step = recipe.getInstructions().get(sectionNumber - 2); TextView instructionBody = (TextView) rootView.findViewById(R.id.instructionBody); instructionBody.setText(step.getDescription()); return rootView; } } /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to * one of the sections/tabs/pages. */ public class SectionsPagerAdapter extends FragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { // getItem is called to instantiate the fragment for the given page. // Return a PlaceholderFragment (defined as a static inner class below). return RecipeFragment.newInstance(mRecipe, position + 1); } @Override public int getCount() { if (mRecipe != null) { return mRecipe.getInstructions().size() + 1; } else { return 0; } } @Override public CharSequence getPageTitle(int position) { return null; } } }