Java tutorial
/* * 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; } } }