· 7 years ago · Nov 07, 2018, 03:36 AM
1package com.example.bryce.bookstoreapp2;
2
3import android.app.AlertDialog;
4import android.app.LoaderManager;
5import android.content.ContentValues;
6import android.content.CursorLoader;
7import android.content.DialogInterface;
8import android.content.Intent;
9import android.content.Loader;
10import android.database.Cursor;
11import android.net.Uri;
12import android.os.Bundle;
13import android.support.v4.app.NavUtils;
14import android.support.v7.app.AppCompatActivity;
15import android.view.Menu;
16import android.view.MenuItem;
17import android.view.MotionEvent;
18import android.view.View;
19import android.widget.EditText;
20import android.widget.Toast;
21
22import com.example.bryce.bookstoreapp2.data.BookStoreContract;
23
24/**
25 * Allows user to create a new product or edit an existing one.
26 */
27public class EditorActivity extends AppCompatActivity implements
28 LoaderManager.LoaderCallbacks<Cursor> {
29 /**
30 * Identifier for the product data loader
31 */
32 private static final int EXISTING_PRODUCT_LOADER = 0;
33
34 /**
35 * Content URI for the existing product (null if it's a new product)
36 */
37 private Uri mCurrentProductUri;
38
39 private EditText mName;
40 private EditText mPrice;
41 private EditText mQuantity;
42 private EditText mSupplier_Name;
43 private EditText mSupplier_Phone_Number;
44
45
46 /**
47 * Boolean flag that keeps track of whether edited (true) or not (false)
48 */
49 private boolean mProductHasChanged = false;
50
51 /**
52 * OnTouchListener that listens for any user touches on a View, implying that they are modifying
53 * the view, and we change the boolean to true.
54 */
55 private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
56 @Override
57 public boolean onTouch(View view, MotionEvent motionEvent) {
58 mProductHasChanged = true;
59 return false;
60 }
61
62
63
64 };
65
66
67 @Override
68 protected void onCreate(Bundle savedInstanceState) {
69 super.onCreate(savedInstanceState);
70 setContentView(R.layout.activity_editor);
71
72 Intent intent = getIntent();
73 mCurrentProductUri = intent.getData();
74
75 if (mCurrentProductUri == null) {
76 setTitle(R.string.add_a_product);
77 } else {
78 setTitle(R.string.edit_a_product);
79
80
81 // Initialize a loader to read the data from the database
82 // and display the current values in the editor
83 getLoaderManager().initLoader(EXISTING_PRODUCT_LOADER, null, this);
84 }
85
86
87 // Find all relevant views that we will need to read user input from
88 mName = findViewById(R.id.edit_name);
89 mPrice = findViewById(R.id.edit_price);
90 mQuantity = findViewById(R.id.edit_quantity);
91 mSupplier_Name = findViewById(R.id.supplier_name);
92 mSupplier_Phone_Number = findViewById(R.id.supplier_phone_number);
93
94
95 mName.setOnTouchListener(mTouchListener);
96 mPrice.setOnTouchListener(mTouchListener);
97 mQuantity.setOnTouchListener(mTouchListener);
98 mSupplier_Name.setOnTouchListener(mTouchListener);
99 mSupplier_Phone_Number.setOnTouchListener(mTouchListener);
100
101 }
102
103
104 /**
105 * Get user input from editor and save product into database.
106 */
107 private void saveProduct() {
108 // Read from input fields
109 String nameString = mName.getText().toString().trim();
110 String priceString = mPrice.getText().toString().trim();
111 String quantityString = mQuantity.getText().toString().trim();
112 String supplierNameString = mSupplier_Name.getText().toString().trim();
113 String supplierPhoneNumber = mSupplier_Phone_Number.getText().toString().trim();
114
115
116 // Create a ContentValues object where column names are the keys,
117 // and product attributes from the editor are the values.
118 ContentValues values = new ContentValues();
119 values.put(BookStoreContract.Products.COLUMN_NAME, nameString);
120 values.put(BookStoreContract.Products.COLUMN_PRICE, priceString);
121 values.put(BookStoreContract.Products.COLUMN_QUANTITY, quantityString);
122 values.put(BookStoreContract.Products.COLUMN_SUPPLIER_NAME, supplierNameString);
123 values.put(BookStoreContract.Products.COLUMN_SUPPLIER_PHONE_NUMBER, supplierPhoneNumber);
124 // Determine if this is a new or existing by checking if mCurrentProductUri is null or not
125 if (mCurrentProductUri == null) {
126 // This is NEW , so insert a into the provider,
127 // returning the content URI for the new product
128 Uri newUri = getContentResolver().insert(BookStoreContract.Products.CONTENT_URI, values);
129
130 // Show a toast message depending on whether or not the insertion was successful
131 if (newUri == null) {
132 // If the new content URI is null, then there was an error with insertion.
133 Toast.makeText(this, getString(R.string.insert_product_failed),
134 Toast.LENGTH_SHORT).show();
135 } else {
136 // Otherwise, the insertion was successful and we can display a toast.
137 Toast.makeText(this, getString(R.string.insert_product_successful),
138 Toast.LENGTH_SHORT).show();
139 }
140 } else {
141
142 // Otherwise this is an EXISTING, so update with content URI: mCurrentProductUri
143 // and pass in the new ContentValues. Pass in null for the selection and selection args
144 // because it will already identify the correct row in the database that
145 // we want to modify.
146 int rowsAffected = getContentResolver().update(mCurrentProductUri, values, null, null);
147 // Show a toast message depending on whether or not the update was successful.
148 if (rowsAffected == 0) {
149 // If no rows were affected, then there was an error with the update.
150 Toast.makeText(this, getString(R.string.editor_update_product_failed),
151 Toast.LENGTH_SHORT).show();
152 } else {
153 // Otherwise, the update was successful and we can display a toast.
154 Toast.makeText(this, getString(R.string.editor_update_product_successful),
155 Toast.LENGTH_SHORT).show();
156 }
157 }
158 }
159
160 @Override
161 public boolean onCreateOptionsMenu(Menu menu) {
162 // Inflate the menu options from the res/menu/menu_editor.xml file.
163 // This adds menu items to the app bar.
164 getMenuInflater().inflate(R.menu.editor, menu);
165 return true;
166 }
167
168 /**
169 * This method is called after invalidateOptionsMenu(), so that the
170 * menu can be updated (some menu items can be hidden or made visible).
171 */
172 @Override
173 public boolean onPrepareOptionsMenu(Menu menu) {
174 super.onPrepareOptionsMenu(menu);
175 // If this is new, hide the "Delete" menu item.
176 if (mCurrentProductUri == null) {
177 MenuItem menuItem = menu.findItem(R.id.action_delete);
178 menuItem.setVisible(false);
179 }
180 return true;
181 }
182
183 @Override
184 public boolean onOptionsItemSelected(MenuItem item) {
185 // User clicked on a menu option in the app bar overflow menu
186 switch (item.getItemId()) {
187 // Respond to a click on the "Save" menu option
188 case R.id.action_save:
189 // Save product to database
190 saveProduct();
191 // Exit activity
192 finish();
193 return true;
194 // Respond to a click on the "Delete" menu option
195 case R.id.action_delete:
196 // Pop up confirmation dialog for deletion
197 showDeleteConfirmationDialog();
198 return true;
199 // Respond to a click on the "Up" arrow button in the app bar
200 case android.R.id.home:
201 // If the product hasn't changed, continue with navigating up to parent activity
202 // which is the {@link CatalogActivity}.
203 if (!mProductHasChanged) {
204 NavUtils.navigateUpFromSameTask(EditorActivity.this);
205 return true;
206 }
207
208 // Otherwise if there are unsaved changes, setup a dialog to warn the user.
209 // Create a click listener to handle the user confirming that
210 // changes should be discarded.
211 DialogInterface.OnClickListener discardButtonClickListener =
212 new DialogInterface.OnClickListener() {
213 @Override
214 public void onClick(DialogInterface dialogInterface, int i) {
215 // User clicked "Discard" button, navigate to parent activity.
216 NavUtils.navigateUpFromSameTask(EditorActivity.this);
217 }
218 };
219
220
221 return true;
222 }
223 return super.onOptionsItemSelected(item);
224 }
225
226
227 /**
228 * Prompt the user to confirm that they want to delete.
229 */
230 private void showDeleteConfirmationDialog() {
231 // Create an AlertDialog.Builder and set the message, and click listeners
232 // for the postivie and negative buttons on the dialog.
233 AlertDialog.Builder builder = new AlertDialog.Builder(this);
234 builder.setMessage(R.string.delete_dialog_msg);
235 builder.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
236 public void onClick(DialogInterface dialog, int id) {
237 // User clicked the "Delete" button, so delete the product.
238 deleteProduct();
239 }
240 });
241 builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
242 public void onClick(DialogInterface dialog, int id) {
243 // User clicked the "Cancel" button, so dismiss the dialog
244 // and continue editing the product.
245 if (dialog != null) {
246 dialog.dismiss();
247 }
248 }
249 });
250 // Create and show the AlertDialog
251 AlertDialog alertDialog = builder.create();
252 alertDialog.show();
253 }
254
255
256 /**
257 * Perform the deletion.
258 */
259 private void deleteProduct() {
260 // Only perform the delete if the data exists.
261 if (mCurrentProductUri == null) {
262 // Call the ContentResolver to delete the product at the given content URI.
263 // Pass in null for the selection and selection args because the mCurrentProductUri
264 // content URI already identifies what we want.
265 int rowsDeleted = getContentResolver().delete(mCurrentProductUri, null, null);
266 // Show a toast message depending on whether or not the delete was successful.
267 if (rowsDeleted == 0) {
268 // If no rows were deleted, then there was an error with the delete.
269 Toast.makeText(this, getString(R.string.editor_delete_product_failed),
270 Toast.LENGTH_SHORT).show();
271 } else {
272 // Otherwise, the delete was successful and we can display a toast.
273 Toast.makeText(this, getString(R.string.editor_delete_product_successful),
274 Toast.LENGTH_SHORT).show();
275 }
276 }
277 // Close the activity
278 finish();
279 }
280
281 @Override
282 public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
283 // Since the editor shows all attributes, define a projection that contains
284 // all columns from the table
285 String[] projection = {
286 BookStoreContract.Products._ID,
287 BookStoreContract.Products.COLUMN_NAME,
288 BookStoreContract.Products.COLUMN_PRICE,
289 BookStoreContract.Products.COLUMN_QUANTITY,
290 BookStoreContract.Products.COLUMN_SUPPLIER_NAME,
291 BookStoreContract.Products.COLUMN_SUPPLIER_PHONE_NUMBER};
292
293 // This loader will execute the ContentProvider's query method on a background thread
294 return new CursorLoader(this, // Parent activity context
295 mCurrentProductUri, // Query the content URI
296 projection, // Columns to include in the resulting Cursor
297 null, // No selection clause
298 null, // No selection arguments
299 null); // Default sort order
300 }
301
302 @Override
303 public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
304 // Bail early if the cursor is null or there is less than 1 row in the cursor
305 if (cursor == null || cursor.getCount() < 1) {
306 return;
307 }
308
309 // Proceed with moving to the first row of the cursor and reading data from it
310 // (This should be the only row in the cursor)
311 if (cursor.moveToFirst()) {
312 // Find the columns of attributes that we're interested in
313 int nameColumnIndex = cursor.getColumnIndex(BookStoreContract.Products.COLUMN_NAME);
314 int priceColumnIndex = cursor.getColumnIndex(BookStoreContract.Products.COLUMN_PRICE);
315 int quantityColumnIndex = cursor.getColumnIndex(BookStoreContract.Products.COLUMN_QUANTITY);
316 int supplierNameColumnIndex = cursor.getColumnIndex(BookStoreContract.Products.COLUMN_SUPPLIER_NAME);
317 int supplierPhoneNumberColumnIndex = cursor.getColumnIndex(BookStoreContract.Products.COLUMN_SUPPLIER_PHONE_NUMBER);
318
319 // Extract out the value from the Cursor for the given column index
320 String name = cursor.getString(nameColumnIndex);
321 String price = cursor.getString(priceColumnIndex);
322 int quantity = cursor.getInt(quantityColumnIndex);
323 int supplierName = cursor.getInt(supplierNameColumnIndex);
324 int supplierPhoneNumber = cursor.getInt(supplierPhoneNumberColumnIndex);
325
326 // Update the views on the screen with the values from the database
327 mName.setText(name);
328 mPrice.setText(price);
329 mQuantity.setText(quantity);
330 mSupplier_Name.setText(supplierName);
331 mSupplier_Phone_Number.setText(supplierPhoneNumber);
332 }
333 }
334
335 @Override
336 public void onLoaderReset(Loader<Cursor> loader) {
337 // If the loader is invalidated, clear out all the data from the input fields.
338 mName.setText("");
339 mPrice.setText("");
340 mQuantity.setText("");
341 mSupplier_Name.setText("");
342 mSupplier_Phone_Number.setText("");
343 }
344}