uno.weichen.abnd10_inventoryapp.DetailActivity.java Source code

Java tutorial

Introduction

Here is the source code for uno.weichen.abnd10_inventoryapp.DetailActivity.java

Source

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * 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 uno.weichen.abnd10_inventoryapp;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.NavUtils;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.FileDescriptor;
import java.io.IOException;

import uno.weichen.abnd10_inventoryapp.data.ProductContract.ProductEntry;

import static uno.weichen.abnd10_inventoryapp.data.ProductProvider.LOG_TAG;

/**
 * Allows user to create a new product or edit an existing one.
 */
public class DetailActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    /**
     * EditText field to enter the product's name
     */
    private EditText mNameEditText;

    /**
     * EditText field to enter the product's price
     */
    private EditText mPriceEditText;

    /**
     * TextView field to enter the product's quantity
     */
    private TextView mQuantityTextView;

    /**
     * TextView field to enter the product's sold quantity
     */
    private TextView mSoldQuantityTextView;

    /**
     * TextView field to enter the product's restock quantity
     */
    private TextView mRestockQuantityTextView;

    /**
     * EditText field to enter the product's supplier email
     */
    private EditText mContactEditText;

    /**
     * Button for Sale
     */
    private Button mSaleButton;

    /**
     * Button for Restock
     */
    private Button mRestockButton;

    /**
     * Button for Order
     */

    private Button mOrderButton;
    /**
     * Button for Delete
     */
    private Button mDeleteButton;

    /**
     * CONSTANT for PRODUCTION_ITEM_LOADER
     */
    private static final int PRODUCTION_ITEM_LOADER = 1;

    /**
     * Content URI for the existing product (null if it's a new product)
     */
    private Uri mCurrentProductUri;

    /**
     * Boolean to check if user has change the default value
     */
    private boolean mProductHasChanged = false;

    /**
     * OnTouchListener that listens for any user touches on a View, implying that they are modifying
     * the view, and we change the mProductHasChanged boolean to true.
     */
    private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            mProductHasChanged = true;
            return false;
        }
    };

    /**
     * CONSTANT for PICK_IMAGE_REQUEST
     */
    private static final int PICK_IMAGE_REQUEST = 2;

    /**
     * Content URI for picking photo from gallery
     */
    private Uri mPhotoUri;

    /**
     * ImageView field to enter the product's photo
     */
    private ImageView mProductPhotoImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);

        // Find all relevant views that we will need to read user input from
        mNameEditText = (EditText) findViewById(R.id.edit_product_name);
        mPriceEditText = (EditText) findViewById(R.id.edit_product_price);
        mQuantityTextView = (TextView) findViewById(R.id.textview_product_quantity);
        mContactEditText = (EditText) findViewById(R.id.edit_product_contact);
        mSoldQuantityTextView = (TextView) findViewById(R.id.textview_product_sold_quantity);
        mRestockQuantityTextView = (TextView) findViewById(R.id.textview_product_restock_quantity);
        mSaleButton = (Button) findViewById(R.id.button_sale_product);
        mRestockButton = (Button) findViewById(R.id.button_restock_product);
        mDeleteButton = (Button) findViewById(R.id.button_delete_product);
        mOrderButton = (Button) findViewById(R.id.button_order);

        mProductPhotoImageView = (ImageView) findViewById(R.id.image);

        // Use getIntent and getData to get the associated URI
        mCurrentProductUri = getIntent().getData();
        //Set title of DetailActivity on which situation we have
        if (mCurrentProductUri == null) {
            setTitle(getString(R.string.editor_activity_title_new_product));
            invalidateOptionsMenu();
            mDeleteButton.setVisibility(View.GONE);
        } else {
            setTitle(getString(R.string.editor_activity_title_edit_product));
            /**
             * Initializes the CursorLoader. The URL_LOADER value is eventually passed
             * to onCreateLoader().
             */
            getSupportLoaderManager().initLoader(PRODUCTION_ITEM_LOADER, null, this);
        }

        // Setup onTouchListener for editors components.
        mNameEditText.setOnTouchListener(mTouchListener);
        mPriceEditText.setOnTouchListener(mTouchListener);
        mContactEditText.setOnTouchListener(mTouchListener);

        //Onclick Listener for Sale
        mSaleButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String soldQuantityString = mSoldQuantityTextView.getText().toString().trim();
                String quantityString = mQuantityTextView.getText().toString().trim();
                int soldQuantityInt = Integer.parseInt(soldQuantityString);
                int quantityInt = Integer.parseInt(quantityString);
                if (quantityInt > 0) {
                    //if button clicked and quantity > 0
                    soldQuantityInt++;
                    quantityInt--;
                    soldQuantityString = Integer.toString(soldQuantityInt);
                    quantityString = Integer.toString(quantityInt);
                    mQuantityTextView.setText(quantityString);
                    mSoldQuantityTextView.setText(soldQuantityString);
                    //inform the user the product detail has been changed.
                    mProductHasChanged = true;
                }
            }
        });

        //Onclick Listener for Restock
        mRestockButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String restockQuantityString = mRestockQuantityTextView.getText().toString().trim();
                String quantityString = mQuantityTextView.getText().toString().trim();
                int restockQuantityInt = Integer.parseInt(restockQuantityString);
                int quantityInt = Integer.parseInt(quantityString);
                //if button clicked
                restockQuantityInt++;
                quantityInt++;
                restockQuantityString = Integer.toString(restockQuantityInt);
                quantityString = Integer.toString(quantityInt);
                mQuantityTextView.setText(quantityString);
                mRestockQuantityTextView.setText(restockQuantityString);
                //inform the user the product detail has been changed.
                mProductHasChanged = true;
            }
        });

        //Onclick Listener for Delete
        mDeleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showDeleteConfirmationDialog();
            }
        });

        //Onclick Listener for Order
        mOrderButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String subject = "Restocking Request";
                String body = "Dear Supplier, \n" + "We need more " + mNameEditText.getText().toString().trim()
                        + ".\n" + "Please contact us! \n";

                Intent intent = new Intent(Intent.ACTION_SENDTO);
                intent.setData(Uri.parse("mailto:")); // only email apps should handle this
                intent.putExtra(Intent.EXTRA_EMAIL, new String[] { mContactEditText.getText().toString().trim() });
                intent.putExtra(Intent.EXTRA_SUBJECT, subject);
                intent.putExtra(Intent.EXTRA_TEXT, body);
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu options from the res/menu/menu_detail.xml file.
        // This adds menu items to the app bar.
        getMenuInflater().inflate(R.menu.menu_detail, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // User clicked on a menu option in the app bar overflow menu
        switch (item.getItemId()) {
        // Respond to a click on the "Save" menu option
        case R.id.action_save:
            //Save product to the database
            saveProduct();

            return true;
        // Respond to a click on the "Delete" menu option
        case R.id.action_delete:
            // Show delete Confirmation dialog
            showDeleteConfirmationDialog();

            return true;
        // Respond to a click on the "Up" arrow button in the app bar
        case android.R.id.home:
            // If the product hasn't changed, continue with navigating up to parent activity
            // which is the {@link CatalogActivity}.
            if (!mProductHasChanged) {
                NavUtils.navigateUpFromSameTask(DetailActivity.this);
                return true;
            }

            // Otherwise if there are unsaved changes, setup a dialog to warn the user.
            // Create a click listener to handle the user confirming that
            // changes should be discarded.
            DialogInterface.OnClickListener discardButtonClickListener = new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    // User clicked "Discard" button, navigate to parent activity.
                    NavUtils.navigateUpFromSameTask(DetailActivity.this);
                }
            };

            // Show a dialog that notifies the user they have unsaved changes
            showUnsavedChangesDialog(discardButtonClickListener);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void saveProduct() {
        String nameString = mNameEditText.getText().toString().trim();
        String priceString = mPriceEditText.getText().toString().trim();
        String soldQuantityString = mSoldQuantityTextView.getText().toString().trim();
        String restockQuantityString = mRestockQuantityTextView.getText().toString().trim();
        String contactString = mContactEditText.getText().toString().trim();
        String photoString = "";

        // if there is a picture associated with the app.
        if (mPhotoUri != null) {
            photoString = mPhotoUri.toString();
        }

        // Nothing was input, we directly return and won't save the data into database.
        if (mCurrentProductUri == null && TextUtils.isEmpty(nameString) && TextUtils.isEmpty(priceString)
                && TextUtils.isEmpty(contactString)) {
            return;
        }

        //Check name, contact, price cannot be empty;
        if (TextUtils.isEmpty(nameString) || TextUtils.isEmpty(priceString) || TextUtils.isEmpty(contactString)) {
            Toast.makeText(this, getString(R.string.editor_required_field_empty), Toast.LENGTH_SHORT).show();
            return;
        }

        //Check email address pattern
        if (!isEmailValid(contactString)) {
            Toast.makeText(this, getString(R.string.editor_required_valid_email), Toast.LENGTH_SHORT).show();
            return;
        }

        int priceInt = Integer.parseInt(priceString);
        int soldQuantityInt = Integer.parseInt(soldQuantityString);
        int restockQuantityInt = Integer.parseInt(restockQuantityString);

        // Create a new map of values, where column names are the keys
        ContentValues values = new ContentValues();
        values.put(ProductEntry.COLUMN_PRODUCT_NAME, nameString);
        values.put(ProductEntry.COLUMN_PRODUCT_PRICE, priceInt);
        values.put(ProductEntry.COLUMN_PRODUCT_PHOTO, photoString);
        values.put(ProductEntry.COLUMN_PRODUCT_SOLD_QUANTITY, soldQuantityInt);
        values.put(ProductEntry.COLUMN_PRODUCT_RESTOCK_QUANTITY, restockQuantityInt);
        values.put(ProductEntry.COLUMN_PRODUCT_CONTACT, contactString);

        Uri mProductUri;
        //if new product mode
        if (mCurrentProductUri == null) {

            mProductUri = getContentResolver().insert(ProductEntry.CONTENT_URI, values);
            // Show a toast message depending on whether or not the insertion was successful
            if (mProductUri != null) {
                // If the new content URI is null, then there was an error with insertion.
                Toast.makeText(this, getString(R.string.editor_insert_product_successful), Toast.LENGTH_SHORT)
                        .show();
            } else {
                // Otherwise, the insertion was successful and we can display a toast.
                Toast.makeText(this, getString(R.string.editor_insert_product_failed), Toast.LENGTH_SHORT).show();
            }
        } else {
            int rowAffected = getContentResolver().update(mCurrentProductUri, values, null, null);
            if (rowAffected == 0) {
                Toast.makeText(this, getString(R.string.editor_update_product_failed), Toast.LENGTH_SHORT).show();
            } else {
                // Otherwise, the insertion was successful and we can display a toast.
                Toast.makeText(this, getString(R.string.editor_update_product_successful), Toast.LENGTH_SHORT)
                        .show();
            }
        }

        finish();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        String[] projection = { ProductEntry._ID, ProductEntry.COLUMN_PRODUCT_NAME,
                ProductEntry.COLUMN_PRODUCT_SOLD_QUANTITY, ProductEntry.COLUMN_PRODUCT_RESTOCK_QUANTITY,
                ProductEntry.COLUMN_PRODUCT_PRICE, ProductEntry.COLUMN_PRODUCT_PHOTO,
                ProductEntry.COLUMN_PRODUCT_CONTACT };

        return new CursorLoader(this, mCurrentProductUri, projection, null, null, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        if (cursor.moveToFirst()) {
            String nameString = cursor.getString(cursor.getColumnIndex(ProductEntry.COLUMN_PRODUCT_NAME));
            String priceString = cursor.getString(cursor.getColumnIndex(ProductEntry.COLUMN_PRODUCT_PRICE));
            String soldQuantityString = cursor
                    .getString(cursor.getColumnIndex(ProductEntry.COLUMN_PRODUCT_SOLD_QUANTITY));
            String restockQuantityString = cursor
                    .getString(cursor.getColumnIndex(ProductEntry.COLUMN_PRODUCT_RESTOCK_QUANTITY));
            String contactString = cursor.getString(cursor.getColumnIndex(ProductEntry.COLUMN_PRODUCT_CONTACT));
            String photoUriString;

            // Calculate current quantity
            int productQuantityInt = Integer.parseInt(restockQuantityString) - Integer.parseInt(soldQuantityString);
            String productQuantityString = Integer.toString(productQuantityInt);

            mNameEditText.setText(nameString);
            mPriceEditText.setText(priceString);
            mQuantityTextView.setText(productQuantityString);
            mSoldQuantityTextView.setText(soldQuantityString);
            mRestockQuantityTextView.setText(restockQuantityString);
            mContactEditText.setText(contactString);
            photoUriString = cursor.getString(cursor.getColumnIndex(ProductEntry.COLUMN_PRODUCT_PHOTO));
            if (!TextUtils.isEmpty(photoUriString)) {
                mProductPhotoImageView.setImageURI(Uri.parse(photoUriString));
            }
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mNameEditText.setText("");
        mPriceEditText.setText("");
        mQuantityTextView.setText("0");
        mRestockQuantityTextView.setText("0");
        mSoldQuantityTextView.setText("0");
        mContactEditText.setText("");
        mProductPhotoImageView.setImageResource(0);
    }

    private void showUnsavedChangesDialog(DialogInterface.OnClickListener discardButtonClickListener) {
        // Create an AlertDialog.Builder and set the message, and click listeners
        // for the positive and negative buttons on the dialog.
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(R.string.unsaved_changes_dialog_msg);
        builder.setPositiveButton(R.string.discard, discardButtonClickListener);
        builder.setNegativeButton(R.string.keep_editing, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // User clicked the "Keep editing" button, so dismiss the dialog
                // and continue editing the product.
                if (dialog != null) {
                    dialog.dismiss();
                }
            }
        });

        // Create and show the AlertDialog
        AlertDialog alertDialog = builder.create();
        alertDialog.show();
    }

    @Override
    public void onBackPressed() {
        // If the product hasn't changed, continue with handling back button press
        if (!mProductHasChanged) {
            super.onBackPressed();
            return;
        }

        // Otherwise if there are unsaved changes, setup a dialog to warn the user.
        // Create a click listener to handle the user confirming that changes should be discarded.
        DialogInterface.OnClickListener discardButtonClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                // User clicked "Discard" button, close the current activity.
                finish();
            }
        };

        // Show dialog that there are unsaved changes
        showUnsavedChangesDialog(discardButtonClickListener);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        // If this is a new product, hide the "Delete" menu item.
        if (mCurrentProductUri == null) {
            MenuItem menuItem = menu.findItem(R.id.action_delete);
            menuItem.setVisible(false);
        }
        return true;
    }

    private void showDeleteConfirmationDialog() {
        // Create an AlertDialog.Builder and set the message, and click listeners
        // for the positive and negative buttons on the dialog.
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(R.string.delete_dialog_msg);
        builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // User clicked the "Delete" button, so delete the product.
                deleteProduct();
            }
        });
        builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // User clicked the "Cancel" button, so dismiss the dialog
                // and continue editing the product.
                if (dialog != null) {
                    dialog.dismiss();
                }
            }
        });

        // Create and show the AlertDialog
        AlertDialog alertDialog = builder.create();
        alertDialog.show();
    }

    /**
     * Perform the deletion of the product in the database.
     */
    private void deleteProduct() {
        int mRowsDeleted = 0;
        if (mCurrentProductUri != null) {

            mRowsDeleted = getContentResolver().delete(mCurrentProductUri, // the user dictionary content URI
                    null, // the column to select on
                    null // the value to compare to
            );
        }
        if (mRowsDeleted == 0) {
            // If no rows were deleted, then there was an error with the delete.
            Toast.makeText(this, getString(R.string.editor_delete_product_failed), Toast.LENGTH_SHORT).show();
        } else {
            // Otherwise, the delete was successful and we can display a toast.
            Toast.makeText(this, getString(R.string.editor_delete_product_successful), Toast.LENGTH_SHORT).show();
        }
        finish();
    }

    private Bitmap getBitmapFromUri(Uri uri) {
        ParcelFileDescriptor parcelFileDescriptor = null;
        try {
            parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");
            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
            Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
            parcelFileDescriptor.close();
            return image;
        } catch (Exception e) {
            Log.e(LOG_TAG, getString(R.string.get_bitmap_from_uri_exception), e);
            return null;
        } finally {
            try {
                if (parcelFileDescriptor != null) {
                    parcelFileDescriptor.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(LOG_TAG, getString(R.string.get_bitmap_from_uri_error));
            }
        }
    }

    public void openImageSelector(View view) {
        Intent intent;

        if (Build.VERSION.SDK_INT < 19) {
            intent = new Intent(Intent.ACTION_GET_CONTENT);
        } else {
            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
        }

        intent.setType("image/*");
        startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
        // The ACTION_OPEN_DOCUMENT intent was sent with the request code READ_REQUEST_CODE.
        // If the request code seen here doesn't match, it's the response to some other intent,
        // and the below code shouldn't run at all.

        if (requestCode == PICK_IMAGE_REQUEST && resultCode == Activity.RESULT_OK) {
            // The document selected by the user won't be returned in the intent.
            // Instead, a URI to that document will be contained in the return intent
            // provided to this method as a parameter.  Pull that uri using "resultData.getData()"

            if (resultData != null) {
                mPhotoUri = resultData.getData();
                Log.i(LOG_TAG, "Uri: " + mPhotoUri.toString());
                mProductPhotoImageView.setImageBitmap(getBitmapFromUri(mPhotoUri));
            }
        }
    }

    /**
     * Validate Email address
     */
    public static boolean isEmailValid(String email) {
        return !(email == null || TextUtils.isEmpty(email))
                && android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches();
    }
}