net.potterpcs.recipebook.RecipeViewer.java Source code

Java tutorial

Introduction

Here is the source code for net.potterpcs.recipebook.RecipeViewer.java

Source

/*
 *    Copyright 2012 Michael Potter
 *
 * 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.
 * 
 */

/*
 * RecipeViewer.java - Shows a single recipe, with all its parts: metadata,
 * ingredients, directions, and images. 
 */

package net.potterpcs.recipebook;

import java.util.HashMap;

import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ShareCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;
import android.text.format.DateUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RatingBar;
import android.widget.TextView;

public class RecipeViewer extends FragmentActivity {
    // Tag for logging
    static final String TAG = "RecipeViewer";

    // Projections for the cursor adapter
    static final String[] INGREDIENTS_FIELDS = { RecipeData.IT_NAME };
    static final String[] DIRECTIONS_FIELDS = { RecipeData.DT_STEP, RecipeData.DT_SEQUENCE, RecipeData.DT_PHOTO };
    static final int[] INGREDIENTS_IDS = { android.R.id.text1 };
    static final int[] DIRECTIONS_IDS = { R.id.direction, R.id.number, R.id.directionphoto };

    // The helpfile name
    private static final String HELP_FILENAME = "viewer";

    private ShareCompat.IntentBuilder shareIntent;
    private SharedPreferences preferences;
    private RecipeData data;
    private long rid;
    private boolean photoPref;
    private boolean dirPhotoPref;
    RecipeBook app;
    TextView rvname;
    TextView rvcreator;
    TextView rvserving;
    TextView rvtime;
    RatingBar rvrating;
    GridView lvingredients;
    ListView lvdirections;
    FrameLayout rvphoto;
    String photoUri;

    // Cursor adapters for the directions and ingredients lists
    private SimpleCursorAdapter directionsAdapter;
    private SimpleCursorAdapter ingredientsAdapter;

    // Cursors for use by the ListView adapters
    private Cursor directionsCursor;
    private Cursor ingredientsCursor;

    @SuppressWarnings("deprecation")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recipeviewer);

        app = (RecipeBook) getApplication();
        data = app.getData();
        preferences = PreferenceManager.getDefaultSharedPreferences(this);
        photoPref = preferences.getBoolean(getResources().getString(R.string.prefphotokey), true);
        dirPhotoPref = preferences.getBoolean(getResources().getString(R.string.prefdirphotokey), true);

        // Fill in the UI
        rvname = (TextView) findViewById(R.id.rvname);
        rvcreator = (TextView) findViewById(R.id.rvcreator);
        rvserving = (TextView) findViewById(R.id.rvserving);
        rvtime = (TextView) findViewById(R.id.rvtime);
        rvrating = (RatingBar) findViewById(R.id.rvrating);

        lvingredients = (GridView) findViewById(R.id.ingredients);
        lvdirections = (ListView) findViewById(R.id.directions);

        rid = Long.parseLong(getIntent().getData().getLastPathSegment());

        RecipeData.Recipe r = data.getSingleRecipeObject(rid);

        rvname.setText(r.name);
        rvcreator.setText(r.creator);
        rvserving.setText(Integer.toString(r.serving));
        rvtime.setText(DateUtils.formatElapsedTime(r.time));
        rvrating.setRating(r.rating);

        photoUri = r.photo;
        if (photoPref && photoUri != null && !photoUri.equals("")) {
            rvphoto = (FrameLayout) findViewById(R.id.photofragment);
            ImageView iv = new ImageView(this);
            setOrDownloadImage(iv, photoUri);
            iv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                    PhotoDialog pd = PhotoDialog.newInstance(photoUri);
                    pd.show(ft, "dialog");
                }
            });
            rvphoto.addView(iv);
        }

        directionsAdapter = new SimpleCursorAdapter(this, R.layout.recipedirectionrow, null, DIRECTIONS_FIELDS,
                DIRECTIONS_IDS, 0);
        directionsAdapter.setViewBinder(new DirectionViewBinder(this));

        lvdirections.setAdapter(directionsAdapter);
        lvdirections.setDividerHeight(0);

        ingredientsAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null,
                INGREDIENTS_FIELDS, INGREDIENTS_IDS, 0);
        lvingredients.setAdapter(ingredientsAdapter);

        // Set up sharing intent
        shareIntent = ShareCompat.IntentBuilder.from(this).setSubject(r.name).setText(r.toText(this))
                .setType("text/plain");
    }

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

        // This is essentially a single-threaded emulation of what
        // the Loader framework already does. Hopefully it will work
        // until I rewrite the app to use Loaders everywhere.
        directionsCursor = data.getRecipeDirections(rid);
        directionsAdapter.swapCursor(directionsCursor);

        ingredientsCursor = data.getRecipeIngredients(rid);
        ingredientsAdapter.swapCursor(ingredientsCursor);

        if (dirPhotoPref) {
            lvdirections.setOnItemClickListener(new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    Cursor c = data.getRecipeDirections(rid);
                    c.moveToPosition(position);
                    String uri = c.getString(c.getColumnIndex(RecipeData.DT_PHOTO));
                    if (uri != null && !uri.equals("")) {
                        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                        PhotoDialog pd = PhotoDialog.newInstance(uri);
                        pd.show(ft, "dialog");
                    }
                    c.close();
                }
            });
        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        // Clean up after ourselves. The Cursors are created in onResume,
        // so they have to be closed in onPause, really for symmetry reasons.
        // But first, we swap the Cursors out of their adapters, so that
        // *those* aren't trying to access a closed cursor, which leads to
        // all kinds of crashes and bugs.
        directionsAdapter.swapCursor(null);
        ingredientsAdapter.swapCursor(null);
        directionsCursor.close();
        ingredientsCursor.close();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.viewermenu, menu);

        // Set up the action bar if we have one
        MenuItemCompat.setShowAsAction(menu.findItem(R.id.viewertimer),
                MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
        MenuItemCompat.setShowAsAction(menu.findItem(R.id.viewershare), MenuItem.SHOW_AS_ACTION_ALWAYS);
        MenuItemCompat.setShowAsAction(menu.findItem(R.id.viewerhelp), MenuItem.SHOW_AS_ACTION_IF_ROOM);

        // Set up Share menu item
        ShareCompat.configureMenuItem(menu.findItem(R.id.viewershare), shareIntent);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.viewertimer:
            onTimerSelected(item);
            return true;
        case R.id.viewershare:
            onShare(item);
            return true;
        case R.id.viewerhelp:
            onHelpItemSelected(item);
            return true;
        case android.R.id.home:
            Intent intent = new Intent(this, RecipeBookActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    public void onHelpItemSelected(MenuItem item) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        DialogFragment helpFragment = HelpDialog.newInstance(HELP_FILENAME);
        helpFragment.show(ft, "help");
    }

    public void onTimerSelected(MenuItem item) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        Fragment timerFragment = new TimerFragment();
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.replace(R.id.timerfragment, timerFragment);
        transaction.addToBackStack(null);
        transaction.commit();
    }

    public void onShare(MenuItem item) {
        startActivity(shareIntent.createChooserIntent());
    }

    private void setOrDownloadImage(ImageView iv, String photoUri) {
        RecipeBook.setImageViewBitmapDecoded(this, iv, photoUri);
    }

    public class DirectionViewBinder implements ViewBinder {
        // This class helps set up the directions. The main point of the class
        // is to prevent repeated loads, downloads, and scales of direction
        // photos. This lowers data usage and memory usage.
        FragmentActivity parent;
        HashMap<View, Boolean> boundViews;

        public DirectionViewBinder(FragmentActivity a) {
            parent = a;
            boundViews = new HashMap<View, Boolean>();
        }

        @Override
        public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
            boolean isBound = false;
            if (boundViews.get(view) != null) {
                isBound = boundViews.get(view);
            }

            switch (view.getId()) {
            case R.id.direction:
                if (!isBound && view instanceof TextView) {
                    ((TextView) view).setText(cursor.getString(columnIndex));
                    boundViews.put(view, true);
                }
                break;
            case R.id.number:
                if (!isBound && view instanceof TextView) {
                    ((TextView) view).setText(Integer.toString(cursor.getInt(columnIndex)));
                    boundViews.put(view, true);
                }
            case R.id.directionphoto:
                if (!isBound && view instanceof ImageView) {
                    if (dirPhotoPref) {
                        RecipeBook.setImageViewBitmapDecoded(parent, (ImageView) view,
                                cursor.getString(columnIndex), 160);
                    }
                    boundViews.put(view, true);
                }
                break;
            default:
                return false;
            }
            return true;
        }
    }
}