From 7143972f263c7c53f49a4b6c0fc0e2df85ec7e7c Mon Sep 17 00:00:00 2001 From: Lyla Date: Fri, 23 Oct 2015 19:53:06 -0700 Subject: [PATCH 01/78] 2.04 Edit Shopping List Name - Solution --- .../EditListNameDialogFragment.java | 56 +++++++++++++++---- .../shoppinglistplusplus/utils/Constants.java | 2 + .../shoppinglistplusplus/utils/Utils.java | 1 - 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java index 7326e46..4c44deb 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java @@ -3,14 +3,20 @@ import android.app.Dialog; import android.os.Bundle; +import com.firebase.client.Firebase; +import com.firebase.client.ServerValue; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; +import com.udacity.firebase.shoppinglistplusplus.utils.Constants; + +import java.util.HashMap; /** * Lets user edit the list name for all copies of the current list */ public class EditListNameDialogFragment extends EditListDialogFragment { private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName(); + String mListName; /** * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created @@ -18,8 +24,7 @@ public class EditListNameDialogFragment extends EditListDialogFragment { public static EditListNameDialogFragment newInstance(ShoppingList shoppingList) { EditListNameDialogFragment editListNameDialogFragment = new EditListNameDialogFragment(); Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_list); - // TODO add any values you need here from the shopping list to make this change. - // Once you put a value in the bundle, it available to you in onCreate + bundle.putString(Constants.KEY_LIST_NAME, shoppingList.getListName()); editListNameDialogFragment.setArguments(bundle); return editListNameDialogFragment; } @@ -30,9 +35,7 @@ public static EditListNameDialogFragment newInstance(ShoppingList shoppingList) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // TODO Extract any arguments you put in the bundle when the newInstance method - // created the dialog. You can store these in an instance variable so that they - // are available to you. + mListName = getArguments().getString(Constants.KEY_LIST_NAME); } @@ -43,10 +46,11 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { * superclass method that creates the dialog **/ Dialog dialog = super.createDialogHelper(R.string.positive_button_edit_item); - // TODO You can use the helper method in the superclass I made (EditListDialogFragment) - // called helpSetDefaultValueEditText. This will allow you to set what text the - // user sees when the dialog opens. - + /** + * {@link EditListDialogFragment#helpSetDefaultValueEditText(String)} is a superclass + * method that sets the default text of the TextView + */ + helpSetDefaultValueEditText(mListName); return dialog; } @@ -54,9 +58,37 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { * Changes the list name in all copies of the current list */ protected void doListEdit() { - // TODO Do the actual edit operation here. - // Remember, you need to update the timestampLastChanged for - // the shopping list. + final String inputListName = mEditTextForList.getText().toString(); + + /** + * Set input text to be the current list name if it is not empty + */ + if (!inputListName.equals("")) { + + if (mListName != null) { + + /** + * If editText input is not equal to the previous name + */ + if (!inputListName.equals(mListName)) { + Firebase shoppingListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LIST); + + /* Make a Hashmap for the specific properties you are changing */ + HashMap updatedProperties = new HashMap(); + updatedProperties.put(Constants.FIREBASE_PROPERTY_LIST_NAME, inputListName); + + /* Add the timestamp for last changed to the updatedProperties Hashmap */ + HashMap changedTimestampMap = new HashMap<>(); + changedTimestampMap.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); + + /* Add the updated timestamp */ + updatedProperties.put(Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED, changedTimestampMap); + + /* Do the update */ + shoppingListRef.updateChildren(updatedProperties); + } + } + } } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index 65d1d94..91429e1 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -17,6 +17,7 @@ public final class Constants { * Constants for Firebase object properties */ public static final String FIREBASE_PROPERTY_LIST_NAME = "listName"; + public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED = "timestampLastChanged"; public static final String FIREBASE_PROPERTY_TIMESTAMP = "timestamp"; @@ -30,6 +31,7 @@ public final class Constants { /** * Constants for bundles, extras and shared preferences keys */ + public static final String KEY_LIST_NAME = "LIST_NAME"; public static final String KEY_LAYOUT_RESOURCE = "LAYOUT_RESOURCE"; diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java index a98ce1b..cff5a08 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java @@ -21,5 +21,4 @@ public class Utils { public Utils(Context con) { mContext = con; } - } From 09ab6cd184c48ca19d165b9b21382cddbbfcbb7f Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 22 Oct 2015 00:11:37 -0700 Subject: [PATCH 02/78] 2.05 Add a New Shopping List - Quiz --- .../firebase/shoppinglistplusplus/model/ShoppingList.java | 2 ++ .../ui/activeLists/AddListDialogFragment.java | 4 ++++ .../firebase/shoppinglistplusplus/utils/Constants.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java index e132b6a..b37b45f 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java @@ -14,6 +14,8 @@ public class ShoppingList { private String listName; private String owner; private HashMap timestampLastChanged; + // TODO Now that we have more than one list, let's add an unchanging timestamp for when + // the list was created. /** * Required public constructor diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java index eb5a2b3..97a4231 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java @@ -92,6 +92,10 @@ public void onClick(DialogInterface dialog, int id) { * Add new active list */ public void addShoppingList() { + // TODO This is where you add a shopping list. + // You'll need to change the code in this method around to properly + // add a shopping list so that you may have more than one. + // Get the reference to the root node in Firebase Firebase ref = new Firebase(Constants.FIREBASE_URL); // Get the string that the user entered into the EditText and make an object with it diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index 91429e1..8376abc 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -12,6 +12,9 @@ public final class Constants { * where active lists are stored (ie "activeLists") */ public static final String FIREBASE_LOCATION_ACTIVE_LIST = "activeList"; + // TODO instead of using the location activeList, let's store the new lists under + // activeLists (plural). This will also keep the app from crashing since we're + // only updating the write portion. /** * Constants for Firebase object properties From 53f2d697be983da43267757ee79a54a2b1df1bc7 Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 22 Oct 2015 00:14:41 -0700 Subject: [PATCH 03/78] 2.06 Add a New Shopping List - Solution --- .../model/ShoppingList.java | 23 ++++++---- .../ui/activeLists/AddListDialogFragment.java | 46 ++++++++++++++----- .../shoppinglistplusplus/utils/Constants.java | 5 +- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java index b37b45f..cbcaca4 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java @@ -14,8 +14,8 @@ public class ShoppingList { private String listName; private String owner; private HashMap timestampLastChanged; - // TODO Now that we have more than one list, let's add an unchanging timestamp for when - // the list was created. + private HashMap timestampCreated; + /** * Required public constructor @@ -30,15 +30,14 @@ public ShoppingList() { * * @param listName * @param owner - * */ - public ShoppingList(String listName, String owner) { + public ShoppingList(String listName, String owner, HashMap timestampCreated) { this.listName = listName; this.owner = owner; - HashMap timestampLastChangedObj = new HashMap(); - timestampLastChangedObj.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); - this.timestampLastChanged = timestampLastChangedObj; - + this.timestampCreated = timestampCreated; + HashMap timestampNowObject = new HashMap(); + timestampNowObject.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); + this.timestampLastChanged = timestampNowObject; } public String getListName() { @@ -53,6 +52,9 @@ public HashMap getTimestampLastChanged() { return timestampLastChanged; } + public HashMap getTimestampCreated() { + return timestampCreated; + } @JsonIgnore public long getTimestampLastChangedLong() { @@ -60,5 +62,10 @@ public long getTimestampLastChangedLong() { return (long) timestampLastChanged.get(Constants.FIREBASE_PROPERTY_TIMESTAMP); } + @JsonIgnore + public long getTimestampCreatedLong() { + return (long) timestampLastChanged.get(Constants.FIREBASE_PROPERTY_TIMESTAMP); + } + } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java index 97a4231..2f88fdd 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java @@ -14,10 +14,13 @@ import android.widget.TextView; import com.firebase.client.Firebase; +import com.firebase.client.ServerValue; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; import com.udacity.firebase.shoppinglistplusplus.utils.Constants; +import java.util.HashMap; + /** * Adds a new shopping list */ @@ -92,22 +95,43 @@ public void onClick(DialogInterface dialog, int id) { * Add new active list */ public void addShoppingList() { - // TODO This is where you add a shopping list. - // You'll need to change the code in this method around to properly - // add a shopping list so that you may have more than one. - - // Get the reference to the root node in Firebase - Firebase ref = new Firebase(Constants.FIREBASE_URL); // Get the string that the user entered into the EditText and make an object with it // We'll use "Anonymous Owner" for the owner because we don't have user accounts yet String userEnteredName = mEditTextListName.getText().toString(); String owner = "Anonymous Owner"; - ShoppingList currentList = new ShoppingList(userEnteredName, owner); - // Go to the "activeList" child node of the root node. - // This will create the node for you if it doesn't already exist. - // Then using the setValue menu it will serialize the ShoppingList POJO - ref.child(Constants.FIREBASE_LOCATION_ACTIVE_LIST).setValue(currentList); + /** + * If EditText input is not empty + */ + if (!userEnteredName.equals("")) { + + /** + * Create Firebase references + */ + Firebase listsRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS); + Firebase newListRef = listsRef.push(); + + /* Save listsRef.push() to maintain same random Id */ + final String listId = newListRef.getKey(); + + /** + * Set raw version of date to the ServerValue.TIMESTAMP value and save into + * timestampCreatedMap + */ + HashMap timestampCreated = new HashMap<>(); + timestampCreated.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); + + /* Build the shopping list */ + ShoppingList newShoppingList = new ShoppingList(userEnteredName, owner, + timestampCreated); + + /* Add the shopping list */ + newListRef.setValue(newShoppingList); + + /* Close the dialog fragment */ + AddListDialogFragment.this.getDialog().cancel(); + } + } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index 8376abc..d512016 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -12,9 +12,7 @@ public final class Constants { * where active lists are stored (ie "activeLists") */ public static final String FIREBASE_LOCATION_ACTIVE_LIST = "activeList"; - // TODO instead of using the location activeList, let's store the new lists under - // activeLists (plural). This will also keep the app from crashing since we're - // only updating the write portion. + public static final String FIREBASE_LOCATION_ACTIVE_LISTS = "activeLists"; /** * Constants for Firebase object properties @@ -29,6 +27,7 @@ public final class Constants { */ public static final String FIREBASE_URL = BuildConfig.UNIQUE_FIREBASE_ROOT_URL; public static final String FIREBASE_URL_ACTIVE_LIST = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LIST; + public static final String FIREBASE_URL_ACTIVE_LISTS = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LISTS; /** From e9b610a538f863cc9d87a6f754dd49b16fd5ff60 Mon Sep 17 00:00:00 2001 From: Lyla Date: Wed, 9 Dec 2015 16:59:51 -0800 Subject: [PATCH 04/78] 2.07 The Firebase List Adapter Repo - Quiz --- app/build.gradle | 2 ++ .../ui/activeLists/ActiveListAdapter.java | 35 +++++++++++++++++++ .../ui/activeLists/ShoppingListsFragment.java | 9 +++++ .../res/layout/fragment_shopping_lists.xml | 5 ++- 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java diff --git a/app/build.gradle b/app/build.gradle index 057d9df..b2dca08 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,6 +45,8 @@ dependencies { compile 'com.firebase:firebase-client-android:2.4.0' + // TODO Add the library for FirebaseUI here + /* For Google Play Services */ compile 'com.google.android.gms:play-services-safetynet:8.3.0' compile 'com.google.android.gms:play-services-auth:8.3.0' diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java new file mode 100644 index 0000000..1179af4 --- /dev/null +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java @@ -0,0 +1,35 @@ +package com.udacity.firebase.shoppinglistplusplus.ui.activeLists; + +import android.app.Activity; +import android.view.View; + +import com.firebase.client.Query; +import com.firebase.ui.FirebaseListAdapter; +import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; + +/** + * Populates the list_view_active_lists inside ShoppingListsFragment + */ +public class ActiveListAdapter extends FirebaseListAdapter { + + /** + * Public constructor that initializes private instance variables when adapter is created + */ + public ActiveListAdapter(Activity activity, Class modelClass, int modelLayout, + Query ref) { + super(activity, modelClass, modelLayout, ref); + this.mActivity = activity; + } + + /** + * Protected method that populates the view attached to the adapter (list_view_active_lists) + * with items inflated from single_active_list.xml + * populateView also handles data changes and updates the listView accordingly + */ + @Override + protected void populateView(View view, ShoppingList list) { + // TODO This is where you need to populate the single_active_list layout with + // the data in the current shopping list. It should be similar to what you + // were displaying in ShoppingListsFragment + } +} diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java index 346316c..bbf7db2 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java @@ -74,15 +74,21 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View rootView = inflater.inflate(R.layout.fragment_shopping_lists, container, false); initializeScreen(rootView); + // TODO Here is where you need to set up the adapter. You'll also need to delete + // your old code that referenced the single "activeList", instead, you should save + // data to "activeLists" (plural). + /** * Create Firebase references */ + // TODO This is referring to an old location Firebase refListName = new Firebase(Constants.FIREBASE_URL).child(Constants.FIREBASE_LOCATION_ACTIVE_LIST); /** * Add ValueEventListeners to Firebase references * to control get data and control behavior and visibility of elements */ + // TODO This is referring to an old location refListName.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { @@ -123,6 +129,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } }); + // TODO This OnClickListener is old code. mTextViewListName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -138,6 +145,7 @@ public void onClick(View v) { @Override public void onDestroy() { super.onDestroy(); + // TODO Don't forget to clean up your adapter! } @@ -147,6 +155,7 @@ public void onDestroy() { private void initializeScreen(View rootView) { mListView = (ListView) rootView.findViewById(R.id.list_view_active_lists); // Get the TextViews in the single_active_list layout for list name, edit time and owner + // TODO Clean up the code here. Do you need all these TextViews? mTextViewListName = (TextView) rootView.findViewById(R.id.text_view_list_name); mTextViewListOwner = (TextView) rootView.findViewById(R.id.text_view_created_by_user); mTextViewEditTime = (TextView) rootView.findViewById(R.id.text_view_edit_time); diff --git a/app/src/main/res/layout/fragment_shopping_lists.xml b/app/src/main/res/layout/fragment_shopping_lists.xml index d955a9e..25507cf 100755 --- a/app/src/main/res/layout/fragment_shopping_lists.xml +++ b/app/src/main/res/layout/fragment_shopping_lists.xml @@ -11,9 +11,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - - + + Date: Fri, 22 Jan 2016 14:23:21 -0800 Subject: [PATCH 05/78] 2.08 The Firebase List Adapter Repo - Solution --- app/build.gradle | 4 +- .../ui/activeLists/ActiveListAdapter.java | 16 +++- .../ui/activeLists/ShoppingListsFragment.java | 84 ++++--------------- .../res/layout/fragment_shopping_lists.xml | 2 - 4 files changed, 31 insertions(+), 75 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b2dca08..b44b00b 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,9 +43,11 @@ dependencies { compile 'com.android.support:support-v4:23.0.1' compile 'com.android.support:cardview-v7:23.0.1' + /* Firebase SDK */ compile 'com.firebase:firebase-client-android:2.4.0' - // TODO Add the library for FirebaseUI here + /* Firebase UI */ + compile 'com.firebaseui:firebase-ui:0.2.2' /* For Google Play Services */ compile 'com.google.android.gms:play-services-safetynet:8.3.0' diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java index 1179af4..d3736fe 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java @@ -2,9 +2,11 @@ import android.app.Activity; import android.view.View; +import android.widget.TextView; import com.firebase.client.Query; import com.firebase.ui.FirebaseListAdapter; +import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; /** @@ -28,8 +30,16 @@ public ActiveListAdapter(Activity activity, Class modelClass, int */ @Override protected void populateView(View view, ShoppingList list) { - // TODO This is where you need to populate the single_active_list layout with - // the data in the current shopping list. It should be similar to what you - // were displaying in ShoppingListsFragment + + /** + * Grab the needed Textivews and strings + */ + TextView textViewListName = (TextView) view.findViewById(R.id.text_view_list_name); + TextView textViewCreatedByUser = (TextView) view.findViewById(R.id.text_view_created_by_user); + + + /* Set the list name and owner */ + textViewListName.setText(list.getListName()); + textViewCreatedByUser.setText(list.getOwner()); } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java index bbf7db2..61ef684 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java @@ -1,7 +1,6 @@ package com.udacity.firebase.shoppinglistplusplus.ui.activeLists; -import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -9,19 +8,11 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; -import android.widget.TextView; -import com.firebase.client.DataSnapshot; import com.firebase.client.Firebase; -import com.firebase.client.FirebaseError; -import com.firebase.client.ValueEventListener; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; -import com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails.ActiveListDetailsActivity; import com.udacity.firebase.shoppinglistplusplus.utils.Constants; -import com.udacity.firebase.shoppinglistplusplus.utils.Utils; - -import java.util.Date; /** @@ -31,8 +22,8 @@ */ public class ShoppingListsFragment extends Fragment { private ListView mListView; - private TextView mTextViewListName, mTextViewListOwner; - private TextView mTextViewEditTime; + private ActiveListAdapter mActiveListAdapter; + public ShoppingListsFragment() { /* Required empty public constructor */ @@ -48,12 +39,6 @@ public static ShoppingListsFragment newInstance() { fragment.setArguments(args); return fragment; } - - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - } /** * Initialize instance variables with data from bundle @@ -74,50 +59,23 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, View rootView = inflater.inflate(R.layout.fragment_shopping_lists, container, false); initializeScreen(rootView); - // TODO Here is where you need to set up the adapter. You'll also need to delete - // your old code that referenced the single "activeList", instead, you should save - // data to "activeLists" (plural). - /** * Create Firebase references */ - // TODO This is referring to an old location - Firebase refListName = new Firebase(Constants.FIREBASE_URL).child(Constants.FIREBASE_LOCATION_ACTIVE_LIST); + Firebase activeListsRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS); /** * Add ValueEventListeners to Firebase references * to control get data and control behavior and visibility of elements */ - // TODO This is referring to an old location - refListName.addValueEventListener(new ValueEventListener() { - @Override - public void onDataChange(DataSnapshot dataSnapshot) { - // You can use getValue to deserialize the data at dataSnapshot - // into a ShoppingList. - ShoppingList shoppingList = dataSnapshot.getValue(ShoppingList.class); - - // If there was no data at the location we added the listener, then - // shoppingList will be null. - if (shoppingList != null) { - // If there was data, take the TextViews and set the appropriate values. - mTextViewListName.setText(shoppingList.getListName()); - mTextViewListOwner.setText(shoppingList.getOwner()); - if (shoppingList.getTimestampLastChanged() != null) { - mTextViewEditTime.setText( - Utils.SIMPLE_DATE_FORMAT.format( - new Date(shoppingList.getTimestampLastChangedLong()))); - } else { - mTextViewEditTime.setText(""); - } - - } - } + mActiveListAdapter = new ActiveListAdapter(getActivity(), ShoppingList.class, + R.layout.single_active_list, activeListsRef); - @Override - public void onCancelled(FirebaseError firebaseError) { + /** + * Set the adapter to the mListView + */ + mListView.setAdapter(mActiveListAdapter); - } - }); /** * Set interactive bits, such as click events and adapters @@ -129,35 +87,23 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } }); - // TODO This OnClickListener is old code. - mTextViewListName.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - /* Starts an active showing the details for the selected list */ - Intent intent = new Intent(getActivity(), ActiveListDetailsActivity.class); - startActivity(intent); - } - }); - return rootView; } - + + /** + * Cleanup the adapter when activity is destroyed. + */ @Override public void onDestroy() { super.onDestroy(); - // TODO Don't forget to clean up your adapter! + mActiveListAdapter.cleanup(); } /** - * Link layout elements from XML + * Link list view from XML */ private void initializeScreen(View rootView) { mListView = (ListView) rootView.findViewById(R.id.list_view_active_lists); - // Get the TextViews in the single_active_list layout for list name, edit time and owner - // TODO Clean up the code here. Do you need all these TextViews? - mTextViewListName = (TextView) rootView.findViewById(R.id.text_view_list_name); - mTextViewListOwner = (TextView) rootView.findViewById(R.id.text_view_created_by_user); - mTextViewEditTime = (TextView) rootView.findViewById(R.id.text_view_edit_time); } } diff --git a/app/src/main/res/layout/fragment_shopping_lists.xml b/app/src/main/res/layout/fragment_shopping_lists.xml index 25507cf..d58c09f 100755 --- a/app/src/main/res/layout/fragment_shopping_lists.xml +++ b/app/src/main/res/layout/fragment_shopping_lists.xml @@ -11,8 +11,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - Date: Wed, 4 Nov 2015 19:59:04 -0800 Subject: [PATCH 06/78] 2.09 Send Over The Push ID Spec - Quiz --- .../activeListDetails/ActiveListDetailsActivity.java | 7 +++++++ .../activeListDetails/EditListNameDialogFragment.java | 10 ++++++++++ .../ui/activeLists/ActiveListAdapter.java | 2 +- .../ui/activeLists/ShoppingListsFragment.java | 7 ++++++- .../firebase/shoppinglistplusplus/utils/Constants.java | 5 ++++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index d08f1ca..9a3610d 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -34,9 +34,16 @@ protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_active_list_details); + // TODO You should grab a list id, passed from the ShoppingListFragment somewhere + // in onCreate. You'll need it. + /** * Create Firebase references */ + // TODO Given the updated location of the Shopping Lists + // (all lists are now nested within push ids) + // you'll need to update this and other Firebase references to point to the + // actual location of the list you're interested in. mActiveListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LIST); diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java index 4c44deb..72341bf 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java @@ -23,6 +23,13 @@ public class EditListNameDialogFragment extends EditListDialogFragment { */ public static EditListNameDialogFragment newInstance(ShoppingList shoppingList) { EditListNameDialogFragment editListNameDialogFragment = new EditListNameDialogFragment(); + // TODO If you look below, you'll see that you're going to need the push id + // of the list. Instead of putting it in the bundle here, I'd suggest adding + // it to the newInstanceHelper method in EditListDialogFragment. This method + // creates a bundle with information that all dialogs extending EditListDialogFragment + // need. + // Note, this refactor will require updating a few different classes that also extend + // from EditListDialogFragment, as well as places where they are constructed. Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_list); bundle.putString(Constants.KEY_LIST_NAME, shoppingList.getListName()); editListNameDialogFragment.setArguments(bundle); @@ -36,6 +43,7 @@ public static EditListNameDialogFragment newInstance(ShoppingList shoppingList) public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mListName = getArguments().getString(Constants.KEY_LIST_NAME); + // TODO Don't forget to set an instance variable to your push id argument. } @@ -60,6 +68,8 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { protected void doListEdit() { final String inputListName = mEditTextForList.getText().toString(); + // TODO you'll need to update this code to properly change the correct list name, + // now that there is more than one list. /** * Set input text to be the current list name if it is not empty */ diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java index d3736fe..1172027 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java @@ -36,7 +36,7 @@ protected void populateView(View view, ShoppingList list) { */ TextView textViewListName = (TextView) view.findViewById(R.id.text_view_list_name); TextView textViewCreatedByUser = (TextView) view.findViewById(R.id.text_view_created_by_user); - + /* Set the list name and owner */ textViewListName.setText(list.getListName()); diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java index 61ef684..8a208c8 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java @@ -80,13 +80,18 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, /** * Set interactive bits, such as click events and adapters */ + // TODO Here is where you will launch ActiveListDetailsActivity for the particular + // shopping list the user clicks on. Make sure to pass along the push id of + // the chosen list in an intent extra. + // Then in ActiveListDetailsActivity you can use this push id to help + // display the list. mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { } }); - + return rootView; } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index d512016..446e22b 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -11,6 +11,9 @@ public final class Constants { * Constants related to locations in Firebase, such as the name of the node * where active lists are stored (ie "activeLists") */ + // TODO You should no longer be using the location for a single list ("activeList"). + // It can safely be deleted since now the lists are stored withing a list of + // shopping lists. public static final String FIREBASE_LOCATION_ACTIVE_LIST = "activeList"; public static final String FIREBASE_LOCATION_ACTIVE_LISTS = "activeLists"; @@ -21,11 +24,11 @@ public final class Constants { public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED = "timestampLastChanged"; public static final String FIREBASE_PROPERTY_TIMESTAMP = "timestamp"; - /** * Constants for Firebase URL */ public static final String FIREBASE_URL = BuildConfig.UNIQUE_FIREBASE_ROOT_URL; + // TODO Same as above; this URL for a single list is no longer needed. public static final String FIREBASE_URL_ACTIVE_LIST = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LIST; public static final String FIREBASE_URL_ACTIVE_LISTS = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LISTS; From 11709f968699e59c3d0f11898953be7efe7be886 Mon Sep 17 00:00:00 2001 From: Lyla Date: Fri, 6 Nov 2015 15:03:11 -0800 Subject: [PATCH 07/78] 2.10 Send Over The Push ID - Solution --- .../ActiveListDetailsActivity.java | 24 +++++++------ .../AddListItemDialogFragment.java | 4 +-- .../EditListDialogFragment.java | 5 ++- .../EditListItemNameDialogFragment.java | 5 +-- .../EditListNameDialogFragment.java | 22 ++++-------- .../ui/activeLists/ShoppingListsFragment.java | 35 +++++++++++-------- .../shoppinglistplusplus/utils/Constants.java | 8 ++--- 7 files changed, 53 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index 9a3610d..575ace4 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -1,6 +1,7 @@ package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails; import android.app.DialogFragment; +import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.util.Log; @@ -26,6 +27,7 @@ public class ActiveListDetailsActivity extends BaseActivity { private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName(); private Firebase mActiveListRef; private ListView mListView; + private String mListId; private ShoppingList mShoppingList; @@ -34,17 +36,19 @@ protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_active_list_details); - // TODO You should grab a list id, passed from the ShoppingListFragment somewhere - // in onCreate. You'll need it. + /* Get the push ID from the extra passed by ShoppingListFragment */ + Intent intent = this.getIntent(); + mListId = intent.getStringExtra(Constants.KEY_LIST_ID); + if (mListId == null) { + /* No point in continuing without a valid ID. */ + finish(); + return; + } /** * Create Firebase references */ - // TODO Given the updated location of the Shopping Lists - // (all lists are now nested within push ids) - // you'll need to update this and other Firebase references to point to the - // actual location of the list you're interested in. - mActiveListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LIST); + mActiveListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS).child(mListId); /** @@ -226,7 +230,7 @@ public void removeList() { */ public void showAddListItemDialog(View view) { /* Create an instance of the dialog fragment and show it */ - DialogFragment dialog = AddListItemDialogFragment.newInstance(mShoppingList); + DialogFragment dialog = AddListItemDialogFragment.newInstance(mShoppingList, mListId); dialog.show(getFragmentManager(), "AddListItemDialogFragment"); } @@ -235,7 +239,7 @@ public void showAddListItemDialog(View view) { */ public void showEditListNameDialog() { /* Create an instance of the dialog fragment and show it */ - DialogFragment dialog = EditListNameDialogFragment.newInstance(mShoppingList); + DialogFragment dialog = EditListNameDialogFragment.newInstance(mShoppingList, mListId); dialog.show(this.getFragmentManager(), "EditListNameDialogFragment"); } @@ -244,7 +248,7 @@ public void showEditListNameDialog() { */ public void showEditListItemNameDialog() { /* Create an instance of the dialog fragment and show it */ - DialogFragment dialog = EditListItemNameDialogFragment.newInstance(mShoppingList); + DialogFragment dialog = EditListItemNameDialogFragment.newInstance(mShoppingList, mListId); dialog.show(this.getFragmentManager(), "EditListItemNameDialogFragment"); } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java index 8f0bc9a..299dd1d 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java @@ -14,10 +14,10 @@ public class AddListItemDialogFragment extends EditListDialogFragment { /** * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created */ - public static AddListItemDialogFragment newInstance(ShoppingList shoppingList) { + public static AddListItemDialogFragment newInstance(ShoppingList shoppingList, String listId) { AddListItemDialogFragment addListItemDialogFragment = new AddListItemDialogFragment(); - Bundle bundle = newInstanceHelper(shoppingList, R.layout.dialog_add_item); + Bundle bundle = newInstanceHelper(shoppingList, R.layout.dialog_add_item, listId); addListItemDialogFragment.setArguments(bundle); return addListItemDialogFragment; diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java index c68b105..040c297 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java @@ -21,6 +21,7 @@ * Base class for {@link DialogFragment}s involved with editing a shopping list. */ public abstract class EditListDialogFragment extends DialogFragment { + String mListId; EditText mEditTextForList; int mResource; @@ -32,8 +33,9 @@ public abstract class EditListDialogFragment extends DialogFragment { * @param resource * @return */ - protected static Bundle newInstanceHelper(ShoppingList shoppingList, int resource) { + protected static Bundle newInstanceHelper(ShoppingList shoppingList, int resource, String listId) { Bundle bundle = new Bundle(); + bundle.putString(Constants.KEY_LIST_ID, listId); bundle.putInt(Constants.KEY_LAYOUT_RESOURCE, resource); return bundle; } @@ -44,6 +46,7 @@ protected static Bundle newInstanceHelper(ShoppingList shoppingList, int resourc @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mListId = getArguments().getString(Constants.KEY_LIST_ID); mResource = getArguments().getInt(Constants.KEY_LAYOUT_RESOURCE); } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java index 150a7db..e2acdc3 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java @@ -14,10 +14,11 @@ public class EditListItemNameDialogFragment extends EditListDialogFragment { /** * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created */ - public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingList) { + public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingList, String listId) { EditListItemNameDialogFragment editListItemNameDialogFragment = new EditListItemNameDialogFragment(); - Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_item); + Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, + R.layout.dialog_edit_item, listId); editListItemNameDialogFragment.setArguments(bundle); return editListItemNameDialogFragment; diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java index 72341bf..64478f7 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java @@ -21,16 +21,10 @@ public class EditListNameDialogFragment extends EditListDialogFragment { /** * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created */ - public static EditListNameDialogFragment newInstance(ShoppingList shoppingList) { + public static EditListNameDialogFragment newInstance(ShoppingList shoppingList, String listId) { EditListNameDialogFragment editListNameDialogFragment = new EditListNameDialogFragment(); - // TODO If you look below, you'll see that you're going to need the push id - // of the list. Instead of putting it in the bundle here, I'd suggest adding - // it to the newInstanceHelper method in EditListDialogFragment. This method - // creates a bundle with information that all dialogs extending EditListDialogFragment - // need. - // Note, this refactor will require updating a few different classes that also extend - // from EditListDialogFragment, as well as places where they are constructed. - Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_list); + Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, + R.layout.dialog_edit_list, listId); bundle.putString(Constants.KEY_LIST_NAME, shoppingList.getListName()); editListNameDialogFragment.setArguments(bundle); return editListNameDialogFragment; @@ -43,7 +37,6 @@ public static EditListNameDialogFragment newInstance(ShoppingList shoppingList) public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mListName = getArguments().getString(Constants.KEY_LIST_NAME); - // TODO Don't forget to set an instance variable to your push id argument. } @@ -67,21 +60,20 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { */ protected void doListEdit() { final String inputListName = mEditTextForList.getText().toString(); - - // TODO you'll need to update this code to properly change the correct list name, - // now that there is more than one list. + /** * Set input text to be the current list name if it is not empty */ if (!inputListName.equals("")) { - if (mListName != null) { + if (mListName != null && mListId != null) { /** * If editText input is not equal to the previous name */ if (!inputListName.equals(mListName)) { - Firebase shoppingListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LIST); + Firebase shoppingListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS). + child(mListId); /* Make a Hashmap for the specific properties you are changing */ HashMap updatedProperties = new HashMap(); diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java index 8a208c8..5bbe153 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java @@ -1,6 +1,7 @@ package com.udacity.firebase.shoppinglistplusplus.ui.activeLists; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -12,6 +13,7 @@ import com.firebase.client.Firebase; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; +import com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails.ActiveListDetailsActivity; import com.udacity.firebase.shoppinglistplusplus.utils.Constants; @@ -21,9 +23,8 @@ * create an instance of this fragment. */ public class ShoppingListsFragment extends Fragment { - private ListView mListView; private ActiveListAdapter mActiveListAdapter; - + private ListView mListView; public ShoppingListsFragment() { /* Required empty public constructor */ @@ -53,6 +54,7 @@ public void onCreate(Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + /** * Initialize UI elements */ @@ -65,36 +67,42 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Firebase activeListsRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS); /** - * Add ValueEventListeners to Firebase references - * to control get data and control behavior and visibility of elements + * Create the adapter, giving it the activity, model class, layout for each row in + * the list and finally, a reference to the Firebase location with the list data */ mActiveListAdapter = new ActiveListAdapter(getActivity(), ShoppingList.class, R.layout.single_active_list, activeListsRef); + /** * Set the adapter to the mListView */ mListView.setAdapter(mActiveListAdapter); - /** * Set interactive bits, such as click events and adapters */ - // TODO Here is where you will launch ActiveListDetailsActivity for the particular - // shopping list the user clicks on. Make sure to pass along the push id of - // the chosen list in an intent extra. - // Then in ActiveListDetailsActivity you can use this push id to help - // display the list. mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - + ShoppingList selectedList = mActiveListAdapter.getItem(position); + if (selectedList != null) { + Intent intent = new Intent(getActivity(), ActiveListDetailsActivity.class); + /* Get the list ID using the adapter's get ref method to get the Firebase + * ref and then grab the key. + */ + String listId = mActiveListAdapter.getRef(position).getKey(); + intent.putExtra(Constants.KEY_LIST_ID, listId); + /* Starts an active showing the details for the selected list */ + startActivity(intent); + } } }); - + + return rootView; } - + /** * Cleanup the adapter when activity is destroyed. */ @@ -104,7 +112,6 @@ public void onDestroy() { mActiveListAdapter.cleanup(); } - /** * Link list view from XML */ diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index 446e22b..7817d57 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -11,12 +11,9 @@ public final class Constants { * Constants related to locations in Firebase, such as the name of the node * where active lists are stored (ie "activeLists") */ - // TODO You should no longer be using the location for a single list ("activeList"). - // It can safely be deleted since now the lists are stored withing a list of - // shopping lists. - public static final String FIREBASE_LOCATION_ACTIVE_LIST = "activeList"; public static final String FIREBASE_LOCATION_ACTIVE_LISTS = "activeLists"; + /** * Constants for Firebase object properties */ @@ -28,8 +25,6 @@ public final class Constants { * Constants for Firebase URL */ public static final String FIREBASE_URL = BuildConfig.UNIQUE_FIREBASE_ROOT_URL; - // TODO Same as above; this URL for a single list is no longer needed. - public static final String FIREBASE_URL_ACTIVE_LIST = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LIST; public static final String FIREBASE_URL_ACTIVE_LISTS = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LISTS; @@ -38,6 +33,7 @@ public final class Constants { */ public static final String KEY_LIST_NAME = "LIST_NAME"; public static final String KEY_LAYOUT_RESOURCE = "LAYOUT_RESOURCE"; + public static final String KEY_LIST_ID = "LIST_ID"; } From 341c89d103d6e25ef6cf8d676e3a44619fd24fde Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 22 Oct 2015 00:22:08 -0700 Subject: [PATCH 08/78] 2.11 Removing A Shopping List - Quiz --- .../ui/activeListDetails/RemoveListDialogFragment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java index 2d42e29..3444ea4 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java @@ -57,6 +57,8 @@ public void onClick(DialogInterface dialog, int which) { } private void removeList() { + // TODO Here is the method that is called when the user selects yes, they want to + // remove a list. } From 1a1f47e9eceab7370540b873fb82642e81645fd4 Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 22 Oct 2015 00:25:13 -0700 Subject: [PATCH 09/78] 2.12 Removing A Shopping List - Solution --- .../ActiveListDetailsActivity.java | 2 +- .../RemoveListDialogFragment.java | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index 575ace4..87a104e 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -221,7 +221,7 @@ public void addMeal(View view) { */ public void removeList() { /* Create an instance of the dialog fragment and show it */ - DialogFragment dialog = RemoveListDialogFragment.newInstance(mShoppingList); + DialogFragment dialog = RemoveListDialogFragment.newInstance(mShoppingList, mListId); dialog.show(getFragmentManager(), "RemoveListDialogFragment"); } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java index 3444ea4..55edf5b 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java @@ -6,21 +6,25 @@ import android.os.Bundle; import android.support.v7.app.AlertDialog; +import com.firebase.client.Firebase; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; +import com.udacity.firebase.shoppinglistplusplus.utils.Constants; /** * Lets the user remove active shopping list */ public class RemoveListDialogFragment extends DialogFragment { + String mListId; final static String LOG_TAG = RemoveListDialogFragment.class.getSimpleName(); /** * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created */ - public static RemoveListDialogFragment newInstance(ShoppingList shoppingList) { + public static RemoveListDialogFragment newInstance(ShoppingList shoppingList, String listId) { RemoveListDialogFragment removeListDialogFragment = new RemoveListDialogFragment(); Bundle bundle = new Bundle(); + bundle.putString(Constants.KEY_LIST_ID, listId); removeListDialogFragment.setArguments(bundle); return removeListDialogFragment; } @@ -31,6 +35,7 @@ public static RemoveListDialogFragment newInstance(ShoppingList shoppingList) { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mListId = getArguments().getString(Constants.KEY_LIST_ID); } @Override @@ -57,9 +62,10 @@ public void onClick(DialogInterface dialog, int which) { } private void removeList() { - // TODO Here is the method that is called when the user selects yes, they want to - // remove a list. - + /* Get the location to remove from */ + Firebase listToRemoveRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS).child(mListId); + /* Remove the value */ + listToRemoveRef.removeValue(); } } From a26ca99b4394d1c644e99a2f7092f655bcfea713 Mon Sep 17 00:00:00 2001 From: Lyla Date: Wed, 21 Oct 2015 14:53:12 -0700 Subject: [PATCH 10/78] 2.13 Items - Quiz --- .../shoppinglistplusplus/model/ShoppingListItem.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java new file mode 100644 index 0000000..1ac8012 --- /dev/null +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java @@ -0,0 +1,10 @@ +package com.udacity.firebase.shoppinglistplusplus.model; + +/** + * Defines the data structure for ShoppingListItem objects. + */ +public class ShoppingListItem { + // TODO Create this class. + // Don't forget the rules for creating a POJO for Firebase. + +} From 0a4a4552cc7881973ac393ca47343cb4ba22905e Mon Sep 17 00:00:00 2001 From: Lyla Date: Fri, 6 Nov 2015 15:10:21 -0800 Subject: [PATCH 11/78] 2.14 Items - Solution --- .../model/ShoppingListItem.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java index 1ac8012..d3ac2e7 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java @@ -4,7 +4,33 @@ * Defines the data structure for ShoppingListItem objects. */ public class ShoppingListItem { - // TODO Create this class. - // Don't forget the rules for creating a POJO for Firebase. + private String itemName; + private String owner; + + /** + * Required public constructor + */ + public ShoppingListItem() { + } + + /** + * Use this constructor to create new ShoppingListItem. + * + * @param itemName + */ + public ShoppingListItem(String itemName) { + this.itemName = itemName; + /** + * This is a default value until we can differentiate users. + * Which will be soon, I promise. + */ + this.owner = "Anonymous Owner"; + } + + public String getItemName() { return itemName; } + + public String getOwner() { + return owner; + } } From b54c8f4ba29a9362c31d946f5c365649e97265d7 Mon Sep 17 00:00:00 2001 From: Lyla Date: Fri, 6 Nov 2015 15:11:01 -0800 Subject: [PATCH 12/78] 2.15 Add Items to the List Detail - Quiz --- .../ActiveListDetailsActivity.java | 3 + .../ActiveListItemAdapter.java | 60 +++++++++++++++++++ .../AddListItemDialogFragment.java | 3 + .../RemoveListDialogFragment.java | 5 ++ 4 files changed, 71 insertions(+) create mode 100644 app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index 87a104e..000456f 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -35,6 +35,9 @@ public class ActiveListDetailsActivity extends BaseActivity { protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_active_list_details); + // TODO Here is where you should set everything up for the adapter, much like you + // did with the ShoppingListsFragment class and the ActiveListAdapter. + // I've created the list item layout "single_active_list_item.xml" for you to use. /* Get the push ID from the extra passed by ShoppingListFragment */ Intent intent = this.getIntent(); diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java new file mode 100644 index 0000000..45f5cee --- /dev/null +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java @@ -0,0 +1,60 @@ +package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails; + +import android.app.Activity; +import android.view.View; + +import com.firebase.client.Query; +import com.firebase.ui.FirebaseListAdapter; +import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem; + + +/** + * Populates list_view_shopping_list_items inside ActiveListDetailsActivity + */ +public class ActiveListItemAdapter extends FirebaseListAdapter { + + /** + * Public constructor that initializes private instance variables when adapter is created + */ + public ActiveListItemAdapter(Activity activity, Class modelClass, int modelLayout, + Query ref) { + super(activity, modelClass, modelLayout, ref); + this.mActivity = activity; + } + + /** + * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete) + * with items inflated from single_active_list_item.xml + * populateView also handles data changes and updates the listView accordingly + */ + @Override + protected void populateView(View view, final ShoppingListItem item) { + // TODO Set up the view so that it shows the name of the item and the trash can button. + // The trashcan button should trigger a dialog to appear. + // The code to make the dialog appear is commented out below. + +// AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity, R.style.CustomTheme_Dialog) +// .setTitle(mActivity.getString(R.string.remove_item_option)) +// .setMessage(mActivity.getString(R.string.dialog_message_are_you_sure_remove_item)) +// .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { +// public void onClick(DialogInterface dialog, int which) { +// removeItem(); +// } +// }) +// .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { +// public void onClick(DialogInterface dialog, int which) { +// /* Dismiss the dialog */ +// dialog.dismiss(); +// } +// }) +// .setIcon(android.R.drawable.ic_dialog_alert); +// +// AlertDialog alertDialog = alertDialogBuilder.create(); +// alertDialog.show(); + + } + + private void removeItem() { + + } +} diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java index 299dd1d..72dd484 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java @@ -44,6 +44,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { */ @Override protected void doListEdit() { + // TODO The code here should do the write which adds a new item to the list. + // You should also update the list's last edit timestamp. You can do both of + // these actions in one call to the server. } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java index 55edf5b..11c14b5 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java @@ -62,6 +62,11 @@ public void onClick(DialogInterface dialog, int which) { } private void removeList() { + // TODO Now that you have shopping list items in your database, you need to delete + // those as well as the shopping list itself. This should all be done in one write + // request to the Firebase database (as in don't use removeValue twice). Is there + // a write method which will allow you to do this? + /* Get the location to remove from */ Firebase listToRemoveRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS).child(mListId); /* Remove the value */ From 1e125141e0269d91b9858f7e682995d746102639 Mon Sep 17 00:00:00 2001 From: Lyla Date: Fri, 23 Oct 2015 20:27:05 -0700 Subject: [PATCH 13/78] 2.16 Add Items to the List Detail - Solution --- .../ActiveListDetailsActivity.java | 22 ++++++- .../ActiveListItemAdapter.java | 60 ++++++++++++------- .../AddListItemDialogFragment.java | 51 +++++++++++++++- .../RemoveListDialogFragment.java | 34 ++++++++--- .../shoppinglistplusplus/utils/Constants.java | 3 + 5 files changed, 134 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index 000456f..85ab7f2 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -17,6 +17,7 @@ import com.firebase.client.ValueEventListener; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; +import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem; import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity; import com.udacity.firebase.shoppinglistplusplus.utils.Constants; @@ -26,6 +27,7 @@ public class ActiveListDetailsActivity extends BaseActivity { private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName(); private Firebase mActiveListRef; + private ActiveListItemAdapter mActiveListItemAdapter; private ListView mListView; private String mListId; private ShoppingList mShoppingList; @@ -35,9 +37,6 @@ public class ActiveListDetailsActivity extends BaseActivity { protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_active_list_details); - // TODO Here is where you should set everything up for the adapter, much like you - // did with the ShoppingListsFragment class and the ActiveListAdapter. - // I've created the list item layout "single_active_list_item.xml" for you to use. /* Get the push ID from the extra passed by ShoppingListFragment */ Intent intent = this.getIntent(); @@ -52,6 +51,7 @@ protected void onCreate(final Bundle savedInstanceState) { * Create Firebase references */ mActiveListRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS).child(mListId); + Firebase listItemsRef = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS).child(mListId); /** @@ -59,6 +59,21 @@ protected void onCreate(final Bundle savedInstanceState) { */ initializeScreen(); + + /** + * Setup the adapter + */ + mActiveListItemAdapter = new ActiveListItemAdapter(this, ShoppingListItem.class, + R.layout.single_active_list_item, listItemsRef); + /* Create ActiveListItemAdapter and set to listView */ + mListView.setAdapter(mActiveListItemAdapter); + + /** + * Add ValueEventListeners to Firebase references + * to control get data and control behavior and visibility of elements + */ + + /** * Save the most recent version of current shopping list into mShoppingList instance * variable an update the UI to match the current list. @@ -185,6 +200,7 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onDestroy() { super.onDestroy(); + mActiveListItemAdapter.cleanup(); } /** diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java index 45f5cee..ee12b75 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java @@ -1,10 +1,15 @@ package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails; import android.app.Activity; +import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; import android.view.View; +import android.widget.ImageButton; +import android.widget.TextView; import com.firebase.client.Query; import com.firebase.ui.FirebaseListAdapter; +import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem; @@ -22,6 +27,7 @@ public ActiveListItemAdapter(Activity activity, Class modelCla this.mActivity = activity; } + /** * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete) * with items inflated from single_active_list_item.xml @@ -29,29 +35,39 @@ public ActiveListItemAdapter(Activity activity, Class modelCla */ @Override protected void populateView(View view, final ShoppingListItem item) { - // TODO Set up the view so that it shows the name of the item and the trash can button. - // The trashcan button should trigger a dialog to appear. - // The code to make the dialog appear is commented out below. - -// AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity, R.style.CustomTheme_Dialog) -// .setTitle(mActivity.getString(R.string.remove_item_option)) -// .setMessage(mActivity.getString(R.string.dialog_message_are_you_sure_remove_item)) -// .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { -// public void onClick(DialogInterface dialog, int which) { -// removeItem(); -// } -// }) -// .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { -// public void onClick(DialogInterface dialog, int which) { -// /* Dismiss the dialog */ -// dialog.dismiss(); -// } -// }) -// .setIcon(android.R.drawable.ic_dialog_alert); -// -// AlertDialog alertDialog = alertDialogBuilder.create(); -// alertDialog.show(); + ImageButton buttonRemoveItem = (ImageButton) view.findViewById(R.id.button_remove_item); + TextView textViewMealItemName = (TextView) view.findViewById(R.id.text_view_active_list_item_name); + + textViewMealItemName.setText(item.getItemName()); + + /** + * Set the on click listener for "Remove list item" button + */ + buttonRemoveItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity, R.style.CustomTheme_Dialog) + .setTitle(mActivity.getString(R.string.remove_item_option)) + .setMessage(mActivity.getString(R.string.dialog_message_are_you_sure_remove_item)) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + removeItem(); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + /* Dismiss the dialog */ + dialog.dismiss(); + } + }) + .setIcon(android.R.drawable.ic_dialog_alert); + + AlertDialog alertDialog = alertDialogBuilder.create(); + alertDialog.show(); + } + }); } private void removeItem() { diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java index 72dd484..f0a7950 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java @@ -3,8 +3,16 @@ import android.app.Dialog; import android.os.Bundle; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.firebase.client.Firebase; +import com.firebase.client.ServerValue; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; +import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem; +import com.udacity.firebase.shoppinglistplusplus.utils.Constants; + +import java.util.HashMap; +import java.util.Map; /** * Lets user add new list item. @@ -44,9 +52,46 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { */ @Override protected void doListEdit() { - // TODO The code here should do the write which adds a new item to the list. - // You should also update the list's last edit timestamp. You can do both of - // these actions in one call to the server. + String mItemName = mEditTextForList.getText().toString(); + /** + * Adds list item if the input name is not empty + */ + if (!mItemName.equals("")) { + + Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL); + Firebase itemsRef = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS).child(mListId); + + /* Make a map for the item you are adding */ + HashMap updatedItemToAddMap = new HashMap(); + + /* Save push() to maintain same random Id */ + Firebase newRef = itemsRef.push(); + String itemId = newRef.getKey(); + + /* Make a POJO for the item and immediately turn it into a HashMap */ + ShoppingListItem itemToAddObject = new ShoppingListItem(mItemName); + HashMap itemToAdd = + (HashMap) new ObjectMapper().convertValue(itemToAddObject, Map.class); + + /* Add the item to the update map*/ + updatedItemToAddMap.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/" + + mListId + "/" + itemId, itemToAdd); + + /* Make the timestamp for last changed */ + HashMap changedTimestampMap = new HashMap<>(); + changedTimestampMap.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); + + /* Add the updated timestamp */ + updatedItemToAddMap.put("/" + Constants.FIREBASE_LOCATION_ACTIVE_LISTS + + "/" + mListId + "/" + Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED, changedTimestampMap); + + /* Do the update */ + firebaseRef.updateChildren(updatedItemToAddMap); + /** + * Close the dialog fragment when done + */ + AddListItemDialogFragment.this.getDialog().cancel(); + } } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java index 11c14b5..37bf797 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java @@ -5,12 +5,16 @@ import android.content.DialogInterface; import android.os.Bundle; import android.support.v7.app.AlertDialog; +import android.util.Log; import com.firebase.client.Firebase; +import com.firebase.client.FirebaseError; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; import com.udacity.firebase.shoppinglistplusplus.utils.Constants; +import java.util.HashMap; + /** * Lets the user remove active shopping list */ @@ -62,15 +66,29 @@ public void onClick(DialogInterface dialog, int which) { } private void removeList() { - // TODO Now that you have shopping list items in your database, you need to delete - // those as well as the shopping list itself. This should all be done in one write - // request to the Firebase database (as in don't use removeValue twice). Is there - // a write method which will allow you to do this? - /* Get the location to remove from */ - Firebase listToRemoveRef = new Firebase(Constants.FIREBASE_URL_ACTIVE_LISTS).child(mListId); - /* Remove the value */ - listToRemoveRef.removeValue(); + /** + * Create map and fill it in with deep path multi write operations list + */ + HashMap removeListData = new HashMap(); + + removeListData.put("/" + Constants.FIREBASE_LOCATION_ACTIVE_LISTS + "/" + + mListId, null); + removeListData.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/" + + mListId, null); + + Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL); + + /* Do a deep-path update */ + firebaseRef.updateChildren(removeListData, new Firebase.CompletionListener() { + @Override + public void onComplete(FirebaseError firebaseError, Firebase firebase) { + + if (firebaseError != null) { + Log.e(LOG_TAG, getString(R.string.log_error_updating_data) + firebaseError.getMessage()); + } + } + }); } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index 7817d57..222e430 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -12,6 +12,7 @@ public final class Constants { * where active lists are stored (ie "activeLists") */ public static final String FIREBASE_LOCATION_ACTIVE_LISTS = "activeLists"; + public static final String FIREBASE_LOCATION_SHOPPING_LIST_ITEMS = "shoppingListItems"; /** @@ -20,12 +21,14 @@ public final class Constants { public static final String FIREBASE_PROPERTY_LIST_NAME = "listName"; public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED = "timestampLastChanged"; public static final String FIREBASE_PROPERTY_TIMESTAMP = "timestamp"; + public static final String FIREBASE_PROPERTY_ITEM_NAME = "itemName"; /** * Constants for Firebase URL */ public static final String FIREBASE_URL = BuildConfig.UNIQUE_FIREBASE_ROOT_URL; public static final String FIREBASE_URL_ACTIVE_LISTS = FIREBASE_URL + "/" + FIREBASE_LOCATION_ACTIVE_LISTS; + public static final String FIREBASE_URL_SHOPPING_LIST_ITEMS = FIREBASE_URL + "/" + FIREBASE_LOCATION_SHOPPING_LIST_ITEMS; /** From ffe869cd981b33dd2933fe6e406faa1e9b020000 Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 22 Oct 2015 15:43:52 -0700 Subject: [PATCH 14/78] 2.17 Remove Event Listeners - Quiz --- .../ui/activeListDetails/ActiveListDetailsActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index 85ab7f2..c06cc78 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -33,6 +33,8 @@ public class ActiveListDetailsActivity extends BaseActivity { private ShoppingList mShoppingList; + //TODO Hint : The value event listener we want to make sure to detach is somewhere in + // this class. @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); From 8698f1be8a595d6aef6b482946527936fb33bd37 Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 22 Oct 2015 15:45:29 -0700 Subject: [PATCH 15/78] 2.18 Remove Event Listeners - Solution --- .../ui/activeListDetails/ActiveListDetailsActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index c06cc78..cff06d8 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -31,10 +31,8 @@ public class ActiveListDetailsActivity extends BaseActivity { private ListView mListView; private String mListId; private ShoppingList mShoppingList; + private ValueEventListener mActiveListRefListener; - - //TODO Hint : The value event listener we want to make sure to detach is somewhere in - // this class. @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -80,7 +78,8 @@ protected void onCreate(final Bundle savedInstanceState) { * Save the most recent version of current shopping list into mShoppingList instance * variable an update the UI to match the current list. */ - mActiveListRef.addValueEventListener(new ValueEventListener() { + mActiveListRefListener = mActiveListRef.addValueEventListener(new ValueEventListener() { + @Override public void onDataChange(DataSnapshot snapshot) { @@ -203,6 +202,7 @@ public boolean onOptionsItemSelected(MenuItem item) { public void onDestroy() { super.onDestroy(); mActiveListItemAdapter.cleanup(); + mActiveListRef.removeEventListener(mActiveListRefListener); } /** From 3751978c45b1b48e195556ace102bc9aa4ac3bdc Mon Sep 17 00:00:00 2001 From: Lyla Date: Wed, 9 Dec 2015 17:05:29 -0800 Subject: [PATCH 16/78] 2.19 OYO Edit and Remove List --- .../ActiveListDetailsActivity.java | 27 ++++++++-- .../ActiveListItemAdapter.java | 50 ++++++++++++++++-- .../EditListItemNameDialogFragment.java | 51 +++++++++++++++++-- .../shoppinglistplusplus/utils/Constants.java | 2 + 4 files changed, 117 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java index cff06d8..9b59ba6 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java @@ -64,7 +64,7 @@ protected void onCreate(final Bundle savedInstanceState) { * Setup the adapter */ mActiveListItemAdapter = new ActiveListItemAdapter(this, ShoppingListItem.class, - R.layout.single_active_list_item, listItemsRef); + R.layout.single_active_list_item, listItemsRef, mListId); /* Create ActiveListItemAdapter and set to listView */ mListView.setAdapter(mActiveListItemAdapter); @@ -99,6 +99,11 @@ public void onDataChange(DataSnapshot snapshot) { return; } mShoppingList = shoppingList; + /** + * Pass the shopping list to the adapter if it is not null. + * We do this here because mShoppingList is null when first created. + */ + mActiveListItemAdapter.setShoppingList(mShoppingList); /* Calling invalidateOptionsMenu causes onCreateOptionsMenu to be called */ invalidateOptionsMenu(); @@ -127,9 +132,17 @@ public void onCancelled(FirebaseError firebaseError) { public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { /* Check that the view is not the empty footer item */ if(view.getId() != R.id.list_view_footer_empty) { - showEditListItemNameDialog(); + ShoppingListItem shoppingListItem = mActiveListItemAdapter.getItem(position); + + if (shoppingListItem != null) { + String itemName = shoppingListItem.getItemName(); + String itemId = mActiveListItemAdapter.getRef(position).getKey(); + + showEditListItemNameDialog(itemName, itemId); + return true; + } } - return true; + return false; } }); } @@ -266,10 +279,14 @@ public void showEditListNameDialog() { /** * Show the edit list item name dialog after longClick on the particular item + * @param itemName + * @param itemId */ - public void showEditListItemNameDialog() { + public void showEditListItemNameDialog(String itemName, String itemId) { /* Create an instance of the dialog fragment and show it */ - DialogFragment dialog = EditListItemNameDialogFragment.newInstance(mShoppingList, mListId); + DialogFragment dialog = EditListItemNameDialogFragment.newInstance(mShoppingList, itemName, + itemId, mListId); + dialog.show(this.getFragmentManager(), "EditListItemNameDialogFragment"); } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java index ee12b75..33daa53 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java @@ -7,26 +7,42 @@ import android.widget.ImageButton; import android.widget.TextView; +import com.firebase.client.Firebase; import com.firebase.client.Query; +import com.firebase.client.ServerValue; import com.firebase.ui.FirebaseListAdapter; import com.udacity.firebase.shoppinglistplusplus.R; +import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem; +import com.udacity.firebase.shoppinglistplusplus.utils.Constants; + +import java.util.HashMap; /** * Populates list_view_shopping_list_items inside ActiveListDetailsActivity */ public class ActiveListItemAdapter extends FirebaseListAdapter { + private ShoppingList mShoppingList; + private String mListId; /** * Public constructor that initializes private instance variables when adapter is created */ public ActiveListItemAdapter(Activity activity, Class modelClass, int modelLayout, - Query ref) { + Query ref, String listId) { super(activity, modelClass, modelLayout, ref); this.mActivity = activity; + this.mListId = listId; } + /** + * Public method that is used to pass shoppingList object when it is loaded in ValueEventListener + */ + public void setShoppingList(ShoppingList shoppingList) { + this.mShoppingList = shoppingList; + this.notifyDataSetChanged(); + } /** * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete) @@ -34,13 +50,16 @@ public ActiveListItemAdapter(Activity activity, Class modelCla * populateView also handles data changes and updates the listView accordingly */ @Override - protected void populateView(View view, final ShoppingListItem item) { + protected void populateView(View view, final ShoppingListItem item, int position) { ImageButton buttonRemoveItem = (ImageButton) view.findViewById(R.id.button_remove_item); TextView textViewMealItemName = (TextView) view.findViewById(R.id.text_view_active_list_item_name); textViewMealItemName.setText(item.getItemName()); + /* Gets the id of the item to remove */ + final String itemToRemoveId = this.getRef(position).getKey(); + /** * Set the on click listener for "Remove list item" button */ @@ -53,7 +72,10 @@ public void onClick(View v) { .setMessage(mActivity.getString(R.string.dialog_message_are_you_sure_remove_item)) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - removeItem(); + + removeItem(itemToRemoveId); + /* Dismiss the dialog */ + dialog.dismiss(); } }) .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { @@ -70,7 +92,25 @@ public void onClick(DialogInterface dialog, int which) { }); } - private void removeItem() { + private void removeItem(String itemId) { + Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL); + + /* Make a map for the removal */ + HashMap updatedRemoveItemMap = new HashMap(); + + /* Remove the item by passing null */ + updatedRemoveItemMap.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/" + + mListId + "/" + itemId, null); + + /* Make the timestamp for last changed */ + HashMap changedTimestampMap = new HashMap<>(); + changedTimestampMap.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); + + /* Add the updated timestamp */ + updatedRemoveItemMap.put("/" + Constants.FIREBASE_LOCATION_ACTIVE_LISTS + + "/" + mListId + "/" + Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED, changedTimestampMap); + /* Do the update */ + firebaseRef.updateChildren(updatedRemoveItemMap); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java index e2acdc3..cadfbc8 100644 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java @@ -3,22 +3,30 @@ import android.app.Dialog; import android.os.Bundle; +import com.firebase.client.Firebase; +import com.firebase.client.ServerValue; import com.udacity.firebase.shoppinglistplusplus.R; import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList; +import com.udacity.firebase.shoppinglistplusplus.utils.Constants; + +import java.util.HashMap; /** * Lets user edit list item name for all copies of the current list */ public class EditListItemNameDialogFragment extends EditListDialogFragment { + String mItemName, mItemId; /** * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created */ - public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingList, String listId) { + public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingList, String itemName, + String itemId, String listId) { EditListItemNameDialogFragment editListItemNameDialogFragment = new EditListItemNameDialogFragment(); - Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, - R.layout.dialog_edit_item, listId); + Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_item, listId); + bundle.putString(Constants.KEY_LIST_ITEM_NAME, itemName); + bundle.putString(Constants.KEY_LIST_ITEM_ID, itemId); editListItemNameDialogFragment.setArguments(bundle); return editListItemNameDialogFragment; @@ -30,6 +38,8 @@ public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingLi @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mItemName = getArguments().getString(Constants.KEY_LIST_ITEM_NAME); + mItemId = getArguments().getString(Constants.KEY_LIST_ITEM_ID); } @@ -40,6 +50,12 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { * superclass method that creates the dialog */ Dialog dialog = super.createDialogHelper(R.string.positive_button_edit_item); + /** + * {@link EditListDialogFragment#helpSetDefaultValueEditText(String)} is a superclass + * method that sets the default text of the TextView + */ + super.helpSetDefaultValueEditText(mItemName); + return dialog; } @@ -47,5 +63,34 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { * Change selected list item name to the editText input if it is not empty */ protected void doListEdit() { + String nameInput = mEditTextForList.getText().toString(); + + /** + * Set input text to be the current list item name if it is not empty and is not the + * previous name. + */ + if (!nameInput.equals("") && !nameInput.equals(mItemName)) { + Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL); + + /* Make a map for the item you are editing the name of */ + HashMap updatedItemToAddMap = new HashMap(); + + /* Add the new name to the update map*/ + updatedItemToAddMap.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/" + + mListId + "/" + mItemId + "/" + Constants.FIREBASE_PROPERTY_ITEM_NAME, + nameInput); + + /* Make the timestamp for last changed */ + HashMap changedTimestampMap = new HashMap<>(); + changedTimestampMap.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP); + + /* Add the updated timestamp */ + updatedItemToAddMap.put("/" + Constants.FIREBASE_LOCATION_ACTIVE_LISTS + + "/" + mListId + "/" + Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED, changedTimestampMap); + + /* Do the update */ + firebaseRef.updateChildren(updatedItemToAddMap); + + } } } diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java index 222e430..4b91df6 100755 --- a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java @@ -37,6 +37,8 @@ public final class Constants { public static final String KEY_LIST_NAME = "LIST_NAME"; public static final String KEY_LAYOUT_RESOURCE = "LAYOUT_RESOURCE"; public static final String KEY_LIST_ID = "LIST_ID"; + public static final String KEY_LIST_ITEM_NAME = "ITEM_NAME"; + public static final String KEY_LIST_ITEM_ID = "LIST_ITEM_ID"; } From 9d4e7818ed4b38775d9cc3f61487e99de7c4d321 Mon Sep 17 00:00:00 2001 From: Lyla Date: Thu, 29 Oct 2015 01:19:02 -0700 Subject: [PATCH 17/78] 3.00 Version 0.3 Start Code --- app/src/main/AndroidManifest.xml | 17 +- .../ui/login/CreateAccountActivity.java | 104 ++++++ .../ui/login/LoginActivity.java | 309 ++++++++++++++++++ .../layout-land/activity_create_account.xml | 122 +++++++ .../main/res/layout-land/activity_login.xml | 109 ++++++ .../res/layout/activity_create_account.xml | 120 +++++++ app/src/main/res/layout/activity_login.xml | 116 +++++++ 7 files changed, 895 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java create mode 100644 app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java create mode 100644 app/src/main/res/layout-land/activity_create_account.xml create mode 100644 app/src/main/res/layout-land/activity_login.xml create mode 100644 app/src/main/res/layout/activity_create_account.xml create mode 100644 app/src/main/res/layout/activity_login.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8eabfb..8604a24 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,9 +17,8 @@ - + - @@ -33,5 +32,19 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".ui.MainActivity" /> + + + + + + + + + diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java new file mode 100644 index 0000000..68e1707 --- /dev/null +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java @@ -0,0 +1,104 @@ +package com.udacity.firebase.shoppinglistplusplus.ui.login; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Toast; + +import com.udacity.firebase.shoppinglistplusplus.R; +import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity; + +/** + * Represents Sign up screen and functionality of the app + */ +public class CreateAccountActivity extends BaseActivity { + private static final String LOG_TAG = CreateAccountActivity.class.getSimpleName(); + private ProgressDialog mAuthProgressDialog; + private EditText mEditTextUsernameCreate, mEditTextEmailCreate, mEditTextPasswordCreate; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_create_account); + + /** + * Link layout elements from XML and setup the progress dialog + */ + initializeScreen(); + } + + /** + * Override onCreateOptionsMenu to inflate nothing + * + * @param menu The menu with which nothing will happen + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return true; + } + + + /** + * Link layout elements from XML and setup the progress dialog + */ + public void initializeScreen() { + mEditTextUsernameCreate = (EditText) findViewById(R.id.edit_text_username_create); + mEditTextEmailCreate = (EditText) findViewById(R.id.edit_text_email_create); + mEditTextPasswordCreate = (EditText) findViewById(R.id.edit_text_password_create); + LinearLayout linearLayoutCreateAccountActivity = (LinearLayout) findViewById(R.id.linear_layout_create_account_activity); + initializeBackground(linearLayoutCreateAccountActivity); + + /* Setup the progress dialog that is displayed later when authenticating with Firebase */ + mAuthProgressDialog = new ProgressDialog(this); + mAuthProgressDialog.setTitle(getResources().getString(R.string.progress_dialog_loading)); + mAuthProgressDialog.setMessage(getResources().getString(R.string.progress_dialog_creating_user_with_firebase)); + mAuthProgressDialog.setCancelable(false); + } + + /** + * Open LoginActivity when user taps on "Sign in" textView + */ + public void onSignInPressed(View view) { + Intent intent = new Intent(CreateAccountActivity.this, LoginActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + finish(); + } + + /** + * Create new account using Firebase email/password provider + */ + public void onCreateAccountPressed(View view) { + + } + + /** + * Creates a new user in Firebase from the Java POJO + */ + private void createUserInFirebaseHelper(final String encodedEmail) { + } + + private boolean isEmailValid(String email) { + return true; + } + + private boolean isUserNameValid(String userName) { + return true; + } + + private boolean isPasswordValid(String password) { + return true; + } + + /** + * Show error toast to users + */ + private void showErrorToast(String message) { + Toast.makeText(CreateAccountActivity.this, message, Toast.LENGTH_LONG).show(); + } +} diff --git a/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java new file mode 100644 index 0000000..aab6b30 --- /dev/null +++ b/app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java @@ -0,0 +1,309 @@ +package com.udacity.firebase.shoppinglistplusplus.ui.login; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.firebase.client.AuthData; +import com.google.android.gms.auth.GoogleAuthException; +import com.google.android.gms.auth.GoogleAuthUtil; +import com.google.android.gms.auth.UserRecoverableAuthException; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.Scopes; +import com.google.android.gms.common.SignInButton; +import com.google.android.gms.common.api.Scope; +import com.udacity.firebase.shoppinglistplusplus.R; +import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity; + +import java.io.IOException; + +/** + * Represents Sign in screen and functionality of the app + */ +public class LoginActivity extends BaseActivity { + + private static final String LOG_TAG = LoginActivity.class.getSimpleName(); + /* A dialog that is presented until the Firebase authentication finished. */ + private ProgressDialog mAuthProgressDialog; + private EditText mEditTextEmailInput, mEditTextPasswordInput; + + /** + * Variables related to Google Login + */ + /* A flag indicating that a PendingIntent is in progress and prevents us from starting further intents. */ + private boolean mGoogleIntentInProgress; + /* Request code used to invoke sign in user interactions for Google+ */ + public static final int RC_GOOGLE_LOGIN = 1; + /* A Google account object that is populated if the user signs in with Google */ + GoogleSignInAccount mGoogleAccount; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + + /** + * Link layout elements from XML and setup progress dialog + */ + initializeScreen(); + + /** + * Call signInPassword() when user taps "Done" keyboard action + */ + mEditTextPasswordInput.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { + + if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) { + signInPassword(); + } + return true; + } + }); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + } + + /** + * Override onCreateOptionsMenu to inflate nothing + * + * @param menu The menu with which nothing will happen + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return true; + } + + + /** + * Sign in with Password provider when user clicks sign in button + */ + public void onSignInPressed(View view) { + signInPassword(); + } + + /** + * Open CreateAccountActivity when user taps on "Sign up" TextView + */ + public void onSignUpPressed(View view) { + Intent intent = new Intent(LoginActivity.this, CreateAccountActivity.class); + startActivity(intent); + } + + /** + * Link layout elements from XML and setup the progress dialog + */ + public void initializeScreen() { + mEditTextEmailInput = (EditText) findViewById(R.id.edit_text_email); + mEditTextPasswordInput = (EditText) findViewById(R.id.edit_text_password); + LinearLayout linearLayoutLoginActivity = (LinearLayout) findViewById(R.id.linear_layout_login_activity); + initializeBackground(linearLayoutLoginActivity); + /* Setup the progress dialog that is displayed later when authenticating with Firebase */ + mAuthProgressDialog = new ProgressDialog(this); + mAuthProgressDialog.setTitle(getString(R.string.progress_dialog_loading)); + mAuthProgressDialog.setMessage(getString(R.string.progress_dialog_authenticating_with_firebase)); + mAuthProgressDialog.setCancelable(false); + /* Setup Google Sign In */ + setupGoogleSignIn(); + } + + /** + * Sign in with Password provider (used when user taps "Done" action on keyboard) + */ + public void signInPassword() { + } + + /** + * Helper method that makes sure a user is created if the user + * logs in with Firebase's email/password provider. + * @param authData AuthData object returned from onAuthenticated + */ + private void setAuthenticatedUserPasswordProvider(AuthData authData) { + } + + /** + * Helper method that makes sure a user is created if the user + * logs in with Firebase's Google login provider. + * @param authData AuthData object returned from onAuthenticated + */ + private void setAuthenticatedUserGoogle(AuthData authData){ + + } + + /** + * Show error toast to users + */ + private void showErrorToast(String message) { + Toast.makeText(LoginActivity.this, message, Toast.LENGTH_LONG).show(); + } + + + /** + * Signs you into ShoppingList++ using the Google Login Provider + * @param token A Google OAuth access token returned from Google + */ + private void loginWithGoogle(String token) { + } + + /** + * GOOGLE SIGN IN CODE + * + * This code is mostly boiler plate from + * https://developers.google.com/identity/sign-in/android/start-integrating + * and + * https://github.com/googlesamples/google-services/blob/master/android/signin/app/src/main/java/com/google/samples/quickstart/signin/SignInActivity.java + * + * The big picture steps are: + * 1. User clicks the sign in with Google button + * 2. An intent is started for sign in. + * - If the connection fails it is caught in the onConnectionFailed callback + * - If it finishes, onActivityResult is called with the correct request code. + * 3. If the sign in was successful, set the mGoogleAccount to the current account and + * then call get GoogleOAuthTokenAndLogin + * 4. getGoogleOAuthTokenAndLogin launches an AsyncTask to get an OAuth2 token from Google. + * 5. Once this token is retrieved it is available to you in the onPostExecute method of + * the AsyncTask. **This is the token required by Firebase** + */ + + + /* Sets up the Google Sign In Button : https://developers.google.com/android/reference/com/google/android/gms/common/SignInButton */ + private void setupGoogleSignIn() { + SignInButton signInButton = (SignInButton)findViewById(R.id.login_with_google); + signInButton.setSize(SignInButton.SIZE_WIDE); + signInButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onSignInGooglePressed(v); + } + }); + } + + /** + * Sign in with Google plus when user clicks "Sign in with Google" textView (button) + */ + public void onSignInGooglePressed(View view) { + Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); + startActivityForResult(signInIntent, RC_GOOGLE_LOGIN); + mAuthProgressDialog.show(); + + } + + @Override + public void onConnectionFailed(ConnectionResult result) { + /** + * An unresolvable error has occurred and Google APIs (including Sign-In) will not + * be available. + */ + mAuthProgressDialog.dismiss(); + showErrorToast(result.toString()); + } + + + /** + * This callback is triggered when any startActivityForResult finishes. The requestCode maps to + * the value passed into startActivityForResult. + */ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + /* Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); */ + if (requestCode == RC_GOOGLE_LOGIN) { + GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); + handleSignInResult(result); + } + + } + + private void handleSignInResult(GoogleSignInResult result) { + Log.d(LOG_TAG, "handleSignInResult:" + result.isSuccess()); + if (result.isSuccess()) { + /* Signed in successfully, get the OAuth token */ + mGoogleAccount = result.getSignInAccount(); + getGoogleOAuthTokenAndLogin(); + + + } else { + if (result.getStatus().getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) { + showErrorToast("The sign in was cancelled. Make sure you're connected to the internet and try again."); + } else { + showErrorToast("Error handling the sign in: " + result.getStatus().getStatusMessage()); + } + mAuthProgressDialog.dismiss(); + } + } + + /** + * Gets the GoogleAuthToken and logs in. + */ + private void getGoogleOAuthTokenAndLogin() { + /* Get OAuth token in Background */ + AsyncTask task = new AsyncTask() { + String mErrorMessage = null; + + @Override + protected String doInBackground(Void... params) { + String token = null; + + try { + String scope = String.format(getString(R.string.oauth2_format), new Scope(Scopes.PROFILE)) + " email"; + + token = GoogleAuthUtil.getToken(LoginActivity.this, mGoogleAccount.getEmail(), scope); + } catch (IOException transientEx) { + /* Network or server error */ + Log.e(LOG_TAG, getString(R.string.google_error_auth_with_google) + transientEx); + mErrorMessage = getString(R.string.google_error_network_error) + transientEx.getMessage(); + } catch (UserRecoverableAuthException e) { + Log.w(LOG_TAG, getString(R.string.google_error_recoverable_oauth_error) + e.toString()); + + /* We probably need to ask for permissions, so start the intent if there is none pending */ + if (!mGoogleIntentInProgress) { + mGoogleIntentInProgress = true; + Intent recover = e.getIntent(); + startActivityForResult(recover, RC_GOOGLE_LOGIN); + } + } catch (GoogleAuthException authEx) { + /* The call is not ever expected to succeed assuming you have already verified that + * Google Play services is installed. */ + Log.e(LOG_TAG, " " + authEx.getMessage(), authEx); + mErrorMessage = getString(R.string.google_error_auth_with_google) + authEx.getMessage(); + } + return token; + } + + @Override + protected void onPostExecute(String token) { + mAuthProgressDialog.dismiss(); + if (token != null) { + /* Successfully got OAuth token, now login with Google */ + loginWithGoogle(token); + } else if (mErrorMessage != null) { + showErrorToast(mErrorMessage); + } + } + }; + + task.execute(); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_create_account.xml b/app/src/main/res/layout-land/activity_create_account.xml new file mode 100644 index 0000000..28851a8 --- /dev/null +++ b/app/src/main/res/layout-land/activity_create_account.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + +