[
  {
    "path": ".gitignore",
    "content": "#built application files\n*.apk\n*.ap_\n# files for the dex VM\n*.dex\n# Java class files\n*.class\n# generated files\nbin/\ngen/\n# Local configuration file (sdk path, etc)\nlocal.properties\n# Windows thumbnail db\nThumbs.db\n# OSX files\n.DS_Store\n# Eclipse project files\n.classpath\n.project\n# Android Studio\n*.iml\n.idea\n#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.\n.gradle\nbuild/\n#.idea/vcs.xml\ngradle.properties\n#don't store google-services.json\napp/google-services.json\n\n"
  },
  {
    "path": "README.md",
    "content": "ShoppingList++\n========\n\nShoppingList++ is the companion Android app for the Udacity course [Firebase Essentials : Build a Shopping List App](https://www.udacity.com/course/firebase-essentials-for-android--ud009). \n\nThe course covers everything you need to know to incorporate the [Firebase](https://www.firebase.com) into an Android App.\n\nFor an explanation of how to use this repository, please refer to the [Using ShoppingList++ Github Repository document](https://www.udacity.com/wiki/ud009/navigategithubrepo?nocache) and [The Github Code & Resources document](https://www.udacity.com/course/viewer#!/c-ud009/l-5389267455/e-5498429638/m-5528878616).\n\n##Seeing Errors?\n\n![Conflict errors](http://lh3.googleusercontent.com/zQxJRMJIEnWEQb7csOy3AiJoIJiOnY_dqf4D-seEEpJQFBMwmdpCF_JeszhB96K57tFxW2_FtOQvmIhUvQ=s0#w=1014&h=359)\n\nAs mistakes in the original repository are fixed, the code will be <a href=\"https://git-scm.com/book/en/v2/Git-Branching-Rebasing\" target=\"_blank\">rebased</a> and re-uploaded here. The good news is if students discover errors or optimizations, the code will be updated. The unfortunate side effect is that this will destroy Github forks and stars as well as require the code to be re-downloaded to get the newest updates. So my humble suggestion is to **NOT** fork or star this repository. Instead, you can bookmark this page. If you are not checking out branches and instead making the app the **thorough way** you can check out the change log to see what was fixed. If you're working through the **fast way** you can re-clone the repository.\n\n##Changelogs\n\n- **1/26/2016** : [Changelog](https://docs.google.com/document/d/1SgBmUu7COQQT5maqKVvIV4Iv0Oyva9-9-YRnpQ88XuY/pub)\n- **12/10/2015** : [Changelog](https://docs.google.com/document/d/1A5BSoLyEHkXrcBC50lNXqrl1Rkh0G2nM-h4ER8lKovw/pub)\n- **11/30/2015** : Lesson 1 and 2 launch\n # Archival Note \n This repository is deprecated; therefore, we are going to archive it. However, learners will be able to fork it to their personal Github account but cannot submit PRs to this repository. If you have any issues or suggestions to make, feel free to: \n- Utilize the https://knowledge.udacity.com/ forum to seek help on content-specific issues. \n- Submit a support ticket along with the link to your forked repository if (learners are) blocked for other reasons. Here are the links for the [retail consumers](https://udacity.zendesk.com/hc/en-us/requests/new) and [enterprise learners](https://udacityenterprise.zendesk.com/hc/en-us/requests/new?ticket_form_id=360000279131)."
  },
  {
    "path": "app/.gitignore",
    "content": "#built application files\n*.apk\n*.ap_\n# files for the dex VM\n*.dex\n# Java class files\n*.class\n# generated files\nbin/\ngen/\n# Local configuration file (sdk path, etc)\nlocal.properties\n# Windows thumbnail db\nThumbs.db\n# OSX files\n.DS_Store\n# Eclipse project files\n.classpath\n.project\n# Android Studio\n*.iml\n.idea\n#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.\n.gradle\nbuild/\n#.idea/vcs.xml"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'com.google.gms.google-services'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.1\"\n\n    defaultConfig {\n        applicationId \"com.udacity.firebase.shoppinglistplusplus\"\n        minSdkVersion 15\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n        debug {\n        }\n    }\n\n    /* This for anyone following along with the repo. Since you will have a different\n     * root URL, this code loads up a value from your gradle.properties file.\n     */\n\n    buildTypes.each {\n        it.buildConfigField 'String', 'UNIQUE_FIREBASE_ROOT_URL', UniqueFirebaseRootUrl\n    }\n\n    packagingOptions {\n        exclude 'META-INF/LICENSE'\n        exclude 'META-INF/LICENSE-FIREBASE.txt'\n        exclude 'META-INF/NOTICE'\n    }\n\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    compile 'com.android.support:appcompat-v7:23.0.1'\n    compile 'com.android.support:design:23.0.1'\n    compile 'com.android.support:support-v4:23.0.1'\n    compile 'com.android.support:cardview-v7:23.0.1'\n\n    /* Firebase SDK */\n    compile 'com.firebase:firebase-client-android:2.4.0'\n\n    /* Firebase UI */\n    compile 'com.firebaseui:firebase-ui:0.2.2'\n\n    /* For Google Play Services */\n    compile 'com.google.android.gms:play-services-safetynet:8.3.0'\n    compile 'com.google.android.gms:play-services-auth:8.3.0'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/admin/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/com/udacity/firebase/shoppinglistplusplus/ApplicationTest.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.udacity.firebase.shoppinglistplusplus\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n    <uses-permission android:name=\"android.permission.USE_CREDENTIALS\" />\n\n    <application\n        android:name=\"com.udacity.firebase.shoppinglistplusplus.ShoppingListApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_shopping_list\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\"com.udacity.firebase.shoppinglistplusplus.ui.MainActivity\"\n            android:label=\"@string/app_name\"\n            android:windowSoftInputMode=\"adjustPan\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\"com.udacity.firebase.shoppinglistplusplus.ui.MainActivity\" />\n        </activity>\n\n        <activity\n            android:name=\".ui.activeListDetails.ActiveListDetailsActivity\"\n            android:label=\"@string/title_activity_list_details\"\n            android:parentActivityName=\".ui.MainActivity\"\n            android:windowSoftInputMode=\"adjustPan\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.MainActivity\" />\n        </activity>\n\n        <activity android:name=\".ui.login.LoginActivity\">\n            <intent-filter android:label=\"@string/app_name\">\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".ui.login.CreateAccountActivity\"\n            android:label=\"@string/title_activity_create_account\"\n            android:parentActivityName=\".ui.login.LoginActivity\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.login.LoginActivity\" />\n        </activity>\n        <activity\n            android:name=\".ui.SettingsActivity\"\n            android:label=\"@string/action_settings\" />\n        <activity\n            android:name=\".ui.sharing.ShareListActivity\"\n            android:label=\"@string/title_activity_share_list\"\n            android:parentActivityName=\".ui.activeListDetails.ActiveListDetailsActivity\"\n            android:windowSoftInputMode=\"adjustPan\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.activeListDetails.ActiveListDetailsActivity\" />\n        </activity>\n        <activity\n            android:name=\".ui.sharing.AddFriendActivity\"\n            android:label=\"@string/title_activity_add_friend\"\n            android:parentActivityName=\".ui.sharing.ShareListActivity\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.sharing.ShareListActivity\" />\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ShoppingListApplication.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus;\n\nimport com.firebase.client.Firebase;\n\n/**\n * Includes one-time initialization of Firebase related code\n */\npublic class ShoppingListApplication extends android.app.Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        /* Initialize Firebase */\n        Firebase.setAndroidContext(this);\n        /* Enable disk persistence  */\n        Firebase.getDefaultConfig().setPersistenceEnabled(true);\n    }\n\n}"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.model;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com.firebase.client.ServerValue;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\nimport java.util.HashMap;\n\n/**\n * Defines the data structure for both Active and Archived ShoppingList objects.\n */\n\npublic class ShoppingList {\n    private String listName;\n    private String owner;\n    private HashMap<String, Object> timestampLastChanged;\n    private HashMap<String, Object> timestampCreated;\n    private HashMap<String, Object> timestampLastChangedReverse;\n    private HashMap<String, User> usersShopping;\n\n    /**\n     * Required public constructor\n     */\n    public ShoppingList() {\n    }\n\n    /**\n     * Use this constructor to create new ShoppingLists.\n     * Takes shopping list listName and owner. Set's the last\n     * changed time to what is stored in ServerValue.TIMESTAMP\n     *\n     * @param listName\n     * @param owner\n     */\n    public ShoppingList(String listName, String owner, HashMap<String, Object> timestampCreated) {\n        this.listName = listName;\n        this.owner = owner;\n        this.timestampCreated = timestampCreated;\n        HashMap<String, Object> timestampNowObject = new HashMap<String, Object>();\n        timestampNowObject.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);\n        this.timestampLastChanged = timestampNowObject;\n        this.timestampLastChangedReverse = null;\n        this.usersShopping = new HashMap<>();\n    }\n\n    public String getListName() {\n        return listName;\n    }\n\n    public String getOwner() {\n        return owner;\n    }\n\n    public HashMap<String, Object> getTimestampLastChanged() {\n        return timestampLastChanged;\n    }\n\n    public HashMap<String, Object> getTimestampCreated() {\n        return timestampCreated;\n    }\n\n    public HashMap<String, Object> getTimestampLastChangedReverse() {\n        return timestampLastChangedReverse;\n    }\n\n    @JsonIgnore\n    public long getTimestampLastChangedLong() {\n\n        return (long) timestampLastChanged.get(Constants.FIREBASE_PROPERTY_TIMESTAMP);\n    }\n\n    @JsonIgnore\n    public long getTimestampCreatedLong() {\n        return (long) timestampLastChanged.get(Constants.FIREBASE_PROPERTY_TIMESTAMP);\n    }\n\n    @JsonIgnore\n    public long getTimestampLastChangedReverseLong() {\n\n        return (long) timestampLastChangedReverse.get(Constants.FIREBASE_PROPERTY_TIMESTAMP);\n    }\n\n    public HashMap getUsersShopping() {\n        return usersShopping;\n    }\n\n    public void setTimestampLastChangedToNow() {\n        HashMap<String, Object> timestampNowObject = new HashMap<String, Object>();\n        timestampNowObject.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);\n        this.timestampLastChanged = timestampNowObject;\n    }\n\n\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.model;\n\n/**\n * Defines the data structure for ShoppingListItem objects.\n */\npublic class ShoppingListItem {\n    private String itemName;\n    private String owner;\n    private String boughtBy;\n    private boolean bought;\n\n    /**\n     * Required public constructor\n     */\n    public ShoppingListItem() {\n    }\n\n    /**\n     * Use this constructor to create new ShoppingListItem.\n     * Takes shopping list item name and list item owner email as params\n     *\n     * @param itemName\n     * @param owner\n     */\n    public ShoppingListItem(String itemName, String owner) {\n        this.itemName = itemName;\n        this.owner = owner;\n        this.boughtBy = null;\n        this.bought = false;\n\n    }\n\n    public String getItemName() { return itemName; }\n\n    public String getOwner() {\n        return owner;\n    }\n\n    public String getBoughtBy() {\n        return boughtBy;\n    }\n\n    public boolean isBought() {\n        return bought;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/User.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.model;\n\nimport java.util.HashMap;\n\n/**\n * Defines the data structure for User objects.\n */\npublic class User {\n    private String name;\n    private String email;\n    private HashMap<String, Object> timestampJoined;\n    private boolean hasLoggedInWithPassword;\n\n\n    /**\n     * Required public constructor\n     */\n    public User() {\n    }\n\n    /**\n     * Use this constructor to create new User.\n     * Takes user name, email and timestampJoined as params\n     *\n     * @param name\n     * @param email\n     * @param timestampJoined\n     */\n    public User(String name, String email, HashMap<String, Object> timestampJoined) {\n        this.name = name;\n        this.email = email;\n        this.timestampJoined = timestampJoined;\n        this.hasLoggedInWithPassword = false;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public HashMap<String, Object> getTimestampJoined() {\n        return timestampJoined;\n    }\n\n    public boolean isHasLoggedInWithPassword() {\n        return hasLoggedInWithPassword;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/BaseActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui;\n\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.res.Configuration;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.widget.LinearLayout;\n\nimport com.firebase.client.AuthData;\nimport com.firebase.client.Firebase;\nimport com.google.android.gms.auth.api.Auth;\nimport com.google.android.gms.auth.api.signin.GoogleSignInOptions;\nimport com.google.android.gms.common.ConnectionResult;\nimport com.google.android.gms.common.api.GoogleApiClient;\nimport com.google.android.gms.common.api.ResultCallback;\nimport com.google.android.gms.common.api.Status;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.ui.login.CreateAccountActivity;\nimport com.udacity.firebase.shoppinglistplusplus.ui.login.LoginActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\n/**\n * BaseActivity class is used as a base class for all activities in the app\n * It implements GoogleApiClient callbacks to enable \"Logout\" in all activities\n * and defines variables that are being shared across all activities\n */\npublic abstract class BaseActivity extends AppCompatActivity implements\n        GoogleApiClient.OnConnectionFailedListener {\n    protected String mProvider, mEncodedEmail;\n    /* Client used to interact with Google APIs. */\n    protected GoogleApiClient mGoogleApiClient;\n    protected Firebase.AuthStateListener mAuthListener;\n    protected Firebase mFirebaseRef;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        /* Setup the Google API object to allow Google logins */\n        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)\n                .requestEmail()\n                .build();\n\n        /**\n         * Build a GoogleApiClient with access to the Google Sign-In API and the\n         * options specified by gso.\n         */\n\n        /* Setup the Google API object to allow Google+ logins */\n        mGoogleApiClient = new GoogleApiClient.Builder(this)\n                .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)\n                .addApi(Auth.GOOGLE_SIGN_IN_API, gso)\n                .build();\n\n        /**\n         * Getting mProvider and mEncodedEmail from SharedPreferences\n         */\n        final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(BaseActivity.this);\n        /* Get mEncodedEmail and mProvider from SharedPreferences, use null as default value */\n        mEncodedEmail = sp.getString(Constants.KEY_ENCODED_EMAIL, null);\n        mProvider = sp.getString(Constants.KEY_PROVIDER, null);\n\n\n        if (!((this instanceof LoginActivity) || (this instanceof CreateAccountActivity))) {\n            mFirebaseRef = new Firebase(Constants.FIREBASE_URL);\n            mAuthListener = new Firebase.AuthStateListener() {\n                @Override\n                public void onAuthStateChanged(AuthData authData) {\n                     /* The user has been logged out */\n                    if (authData == null) {\n                        /* Clear out shared preferences */\n                        SharedPreferences.Editor spe = sp.edit();\n                        spe.putString(Constants.KEY_ENCODED_EMAIL, null);\n                        spe.putString(Constants.KEY_PROVIDER, null);\n                        \n                        takeUserToLoginScreenOnUnAuth();\n                    }\n                }\n            };\n            mFirebaseRef.addAuthStateListener(mAuthListener);\n        }\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        /* Cleanup the AuthStateListener */\n        if (!((this instanceof LoginActivity) || (this instanceof CreateAccountActivity))) {\n            mFirebaseRef.removeAuthStateListener(mAuthListener);\n        }\n\n    }\n\n    @Override\n    public void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        /* Inflate the menu; this adds items to the action bar if it is present. */\n        getMenuInflater().inflate(R.menu.menu_base, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        int id = item.getItemId();\n\n        if (id == android.R.id.home) {\n            super.onBackPressed();\n            return true;\n        }\n\n        if (id == R.id.action_logout) {\n            logout();\n            return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n    protected void initializeBackground(LinearLayout linearLayout) {\n\n        /**\n         * Set different background image for landscape and portrait layouts\n         */\n        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {\n            linearLayout.setBackgroundResource(R.drawable.background_loginscreen_land);\n        } else {\n            linearLayout.setBackgroundResource(R.drawable.background_loginscreen);\n        }\n    }\n\n    /**\n     * Logs out the user from their current session and starts LoginActivity.\n     * Also disconnects the mGoogleApiClient if connected and provider is Google\n     */\n    protected void logout() {\n\n        /* Logout if mProvider is not null */\n        if (mProvider != null) {\n            mFirebaseRef.unauth();\n\n            if (mProvider.equals(Constants.GOOGLE_PROVIDER)) {\n\n                /* Logout from Google+ */\n                Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(\n                        new ResultCallback<Status>() {\n                            @Override\n                            public void onResult(Status status) {\n                                //nothing\n                            }\n                        });\n            }\n        }\n    }\n\n    private void takeUserToLoginScreenOnUnAuth() {\n        /* Move user to LoginActivity, and remove the backstack */\n        Intent intent = new Intent(BaseActivity.this, LoginActivity.class);\n        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);\n        startActivity(intent);\n        finish();\n    }\n    \n    @Override\n    public void onConnectionFailed(ConnectionResult connectionResult) {\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/MainActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui;\n\nimport android.app.DialogFragment;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.design.widget.TabLayout;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentStatePagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\n\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ValueEventListener;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.ui.activeLists.AddListDialogFragment;\nimport com.udacity.firebase.shoppinglistplusplus.ui.activeLists.ShoppingListsFragment;\nimport com.udacity.firebase.shoppinglistplusplus.ui.meals.AddMealDialogFragment;\nimport com.udacity.firebase.shoppinglistplusplus.ui.meals.MealsFragment;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\n/**\n * Represents the home screen of the app which\n * has a {@link ViewPager} with {@link ShoppingListsFragment} and {@link MealsFragment}\n */\npublic class MainActivity extends BaseActivity {\n    private Firebase mUserRef;\n    private static final String LOG_TAG = MainActivity.class.getSimpleName();\n    private ValueEventListener mUserRefListener;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        /**\n         * Create Firebase references\n         */\n        mUserRef = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);\n\n        /**\n         * Link layout elements from XML and setup the toolbar\n         */\n        initializeScreen();\n\n        /**\n         * Add ValueEventListeners to Firebase references\n         * to control get data and control behavior and visibility of elements\n         */\n        mUserRefListener = mUserRef.addValueEventListener(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot snapshot) {\n                User user = snapshot.getValue(User.class);\n\n                /**\n                 * Set the activity title to current user name if user is not null\n                 */\n                if (user != null) {\n                    /* Assumes that the first word in the user's name is the user's first name. */\n                    String firstName = user.getName().split(\"\\\\s+\")[0];\n                    String title = firstName + \"'s Lists\";\n                    setTitle(title);\n                }\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n    }\n\n\n    /**\n     * Override onOptionsItemSelected to use main_menu instead of BaseActivity menu\n     *\n     * @param menu\n     */\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        /* Inflate the menu; this adds items to the action bar if it is present. */\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    /**\n     * Override onOptionsItemSelected to add action_settings only to the MainActivity\n     *\n     * @param item\n     */\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        int id = item.getItemId();\n        /**\n         * Open SettingsActivity with sort options when Sort icon was clicked\n         */\n        if (id == R.id.action_sort) {\n            startActivity(new Intent(MainActivity.this, SettingsActivity.class));\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        mUserRef.removeEventListener(mUserRefListener);\n    }\n\n    /**\n     * Link layout elements from XML and setup the toolbar\n     */\n    public void initializeScreen() {\n        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);\n        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);\n        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);\n        setSupportActionBar(toolbar);\n        /**\n         * Create SectionPagerAdapter, set it as adapter to viewPager with setOffscreenPageLimit(2)\n         **/\n        SectionPagerAdapter adapter = new SectionPagerAdapter(getSupportFragmentManager());\n        viewPager.setOffscreenPageLimit(2);\n        viewPager.setAdapter(adapter);\n        /**\n         * Setup the mTabLayout with view pager\n         */\n        tabLayout.setupWithViewPager(viewPager);\n    }\n\n    /**\n     * Create an instance of the AddList dialog fragment and show it\n     */\n    public void showAddListDialog(View view) {\n        /* Create an instance of the dialog fragment and show it */\n        DialogFragment dialog = AddListDialogFragment.newInstance(mEncodedEmail);\n        dialog.show(MainActivity.this.getFragmentManager(), \"AddListDialogFragment\");\n    }\n\n    /**\n     * Create an instance of the AddMeal dialog fragment and show it\n     */\n    public void showAddMealDialog(View view) {\n        /* Create an instance of the dialog fragment and show it */\n        DialogFragment dialog = AddMealDialogFragment.newInstance();\n        dialog.show(MainActivity.this.getFragmentManager(), \"AddMealDialogFragment\");\n    }\n\n    /**\n     * SectionPagerAdapter class that extends FragmentStatePagerAdapter to save fragments state\n     */\n    public class SectionPagerAdapter extends FragmentStatePagerAdapter {\n\n        public SectionPagerAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        /**\n         * Use positions (0 and 1) to find and instantiate fragments with newInstance()\n         *\n         * @param position\n         */\n        @Override\n        public Fragment getItem(int position) {\n\n            Fragment fragment = null;\n\n            /**\n             * Set fragment to different fragments depending on position in ViewPager\n             */\n            switch (position) {\n                case 0:\n                    fragment = ShoppingListsFragment.newInstance(mEncodedEmail);\n                    break;\n                case 1:\n                    fragment = MealsFragment.newInstance();\n                    break;\n                default:\n                    fragment = ShoppingListsFragment.newInstance(mEncodedEmail);\n                    break;\n            }\n\n            return fragment;\n        }\n\n\n        @Override\n        public int getCount() {\n            return 2;\n        }\n\n        /**\n         * Set string resources as titles for each fragment by it's position\n         *\n         * @param position\n         */\n        @Override\n        public CharSequence getPageTitle(int position) {\n            switch (position) {\n                case 0:\n                    return getString(R.string.pager_title_shopping_lists);\n                case 1:\n                default:\n                    return getString(R.string.pager_title_meals);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/SettingsActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui;\n\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.preference.ListPreference;\nimport android.preference.Preference;\nimport android.preference.PreferenceActivity;\nimport android.preference.PreferenceFragment;\nimport android.preference.PreferenceManager;\n\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\n/**\n * SettingsActivity represents preference screen and functionality\n */\npublic class SettingsActivity extends PreferenceActivity {\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setTheme(R.style.PrefScreenTheme);\n\n        getFragmentManager().beginTransaction()\n                .replace(android.R.id.content, new SortPreferenceFragment())\n                .commit();\n    }\n\n    /**\n     * This fragment shows the preferences for the first header.\n     */\n    public static class SortPreferenceFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {\n        @Override\n        public void onCreate(Bundle savedInstanceState) {\n            super.onCreate(savedInstanceState);\n            /* Load the preferences from an XML resource */\n            addPreferencesFromResource(R.xml.preference_screen);\n\n            /**\n             * Bind preference summary to value for lists and meals sorting list preferences\n             */\n            bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_name_sort_order_lists)));\n        }\n\n        /**\n         * When preference is changed, save it's new value to default shared preferences\n         *\n         * @param preference\n         * @param newValue\n         */\n        @Override\n        public boolean onPreferenceChange(Preference preference, Object newValue) {\n            setPreferenceSummary(preference, newValue);\n            SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());\n            SharedPreferences.Editor spe = sharedPref.edit();\n            spe.putString(Constants.KEY_PREF_SORT_ORDER_LISTS, newValue.toString()).apply();\n            return true;\n        }\n\n        private void bindPreferenceSummaryToValue(Preference preference) {\n        /* Set the listener to watch for value changes. */\n            preference.setOnPreferenceChangeListener(this);\n            /* Trigger the listener immediately with the preference's current value. */\n            setPreferenceSummary(preference,\n                    PreferenceManager\n                            .getDefaultSharedPreferences(preference.getContext())\n                            .getString(preference.getKey(), \"\"));\n        }\n\n        /**\n         * Sets preference summary to appropriate value\n         *\n         * @param preference\n         * @param value\n         */\n        private void setPreferenceSummary(Preference preference, Object value) {\n            String stringValue = value.toString();\n\n            if (preference instanceof ListPreference) {\n                ListPreference listPreference = (ListPreference) preference;\n                int prefIndex = listPreference.findIndexOfValue(stringValue);\n\n                if (prefIndex >= 0) {\n                    preference.setSummary(listPreference.getEntries()[prefIndex]);\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Activity;\nimport android.app.DialogFragment;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.widget.Toolbar;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.Button;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ValueEventListener;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;\nimport com.udacity.firebase.shoppinglistplusplus.ui.sharing.ShareListActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Represents the details screen for the selected shopping list\n */\npublic class ActiveListDetailsActivity extends BaseActivity {\n    private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName();\n    private Firebase mCurrentListRef, mCurrentUserRef, mSharedWithRef;\n    private ActiveListItemAdapter mActiveListItemAdapter;\n    private Button mButtonShopping;\n    private TextView mTextViewPeopleShopping;\n    private ListView mListView;\n    private String mListId;\n    private User mCurrentUser;\n    /* Stores whether the current user is shopping */\n    private boolean mShopping = false;\n    /* Stores whether the current user is the owner */\n    private boolean mCurrentUserIsOwner = false;\n    private ShoppingList mShoppingList;\n    private ValueEventListener mCurrentUserRefListener, mCurrentListRefListener, mSharedWithListener;\n    private HashMap<String, User> mSharedWithUsers;\n\n    @Override\n    protected void onCreate(final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_active_list_details);\n\n        /* Get the push ID from the extra passed by ShoppingListFragment */\n        Intent intent = this.getIntent();\n        mListId = intent.getStringExtra(Constants.KEY_LIST_ID);\n        if (mListId == null) {\n            /* No point in continuing without a valid ID. */\n            finish();\n            return;\n        }\n\n        /**\n         * Create Firebase references\n         */\n        mCurrentListRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS).child(mEncodedEmail).child(mListId);\n        mCurrentUserRef = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);\n        mSharedWithRef = new Firebase (Constants.FIREBASE_URL_LISTS_SHARED_WITH).child(mListId);\n        Firebase listItemsRef = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS).child(mListId);\n\n\n        /**\n         * Link layout elements from XML and setup the toolbar\n         */\n        initializeScreen();\n\n\n        /**\n         * Setup the adapter\n         */\n        mActiveListItemAdapter = new ActiveListItemAdapter(this, ShoppingListItem.class,\n                R.layout.single_active_list_item, listItemsRef.orderByChild(Constants.FIREBASE_PROPERTY_BOUGHT_BY),\n                mListId, mEncodedEmail);\n        /* Create ActiveListItemAdapter and set to listView */\n        mListView.setAdapter(mActiveListItemAdapter);\n\n\n        /**\n         * Add ValueEventListeners to Firebase references\n         * to control get data and control behavior and visibility of elements\n         */\n\n        /* Save the most up-to-date version of current user in mCurrentUser */\n        mCurrentUserRefListener = mCurrentUserRef.addValueEventListener(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot dataSnapshot) {\n                User currentUser = dataSnapshot.getValue(User.class);\n                if (currentUser != null) mCurrentUser = currentUser;\n                else finish();\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n        final Activity thisActivity = this;\n\n\n        /**\n         * Save the most recent version of current shopping list into mShoppingList instance\n         * variable an update the UI to match the current list.\n         */\n        mCurrentListRefListener = mCurrentListRef.addValueEventListener(new ValueEventListener() {\n\n            @Override\n            public void onDataChange(DataSnapshot snapshot) {\n\n                /**\n                 * Saving the most recent version of current shopping list into mShoppingList if present\n                 * finish() the activity if the list is null (list was removed or unshared by it's owner\n                 * while current user is in the list details activity)\n                 */\n                ShoppingList shoppingList = snapshot.getValue(ShoppingList.class);\n\n                if (shoppingList == null) {\n                    finish();\n                    /**\n                     * Make sure to call return, otherwise the rest of the method will execute,\n                     * even after calling finish.\n                     */\n                    return;\n                }\n                mShoppingList = shoppingList;\n                /**\n                 * Pass the shopping list to the adapter if it is not null.\n                 * We do this here because mShoppingList is null when first created.\n                 */\n                mActiveListItemAdapter.setShoppingList(mShoppingList);\n\n                /* Check if the current user is owner */\n                mCurrentUserIsOwner = Utils.checkIfOwner(shoppingList, mEncodedEmail);\n\n\n                /* Calling invalidateOptionsMenu causes onCreateOptionsMenu to be called */\n                invalidateOptionsMenu();\n\n                /* Set title appropriately. */\n                setTitle(shoppingList.getListName());\n\n                HashMap<String, User> usersShopping = mShoppingList.getUsersShopping();\n                if (usersShopping != null && usersShopping.size() != 0 &&\n                        usersShopping.containsKey(mEncodedEmail)) {\n                    mShopping = true;\n                    mButtonShopping.setText(getString(R.string.button_stop_shopping));\n                    mButtonShopping.setBackgroundColor(ContextCompat.getColor(ActiveListDetailsActivity.this, R.color.dark_grey));\n                } else {\n                    mButtonShopping.setText(getString(R.string.button_start_shopping));\n                    mButtonShopping.setBackgroundColor(ContextCompat.getColor(ActiveListDetailsActivity.this, R.color.primary_dark));\n                    mShopping = false;\n\n                }\n\n                setWhosShoppingText(mShoppingList.getUsersShopping());\n\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n        mSharedWithListener = mSharedWithRef.addValueEventListener(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot dataSnapshot) {\n                mSharedWithUsers = new HashMap<String, User>();\n                for (DataSnapshot currentUser : dataSnapshot.getChildren()) {\n                    mSharedWithUsers.put(currentUser.getKey(), currentUser.getValue(User.class));\n                }\n                mActiveListItemAdapter.setSharedWithUsers(mSharedWithUsers);\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n        /**\n         * Set up click listeners for interaction.\n         */\n\n        /* Show edit list item name dialog on listView item long click event */\n        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {\n\n            @Override\n            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {\n                /* Check that the view is not the empty footer item */\n                if (view.getId() != R.id.list_view_footer_empty) {\n                    ShoppingListItem shoppingListItem = mActiveListItemAdapter.getItem(position);\n\n                    if (shoppingListItem != null) {\n                        /*\n                        If the person is the owner and not shopping and the item is not bought, then\n                        they can edit it.\n                         */\n                        if (shoppingListItem.getOwner().equals(mEncodedEmail) && !mShopping && !shoppingListItem.isBought()) {\n                            String itemName = shoppingListItem.getItemName();\n                            String itemId = mActiveListItemAdapter.getRef(position).getKey();\n                            showEditListItemNameDialog(itemName, itemId);\n                            return true;\n                        }\n                    }\n                }\n                return false;\n            }\n        });\n\n        /* Perform buy/return action on listView item click event if current user is shopping. */\n        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                /* Check that the view is not the empty footer item */\n                if (view.getId() != R.id.list_view_footer_empty) {\n                    final ShoppingListItem selectedListItem = mActiveListItemAdapter.getItem(position);\n                    String itemId = mActiveListItemAdapter.getRef(position).getKey();\n\n                    if (selectedListItem != null) {\n                        /* If current user is shopping */\n                        if (mShopping) {\n\n                            /* Create map and fill it in with deep path multi write operations list */\n                            HashMap<String, Object> updatedItemBoughtData = new HashMap<String, Object>();\n\n                            /* Buy selected item if it is NOT already bought */\n                            if (!selectedListItem.isBought()) {\n                                updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT, true);\n                                updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT_BY, mEncodedEmail);\n                            } else {\n                                /* Return selected item only if it was bought by current user */\n                                if (selectedListItem.getBoughtBy().equals(mEncodedEmail)) {\n                                    updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT, false);\n                                    updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT_BY, null);\n                                }\n                            }\n\n                            /* Do update */\n                            Firebase firebaseItemLocation = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS)\n                                    .child(mListId).child(itemId);\n                            firebaseItemLocation.updateChildren(updatedItemBoughtData, new Firebase.CompletionListener() {\n                                @Override\n                                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                                    if (firebaseError != null) {\n                                        Log.d(LOG_TAG, getString(R.string.log_error_updating_data) +\n                                                firebaseError.getMessage());\n                                    }\n                                }\n                            });\n                        }\n                    }\n                }\n            }\n        });\n\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        /* Inflate the menu; this adds items to the action bar if it is present. */\n        getMenuInflater().inflate(R.menu.menu_list_details, menu);\n\n        /**\n         * Get menu items\n         */\n        MenuItem remove = menu.findItem(R.id.action_remove_list);\n        MenuItem edit = menu.findItem(R.id.action_edit_list_name);\n        MenuItem share = menu.findItem(R.id.action_share_list);\n        MenuItem archive = menu.findItem(R.id.action_archive);\n\n        /* Only the edit and remove options are implemented */\n        remove.setVisible(mCurrentUserIsOwner);\n        edit.setVisible(mCurrentUserIsOwner);\n        share.setVisible(mCurrentUserIsOwner);\n        archive.setVisible(false);\n\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        int id = item.getItemId();\n\n        /**\n         * Show edit list dialog when the edit action is selected\n         */\n        if (id == R.id.action_edit_list_name) {\n            showEditListNameDialog();\n            return true;\n        }\n\n        /**\n         * removeList() when the remove action is selected\n         */\n        if (id == R.id.action_remove_list) {\n            removeList();\n            return true;\n        }\n\n        /**\n         * Eventually we'll add this\n         */\n        if (id == R.id.action_share_list) {\n            Intent intent = new Intent(ActiveListDetailsActivity.this, ShareListActivity.class);\n            intent.putExtra(Constants.KEY_LIST_ID, mListId);\n            /* Starts an active showing the details for the selected list */\n            startActivity(intent);\n            return true;\n        }\n\n        /**\n         * archiveList() when the archive action is selected\n         */\n        if (id == R.id.action_archive) {\n            archiveList();\n            return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n\n    /**\n     * Cleanup when the activity is destroyed.\n     */\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        mActiveListItemAdapter.cleanup();\n        mCurrentListRef.removeEventListener(mCurrentListRefListener);\n        mCurrentUserRef.removeEventListener(mCurrentUserRefListener);\n        mSharedWithRef.removeEventListener(mSharedWithListener);\n    }\n\n    /**\n     * Link layout elements from XML and setup the toolbar\n     */\n    private void initializeScreen() {\n        mListView = (ListView) findViewById(R.id.list_view_shopping_list_items);\n        mTextViewPeopleShopping = (TextView) findViewById(R.id.text_view_people_shopping);\n        mButtonShopping = (Button) findViewById(R.id.button_shopping);\n        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);\n        /* Common toolbar setup */\n        setSupportActionBar(toolbar);\n        /* Add back button to the action bar */\n        if (getSupportActionBar() != null) {\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n        /* Inflate the footer, set root layout to null*/\n        View footer = getLayoutInflater().inflate(R.layout.footer_empty, null);\n        mListView.addFooterView(footer);\n    }\n\n    /**\n     * Set appropriate text for Start/Stop shopping button and Who's shopping textView\n     * depending on the current user shopping status\n     */\n    private void setWhosShoppingText(HashMap<String, User> usersShopping) {\n\n        if (usersShopping != null) {\n            ArrayList<String> usersWhoAreNotYou = new ArrayList<>();\n            /**\n             * If at least one user is shopping\n             * Add userName to the list of users shopping if this user is not current user\n             */\n            for (User user : usersShopping.values()) {\n                if (user != null && !(user.getEmail().equals(mEncodedEmail))) {\n                    usersWhoAreNotYou.add(user.getName());\n                }\n            }\n\n            int numberOfUsersShopping = usersShopping.size();\n            String usersShoppingText;\n\n            /**\n             * If current user is shopping...\n             * If current user is the only person shopping, set text to \"You are shopping\"\n             * If current user and one user are shopping, set text \"You and userName are shopping\"\n             * Else set text \"You and N others shopping\"\n             */\n            if (mShopping) {\n                switch (numberOfUsersShopping) {\n                    case 1:\n                        usersShoppingText = getString(R.string.text_you_are_shopping);\n                        break;\n                    case 2:\n                        usersShoppingText = String.format(\n                                getString(R.string.text_you_and_other_are_shopping),\n                                usersWhoAreNotYou.get(0));\n                        break;\n                    default:\n                        usersShoppingText = String.format(\n                                getString(R.string.text_you_and_number_are_shopping),\n                                usersWhoAreNotYou.size());\n                }\n                /**\n                 * If current user is not shopping..\n                 * If there is only one person shopping, set text to \"userName is shopping\"\n                 * If there are two users shopping, set text \"userName1 and userName2 are shopping\"\n                 * Else set text \"userName and N others shopping\"\n                 */\n            } else {\n                switch (numberOfUsersShopping) {\n                    case 1:\n                        usersShoppingText = String.format(\n                                getString(R.string.text_other_is_shopping),\n                                usersWhoAreNotYou.get(0));\n                        break;\n                    case 2:\n                        usersShoppingText = String.format(\n                                getString(R.string.text_other_and_other_are_shopping),\n                                usersWhoAreNotYou.get(0),\n                                usersWhoAreNotYou.get(1));\n                        break;\n                    default:\n                        usersShoppingText = String.format(\n                                getString(R.string.text_other_and_number_are_shopping),\n                                usersWhoAreNotYou.get(0),\n                                usersWhoAreNotYou.size() - 1);\n                }\n            }\n            mTextViewPeopleShopping.setText(usersShoppingText);\n        } else {\n            mTextViewPeopleShopping.setText(\"\");\n        }\n    }\n\n\n    /**\n     * Archive current list when user selects \"Archive\" menu item\n     */\n    public void archiveList() {\n    }\n\n\n    /**\n     * Start AddItemsFromMealActivity to add meal ingredients into the shopping list\n     * when the user taps on \"add meal\" fab\n     */\n    public void addMeal(View view) {\n    }\n\n    /**\n     * Remove current shopping list and its items from all nodes\n     */\n    public void removeList() {\n        /* Create an instance of the dialog fragment and show it */\n        DialogFragment dialog = RemoveListDialogFragment.newInstance(mShoppingList, mListId,\n                mSharedWithUsers);\n        dialog.show(getFragmentManager(), \"RemoveListDialogFragment\");\n    }\n\n    /**\n     * Show the add list item dialog when user taps \"Add list item\" fab\n     */\n    public void showAddListItemDialog(View view) {\n        /* Create an instance of the dialog fragment and show it */\n        DialogFragment dialog = AddListItemDialogFragment.newInstance(mShoppingList, mListId,\n                mEncodedEmail, mSharedWithUsers);\n        dialog.show(getFragmentManager(), \"AddListItemDialogFragment\");\n    }\n\n    /**\n     * Show edit list name dialog when user selects \"Edit list name\" menu item\n     */\n    public void showEditListNameDialog() {\n        /* Create an instance of the dialog fragment and show it */\n        DialogFragment dialog = EditListNameDialogFragment.newInstance(mShoppingList, mListId,\n                mEncodedEmail, mSharedWithUsers);\n        dialog.show(this.getFragmentManager(), \"EditListNameDialogFragment\");\n    }\n\n    /**\n     * Show the edit list item name dialog after longClick on the particular item\n     *\n     * @param itemName\n     * @param itemId\n     */\n    public void showEditListItemNameDialog(String itemName, String itemId) {\n        /* Create an instance of the dialog fragment and show it */\n        DialogFragment dialog = EditListItemNameDialogFragment.newInstance(mShoppingList, itemName,\n                itemId, mListId, mEncodedEmail, mSharedWithUsers);\n\n        dialog.show(this.getFragmentManager(), \"EditListItemNameDialogFragment\");\n    }\n\n    /**\n     * This method is called when user taps \"Start/Stop shopping\" button\n     */\n    public void toggleShopping(View view) {\n        /**\n         * Create map and fill it in with deep path multi write operations list\n         */\n        HashMap<String, Object> updatedUserData = new HashMap<String, Object>();\n        String propertyToUpdate = Constants.FIREBASE_PROPERTY_USERS_SHOPPING + \"/\" + mEncodedEmail;\n\n        /**\n         * If current user is already shopping, remove current user from usersShopping map\n         */\n        if (mShopping) {\n\n            /* Add the value to update at the specified property for all lists */\n            Utils.updateMapForAllWithValue(mSharedWithUsers,\n                    mListId, mShoppingList.getOwner(), updatedUserData,\n                    propertyToUpdate, null);\n\n            /* Appends the timestamp changes for all lists */\n            Utils.updateMapWithTimestampLastChanged(mSharedWithUsers,\n                    mListId, mShoppingList.getOwner(), updatedUserData);\n\n\n            /* Do a deep-path update */\n            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {\n                @Override\n                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                    /* Updates the reversed timestamp */\n                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId, mSharedWithUsers,\n                            mShoppingList.getOwner());\n                }\n            });\n        } else {\n            /**\n             * If current user is not shopping, create map to represent User model add to usersShopping map\n             */\n            HashMap<String, Object> currentUser = (HashMap<String, Object>)\n                    new ObjectMapper().convertValue(mCurrentUser, Map.class);\n\n            /* Add the value to update at the specified property for all lists */\n            Utils.updateMapForAllWithValue(mSharedWithUsers,\n                    mListId, mShoppingList.getOwner(), updatedUserData, propertyToUpdate, currentUser);\n\n            /* Appends the timestamp changes for all lists */\n            Utils.updateMapWithTimestampLastChanged(mSharedWithUsers,\n                    mListId, mShoppingList.getOwner(), updatedUserData);\n\n            /* Do a deep-path update */\n            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {\n                @Override\n                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                    /* Updates the reversed timestamp */\n                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId, mSharedWithUsers,\n                            mShoppingList.getOwner());\n                }\n            });\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Activity;\nimport android.content.DialogInterface;\nimport android.graphics.Paint;\nimport android.support.v7.app.AlertDialog;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.ImageButton;\nimport android.widget.TextView;\n\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.Query;\nimport com.firebase.client.ValueEventListener;\nimport com.firebase.ui.FirebaseListAdapter;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\n\n\n/**\n * Populates list_view_shopping_list_items inside ActiveListDetailsActivity\n */\npublic class ActiveListItemAdapter extends FirebaseListAdapter<ShoppingListItem> {\n    private ShoppingList mShoppingList;\n    private String mListId;\n    private String mEncodedEmail;\n    private HashMap<String, User> mSharedWithUsers;\n\n    /**\n     * Public constructor that initializes private instance variables when adapter is created\n     */\n    public ActiveListItemAdapter(Activity activity, Class<ShoppingListItem> modelClass, int modelLayout,\n                                 Query ref, String listId, String encodedEmail) {\n        super(activity, modelClass, modelLayout, ref);\n        this.mActivity = activity;\n        this.mListId = listId;\n        this.mEncodedEmail = encodedEmail;\n    }\n\n    /**\n     * Public method that is used to pass shoppingList object when it is loaded in ValueEventListener\n     */\n    public void setShoppingList(ShoppingList shoppingList) {\n        this.mShoppingList = shoppingList;\n        this.notifyDataSetChanged();\n    }\n\n    public void setSharedWithUsers(HashMap<String, User> sharedWithUsers) {\n        this.mSharedWithUsers = sharedWithUsers;\n        this.notifyDataSetChanged();\n    }\n\n    /**\n     * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete)\n     * with items inflated from single_active_list_item.xml\n     * populateView also handles data changes and updates the listView accordingly\n     */\n    @Override\n    protected void populateView(View view, final ShoppingListItem item, int position) {\n\n        ImageButton buttonRemoveItem = (ImageButton) view.findViewById(R.id.button_remove_item);\n        TextView textViewItemName = (TextView) view.findViewById(R.id.text_view_active_list_item_name);\n        final TextView textViewBoughtByUser = (TextView) view.findViewById(R.id.text_view_bought_by_user);\n        TextView textViewBoughtBy = (TextView) view.findViewById(R.id.text_view_bought_by);\n\n        String owner = item.getOwner();\n\n        textViewItemName.setText(item.getItemName());\n\n\n        setItemAppearanceBaseOnBoughtStatus(owner, textViewBoughtByUser, textViewBoughtBy, buttonRemoveItem,\n                textViewItemName, item);\n\n\n        /* Gets the id of the item to remove */\n        final String itemToRemoveId = this.getRef(position).getKey();\n\n        /**\n         * Set the on click listener for \"Remove list item\" button\n         */\n        buttonRemoveItem.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n\n                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity, R.style.CustomTheme_Dialog)\n                        .setTitle(mActivity.getString(R.string.remove_item_option))\n                        .setMessage(mActivity.getString(R.string.dialog_message_are_you_sure_remove_item))\n                        .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {\n                            public void onClick(DialogInterface dialog, int which) {\n\n                                removeItem(itemToRemoveId);\n                                /* Dismiss the dialog */\n                                dialog.dismiss();\n                            }\n                        })\n                        .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {\n                            public void onClick(DialogInterface dialog, int which) {\n                                /* Dismiss the dialog */\n                                dialog.dismiss();\n                            }\n                        })\n                        .setIcon(android.R.drawable.ic_dialog_alert);\n\n                AlertDialog alertDialog = alertDialogBuilder.create();\n                alertDialog.show();\n            }\n        });\n    }\n\n    private void removeItem(String itemId) {\n        Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n        /* Make a map for the removal */\n        HashMap<String, Object> updatedRemoveItemMap = new HashMap<String, Object>();\n\n        /* Remove the item by passing null */\n        updatedRemoveItemMap.put(\"/\" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + \"/\"\n                + mListId + \"/\" + itemId, null);\n\n        /* Add the updated timestamp */\n        Utils.updateMapWithTimestampLastChanged(mSharedWithUsers,\n                mListId, mShoppingList.getOwner(), updatedRemoveItemMap);\n\n        /* Do the update */\n        firebaseRef.updateChildren(updatedRemoveItemMap, new Firebase.CompletionListener() {\n            @Override\n            public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                Utils.updateTimestampReversed(firebaseError, \"ActListItemAdap\", mListId,\n                        mSharedWithUsers, mShoppingList.getOwner());\n            }\n        });\n    }\n\n    private void setItemAppearanceBaseOnBoughtStatus(String owner, final TextView textViewBoughtByUser,\n                                                     TextView textViewBoughtBy, ImageButton buttonRemoveItem,\n                                                     TextView textViewItemName, ShoppingListItem item) {\n        /**\n         * If selected item is bought\n         * Set \"Bought by\" text to \"You\" if current user is owner of the list\n         * Set \"Bought by\" text to userName if current user is NOT owner of the list\n         * Set the remove item button invisible if current user is NOT list or item owner\n         */\n        if (item.isBought() && item.getBoughtBy() != null) {\n\n            textViewBoughtBy.setVisibility(View.VISIBLE);\n            textViewBoughtByUser.setVisibility(View.VISIBLE);\n            buttonRemoveItem.setVisibility(View.INVISIBLE);\n\n            /* Add a strike-through */\n            textViewItemName.setPaintFlags(textViewItemName.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);\n\n            if (item.getBoughtBy().equals(mEncodedEmail)) {\n                textViewBoughtByUser.setText(mActivity.getString(R.string.text_you));\n            } else {\n\n                Firebase boughtByUserRef = new Firebase(Constants.FIREBASE_URL_USERS).child(item.getBoughtBy());\n                /* Get the item's owner's name; use a SingleValueEvent listener for memory efficiency */\n                boughtByUserRef.addListenerForSingleValueEvent(new ValueEventListener() {\n                    @Override\n                    public void onDataChange(DataSnapshot dataSnapshot) {\n                        User user = dataSnapshot.getValue(User.class);\n                        if (user != null) {\n                            textViewBoughtByUser.setText(user.getName());\n                        }\n                    }\n\n                    @Override\n                    public void onCancelled(FirebaseError firebaseError) {\n                        Log.e(mActivity.getClass().getSimpleName(),\n                                mActivity.getString(R.string.log_error_the_read_failed) +\n                                        firebaseError.getMessage());\n                    }\n                });\n            }\n        } else {\n            /**\n             * If selected item is NOT bought\n             * Set \"Bought by\" text to be empty and invisible\n             * Set the remove item button visible if current user is owner of the list or selected item\n             */\n\n            /* Remove the strike-through */\n            textViewItemName.setPaintFlags(textViewItemName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));\n\n            textViewBoughtBy.setVisibility(View.INVISIBLE);\n            textViewBoughtByUser.setVisibility(View.INVISIBLE);\n            textViewBoughtByUser.setText(\"\");\n            /**\n             * If you are the owner of the item or the owner of the list, then the remove icon\n             * is visible.\n             */\n            if (owner.equals(mEncodedEmail) || (mShoppingList != null && mShoppingList.getOwner().equals(mEncodedEmail))) {\n                buttonRemoveItem.setVisibility(View.VISIBLE);\n            } else {\n                buttonRemoveItem.setVisibility(View.INVISIBLE);\n            }\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.os.Bundle;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Lets user add new list item.\n */\npublic class AddListItemDialogFragment extends EditListDialogFragment {\n\n    /**\n     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created\n     */\n    public static AddListItemDialogFragment newInstance(ShoppingList shoppingList, String listId,\n                                                        String encodedEmail,\n                                                        HashMap<String, User> sharedWithUsers) {\n        AddListItemDialogFragment addListItemDialogFragment = new AddListItemDialogFragment();\n        Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList,\n                R.layout.dialog_add_item, listId, encodedEmail, sharedWithUsers);\n        addListItemDialogFragment.setArguments(bundle);\n\n        return addListItemDialogFragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n    }\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        /** {@link EditListDialogFragment#createDialogHelper(int)} is a\n         * superclass method that creates the dialog\n         **/\n        return super.createDialogHelper(R.string.positive_button_add_list_item);\n    }\n\n    /**\n     * Adds new item to the current shopping list\n     */\n    @Override\n    protected void doListEdit() {\n        String mItemName = mEditTextForList.getText().toString();\n        /**\n         * Adds list item if the input name is not empty\n         */\n        if (!mItemName.equals(\"\")) {\n\n            Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n            Firebase itemsRef = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS).child(mListId);\n\n            /* Make a map for the item you are adding */\n            HashMap<String, Object> updatedItemToAddMap = new HashMap<String, Object>();\n\n            /* Save push() to maintain same random Id */\n            Firebase newRef = itemsRef.push();\n            String itemId = newRef.getKey();\n\n            /* Make a POJO for the item and immediately turn it into a HashMap */\n            ShoppingListItem itemToAddObject = new ShoppingListItem(mItemName, mEncodedEmail);\n            HashMap<String, Object> itemToAdd =\n                    (HashMap<String, Object>) new ObjectMapper().convertValue(itemToAddObject, Map.class);\n\n\n            /* Add the item to the update map*/\n            updatedItemToAddMap.put(\"/\" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + \"/\"\n                    + mListId + \"/\" + itemId, itemToAdd);\n\n            /* Update affected lists timestamps */\n            Utils.updateMapWithTimestampLastChanged(mSharedWith,\n                    mListId, mOwner, updatedItemToAddMap);\n\n            /* Do the update */\n            firebaseRef.updateChildren(updatedItemToAddMap, new Firebase.CompletionListener() {\n                @Override\n                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                    /* Now that we have the timestamp, update the reversed timestamp */\n                    Utils.updateTimestampReversed(firebaseError, \"AddListItem\", mListId,\n                            mSharedWith, mOwner);\n                }\n            });\n\n            /**\n             * Close the dialog fragment when done\n             */\n            AddListItemDialogFragment.this.getDialog().cancel();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.support.v7.app.AlertDialog;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\nimport java.util.HashMap;\n\n/**\n * Base class for {@link DialogFragment}s involved with editing a shopping list.\n */\npublic abstract class EditListDialogFragment extends DialogFragment {\n    String mListId, mOwner, mEncodedEmail;\n    EditText mEditTextForList;\n    int mResource;\n    HashMap mSharedWith;\n\n    /**\n     * Helper method that creates a basic bundle of all of the information needed to change\n     * values in a shopping list.\n     *\n     * @param shoppingList The shopping list that the dialog is editing\n     * @param resource The xml layout file associated with the dialog\n     * @param listId The id of the shopping list the dialog is editing\n     * @param encodedEmail The encoded email of the current user\n     * @param sharedWithUsers The HashMap containing all users that the current shopping list\n     *                        is shared with\n     * @return The bundle containing all the arguments.\n     */\n    protected static Bundle newInstanceHelper(ShoppingList shoppingList, int resource, String listId,\n                                              String encodedEmail, HashMap<String, User> sharedWithUsers) {\n        Bundle bundle = new Bundle();\n        bundle.putSerializable(Constants.KEY_SHARED_WITH_USERS, sharedWithUsers);\n        bundle.putString(Constants.KEY_LIST_ID, listId);\n        bundle.putInt(Constants.KEY_LAYOUT_RESOURCE, resource);\n        bundle.putString(Constants.KEY_LIST_OWNER, shoppingList.getOwner());\n        bundle.putString(Constants.KEY_ENCODED_EMAIL, encodedEmail);\n        return bundle;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mSharedWith = (HashMap) getArguments().getSerializable(Constants.KEY_SHARED_WITH_USERS);\n        mListId = getArguments().getString(Constants.KEY_LIST_ID);\n        mResource = getArguments().getInt(Constants.KEY_LAYOUT_RESOURCE);\n        mOwner = getArguments().getString(Constants.KEY_LIST_OWNER);\n        mEncodedEmail = getArguments().getString(Constants.KEY_ENCODED_EMAIL);\n    }\n\n    /**\n     * Open the keyboard automatically when the dialog fragment is opened\n     */\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);\n    }\n\n    protected Dialog createDialogHelper(int stringResourceForPositiveButton) {\n        /* Use the Builder class for convenient dialog construction */\n        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog);\n        /* Get the layout inflater */\n        LayoutInflater inflater = getActivity().getLayoutInflater();\n        /* Inflate the layout, set root ViewGroup to null*/\n        View rootView = inflater.inflate(mResource, null);\n        mEditTextForList = (EditText) rootView.findViewById(R.id.edit_text_list_dialog);\n\n        /**\n         * Call doListEdit() when user taps \"Done\" keyboard action\n         */\n        mEditTextForList.setOnEditorActionListener(new TextView.OnEditorActionListener() {\n            @Override\n            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {\n                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {\n                    doListEdit();\n\n                    /**\n                     * Close the dialog fragment when done\n                     */\n                    EditListDialogFragment.this.getDialog().cancel();\n                }\n                return true;\n            }\n        });\n        /* Inflate and set the layout for the dialog */\n        /* Pass null as the parent view because its going in the dialog layout */\n        builder.setView(rootView)\n                /* Add action buttons */\n                .setPositiveButton(stringResourceForPositiveButton, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int id) {\n                        doListEdit();\n\n                        /**\n                         * Close the dialog fragment\n                         */\n                        EditListDialogFragment.this.getDialog().cancel();\n                    }\n                })\n                .setNegativeButton(R.string.negative_button_cancel, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int id) {\n\n                        /**\n                         * Close the dialog fragment\n                         */\n                        EditListDialogFragment.this.getDialog().cancel();\n                    }\n                });\n\n        return builder.create();\n    }\n\n    /**\n     * Set the EditText text to be the inputted text\n     * and put the pointer at the end of the input\n     *\n     * @param defaultText\n     */\n    protected void helpSetDefaultValueEditText(String defaultText) {\n        mEditTextForList.setText(defaultText);\n        mEditTextForList.setSelection(defaultText.length());\n    }\n\n    /**\n     * Method to be overriden with whatever edit is supposed to happen to the list\n     */\n    protected abstract void doListEdit();\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.os.Bundle;\n\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\n\n/**\n * Lets user edit list item name for all copies of the current list\n */\npublic class EditListItemNameDialogFragment extends EditListDialogFragment {\n    String mItemName, mItemId;\n\n    /**\n     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created\n     */\n    public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingList, String itemName,\n                                                             String itemId, String listId, String encodedEmail,\n                                                             HashMap<String, User> sharedWithUsers) {\n        EditListItemNameDialogFragment editListItemNameDialogFragment = new EditListItemNameDialogFragment();\n\n        Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_item,\n                listId, encodedEmail, sharedWithUsers);\n        bundle.putString(Constants.KEY_LIST_ITEM_NAME, itemName);\n        bundle.putString(Constants.KEY_LIST_ITEM_ID, itemId);\n        editListItemNameDialogFragment.setArguments(bundle);\n\n        return editListItemNameDialogFragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mItemName = getArguments().getString(Constants.KEY_LIST_ITEM_NAME);\n        mItemId = getArguments().getString(Constants.KEY_LIST_ITEM_ID);\n    }\n\n\n    @Override\n\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        /** {@link EditListDialogFragment#createDialogHelper(int)} is a\n         * superclass method that creates the dialog\n         */\n        Dialog dialog = super.createDialogHelper(R.string.positive_button_edit_item);\n        /**\n         * {@link EditListDialogFragment#helpSetDefaultValueEditText(String)} is a superclass\n         * method that sets the default text of the TextView\n         */\n        super.helpSetDefaultValueEditText(mItemName);\n\n        return dialog;\n    }\n\n    /**\n     * Change selected list item name to the editText input if it is not empty\n     */\n    protected void doListEdit() {\n        String nameInput = mEditTextForList.getText().toString();\n\n        /**\n         * Set input text to be the current list item name if it is not empty and is not the\n         * previous name.\n         */\n        if (!nameInput.equals(\"\") && !nameInput.equals(mItemName)) {\n            Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n            /* Make a map for the item you are changing the name of */\n            HashMap<String, Object> updatedDataItemToEditMap = new HashMap<String, Object>();\n\n            /* Add the new name to the update map*/\n            updatedDataItemToEditMap.put(\"/\" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + \"/\"\n                            + mListId + \"/\" + mItemId + \"/\" + Constants.FIREBASE_PROPERTY_ITEM_NAME,\n                    nameInput);\n\n            /* Update affected lists timestamps */\n            Utils.updateMapWithTimestampLastChanged(mSharedWith, mListId, mOwner, updatedDataItemToEditMap);\n\n            /* Do the update */\n            firebaseRef.updateChildren(updatedDataItemToEditMap, new Firebase.CompletionListener() {\n                @Override\n                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                        /* Now that we have the timestamp, update the reversed timestamp */\n                    Utils.updateTimestampReversed(firebaseError, \"EditListItem\", mListId,\n                            mSharedWith, mOwner);\n                }\n            });\n\n\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.os.Bundle;\n\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\n\n/**\n * Lets user edit the list name for all copies of the current list\n */\npublic class EditListNameDialogFragment extends EditListDialogFragment {\n    private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName();\n    String mListName;\n\n    /**\n     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created\n     */\n    public static EditListNameDialogFragment newInstance(ShoppingList shoppingList, String listId,\n                                                         String encodedEmail,\n                                                         HashMap<String, User> sharedWithUsers) {\n        EditListNameDialogFragment editListNameDialogFragment = new EditListNameDialogFragment();\n        Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList,\n                R.layout.dialog_edit_list, listId, encodedEmail, sharedWithUsers);\n        bundle.putString(Constants.KEY_LIST_NAME, shoppingList.getListName());\n        editListNameDialogFragment.setArguments(bundle);\n        return editListNameDialogFragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mListName = getArguments().getString(Constants.KEY_LIST_NAME);\n    }\n\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n\n        /** {@link EditListDialogFragment#createDialogHelper(int)} is a\n         * superclass method that creates the dialog\n         **/\n        Dialog dialog = super.createDialogHelper(R.string.positive_button_edit_item);\n        /**\n         * {@link EditListDialogFragment#helpSetDefaultValueEditText(String)} is a superclass\n         * method that sets the default text of the TextView\n         */\n        helpSetDefaultValueEditText(mListName);\n        return dialog;\n    }\n\n    /**\n     * Changes the list name in all copies of the current list\n     */\n    protected void doListEdit() {\n        final String inputListName = mEditTextForList.getText().toString();\n        /**\n         * Check that the user inputted list name is not empty, has changed the original name\n         * and that the dialog was properly initialized with the current name and id of the list.\n         */\n        if (!inputListName.equals(\"\") && mListName != null &&\n                mListId != null && !inputListName.equals(mListName)) {\n\n            Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n            /**\n             * Create map and fill it in with deep path multi write operations list\n             */\n            HashMap<String, Object> updatedListData = new HashMap<String, Object>();\n\n            /* Add the value to update at the specified property for all lists */\n            Utils.updateMapForAllWithValue(mSharedWith, mListId, mOwner, updatedListData,\n                    Constants.FIREBASE_PROPERTY_LIST_NAME, inputListName);\n\n            /* Update affected lists timestamps */\n            Utils.updateMapWithTimestampLastChanged(mSharedWith, mListId, mOwner, updatedListData);\n\n            /* Do a deep-path update */\n            firebaseRef.updateChildren(updatedListData, new Firebase.CompletionListener() {\n                @Override\n                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                    /* Now that we have the timestamp, update the reversed timestamp */\n                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId,\n                            mSharedWith, mOwner);\n                }\n            });\n\n        }\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.support.v7.app.AlertDialog;\nimport android.util.Log;\n\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\n\n/**\n * Lets the user remove active shopping list\n */\npublic class RemoveListDialogFragment extends DialogFragment {\n    String mListId;\n    String mListOwner;\n    HashMap mSharedWith;\n\n    final static String LOG_TAG = RemoveListDialogFragment.class.getSimpleName();\n\n    /**\n     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created\n     */\n    public static RemoveListDialogFragment newInstance(ShoppingList shoppingList, String listId,\n                                                       HashMap<String, User> sharedWithUsers) {\n        RemoveListDialogFragment removeListDialogFragment = new RemoveListDialogFragment();\n        Bundle bundle = new Bundle();\n        bundle.putString(Constants.KEY_LIST_ID, listId);\n        bundle.putString(Constants.KEY_LIST_OWNER, shoppingList.getOwner());\n        bundle.putSerializable(Constants.KEY_SHARED_WITH_USERS, sharedWithUsers);\n        removeListDialogFragment.setArguments(bundle);\n        return removeListDialogFragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mListId = getArguments().getString(Constants.KEY_LIST_ID);\n        mListOwner = getArguments().getString(Constants.KEY_LIST_OWNER);\n        mSharedWith = (HashMap) getArguments().getSerializable(Constants.KEY_SHARED_WITH_USERS);\n    }\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog)\n                .setTitle(getActivity().getResources().getString(R.string.action_remove_list))\n                .setMessage(getString(R.string.dialog_message_are_you_sure_remove_list))\n                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {\n                    public void onClick(DialogInterface dialog, int which) {\n                        removeList();\n                        /* Dismiss the dialog */\n                        dialog.dismiss();\n                    }\n                })\n                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {\n                    public void onClick(DialogInterface dialog, int which) {\n                        /* Dismiss the dialog */\n                        dialog.dismiss();\n                    }\n                })\n                .setIcon(android.R.drawable.ic_dialog_alert);\n\n        return builder.create();\n    }\n\n    private void removeList() {\n        Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n        /**\n         * Create map and fill it in with deep path multi write operations list\n         */\n        HashMap<String, Object> removeListData = new HashMap<String, Object>();\n\n        /* Remove the ShoppingLists from both user lists and active lists */\n        Utils.updateMapForAllWithValue(mSharedWith, mListId, mListOwner, removeListData, \"\", null);\n\n        /* Remove the associated list items */\n        removeListData.put(\"/\" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + \"/\" + mListId,\n                null);\n\n        removeListData.put(\"/\" + Constants.FIREBASE_LOCATION_OWNER_MAPPINGS + \"/\" + mListId,\n                null);\n        /* Do a deep-path update */\n        firebaseRef.updateChildren(removeListData, new Firebase.CompletionListener() {\n            @Override\n            public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n\n                if (firebaseError != null) {\n                    Log.e(LOG_TAG, getString(R.string.log_error_updating_data) + firebaseError.getMessage());\n                }\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;\n\nimport android.app.Activity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.Query;\nimport com.firebase.client.ValueEventListener;\nimport com.firebase.ui.FirebaseListAdapter;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\n/**\n * Populates the list_view_active_lists inside ShoppingListsFragment\n */\npublic class ActiveListAdapter extends FirebaseListAdapter<ShoppingList> {\n    private String mEncodedEmail;\n\n    /**\n     * Public constructor that initializes private instance variables when adapter is created\n     */\n    public ActiveListAdapter(Activity activity, Class<ShoppingList> modelClass, int modelLayout,\n                             Query ref, String encodedEmail) {\n        super(activity, modelClass, modelLayout, ref);\n        this.mEncodedEmail = encodedEmail;\n        this.mActivity = activity;\n    }\n\n    /**\n     * Protected method that populates the view attached to the adapter (list_view_active_lists)\n     * with items inflated from single_active_list.xml\n     * populateView also handles data changes and updates the listView accordingly\n     */\n    @Override\n    protected void populateView(View view, ShoppingList list) {\n\n        /**\n         * Grab the needed Textivews and strings\n         */\n        TextView textViewListName = (TextView) view.findViewById(R.id.text_view_list_name);\n        final TextView textViewCreatedByUser = (TextView) view.findViewById(R.id.text_view_created_by_user);\n        final TextView textViewUsersShopping = (TextView) view.findViewById(R.id.text_view_people_shopping_count);\n\n        String ownerEmail = list.getOwner();\n\n        /* Set the list name and owner */\n        textViewListName.setText(list.getListName());\n\n        /**\n         * Show \"1 person is shopping\" if one person is shopping\n         * Show \"N people shopping\" if two or more users are shopping\n         * Show nothing if nobody is shopping\n         */\n        if (list.getUsersShopping() != null) {\n            int usersShopping = list.getUsersShopping().size();\n            if (usersShopping == 1) {\n                textViewUsersShopping.setText(String.format(\n                        mActivity.getResources().getString(R.string.person_shopping),\n                        usersShopping));\n            } else {\n                textViewUsersShopping.setText(String.format(\n                        mActivity.getResources().getString(R.string.people_shopping),\n                        usersShopping));\n            }\n        } else {\n            /* otherwise show nothing */\n            textViewUsersShopping.setText(\"\");\n        }\n\n        /**\n         * Set \"Created by\" text to \"You\" if current user is owner of the list\n         * Set \"Created by\" text to userName if current user is NOT owner of the list\n         */\n        if (ownerEmail != null) {\n            if (ownerEmail.equals(mEncodedEmail)) {\n                textViewCreatedByUser.setText(mActivity.getResources().getString(R.string.text_you));\n            } else {\n                Firebase userRef = new Firebase(Constants.FIREBASE_URL_USERS).child(ownerEmail);\n                /* Get the user's name */\n                userRef.addListenerForSingleValueEvent(new ValueEventListener() {\n                    @Override\n                    public void onDataChange(DataSnapshot dataSnapshot) {\n                        User user = dataSnapshot.getValue(User.class);\n\n                        if (user != null) {\n                            textViewCreatedByUser.setText(user.getName());\n                        }\n                    }\n\n                    @Override\n                    public void onCancelled(FirebaseError firebaseError) {\n                        Log.e(mActivity.getClass().getSimpleName(),\n                                mActivity.getString(R.string.log_error_the_read_failed) +\n                                        firebaseError.getMessage());\n                    }\n                });\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;\n\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.support.v7.app.AlertDialog;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ServerValue;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Adds a new shopping list\n */\npublic class AddListDialogFragment extends DialogFragment {\n    String mEncodedEmail;\n    EditText mEditTextListName;\n\n    /**\n     * Public static constructor that creates fragment and\n     * passes a bundle with data into it when adapter is created\n     */\n    public static AddListDialogFragment newInstance(String encodedEmail) {\n        AddListDialogFragment addListDialogFragment = new AddListDialogFragment();\n        Bundle bundle = new Bundle();\n        bundle.putString(Constants.KEY_ENCODED_EMAIL, encodedEmail);\n        addListDialogFragment.setArguments(bundle);\n        return addListDialogFragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mEncodedEmail = getArguments().getString(Constants.KEY_ENCODED_EMAIL);\n    }\n\n    /**\n     * Open the keyboard automatically when the dialog fragment is opened\n     */\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);\n    }\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        // Use the Builder class for convenient dialog construction\n        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog);\n        // Get the layout inflater\n        LayoutInflater inflater = getActivity().getLayoutInflater();\n        View rootView = inflater.inflate(R.layout.dialog_add_list, null);\n        mEditTextListName = (EditText) rootView.findViewById(R.id.edit_text_list_name);\n\n        /**\n         * Call addShoppingList() when user taps \"Done\" keyboard action\n         */\n        mEditTextListName.setOnEditorActionListener(new TextView.OnEditorActionListener() {\n            @Override\n            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {\n                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {\n                    addShoppingList();\n                }\n                return true;\n            }\n        });\n\n        /* Inflate and set the layout for the dialog */\n        /* Pass null as the parent view because its going in the dialog layout*/\n        builder.setView(rootView)\n                /* Add action buttons */\n                .setPositiveButton(R.string.positive_button_create, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int id) {\n                        addShoppingList();\n                    }\n                });\n\n        return builder.create();\n    }\n\n    /**\n     * Add new active list\n     */\n    public void addShoppingList() {\n        String userEnteredName = mEditTextListName.getText().toString();\n\n        /**\n         * If EditText input is not empty\n         */\n        if (!userEnteredName.equals(\"\")) {\n\n            /**\n             * Create Firebase references\n             */\n            Firebase userListsRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS).\n                    child(mEncodedEmail);\n            final Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n            Firebase newListRef = userListsRef.push();\n\n            /* Save listsRef.push() to maintain same random Id */\n            final String listId = newListRef.getKey();\n\n            /* HashMap for data to update */\n            HashMap<String, Object> updateShoppingListData = new HashMap<>();\n\n            /**\n             * Set raw version of date to the ServerValue.TIMESTAMP value and save into\n             * timestampCreatedMap\n             */\n            HashMap<String, Object> timestampCreated = new HashMap<>();\n            timestampCreated.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);\n\n            /* Build the shopping list */\n            ShoppingList newShoppingList = new ShoppingList(userEnteredName, mEncodedEmail,\n                    timestampCreated);\n\n            HashMap<String, Object> shoppingListMap = (HashMap<String, Object>)\n                    new ObjectMapper().convertValue(newShoppingList, Map.class);\n\n            Utils.updateMapForAllWithValue(null, listId, mEncodedEmail,\n                    updateShoppingListData, \"\", shoppingListMap);\n\n            updateShoppingListData.put(\"/\" + Constants.FIREBASE_LOCATION_OWNER_MAPPINGS + \"/\" + listId,\n                    mEncodedEmail);\n\n            /* Do the update */\n            firebaseRef.updateChildren(updateShoppingListData, new Firebase.CompletionListener() {\n                @Override\n                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                    /* Now that we have the timestamp, update the reversed timestamp */\n                    Utils.updateTimestampReversed(firebaseError, \"AddList\", listId,\n                            null, mEncodedEmail);\n                }\n            });\n\n            /* Close the dialog fragment */\n            AddListDialogFragment.this.getDialog().cancel();\n        }\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;\n\n\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\n\nimport com.firebase.client.Firebase;\nimport com.firebase.client.Query;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails.ActiveListDetailsActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\n\n/**\n * A simple {@link Fragment} subclass that shows a list of all shopping lists a user can see.\n * Use the {@link ShoppingListsFragment#newInstance} factory method to\n * create an instance of this fragment.\n */\npublic class ShoppingListsFragment extends Fragment {\n    private String mEncodedEmail;\n    private ActiveListAdapter mActiveListAdapter;\n    private ListView mListView;\n\n    public ShoppingListsFragment() {\n        /* Required empty public constructor */\n    }\n\n    /**\n     * Create fragment and pass bundle with data as it's arguments\n     * Right now there are not arguments...but eventually there will be.\n     */\n    public static ShoppingListsFragment newInstance(String encodedEmail) {\n        ShoppingListsFragment fragment = new ShoppingListsFragment();\n        Bundle args = new Bundle();\n        args.putString(Constants.KEY_ENCODED_EMAIL, encodedEmail);\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if (getArguments() != null) {\n            mEncodedEmail = getArguments().getString(Constants.KEY_ENCODED_EMAIL);\n        }\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container,\n                             Bundle savedInstanceState) {\n\n        /**\n         * Initialize UI elements\n         */\n        View rootView = inflater.inflate(R.layout.fragment_shopping_lists, container, false);\n        initializeScreen(rootView);\n\n        /**\n         * Set interactive bits, such as click events and adapters\n         */\n        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                ShoppingList selectedList = mActiveListAdapter.getItem(position);\n                if (selectedList != null) {\n                    Intent intent = new Intent(getActivity(), ActiveListDetailsActivity.class);\n                    /* Get the list ID using the adapter's get ref method to get the Firebase\n                     * ref and then grab the key.\n                     */\n                    String listId = mActiveListAdapter.getRef(position).getKey();\n                    intent.putExtra(Constants.KEY_LIST_ID, listId);\n                    /* Starts an active showing the details for the selected list */\n                    startActivity(intent);\n                }\n            }\n        });\n\n\n        return rootView;\n    }\n    /**\n     * Updates the order of mListView onResume to handle sortOrderChanges properly\n     */\n    @Override\n    public void onResume() {\n        super.onResume();\n        final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());\n        String sortOrder = sharedPref.getString(Constants.KEY_PREF_SORT_ORDER_LISTS, Constants.ORDER_BY_KEY);\n\n        Query orderedActiveUserListsRef;\n        Firebase activeListsRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS)\n                .child(mEncodedEmail);\n        /**\n         * Sort active lists by \"date created\"\n         * if it's been selected in the SettingsActivity\n         */\n        if (sortOrder.equals(Constants.ORDER_BY_KEY)) {\n            orderedActiveUserListsRef = activeListsRef.orderByKey();\n        } else {\n\n            /**\n             * Sort active by lists by name or datelastChanged. Otherwise\n             * depending on what's been selected in SettingsActivity\n             */\n\n            orderedActiveUserListsRef = activeListsRef.orderByChild(sortOrder);\n        }\n\n        /**\n         * Create the adapter with selected sort order\n         */\n        mActiveListAdapter = new ActiveListAdapter(getActivity(), ShoppingList.class,\n                R.layout.single_active_list, orderedActiveUserListsRef,\n                mEncodedEmail);\n\n        /**\n         * Set the adapter to the mListView\n         */\n        mListView.setAdapter(mActiveListAdapter);\n    }\n\n    /**\n     * Cleanup the adapter when activity is paused.\n     */\n    @Override\n    public void onPause() {\n        super.onPause();\n        mActiveListAdapter.cleanup();\n    }\n\n    /**\n     * Link list view from XML\n     */\n    private void initializeScreen(View rootView) {\n        mListView = (ListView) rootView.findViewById(R.id.list_view_active_lists);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.login;\n\nimport android.app.ProgressDialog;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.View;\nimport android.widget.EditText;\nimport android.widget.LinearLayout;\nimport android.widget.Toast;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.firebase.client.AuthData;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ServerValue;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.math.BigInteger;\nimport java.security.SecureRandom;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Represents Sign up screen and functionality of the app\n */\npublic class CreateAccountActivity extends BaseActivity {\n    private static final String LOG_TAG = CreateAccountActivity.class.getSimpleName();\n    private ProgressDialog mAuthProgressDialog;\n    private Firebase mFirebaseRef;\n    private EditText mEditTextUsernameCreate, mEditTextEmailCreate;\n    private String mUserName, mUserEmail, mPassword;\n    private SecureRandom mRandom = new SecureRandom();\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_create_account);\n\n        /**\n         * Create Firebase references\n         */\n        mFirebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n        /**\n         * Link layout elements from XML and setup the progress dialog\n         */\n        initializeScreen();\n    }\n\n    /**\n     * Override onCreateOptionsMenu to inflate nothing\n     *\n     * @param menu The menu with which nothing will happen\n     */\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        return true;\n    }\n\n\n    /**\n     * Link layout elements from XML and setup the progress dialog\n     */\n    public void initializeScreen() {\n        mEditTextUsernameCreate = (EditText) findViewById(R.id.edit_text_username_create);\n        mEditTextEmailCreate = (EditText) findViewById(R.id.edit_text_email_create);\n        LinearLayout linearLayoutCreateAccountActivity = (LinearLayout) findViewById(R.id.linear_layout_create_account_activity);\n        initializeBackground(linearLayoutCreateAccountActivity);\n\n        /* Setup the progress dialog that is displayed later when authenticating with Firebase */\n        mAuthProgressDialog = new ProgressDialog(this);\n        mAuthProgressDialog.setTitle(getResources().getString(R.string.progress_dialog_loading));\n        mAuthProgressDialog.setMessage(getResources().getString(R.string.progress_dialog_check_inbox));\n        mAuthProgressDialog.setCancelable(false);\n    }\n\n    /**\n     * Open LoginActivity when user taps on \"Sign in\" textView\n     */\n    public void onSignInPressed(View view) {\n        Intent intent = new Intent(CreateAccountActivity.this, LoginActivity.class);\n        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);\n        startActivity(intent);\n        finish();\n    }\n\n    /**\n     * Create new account using Firebase email/password provider\n     */\n    public void onCreateAccountPressed(View view) {\n        mUserName = mEditTextUsernameCreate.getText().toString();\n        mUserEmail = mEditTextEmailCreate.getText().toString().toLowerCase();\n        mPassword = new BigInteger(130, mRandom).toString(32);\n\n        /**\n         * Check that email and user name are okay\n         */\n        boolean validEmail = isEmailValid(mUserEmail);\n        boolean validUserName = isUserNameValid(mUserName);\n        if (!validEmail || !validUserName) return;\n\n        /**\n         * If everything was valid show the progress dialog to indicate that\n         * account creation has started\n         */\n        mAuthProgressDialog.show();\n\n        /**\n         * Create new user with specified email and password\n         */\n        mFirebaseRef.createUser(mUserEmail, mPassword, new Firebase.ValueResultHandler<Map<String, Object>>() {\n            @Override\n            public void onSuccess(final Map<String, Object> result) {\n                /**\n                 * If user was successfully created, run resetPassword() to send temporary 24h\n                 * password to the user's email and make sure that user owns specified email\n                 */\n                mFirebaseRef.resetPassword(mUserEmail, new Firebase.ResultHandler() {\n                    @Override\n                    public void onSuccess() {\n\n                        mFirebaseRef.authWithPassword(mUserEmail, mPassword, new Firebase.AuthResultHandler() {\n                            @Override\n                            public void onAuthenticated(AuthData authData) {\n                                mAuthProgressDialog.dismiss();\n                                Log.i(LOG_TAG, getString(R.string.log_message_auth_successful));\n\n                                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(CreateAccountActivity.this);\n                                SharedPreferences.Editor spe = sp.edit();\n\n                                /**\n                                 * Save name and email to sharedPreferences to create User database record\n                                 * when the registered user will sign in for the first time\n                                 */\n                                spe.putString(Constants.KEY_SIGNUP_EMAIL, mUserEmail).apply();\n\n                                /**\n                                 * Encode user email replacing \".\" with \",\"\n                                 * to be able to use it as a Firebase db key\n                                 */\n                                createUserInFirebaseHelper((String) result.get(\"uid\"));\n\n                                /**\n                                 *  Password reset email sent, open app chooser to pick app\n                                 *  for handling inbox email intent\n                                 */\n                                Intent intent = new Intent(Intent.ACTION_MAIN);\n                                intent.addCategory(Intent.CATEGORY_APP_EMAIL);\n                                try {\n                                    startActivity(intent);\n                                    finish();\n                                } catch (android.content.ActivityNotFoundException ex) {\n                                    /* User does not have any app to handle email */\n                                }\n                            }\n\n                            @Override\n                            public void onAuthenticationError(FirebaseError firebaseError) {\n                                Log.e(LOG_TAG, firebaseError.getMessage());\n                            }\n                        });\n\n                    }\n\n                    @Override\n                    public void onError(FirebaseError firebaseError) {\n                        /* Error occurred, log the error and dismiss the progress dialog */\n                        Log.d(LOG_TAG, getString(R.string.log_error_occurred) +\n                                firebaseError);\n                        mAuthProgressDialog.dismiss();\n                    }\n                });\n\n\n            }\n\n            @Override\n            public void onError(FirebaseError firebaseError) {\n                /* Error occurred, log the error and dismiss the progress dialog */\n                Log.d(LOG_TAG, getString(R.string.log_error_occurred) +\n                        firebaseError);\n                mAuthProgressDialog.dismiss();\n                /* Display the appropriate error message */\n                if (firebaseError.getCode() == FirebaseError.EMAIL_TAKEN) {\n                    mEditTextEmailCreate.setError(getString(R.string.error_email_taken));\n                } else {\n                    showErrorToast(firebaseError.getMessage());\n                }\n\n            }\n        });\n\n\n    }\n\n    /**\n     * Creates a new user in Firebase from the Java POJO\n     */\n    private void createUserInFirebaseHelper(final String authUserId) {\n        final String encodedEmail = Utils.encodeEmail(mUserEmail);\n\n        /**\n         * Create the user and uid mapping\n         */\n        HashMap<String, Object> userAndUidMapping = new HashMap<String, Object>();\n\n        /* Set raw version of date to the ServerValue.TIMESTAMP value and save into dateCreatedMap */\n        HashMap<String, Object> timestampJoined = new HashMap<>();\n        timestampJoined.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);\n\n        /* Create a HashMap version of the user to add */\n        User newUser = new User(mUserName, encodedEmail, timestampJoined);\n        HashMap<String, Object> newUserMap = (HashMap<String, Object>)\n                new ObjectMapper().convertValue(newUser, Map.class);\n\n        /* Add the user and UID to the update map */\n        userAndUidMapping.put(\"/\" + Constants.FIREBASE_LOCATION_USERS + \"/\" + encodedEmail,\n                newUserMap);\n        userAndUidMapping.put(\"/\" + Constants.FIREBASE_LOCATION_UID_MAPPINGS + \"/\"\n                + authUserId, encodedEmail);\n\n        /* Try to update the database; if there is already a user, this will fail */\n        mFirebaseRef.updateChildren(userAndUidMapping, new Firebase.CompletionListener() {\n            @Override\n            public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                if (firebaseError != null) {\n                    /* Try just making a uid mapping */\n                    mFirebaseRef.child(Constants.FIREBASE_LOCATION_UID_MAPPINGS)\n                            .child(authUserId).setValue(encodedEmail);\n                }\n                /**\n                 *  The value has been set or it failed; either way, log out the user since\n                 *  they were only logged in with a temp password\n                 **/\n                mFirebaseRef.unauth();\n            }\n        });\n    }\n\n    private boolean isEmailValid(String email) {\n        boolean isGoodEmail =\n                (email != null && android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches());\n        if (!isGoodEmail) {\n            mEditTextEmailCreate.setError(String.format(getString(R.string.error_invalid_email_not_valid),\n                    email));\n            return false;\n        }\n        return isGoodEmail;\n    }\n\n    private boolean isUserNameValid(String userName) {\n        if (userName.equals(\"\")) {\n            mEditTextUsernameCreate.setError(getResources().getString(R.string.error_cannot_be_empty));\n            return false;\n        }\n        return true;\n    }\n\n\n    /**\n     * Show error toast to users\n     */\n    private void showErrorToast(String message) {\n        Toast.makeText(CreateAccountActivity.this, message, Toast.LENGTH_LONG).show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.login;\n\nimport android.app.ProgressDialog;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.EditText;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.firebase.client.AuthData;\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ServerValue;\nimport com.firebase.client.ValueEventListener;\nimport com.google.android.gms.auth.GoogleAuthException;\nimport com.google.android.gms.auth.GoogleAuthUtil;\nimport com.google.android.gms.auth.UserRecoverableAuthException;\nimport com.google.android.gms.auth.api.Auth;\nimport com.google.android.gms.auth.api.signin.GoogleSignInAccount;\nimport com.google.android.gms.auth.api.signin.GoogleSignInResult;\nimport com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;\nimport com.google.android.gms.common.ConnectionResult;\nimport com.google.android.gms.common.Scopes;\nimport com.google.android.gms.common.SignInButton;\nimport com.google.android.gms.common.api.Scope;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;\nimport com.udacity.firebase.shoppinglistplusplus.ui.MainActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Represents Sign in screen and functionality of the app\n */\npublic class LoginActivity extends BaseActivity {\n\n    private static final String LOG_TAG = LoginActivity.class.getSimpleName();\n    /* A dialog that is presented until the Firebase authentication finished. */\n    private ProgressDialog mAuthProgressDialog;\n    /* References to the Firebase */\n    private Firebase mFirebaseRef;\n    /* Listener for Firebase session changes */\n    private Firebase.AuthStateListener mAuthStateListener;\n    private EditText mEditTextEmailInput, mEditTextPasswordInput;\n\n    private SharedPreferences mSharedPref;\n    private SharedPreferences.Editor mSharedPrefEditor;\n\n    /**\n     * Variables related to Google Login\n     */\n    /* A flag indicating that a PendingIntent is in progress and prevents us from starting further intents. */\n    private boolean mGoogleIntentInProgress;\n    /* Request code used to invoke sign in user interactions for Google+ */\n    public static final int RC_GOOGLE_LOGIN = 1;\n    /* A Google account object that is populated if the user signs in with Google */\n    GoogleSignInAccount mGoogleAccount;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_login);\n        mSharedPref = PreferenceManager.getDefaultSharedPreferences(this);\n        mSharedPrefEditor = mSharedPref.edit();\n\n        /**\n         * Create Firebase references\n         */\n        mFirebaseRef = new Firebase(Constants.FIREBASE_URL);\n\n        /**\n         * Link layout elements from XML and setup progress dialog\n         */\n        initializeScreen();\n\n        /**\n         * Call signInPassword() when user taps \"Done\" keyboard action\n         */\n        mEditTextPasswordInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {\n            @Override\n            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {\n\n                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {\n                    signInPassword();\n                }\n                return true;\n            }\n        });\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n\n        /**\n         * This is the authentication listener that maintains the current user session\n         * and signs in automatically on application launch\n         */\n        mAuthStateListener = new Firebase.AuthStateListener() {\n            @Override\n            public void onAuthStateChanged(AuthData authData) {\n                mAuthProgressDialog.dismiss();\n\n                /**\n                 * If there is a valid session to be restored, start MainActivity.\n                 * No need to pass data via SharedPreferences because app\n                 * already holds userName/provider data from the latest session\n                 */\n                if (authData != null) {\n                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);\n                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);\n                    startActivity(intent);\n                    finish();\n                }\n            }\n        };\n        /* Add auth listener to Firebase ref */\n        mFirebaseRef.addAuthStateListener(mAuthStateListener);\n\n        /**\n         * Get the newly registered user email if present, use null as default value\n         */\n        String signupEmail = mSharedPref.getString(Constants.KEY_SIGNUP_EMAIL, null);\n\n        /**\n         * Fill in the email editText and remove value from SharedPreferences if email is present\n         */\n        if (signupEmail != null) {\n            mEditTextEmailInput.setText(signupEmail);\n\n            /**\n             * Clear signupEmail sharedPreferences to make sure that they are used just once\n             */\n            mSharedPrefEditor.putString(Constants.KEY_SIGNUP_EMAIL, null).apply();\n        }\n    }\n\n    /**\n     * Cleans up listeners tied to the user's authentication state\n     */\n    @Override\n    public void onPause() {\n        super.onPause();\n        mFirebaseRef.removeAuthStateListener(mAuthStateListener);\n    }\n\n    /**\n     * Override onCreateOptionsMenu to inflate nothing\n     *\n     * @param menu The menu with which nothing will happen\n     */\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        return true;\n    }\n\n\n    /**\n     * Sign in with Password provider when user clicks sign in button\n     */\n    public void onSignInPressed(View view) {\n        signInPassword();\n    }\n\n    /**\n     * Open CreateAccountActivity when user taps on \"Sign up\" TextView\n     */\n    public void onSignUpPressed(View view) {\n        Intent intent = new Intent(LoginActivity.this, CreateAccountActivity.class);\n        startActivity(intent);\n    }\n\n    /**\n     * Link layout elements from XML and setup the progress dialog\n     */\n    public void initializeScreen() {\n        mEditTextEmailInput = (EditText) findViewById(R.id.edit_text_email);\n        mEditTextPasswordInput = (EditText) findViewById(R.id.edit_text_password);\n        LinearLayout linearLayoutLoginActivity = (LinearLayout) findViewById(R.id.linear_layout_login_activity);\n        initializeBackground(linearLayoutLoginActivity);\n        /* Setup the progress dialog that is displayed later when authenticating with Firebase */\n        mAuthProgressDialog = new ProgressDialog(this);\n        mAuthProgressDialog.setTitle(getString(R.string.progress_dialog_loading));\n        mAuthProgressDialog.setMessage(getString(R.string.progress_dialog_authenticating_with_firebase));\n        mAuthProgressDialog.setCancelable(false);\n        /* Setup Google Sign In */\n        setupGoogleSignIn();\n    }\n\n    /**\n     * Sign in with Password provider (used when user taps \"Done\" action on keyboard)\n     */\n    public void signInPassword() {\n        String email = mEditTextEmailInput.getText().toString();\n        String password = mEditTextPasswordInput.getText().toString();\n\n        /**\n         * If email and password are not empty show progress dialog and try to authenticate\n         */\n        if (email.equals(\"\")) {\n            mEditTextEmailInput.setError(getString(R.string.error_cannot_be_empty));\n            return;\n        }\n\n        if (password.equals(\"\")) {\n            mEditTextPasswordInput.setError(getString(R.string.error_cannot_be_empty));\n            return;\n        }\n        mAuthProgressDialog.show();\n        mFirebaseRef.authWithPassword(email, password, new MyAuthResultHandler(Constants.PASSWORD_PROVIDER));\n    }\n\n    /**\n     * Handle user authentication that was initiated with mFirebaseRef.authWithPassword\n     * or mFirebaseRef.authWithOAuthToken\n     */\n    private class MyAuthResultHandler implements Firebase.AuthResultHandler {\n\n        private final String provider;\n\n        public MyAuthResultHandler(String provider) {\n            this.provider = provider;\n        }\n\n        /**\n         * On successful authentication call setAuthenticatedUser if it was not already\n         * called in\n         */\n        @Override\n        public void onAuthenticated(AuthData authData) {\n            mAuthProgressDialog.dismiss();\n            Log.i(LOG_TAG, provider + \" \" + getString(R.string.log_message_auth_successful));\n\n            if (authData != null) {\n                /**\n                 * If user has logged in with Google provider\n                 */\n                if (authData.getProvider().equals(Constants.PASSWORD_PROVIDER)) {\n                    setAuthenticatedUserPasswordProvider(authData);\n                } else\n                /**\n                 * If user has logged in with Password provider\n                 */\n                    if (authData.getProvider().equals(Constants.GOOGLE_PROVIDER)) {\n                        setAuthenticatedUserGoogle(authData);\n                    } else {\n                        Log.e(LOG_TAG, getString(R.string.log_error_invalid_provider) + authData.getProvider());\n                    }\n\n                /* Save provider name and encodedEmail for later use and start MainActivity */\n                mSharedPrefEditor.putString(Constants.KEY_PROVIDER, authData.getProvider()).apply();\n                mSharedPrefEditor.putString(Constants.KEY_ENCODED_EMAIL, mEncodedEmail).apply();\n\n                /* Go to main activity */\n                Intent intent = new Intent(LoginActivity.this, MainActivity.class);\n                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);\n                startActivity(intent);\n                finish();\n            }\n        }\n\n        @Override\n        public void onAuthenticationError(FirebaseError firebaseError) {\n            mAuthProgressDialog.dismiss();\n\n            /**\n             * Use utility method to check the network connection state\n             * Show \"No network connection\" if there is no connection\n             * Show Firebase specific error message otherwise\n             */\n            switch (firebaseError.getCode()) {\n                case FirebaseError.INVALID_EMAIL:\n                case FirebaseError.USER_DOES_NOT_EXIST:\n                    mEditTextEmailInput.setError(getString(R.string.error_message_email_issue));\n                    break;\n                case FirebaseError.INVALID_PASSWORD:\n                    mEditTextPasswordInput.setError(firebaseError.getMessage());\n                    break;\n                case FirebaseError.NETWORK_ERROR:\n                    showErrorToast(getString(R.string.error_message_failed_sign_in_no_network));\n                    break;\n                default:\n                    showErrorToast(firebaseError.toString());\n            }\n        }\n    }\n\n    /**\n     * Helper method that makes sure a user is created if the user\n     * logs in with Firebase's email/password provider.\n     *\n     * @param authData AuthData object returned from onAuthenticated\n     */\n    private void setAuthenticatedUserPasswordProvider(AuthData authData) {\n        final String unprocessedEmail = authData.getProviderData().get(Constants.FIREBASE_PROPERTY_EMAIL).toString().toLowerCase();\n        /**\n         * Encode user email replacing \".\" with \",\"\n         * to be able to use it as a Firebase db key\n         */\n        mEncodedEmail = Utils.encodeEmail(unprocessedEmail);\n\n        final Firebase userRef = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);\n\n        /**\n         * Check if current user has logged in at least once\n         */\n        userRef.addListenerForSingleValueEvent(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot dataSnapshot) {\n                User user = dataSnapshot.getValue(User.class);\n\n                if (user != null) {\n\n                    /**\n                     * If recently registered user has hasLoggedInWithPassword = \"false\"\n                     * (never logged in using password provider)\n                     */\n                    if (!user.isHasLoggedInWithPassword()) {\n\n                        /**\n                         * Change password if user that just signed in signed up recently\n                         * to make sure that user will be able to use temporary password\n                         * from the email more than 24 hours\n                         */\n                        mFirebaseRef.changePassword(unprocessedEmail, mEditTextPasswordInput.getText().toString(), mEditTextPasswordInput.getText().toString(), new Firebase.ResultHandler() {\n                            @Override\n                            public void onSuccess() {\n                                userRef.child(Constants.FIREBASE_PROPERTY_USER_HAS_LOGGED_IN_WITH_PASSWORD).setValue(true);\n                                        /* The password was changed */\n                                Log.d(LOG_TAG, getString(R.string.log_message_password_changed_successfully) + mEditTextPasswordInput.getText().toString());\n                            }\n\n                            @Override\n                            public void onError(FirebaseError firebaseError) {\n                                Log.d(LOG_TAG, getString(R.string.log_error_failed_to_change_password) + firebaseError);\n                            }\n                        });\n                    }\n                }\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n    }\n\n    /**\n     * Helper method that makes sure a user is created if the user\n     * logs in with Firebase's Google login provider.\n     *\n     * @param authData AuthData object returned from onAuthenticated\n     */\n    private void setAuthenticatedUserGoogle(final AuthData authData) {\n        /**\n         * If google api client is connected, get the lowerCase user email\n         * and save in sharedPreferences\n         */\n        String unprocessedEmail;\n        if (mGoogleApiClient.isConnected()) {\n            unprocessedEmail = mGoogleAccount.getEmail().toLowerCase();\n            mSharedPrefEditor.putString(Constants.KEY_GOOGLE_EMAIL, unprocessedEmail).apply();\n        } else {\n\n            /**\n             * Otherwise get email from sharedPreferences, use null as default value\n             * (this mean that user resumes his session)\n             */\n            unprocessedEmail = mSharedPref.getString(Constants.KEY_GOOGLE_EMAIL, null);\n        }\n\n        /**\n         * Encode user email replacing \".\" with \",\" to be able to use it\n         * as a Firebase db key\n         */\n        mEncodedEmail = Utils.encodeEmail(unprocessedEmail);\n\n        /* Get username from authData */\n        final String userName = (String) authData.getProviderData().get(Constants.PROVIDER_DATA_DISPLAY_NAME);\n\n        /* Make a user */\n        final Firebase userLocation = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);\n\n        HashMap<String, Object> userAndUidMapping = new HashMap<String, Object>();\n\n        HashMap<String, Object> timestampJoined = new HashMap<>();\n        timestampJoined.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);\n\n        /* Create a HashMap version of the user to add */\n        User newUser = new User(userName, mEncodedEmail, timestampJoined);\n        HashMap<String, Object> newUserMap = (HashMap<String, Object>)\n                new ObjectMapper().convertValue(newUser, Map.class);\n\n        /* Add the user and UID to the update map */\n        userAndUidMapping.put(\"/\" + Constants.FIREBASE_LOCATION_USERS + \"/\" + mEncodedEmail,\n                newUserMap);\n        userAndUidMapping.put(\"/\" + Constants.FIREBASE_LOCATION_UID_MAPPINGS + \"/\"\n                + authData.getUid(), mEncodedEmail);\n\n        /* Update the database; it will fail if a user already exists */\n        mFirebaseRef.updateChildren(userAndUidMapping, new Firebase.CompletionListener() {\n            @Override\n            public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                if (firebaseError != null) {\n                    /* Try just making a uid mapping */\n                    mFirebaseRef.child(Constants.FIREBASE_LOCATION_UID_MAPPINGS)\n                            .child(authData.getUid()).setValue(mEncodedEmail);\n                }\n            }\n        });\n    }\n\n    /**\n     * Show error toast to users\n     */\n    private void showErrorToast(String message) {\n        Toast.makeText(LoginActivity.this, message, Toast.LENGTH_LONG).show();\n    }\n\n\n    /**\n     * Signs you into ShoppingList++ using the Google Login Provider\n     *\n     * @param token A Google OAuth access token returned from Google\n     */\n    private void loginWithGoogle(String token) {\n        mFirebaseRef.authWithOAuthToken(Constants.GOOGLE_PROVIDER, token, new MyAuthResultHandler(Constants.GOOGLE_PROVIDER));\n    }\n\n    /**\n     * GOOGLE SIGN IN CODE\n     * <p/>\n     * This code is mostly boiler plate from\n     * https://developers.google.com/identity/sign-in/android/start-integrating\n     * and\n     * https://github.com/googlesamples/google-services/blob/master/android/signin/app/src/main/java/com/google/samples/quickstart/signin/SignInActivity.java\n     * <p/>\n     * The big picture steps are:\n     * 1. User clicks the sign in with Google button\n     * 2. An intent is started for sign in.\n     * - If the connection fails it is caught in the onConnectionFailed callback\n     * - If it finishes, onActivityResult is called with the correct request code.\n     * 3. If the sign in was successful, set the mGoogleAccount to the current account and\n     * then call get GoogleOAuthTokenAndLogin\n     * 4. getGoogleOAuthTokenAndLogin launches an AsyncTask to get an OAuth2 token from Google.\n     * 5. Once this token is retrieved it is available to you in the onPostExecute method of\n     * the AsyncTask. **This is the token required by Firebase**\n     */\n\n\n    /* Sets up the Google Sign In Button : https://developers.google.com/android/reference/com/google/android/gms/common/SignInButton */\n    private void setupGoogleSignIn() {\n        SignInButton signInButton = (SignInButton) findViewById(R.id.login_with_google);\n        signInButton.setSize(SignInButton.SIZE_WIDE);\n        signInButton.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                onSignInGooglePressed(v);\n            }\n        });\n    }\n\n    /**\n     * Sign in with Google plus when user clicks \"Sign in with Google\" textView (button)\n     */\n    public void onSignInGooglePressed(View view) {\n        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);\n        startActivityForResult(signInIntent, RC_GOOGLE_LOGIN);\n        mAuthProgressDialog.show();\n\n    }\n\n    @Override\n    public void onConnectionFailed(ConnectionResult result) {\n        /**\n         * An unresolvable error has occurred and Google APIs (including Sign-In) will not\n         * be available.\n         */\n        mAuthProgressDialog.dismiss();\n        showErrorToast(result.toString());\n    }\n\n\n    /**\n     * This callback is triggered when any startActivityForResult finishes. The requestCode maps to\n     * the value passed into startActivityForResult.\n     */\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        /* Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); */\n        if (requestCode == RC_GOOGLE_LOGIN) {\n            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);\n            handleSignInResult(result);\n        }\n\n    }\n\n    private void handleSignInResult(GoogleSignInResult result) {\n        Log.d(LOG_TAG, \"handleSignInResult:\" + result.isSuccess());\n        if (result.isSuccess()) {\n            /* Signed in successfully, get the OAuth token */\n            mGoogleAccount = result.getSignInAccount();\n            getGoogleOAuthTokenAndLogin();\n\n\n        } else {\n            if (result.getStatus().getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) {\n                showErrorToast(\"The sign in was cancelled. Make sure you're connected to the internet and try again.\");\n            } else {\n                showErrorToast(\"Error handling the sign in: \" + result.getStatus().getStatusMessage());\n            }\n            mAuthProgressDialog.dismiss();\n        }\n    }\n\n    /**\n     * Gets the GoogleAuthToken and logs in.\n     */\n    private void getGoogleOAuthTokenAndLogin() {\n        /* Get OAuth token in Background */\n        AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {\n            String mErrorMessage = null;\n\n            @Override\n            protected String doInBackground(Void... params) {\n                String token = null;\n\n                try {\n                    String scope = String.format(getString(R.string.oauth2_format), new Scope(Scopes.PROFILE)) + \" email\";\n\n                    token = GoogleAuthUtil.getToken(LoginActivity.this, mGoogleAccount.getEmail(), scope);\n                } catch (IOException transientEx) {\n                    /* Network or server error */\n                    Log.e(LOG_TAG, getString(R.string.google_error_auth_with_google) + transientEx);\n                    mErrorMessage = getString(R.string.google_error_network_error) + transientEx.getMessage();\n                } catch (UserRecoverableAuthException e) {\n                    Log.w(LOG_TAG, getString(R.string.google_error_recoverable_oauth_error) + e.toString());\n\n                    /* We probably need to ask for permissions, so start the intent if there is none pending */\n                    if (!mGoogleIntentInProgress) {\n                        mGoogleIntentInProgress = true;\n                        Intent recover = e.getIntent();\n                        startActivityForResult(recover, RC_GOOGLE_LOGIN);\n                    }\n                } catch (GoogleAuthException authEx) {\n                    /* The call is not ever expected to succeed assuming you have already verified that\n                     * Google Play services is installed. */\n                    Log.e(LOG_TAG, \" \" + authEx.getMessage(), authEx);\n                    mErrorMessage = getString(R.string.google_error_auth_with_google) + authEx.getMessage();\n                }\n                return token;\n            }\n\n            @Override\n            protected void onPostExecute(String token) {\n                mAuthProgressDialog.dismiss();\n                if (token != null) {\n                    /* Successfully got OAuth token, now login with Google */\n                    loginWithGoogle(token);\n                } else if (mErrorMessage != null) {\n                    showErrorToast(mErrorMessage);\n                }\n            }\n        };\n\n        task.execute();\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/AddMealDialogFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.meals;\n\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.support.v7.app.AlertDialog;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.udacity.firebase.shoppinglistplusplus.R;\n\n/**\n * Adds a new meal\n */\npublic class AddMealDialogFragment extends DialogFragment {\n    EditText editTextMealName;\n\n    /**\n     * Public static constructor that creates fragment and\n     * passes a bundle with data into it when adapter is created\n     */\n    public static AddMealDialogFragment newInstance() {\n        AddMealDialogFragment addMealDialogFragment = new AddMealDialogFragment();\n        Bundle bundle = new Bundle();\n        addMealDialogFragment.setArguments(bundle);\n        return addMealDialogFragment;\n    }\n\n    /**\n     * Initialize instance variables with data from bundle\n     */\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n    }\n\n    /**\n     * Open the keyboard automatically when the dialog fragment is opened\n     */\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);\n    }\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        /* Use the Builder class for convenient dialog construction */\n        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog);\n        /* Get the layout inflater */\n        LayoutInflater inflater = getActivity().getLayoutInflater();\n        View rootView = inflater.inflate(R.layout.dialog_add_meal, null);\n        editTextMealName = (EditText) rootView.findViewById(R.id.edit_text_meal_name);\n\n        /**\n         * Call addMeal() when user taps \"Done\" keyboard action\n         */\n        editTextMealName.setOnEditorActionListener(new TextView.OnEditorActionListener() {\n            @Override\n            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {\n                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {\n                    addMeal();\n                }\n                return true;\n            }\n        });\n\n        /* Inflate and set the layout for the dialog */\n        /* Pass null as the parent view because its going in the dialog layout */\n        builder.setView(rootView)\n                /* Add action buttons */\n                .setPositiveButton(R.string.positive_button_create, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int id) {\n                        addMeal();\n                    }\n                });\n\n        return builder.create();\n    }\n\n    /**\n     * Add new meal\n     */\n    public void addMeal() {\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/MealsFragment.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.meals;\n\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\n\nimport com.udacity.firebase.shoppinglistplusplus.R;\n\n\n/**\n * A simple {@link Fragment} subclass which shows all of the meals in the Firebase database\n * Use the {@link MealsFragment#newInstance} factory method to\n * create an instance of this fragment.\n */\npublic class MealsFragment extends Fragment {\n    private ListView mListView;\n\n    /**\n     * Create fragment and pass bundle with data as its' arguments\n     */\n    public static MealsFragment newInstance() {\n        MealsFragment fragment = new MealsFragment();\n        Bundle args = new Bundle();\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    public MealsFragment() {\n        /* Required empty public constructor*/\n    }\n\n    @Override\n    public void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n    }\n\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n    }\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container,\n                             Bundle savedInstanceState) {\n        /* Inflate the layout for this fragment */\n        View rootView = inflater.inflate(R.layout.fragment_meals, container, false);\n\n        /**\n         * Link layout elements from XML and setup the toolbar\n         */\n        initializeScreen(rootView);\n\n        /**\n         * Set interactive bits, such as click events/adapters\n         */\n        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n\n            }\n        });\n\n        return rootView;\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n    }\n\n    private void initializeScreen(View rootView) {\n        mListView = (ListView) rootView.findViewById(R.id.list_view_meals_list);\n        View footer = getActivity().getLayoutInflater().inflate(R.layout.footer_empty, null);\n        mListView.addFooterView(footer);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AddFriendActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.widget.EditText;\nimport android.widget.ListView;\n\nimport com.firebase.client.Firebase;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\n/**\n * Represents the Add Friend screen and functionality\n */\npublic class AddFriendActivity extends BaseActivity {\n    private EditText mEditTextAddFriendEmail;\n    private AutocompleteFriendAdapter mFriendsAutocompleteAdapter;\n    private String mInput;\n    private ListView mListViewAutocomplete;\n    private Firebase mUsersRef;\n\n\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_add_friend);\n        /**\n         * Create Firebase references\n         */\n        mUsersRef = new Firebase(Constants.FIREBASE_URL_USERS);\n\n        /**\n         * Link layout elements from XML and setup the toolbar\n         */\n        initializeScreen();\n\n        /**\n         * Set interactive bits, such as click events/adapters\n         */\n\n        mEditTextAddFriendEmail.addTextChangedListener(new TextWatcher() {\n        @Override\n        public void beforeTextChanged(CharSequence s, int start, int count, int after) {\n        }\n\n        @Override\n        public void onTextChanged(CharSequence s, int start, int before, int count) {\n        }\n\n        @Override\n        public void afterTextChanged(Editable s) {\n                /* Get the input after every textChanged event and transform it to lowercase */\n            mInput = mEditTextAddFriendEmail.getText().toString().toLowerCase();\n\n            /* Clean up the old adapter */\n            if (mFriendsAutocompleteAdapter != null) mFriendsAutocompleteAdapter.cleanup();\n            /* Nullify the adapter data if the input length is less than 2 characters */\n            if (mInput.equals(\"\") || mInput.length() < 2) {\n                mListViewAutocomplete.setAdapter(null);\n\n            /* Define and set the adapter otherwise. */\n            } else {\n                mFriendsAutocompleteAdapter = new AutocompleteFriendAdapter(AddFriendActivity.this, User.class,\n                        R.layout.single_autocomplete_item, mUsersRef.orderByChild(Constants.FIREBASE_PROPERTY_EMAIL)\n                        .startAt(mInput).endAt(mInput + \"~\").limitToFirst(5), mEncodedEmail);\n\n                mListViewAutocomplete.setAdapter(mFriendsAutocompleteAdapter);\n            }\n\n        }\n        });\n\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        if (mFriendsAutocompleteAdapter != null) {\n            mFriendsAutocompleteAdapter.cleanup();\n        }\n    }\n\n    /**\n     * Link layout elements from XML and setup the toolbar\n     */\n    public void initializeScreen() {\n        mListViewAutocomplete = (ListView) findViewById(R.id.list_view_friends_autocomplete);\n        mEditTextAddFriendEmail = (EditText) findViewById(R.id.edit_text_add_friend_email);\n        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);\n        setSupportActionBar(toolbar);\n        /* Add back button to the action bar */\n        if (getSupportActionBar() != null) {\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AutocompleteFriendAdapter.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.app.Activity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.Query;\nimport com.firebase.client.ValueEventListener;\nimport com.firebase.ui.FirebaseListAdapter;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\n/**\n * Populates the list_view_friends_autocomplete inside AddFriendActivity\n */\npublic class AutocompleteFriendAdapter extends FirebaseListAdapter<User> {\n    private String mEncodedEmail;\n\n    /**\n     * Public constructor that initializes private instance variables when adapter is created\n     */\n    public AutocompleteFriendAdapter(Activity activity, Class<User> modelClass, int modelLayout,\n                                     Query ref, String encodedEmail) {\n        super(activity, modelClass, modelLayout, ref);\n        this.mActivity = activity;\n        this.mEncodedEmail = encodedEmail;\n    }\n\n    /**\n     * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete)\n     * with items inflated from single_autocomplete_item.xml\n     * populateView also handles data changes and updates the listView accordingly\n     */\n    @Override\n    protected void populateView(View view, final User user) {\n         /* Get friends email textview and set it's text to user.email() */\n        TextView textViewFriendEmail = (TextView) view.findViewById(R.id.text_view_autocomplete_item);\n        textViewFriendEmail.setText(Utils.decodeEmail(user.getEmail()));\n\n        /**\n         * Set the onClickListener to a single list item\n         * If selected email is not friend already and if it is not the\n         * current user's email, we add selected user to current user's friends\n         */\n        textViewFriendEmail.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n\n                /**\n                 * If selected user is not current user proceed\n                 */\n                if (isNotCurrentUser(user)) {\n\n                    Firebase currentUserFriendsRef = new Firebase(Constants.FIREBASE_URL_USER_FRIENDS).child(mEncodedEmail);\n                    final Firebase friendRef = currentUserFriendsRef.child(user.getEmail());\n\n                    /**\n                     * Add listener for single value event to perform a one time operation\n                     */\n                    friendRef.addListenerForSingleValueEvent(new ValueEventListener() {\n                        @Override\n                        public void onDataChange(DataSnapshot dataSnapshot) {\n\n                            /**\n                             * Add selected user to current user's friends if not in friends yet\n                             */\n                            if (isNotAlreadyAdded(dataSnapshot, user)) {\n                                friendRef.setValue(user);\n                                mActivity.finish();\n                            }\n                        }\n\n                        @Override\n                        public void onCancelled(FirebaseError firebaseError) {\n                            Log.e(mActivity.getClass().getSimpleName(),\n                                    mActivity.getString(R.string.log_error_the_read_failed) +\n                                            firebaseError.getMessage());\n                        }\n                    });\n\n                }\n            }\n        });\n\n    }\n\n    private boolean isNotCurrentUser(User user) {\n        if (user.getEmail().equals(mEncodedEmail)) {\n            /* Toast appropriate error message if the user is trying to add themselves  */\n            Toast.makeText(mActivity,\n                    mActivity.getResources().getString(R.string.toast_you_cant_add_yourself),\n                    Toast.LENGTH_LONG).show();\n            return false;\n        }\n        return true;\n    }\n\n    private boolean isNotAlreadyAdded(DataSnapshot dataSnapshot, User user) {\n        if (dataSnapshot.getValue(User.class) != null) {\n            /* Toast appropriate error message if the user is already a friend of the user */\n            String friendError = String.format(mActivity.getResources().\n                            getString(R.string.toast_is_already_your_friend),\n                    user.getName());\n\n            Toast.makeText(mActivity,\n                    friendError,\n                    Toast.LENGTH_LONG).show();\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/FriendAdapter.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.app.Activity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.ImageButton;\nimport android.widget.TextView;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.Query;\nimport com.firebase.client.ValueEventListener;\nimport com.firebase.ui.FirebaseListAdapter;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Utils;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Populates the list_view_friends_share inside ShareListActivity\n */\npublic class FriendAdapter extends FirebaseListAdapter<User> {\n    private ShoppingList mShoppingList;\n    private static final String LOG_TAG = FriendAdapter.class.getSimpleName();\n    private String mListId;\n    private Firebase mFirebaseRef;\n    private HashMap<String, User> mSharedUsersList;\n    private HashMap <Firebase, ValueEventListener> mLocationListenerMap;\n\n\n    /**\n     * Public constructor that initializes private instance variables when adapter is created\n     */\n    public FriendAdapter(Activity activity, Class<User> modelClass, int modelLayout,\n                         Query ref, String listId) {\n        super(activity, modelClass, modelLayout, ref);\n        this.mActivity = activity;\n        this.mListId = listId;\n        mFirebaseRef = new Firebase(Constants.FIREBASE_URL);\n        mLocationListenerMap = new HashMap<Firebase, ValueEventListener>();\n    }\n\n    /**\n     * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete)\n     * with items inflated from single_user_item.xml\n     * populateView also handles data changes and updates the listView accordingly\n     */\n    @Override\n    protected void populateView(View view, final User friend) {\n        ((TextView) view.findViewById(R.id.user_name)).setText(friend.getName());\n        final ImageButton buttonToggleShare = (ImageButton) view.findViewById(R.id.button_toggle_share);\n\n        final Firebase sharedFriendInShoppingListRef = new Firebase(Constants.FIREBASE_URL_LISTS_SHARED_WITH)\n                .child(mListId).child(friend.getEmail());\n\n        /**\n         * Gets the value of the friend from the ShoppingList's sharedWith list of users\n         * and then allows the friend to be toggled as shared with or not.\n         *\n         * The friend in the snapshot (sharedFriendInShoppingList) will either be a User object\n         * (if they are in the the sharedWith list) or null (if they are not in the sharedWith\n         * list)\n         */\n\n        ValueEventListener listener = sharedFriendInShoppingListRef.addValueEventListener(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot snapshot) {\n                final User sharedFriendInShoppingList = snapshot.getValue(User.class);\n\n                /**\n                 * If list is already being shared with this friend, set the buttonToggleShare\n                 * to remove selected friend from sharedWith onClick and change the\n                 * buttonToggleShare image to green\n                 */\n                if (sharedFriendInShoppingList != null) {\n                    buttonToggleShare.setImageResource(R.drawable.ic_shared_check);\n                    buttonToggleShare.setOnClickListener(new View.OnClickListener() {\n                        @Override\n                        public void onClick(View v) {\n\n                            /**\n                             * Create map and fill it in with deep path multi write operations list.\n                             * Use false to mark that you are removing this friend\n                             */\n                            HashMap<String, Object> updatedUserData = updateFriendInSharedWith(false, friend);\n\n                            /* Do a deep-path update */\n                            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {\n                                @Override\n                                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId,\n                                            mSharedUsersList, mShoppingList.getOwner());\n                                }\n                            });\n                        }\n                    });\n                } else {\n\n                    /**\n                     * Set the buttonToggleShare onClickListener to add selected friend to sharedWith\n                     * and change the buttonToggleShare image to grey otherwise\n                     */\n                    buttonToggleShare.setImageResource(R.drawable.icon_add_friend);\n                    buttonToggleShare.setOnClickListener(new View.OnClickListener() {\n                        @Override\n                        public void onClick(View v) {\n\n                            /**\n                             * Create map and fill it in with deep path multi write operations list\n                             */\n                            HashMap<String, Object> updatedUserData = updateFriendInSharedWith(true, friend);\n\n                            /* Do a deep-path update */\n                            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {\n                                @Override\n                                public void onComplete(FirebaseError firebaseError, Firebase firebase) {\n                                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId,\n                                            mSharedUsersList, mShoppingList.getOwner());\n                                }\n                            });\n                        }\n                    });\n                }\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        mActivity.getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n        /* Add the listener to the HashMap so that it can be removed on cleanup */\n        mLocationListenerMap.put(sharedFriendInShoppingListRef, listener);\n\n    }\n\n    /**\n     * Public method that is used to pass ShoppingList object when it is loaded in ValueEventListener\n     */\n    public void setShoppingList(ShoppingList shoppingList) {\n        this.mShoppingList = shoppingList;\n        this.notifyDataSetChanged();\n    }\n\n    /**\n     * Public method that is used to pass SharedUsers when they are loaded in ValueEventListener\n     */\n    public void setSharedWithUsers(HashMap<String, User> sharedUsersList) {\n        this.mSharedUsersList = sharedUsersList;\n        this.notifyDataSetChanged();\n    }\n\n    /**\n     * This method does the tricky job of adding or removing a friend from the sharedWith list.\n     * @param addFriend This is true if the friend is being added, false is the friend is being removed.\n     * @param friendToAddOrRemove This is the friend to either add or remove\n     * @return\n     */\n    private HashMap<String, Object> updateFriendInSharedWith(Boolean addFriend, User friendToAddOrRemove) {\n        HashMap<String, Object> updatedUserData = new HashMap<String, Object>();\n\n        /* The newSharedWith lists contains all users who need their last time changed updated */\n        HashMap<String, User> newSharedWith = new HashMap<String, User>(mSharedUsersList);\n\n        if (addFriend) {\n            /**\n             * Changes the timestamp changed to now; Because of ancestry issues, we cannot\n             * have one updateChildren call that both creates data and then updates that same data\n             * because updateChildren has no way of knowing what was the intended update\n             */\n            mShoppingList.setTimestampLastChangedToNow();\n            /* Make it a HashMap of the shopping list and user */\n            final HashMap<String, Object> shoppingListForFirebase = (HashMap<String, Object>)\n                    new ObjectMapper().convertValue(mShoppingList, Map.class);\n\n            final HashMap<String, Object> friendForFirebase = (HashMap<String, Object>)\n                    new ObjectMapper().convertValue(friendToAddOrRemove, Map.class);\n\n            /* Add the friend to the shared list */\n            updatedUserData.put(\"/\" + Constants.FIREBASE_LOCATION_LISTS_SHARED_WITH + \"/\" + mListId +\n                    \"/\" + friendToAddOrRemove.getEmail(), friendForFirebase);\n\n            /* Add that shopping list hashmap to the new user's active lists */\n            updatedUserData.put(\"/\" + Constants.FIREBASE_LOCATION_USER_LISTS + \"/\" + friendToAddOrRemove.getEmail()\n                    + \"/\" + mListId, shoppingListForFirebase);\n\n        } else {\n            /* Remove the friend from the shared list */\n            updatedUserData.put(\"/\" + Constants.FIREBASE_LOCATION_LISTS_SHARED_WITH + \"/\" + mListId +\n                    \"/\" + friendToAddOrRemove.getEmail(), null);\n\n            /* Remove the list from the shared friend */\n            updatedUserData.put(\"/\" + Constants.FIREBASE_LOCATION_USER_LISTS + \"/\" + friendToAddOrRemove.getEmail()\n                    + \"/\" + mListId, null);\n\n            newSharedWith.remove(friendToAddOrRemove.getEmail());\n\n        }\n\n        Utils.updateMapWithTimestampLastChanged(newSharedWith,\n                mListId, mShoppingList.getOwner(), updatedUserData);\n\n        return updatedUserData;\n    }\n\n    @Override\n    public void cleanup() {\n        super.cleanup();\n        /* Clean up the event listeners */\n        for (HashMap.Entry<Firebase, ValueEventListener> listenerToClean : mLocationListenerMap.entrySet())\n        {\n            listenerToClean.getKey().removeEventListener(listenerToClean.getValue());\n        }\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/ShareListActivity.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.ListView;\n\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ValueEventListener;\nimport com.udacity.firebase.shoppinglistplusplus.R;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\nimport com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;\nimport com.udacity.firebase.shoppinglistplusplus.utils.Constants;\n\nimport java.util.HashMap;\n\n/**\n * Allows for you to check and un-check friends that you share the current list with\n */\npublic class ShareListActivity extends BaseActivity {\n    private static final String LOG_TAG = ShareListActivity.class.getSimpleName();\n    private FriendAdapter mFriendAdapter;\n    private ListView mListView;\n    private ShoppingList mShoppingList;\n    private String mListId;\n    private Firebase mActiveListRef, mSharedWithRef;\n    private ValueEventListener mActiveListRefListener, mSharedWithListener;\n    private HashMap<String, User> mSharedWithUsers;\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_share_list);\n\n        /* Get the push ID from the extra passed by ActiveListDetailsActivity */\n        Intent intent = this.getIntent();\n        mListId = intent.getStringExtra(Constants.KEY_LIST_ID);\n        if (mListId == null) {\n            /* No point in continuing without a valid ID. */\n            finish();\n            return;\n        }\n\n        /**\n         * Link layout elements from XML and setup the toolbar\n         */\n        initializeScreen();\n\n        /**\n         * Create Firebase references\n         */\n        Firebase currentUserFriendsRef = new Firebase(Constants.FIREBASE_URL_USER_FRIENDS).child(mEncodedEmail);\n        mActiveListRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS).child(mEncodedEmail).child(mListId);\n        mSharedWithRef = new Firebase (Constants.FIREBASE_URL_LISTS_SHARED_WITH).child(mListId);\n\n        /**\n         * Add ValueEventListeners to Firebase references\n         * to control get data and control behavior and visibility of elements\n         */\n\n        mActiveListRefListener = mActiveListRef.addValueEventListener(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot dataSnapshot) {\n                ShoppingList shoppingList = dataSnapshot.getValue(ShoppingList.class);\n\n                /**\n                 * Saving the most recent version of current shopping list into mShoppingList\n                 * and pass it to setShoppingList() if present\n                 * finish() the activity otherwise\n                 */\n                if (shoppingList != null) {\n                    mShoppingList = shoppingList;\n                    mFriendAdapter.setShoppingList(mShoppingList);\n                } else {\n                    finish();\n                }\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n\n        mSharedWithListener = mSharedWithRef.addValueEventListener(new ValueEventListener() {\n            @Override\n            public void onDataChange(DataSnapshot dataSnapshot) {\n                mSharedWithUsers = new HashMap<String, User>();\n                for (DataSnapshot currentUser : dataSnapshot.getChildren()) {\n                    mSharedWithUsers.put(currentUser.getKey(), currentUser.getValue(User.class));\n                }\n                mFriendAdapter.setSharedWithUsers(mSharedWithUsers);\n            }\n\n            @Override\n            public void onCancelled(FirebaseError firebaseError) {\n                Log.e(LOG_TAG,\n                        getString(R.string.log_error_the_read_failed) +\n                                firebaseError.getMessage());\n            }\n        });\n\n\n        /**\n         * Set interactive bits, such as click events/adapters\n         */\n        mFriendAdapter = new FriendAdapter(ShareListActivity.this, User.class,\n                R.layout.single_user_item, currentUserFriendsRef, mListId);\n\n        /* Set adapter for the listView */\n        mListView.setAdapter(mFriendAdapter);\n    }\n\n    /**\n     * Cleanup the adapter when activity is destroyed\n     */\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        /* Set adapter for the listView */\n        mFriendAdapter.cleanup();\n        mActiveListRef.removeEventListener(mActiveListRefListener);\n        mSharedWithRef.removeEventListener(mSharedWithListener);\n    }\n\n    /**\n     * Link layout elements from XML and setup the toolbar\n     */\n    public void initializeScreen() {\n        mListView = (ListView) findViewById(R.id.list_view_friends_share);\n        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);\n        setSupportActionBar(toolbar);\n        /* Add back button to the action bar */\n        if (getSupportActionBar() != null) {\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n    }\n\n    /**\n     * Launch AddFriendActivity to find and add user to current user's friends list\n     * when the button AddFriend is pressed\n     */\n    public void onAddFriendPressed(View view) {\n        Intent intent = new Intent(ShareListActivity.this, AddFriendActivity.class);\n        startActivity(intent);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.utils;\n\nimport com.udacity.firebase.shoppinglistplusplus.BuildConfig;\n\n/**\n * Constants class store most important strings and paths of the app\n */\npublic final class Constants {\n\n    /**\n     * Constants related to locations in Firebase, such as the name of the node\n     * where user lists are stored (ie \"userLists\")\n     */\n    public static final String FIREBASE_LOCATION_SHOPPING_LIST_ITEMS = \"shoppingListItems\";\n    public static final String FIREBASE_LOCATION_USERS = \"users\";\n    public static final String FIREBASE_LOCATION_USER_LISTS = \"userLists\";\n    public static final String FIREBASE_LOCATION_USER_FRIENDS = \"userFriends\";\n    public static final String FIREBASE_LOCATION_LISTS_SHARED_WITH = \"sharedWith\";\n    public static final String FIREBASE_LOCATION_UID_MAPPINGS = \"uidMappings\";\n    public static final String FIREBASE_LOCATION_OWNER_MAPPINGS = \"ownerMappings\";\n\n\n\n    /**\n     * Constants for Firebase object properties\n     */\n    public static final String FIREBASE_PROPERTY_BOUGHT = \"bought\";\n    public static final String FIREBASE_PROPERTY_BOUGHT_BY = \"boughtBy\";\n    public static final String FIREBASE_PROPERTY_LIST_NAME = \"listName\";\n    public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED = \"timestampLastChanged\";\n    public static final String FIREBASE_PROPERTY_TIMESTAMP = \"timestamp\";\n    public static final String FIREBASE_PROPERTY_ITEM_NAME = \"itemName\";\n    public static final String FIREBASE_PROPERTY_EMAIL = \"email\";\n    public static final String FIREBASE_PROPERTY_USERS_SHOPPING = \"usersShopping\";\n    public static final String FIREBASE_PROPERTY_USER_HAS_LOGGED_IN_WITH_PASSWORD = \"hasLoggedInWithPassword\";\n    public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED_REVERSE = \"timestampLastChangedReverse\";\n\n\n    /**\n     * Constants for Firebase URL\n     */\n    public static final String FIREBASE_URL = BuildConfig.UNIQUE_FIREBASE_ROOT_URL;\n    public static final String FIREBASE_URL_SHOPPING_LIST_ITEMS = FIREBASE_URL + \"/\" + FIREBASE_LOCATION_SHOPPING_LIST_ITEMS;\n    public static final String FIREBASE_URL_USERS = FIREBASE_URL + \"/\" + FIREBASE_LOCATION_USERS;\n    public static final String FIREBASE_URL_USER_LISTS = FIREBASE_URL + \"/\" + FIREBASE_LOCATION_USER_LISTS;\n    public static final String FIREBASE_URL_USER_FRIENDS = FIREBASE_URL + \"/\" + FIREBASE_LOCATION_USER_FRIENDS;\n    public static final String FIREBASE_URL_LISTS_SHARED_WITH = FIREBASE_URL + \"/\" + FIREBASE_LOCATION_LISTS_SHARED_WITH;\n\n    /**\n     * Constants for bundles, extras and shared preferences keys\n     */\n    public static final String KEY_LIST_NAME = \"LIST_NAME\";\n    public static final String KEY_LAYOUT_RESOURCE = \"LAYOUT_RESOURCE\";\n    public static final String KEY_LIST_ID = \"LIST_ID\";\n    public static final String KEY_SIGNUP_EMAIL = \"SIGNUP_EMAIL\";\n    public static final String KEY_LIST_ITEM_NAME = \"ITEM_NAME\";\n    public static final String KEY_LIST_ITEM_ID = \"LIST_ITEM_ID\";\n    public static final String KEY_PROVIDER = \"PROVIDER\";\n    public static final String KEY_ENCODED_EMAIL = \"ENCODED_EMAIL\";\n    public static final String KEY_LIST_OWNER = \"LIST_OWNER\";\n    public static final String KEY_GOOGLE_EMAIL = \"GOOGLE_EMAIL\";\n    public static final String KEY_PREF_SORT_ORDER_LISTS = \"PERF_SORT_ORDER_LISTS\";\n    public static final String KEY_SHARED_WITH_USERS = \"SHARED_WITH_USERS\";\n\n\n    /**\n     * Constants for Firebase login\n     */\n    public static final String PASSWORD_PROVIDER = \"password\";\n    public static final String GOOGLE_PROVIDER = \"google\";\n    public static final String PROVIDER_DATA_DISPLAY_NAME = \"displayName\";\n\n\n    /**\n     * Constant for sorting\n     */\n    public static final String ORDER_BY_KEY = \"orderByPushKey\";\n    public static final String ORDER_BY_OWNER_EMAIL = \"orderByOwnerEmail\";\n\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java",
    "content": "package com.udacity.firebase.shoppinglistplusplus.utils;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport com.firebase.client.DataSnapshot;\nimport com.firebase.client.Firebase;\nimport com.firebase.client.FirebaseError;\nimport com.firebase.client.ServerValue;\nimport com.firebase.client.ValueEventListener;\nimport com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;\nimport com.udacity.firebase.shoppinglistplusplus.model.User;\n\nimport java.text.SimpleDateFormat;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Utility class\n */\npublic class Utils {\n    /**\n     * Format the timestamp with SimpleDateFormat\n     */\n    public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n    private Context mContext = null;\n\n\n    /**\n     * Public constructor that takes mContext for later use\n     */\n    public Utils(Context con) {\n        mContext = con;\n    }\n\n    /**\n     * Return true if currentUserEmail equals to shoppingList.owner()\n     * Return false otherwise\n     */\n    public static boolean checkIfOwner(ShoppingList shoppingList, String currentUserEmail) {\n        return (shoppingList.getOwner() != null &&\n                shoppingList.getOwner().equals(currentUserEmail));\n    }\n\n    /**\n     * Encode user email to use it as a Firebase key (Firebase does not allow \".\" in the key name)\n     * Encoded email is also used as \"userEmail\", list and item \"owner\" value\n     */\n    public static String encodeEmail(String userEmail) {\n        return userEmail.replace(\".\", \",\");\n    }\n    \n    /**\n     * Email is being decoded just once to display real email in AutocompleteFriendAdapter\n     *\n     * @see com.udacity.firebase.shoppinglistplusplus.ui.sharing.AutocompleteFriendAdapter\n     */\n    public static String decodeEmail(String userEmail) {\n        return userEmail.replace(\",\", \".\");\n    }\n\n    /**\n     * Adds values to a pre-existing HashMap for updating a property for all of the ShoppingList copies.\n     * The HashMap can then be used with {@link Firebase#updateChildren(Map)} to update the property\n     * for all ShoppingList copies.\n     *\n     * @param sharedWith            The list of users the shopping list that has been updated is shared with.\n     * @param listId           The id of the shopping list.\n     * @param owner            The owner of the shopping list.\n     * @param mapToUpdate      The map containing the key, value pairs which will be used\n     *                         to update the Firebase database. This MUST be a Hashmap of key\n     *                         value pairs who's urls are absolute (i.e. from the root node)\n     * @param propertyToUpdate The property to update\n     * @param valueToUpdate    The value to update\n     * @return The updated HashMap with the new value inserted in all lists\n     */\n    public static HashMap<String, Object> updateMapForAllWithValue\n    (final HashMap<String, User> sharedWith, final String listId,\n     final String owner, HashMap<String, Object> mapToUpdate,\n     String propertyToUpdate, Object valueToUpdate) {\n\n        mapToUpdate.put(\"/\" + Constants.FIREBASE_LOCATION_USER_LISTS + \"/\" + owner + \"/\"\n                + listId + \"/\" + propertyToUpdate, valueToUpdate);\n        if (sharedWith != null) {\n            for (User user : sharedWith.values()) {\n                mapToUpdate.put(\"/\" + Constants.FIREBASE_LOCATION_USER_LISTS + \"/\" + user.getEmail() + \"/\"\n                        + listId + \"/\" + propertyToUpdate, valueToUpdate);\n            }\n        }\n\n        return mapToUpdate;\n    }\n\n    /**\n     * Adds values to a pre-existing HashMap for updating all Last Changed Timestamps for all of\n     * the ShoppingList copies. This method uses {@link #updateMapForAllWithValue} to update the\n     * last changed timestamp for all ShoppingList copies.\n     *\n     * @param sharedWith           The list of users the shopping list that has been updated is shared with.\n     * @param listId               The id of the shopping list.\n     * @param owner                The owner of the shopping list.\n     * @param mapToAddDateToUpdate The map containing the key, value pairs which will be used\n     *                             to update the Firebase database. This MUST be a Hashmap of key\n     *                             value pairs who's urls are absolute (i.e. from the root node)\n     * @return\n     */\n    public static HashMap<String, Object> updateMapWithTimestampLastChanged\n    (final HashMap<String, User> sharedWith, final String listId,\n     final String owner, HashMap<String, Object> mapToAddDateToUpdate) {\n        /**\n         * Set raw version of date to the ServerValue.TIMESTAMP value and save into dateCreatedMap\n         */\n        HashMap<String, Object> timestampNowHash = new HashMap<>();\n        timestampNowHash.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);\n\n        updateMapForAllWithValue(sharedWith, listId, owner, mapToAddDateToUpdate,\n                Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED, timestampNowHash);\n\n        return mapToAddDateToUpdate;\n    }\n    /**\n     * Once an update is made to a ShoppingList, this method is responsible for updating the\n     * reversed timestamp to be equal to the negation of the current timestamp. This comes after\n     * the updateMapWithTimestampChanged because ServerValue.TIMESTAMP must be resolved to a long\n     * value.\n     *\n     * @param firebaseError      The Firebase error, if there was one, from the original update. This\n     *                           method should only run if the shopping list's timestamp last changed\n     *                           was successfully updated.\n     * @param logTagFromActivity The log tag from the activity calling this method\n     * @param listId             The updated shopping list push ID\n     * @param sharedWith         The list of users that this updated shopping list is shared with\n     * @param owner              The owner of the updated shopping list\n     */\n    public static void updateTimestampReversed(FirebaseError firebaseError, final String logTagFromActivity,\n                                               final String listId, final HashMap<String, User> sharedWith,\n                                               final String owner) {\n        if (firebaseError != null) {\n            Log.d(logTagFromActivity, \"Error updating timestamp: \" + firebaseError.getMessage());\n        } else {\n            final Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);\n            firebaseRef.child(Constants.FIREBASE_LOCATION_USER_LISTS).child(owner)\n                    .child(listId).addListenerForSingleValueEvent(new ValueEventListener() {\n                @Override\n                public void onDataChange(DataSnapshot dataSnapshot) {\n\n                    ShoppingList list = dataSnapshot.getValue(ShoppingList.class);\n                    if (list != null) {\n                        long timeReverse = -(list.getTimestampLastChangedLong());\n                        String timeReverseLocation = Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED_REVERSE\n                                + \"/\" + Constants.FIREBASE_PROPERTY_TIMESTAMP;\n\n                        /**\n                         * Create map and fill it in with deep path multi write operations list\n                         */\n                        HashMap<String, Object> updatedShoppingListData = new HashMap<String, Object>();\n\n                        updateMapForAllWithValue(sharedWith, listId, owner, updatedShoppingListData,\n                                timeReverseLocation, timeReverse);\n                        firebaseRef.updateChildren(updatedShoppingListData);\n                    }\n                }\n\n                @Override\n                public void onCancelled(FirebaseError firebaseError) {\n                    Log.d(logTagFromActivity, \"Error updating data: \" + firebaseError.getMessage());\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/layout/activity_active_list_details.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/grey\"\n    tools:context=\".ui.activeListDetails.ActiveListDetailsActivity\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/app_bar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:theme=\"@style/Toolbar\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@android:color/white\"\n            android:orientation=\"horizontal\"\n            android:padding=\"8dp\"\n            android:visibility=\"visible\">\n\n            <TextView\n                android:id=\"@+id/text_view_people_shopping\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"start\"\n                android:layout_weight=\"1\"\n                android:textSize=\"@dimen/list_item_text_size\" />\n\n            <Button\n                android:id=\"@+id/button_shopping\"\n                style=\"@style/ShoppingButton\"\n                android:layout_gravity=\"end\"\n                android:onClick=\"toggleShopping\"\n                />\n        </LinearLayout>\n\n        <ListView\n            android:id=\"@+id/list_view_shopping_list_items\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:scrollbars=\"none\" />\n    </LinearLayout>\n\n    <android.support.design.widget.FloatingActionButton xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/fab_detail_add_meal\"\n        style=\"@style/FAB\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_toLeftOf=\"@+id/fab_detail_add_item\"\n        android:layout_toStartOf=\"@+id/fab_detail_add_item\"\n        android:onClick=\"addMeal\"\n        android:src=\"@drawable/abc_ic_menu_copy_mtrl_am_alpha\"\n        app:borderWidth=\"0dp\"\n        app:elevation=\"6dp\"\n        app:pressedTranslationZ=\"12dp\"\n        android:visibility=\"gone\"/>\n\n    <android.support.design.widget.FloatingActionButton xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/fab_detail_add_item\"\n        style=\"@style/FAB\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:onClick=\"showAddListItemDialog\"\n        android:src=\"@drawable/icon_add\"\n        app:borderWidth=\"0dp\"\n        app:elevation=\"6dp\"\n        app:pressedTranslationZ=\"12dp\" />\n</RelativeLayout>\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/activity_add_friend.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".ui.sharing.AddFriendActivity\">\n\n    <android.support.v7.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:theme=\"@style/Toolbar\" />\n\n    <ListView\n        android:id=\"@+id/list_view_friends_autocomplete\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@+id/til_add_friend_email\"\n        android:layout_below=\"@+id/app_bar\"\n        android:layout_centerHorizontal=\"true\" />\n\n    <android.support.design.widget.TextInputLayout\n        android:id=\"@+id/til_add_friend_email\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_margin=\"@dimen/margin_small\">\n\n        <EditText\n            android:id=\"@+id/edit_text_add_friend_email\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:hint=\"@string/hint_enter_friends_email\"\n            android:imeOptions=\"flagNoExtractUi\"\n            android:inputType=\"textEmailAddress\"\n            android:textColor=\"@color/black\"\n            android:textColorHint=\"@color/dark_grey\"\n            android:visibility=\"visible\"/>\n    </android.support.design.widget.TextInputLayout>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_create_account.xml",
    "content": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    tools:context=\".ui.login.CreateAccountActivity\">\n\n    <LinearLayout\n        android:id=\"@+id/linear_layout_create_account_activity\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\">\n\n        <LinearLayout\n            android:layout_width=\"0px\"\n            android:layout_height=\"0px\"\n            android:focusable=\"true\"\n            android:focusableInTouchMode=\"true\" />\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_username_create\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/lists_title_bottom_margin\"\n            app:hintTextAppearance=\"@style/HintText\">\n\n            <EditText\n                android:id=\"@+id/edit_text_username_create\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_username\"\n                android:inputType=\"textCapWords\"\n                android:padding=\"@dimen/edit_text_padding\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_email_create\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:hintTextAppearance=\"@style/HintText\"\n            android:layout_marginBottom=\"@dimen/margin_normal\">\n\n            <EditText\n                android:id=\"@+id/edit_text_email_create\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_email\"\n                android:inputType=\"textEmailAddress\"\n                android:padding=\"@dimen/edit_text_padding\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <Button\n            android:id=\"@+id/btn_create_account_final\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/input_field_height\"\n            android:background=\"@color/primary\"\n            android:onClick=\"onCreateAccountPressed\"\n            android:text=\"@string/title_activity_create_account\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"@dimen/login_buttons_text_size\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_small\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_already_have_account\"\n                style=\"@style/LoginCreateText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/text_already_have_account\" />\n\n            <TextView\n                android:id=\"@+id/tv_sign_in\"\n                style=\"@style/LoginCreateTextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onSignInPressed\"\n                android:text=\"@string/button_sign_in\" />\n        </LinearLayout>\n    </LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/activity_login.xml",
    "content": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    tools:context=\".ui.login.LoginActivity\">\n\n    <LinearLayout\n        android:id=\"@+id/linear_layout_login_activity\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\">\n\n        <LinearLayout\n            android:layout_width=\"0px\"\n            android:layout_height=\"0px\"\n            android:focusable=\"true\"\n            android:focusableInTouchMode=\"true\" />\n\n        <ImageView\n            android:id=\"@+id/iv_logo_image\"\n            android:layout_width=\"@dimen/logo_image_size\"\n            android:layout_height=\"@dimen/logo_image_size\"\n            android:layout_marginBottom=\"@dimen/lists_title_bottom_margin\"\n            android:contentDescription=\"@string/image_view_logo_image\"\n            android:src=\"@drawable/ic_shopping_list\" />\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_email\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/lists_title_bottom_margin\"\n            app:hintTextAppearance=\"@style/HintText\">\n\n            <EditText\n                android:id=\"@+id/edit_text_email\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_email\"\n                android:inputType=\"textEmailAddress\"\n                android:nextFocusDown=\"@+id/edit_text_password\"\n                android:padding=\"@dimen/edit_text_padding\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_password\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/margin_normal\"\n            app:hintTextAppearance=\"@style/HintText\">\n\n            <EditText\n                android:id=\"@+id/edit_text_password\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_password\"\n                android:imeOptions=\"actionDone\"\n                android:inputType=\"textPassword\"\n                android:padding=\"@dimen/edit_text_padding\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <Button\n            android:id=\"@+id/login_with_password\"\n            style=\"@style/Widget.Udacity.Button.Login\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/input_field_height\"\n            android:layout_marginBottom=\"@dimen/lv_header_text_size\"\n            android:background=\"@color/primary\"\n            android:onClick=\"onSignInPressed\"\n            android:text=\"@string/button_signin_with_password\"\n            android:textColor=\"@android:color/white\" />\n\n        <com.google.android.gms.common.SignInButton\n            android:id=\"@+id/login_with_google\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_small\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_dont_have_account\"\n                style=\"@style/LoginCreateText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/text_dont_have_account\" />\n\n            <TextView\n                android:id=\"@+id/tv_sign_up\"\n                style=\"@style/LoginCreateTextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onSignUpPressed\"\n                android:text=\"@string/button_create_account\" />\n        </LinearLayout>\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/grey\"\n    android:orientation=\"vertical\">\n\n    <android.support.v7.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:theme=\"@style/Toolbar\" />\n\n    <android.support.design.widget.TabLayout\n        android:id=\"@+id/tab_layout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:theme=\"@style/Toolbar\" />\n\n    <android.support.v4.view.ViewPager\n        android:id=\"@+id/pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_share_list.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\".ui.sharing.ShareListActivity\">\n\n    <android.support.v7.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/app_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:theme=\"@style/Toolbar\" />\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        android:focusable=\"true\"\n        android:focusableInTouchMode=\"true\" />\n\n    <ListView\n        android:id=\"@+id/list_view_friends_share\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:scrollbars=\"none\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"@dimen/margin_small\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/tv_add_friend\"\n            style=\"@style/HeaderText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            android:text=\"@string/text_add_friend\" />\n\n        <ImageButton\n            android:id=\"@+id/btn_add_friend\"\n            android:layout_width=\"@dimen/lists_row_height\"\n            android:layout_height=\"@dimen/lists_row_height\"\n            android:layout_gravity=\"center_vertical\"\n            android:background=\"@android:color/transparent\"\n            android:onClick=\"onAddFriendPressed\"\n            android:src=\"@drawable/icon_add_friend2\" />\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_item.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_vertical_margin\"\n    android:paddingRight=\"@dimen/activity_vertical_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".ui.activeListDetails.ActiveListDetailsActivity\">\n\n        <EditText\n            android:id=\"@+id/edit_text_list_dialog\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:hint=\"@string/positive_button_add_list_item\"\n            android:imeOptions=\"actionDone|flagNoExtractUi\"\n            android:inputType=\"text\"\n            android:textColor=\"@color/black\"\n            android:textColorHint=\"@color/dark_grey\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_list.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_vertical_margin\"\n    android:paddingRight=\"@dimen/activity_vertical_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.udacity.firebase.shoppinglistplusplus.ui.MainActivity\">\n\n    <EditText\n        android:id=\"@+id/edit_text_list_name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"@string/hint_add_shopping_list\"\n        android:imeOptions=\"actionDone|flagNoExtractUi\"\n        android:inputType=\"textCapWords\"\n        android:textColor=\"@color/black\"\n        android:textColorHint=\"@color/dark_grey\" />\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_meal.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_vertical_margin\"\n    android:paddingRight=\"@dimen/activity_vertical_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.udacity.firebase.shoppinglistplusplus.ui.MainActivity\">\n\n    <EditText\n        android:id=\"@+id/edit_text_meal_name\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"@string/hint_add_meal\"\n        android:imeOptions=\"actionDone|flagNoExtractUi\"\n        android:inputType=\"textCapWords\"\n        android:textColor=\"@color/black\"\n        android:textColorHint=\"@color/dark_grey\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/dialog_edit_item.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_vertical_margin\"\n    android:paddingRight=\"@dimen/activity_vertical_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".ui.activeListDetails.ActiveListDetailsActivity\">\n\n    <EditText\n        android:id=\"@+id/edit_text_list_dialog\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:imeOptions=\"actionDone|flagNoExtractUi\"\n        android:inputType=\"text\"\n        android:textColor=\"@color/black\"\n        android:textColorHint=\"@color/dark_grey\" />\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dialog_edit_list.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_vertical_margin\"\n    android:paddingRight=\"@dimen/activity_vertical_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".ui.activeListDetails.ActiveListDetailsActivity\">\n\n    <EditText\n        android:id=\"@+id/edit_text_list_dialog\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:imeOptions=\"actionDone|flagNoExtractUi\"\n        android:inputType=\"textCapWords\"\n        android:textColor=\"@color/black\"\n        android:textColorHint=\"@color/dark_grey\" />\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/footer_empty.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list_view_footer_empty\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"2dp\"\n    android:clickable=\"false\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:focusable=\"false\"\n    android:foreground=\"?selectableItemBackground\">\n\n    <TextView\n        android:id=\"@+id/text_view_listview_footer\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"@dimen/empty_footer_height\" />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_meals.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/rl_fragment_meals\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/grey\"\n    tools:context=\".MealsFragment\">\n\n    <ListView\n        android:id=\"@+id/list_view_meals_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbars=\"none\" />\n\n    <android.support.design.widget.FloatingActionButton xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/fab\"\n        style=\"@style/FAB\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:onClick=\"showAddMealDialog\"\n        android:src=\"@drawable/icon_add\"\n        app:borderWidth=\"0dp\"\n        app:elevation=\"6dp\"\n        app:pressedTranslationZ=\"12dp\" />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_shopping_lists.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/rl_fragment_shopping_lists\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/grey\"\n    tools:context=\".ShoppingListsFragment\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n        <ListView\n            android:id=\"@+id/list_view_active_lists\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\"\n            android:scrollbars=\"none\" />\n\n    </LinearLayout>\n\n    <android.support.design.widget.FloatingActionButton xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/fab\"\n        style=\"@style/FAB\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:onClick=\"showAddListDialog\"\n        android:src=\"@drawable/icon_add\"\n        app:borderWidth=\"0dp\"\n        app:elevation=\"6dp\"\n        app:pressedTranslationZ=\"12dp\"\n        app:rippleColor=\"@android:color/white\" />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/single_active_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/lists_row_height\"\n    android:layout_margin=\"2dp\"\n    android:background=\"@android:color/white\"\n    android:clickable=\"false\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:focusable=\"false\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:paddingLeft=\"@dimen/margin_small\"\n    android:paddingStart=\"@dimen/margin_small\">\n\n    <TextView\n        android:id=\"@+id/text_view_list_name\"\n        style=\"@style/ListItemText\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <TextView\n        android:id=\"@+id/created_by\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/text_view_list_name\"\n        android:layout_marginEnd=\"4dp\"\n        android:layout_marginRight=\"4dp\"\n        android:layout_marginTop=\"2dp\"\n        android:text=\"@string/created_by\"\n        android:textColor=\"@color/light_black\"\n        android:textSize=\"@dimen/list_created_by_text_size\" />\n\n    <TextView\n        android:id=\"@+id/text_view_created_by_user\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/text_view_list_name\"\n        android:layout_marginTop=\"2dp\"\n        android:layout_toEndOf=\"@+id/created_by\"\n        android:layout_toRightOf=\"@+id/created_by\"\n        android:textSize=\"@dimen/list_created_by_text_size\"\n        android:textStyle=\"bold\" />\n\n    <TextView\n        android:id=\"@+id/text_view_people_shopping_count\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@+id/text_view_list_name\"\n        android:layout_marginEnd=\"4dp\"\n        android:layout_marginRight=\"4dp\"\n        android:layout_marginTop=\"2dp\"\n        android:textColor=\"@color/light_black\"\n        android:textSize=\"@dimen/list_created_by_text_size\" />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/single_active_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/lists_row_height\"\n    android:layout_margin=\"2dp\"\n    android:background=\"@android:color/white\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\"\n    android:paddingLeft=\"@dimen/margin_small\"\n    android:paddingStart=\"@dimen/margin_small\">\n\n    <TextView\n        android:id=\"@+id/text_view_active_list_item_name\"\n        style=\"@style/ListItemText\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\" />\n\n    <TextView\n        android:id=\"@+id/text_view_bought_by\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"4dp\"\n        android:layout_marginRight=\"4dp\"\n        android:layout_marginTop=\"2dp\"\n        android:text=\"@string/bought_by\"\n        android:textColor=\"@color/light_black\"\n        android:textSize=\"@dimen/list_created_by_text_size\"\n        android:visibility=\"invisible\" />\n\n    <TextView\n        android:id=\"@+id/text_view_bought_by_user\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"2dp\"\n        android:textSize=\"@dimen/list_created_by_text_size\"\n        android:textStyle=\"bold\"\n        android:visibility=\"invisible\" />\n\n    <ImageButton\n        android:id=\"@+id/button_remove_item\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:background=\"@android:color/transparent\"\n        android:contentDescription=\"@string/remove_item_option\"\n        android:src=\"@drawable/ic_action_remove\"\n        android:visibility=\"visible\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/single_autocomplete_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/input_field_height\"\n    android:layout_margin=\"2dp\"\n    android:background=\"@android:color/white\"\n    android:clickable=\"false\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:focusable=\"false\"\n    android:foreground=\"?selectableItemBackground\"\n    android:paddingLeft=\"@dimen/margin_small\"\n    android:paddingStart=\"@dimen/margin_small\">\n\n    <TextView\n        android:id=\"@+id/text_view_autocomplete_item\"\n        style=\"@style/ListItemText\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center_vertical\" />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/single_user_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/input_field_height\"\n    android:layout_margin=\"2dp\"\n    android:background=\"@android:color/white\"\n    android:clickable=\"false\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:focusable=\"false\"\n    android:foreground=\"?selectableItemBackground\"\n    android:orientation=\"horizontal\"\n    android:paddingLeft=\"@dimen/margin_small\"\n    android:paddingStart=\"@dimen/margin_small\">\n\n    <ImageView\n        android:id=\"@+id/iv_user_image\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\" />\n\n    <TextView\n        android:id=\"@+id/user_name\"\n        style=\"@style/ListItemText\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_weight=\"1\"\n        android:gravity=\"center_vertical\" />\n\n    <ImageButton\n        android:id=\"@+id/button_toggle_share\"\n        android:layout_width=\"@dimen/margin_normal\"\n        android:layout_height=\"@dimen/margin_normal\"\n        android:layout_gravity=\"center_vertical\"\n        android:background=\"@android:color/transparent\"\n        android:src=\"@drawable/icon_add_friend\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout-land/activity_create_account.xml",
    "content": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    tools:context=\".ui.login.CreateAccountActivity\">\n\n    <LinearLayout\n        android:id=\"@+id/linear_layout_create_account_activity\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin_land\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin_land\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\">\n\n        <LinearLayout\n            android:layout_width=\"0px\"\n            android:layout_height=\"0px\"\n            android:focusable=\"true\"\n            android:focusableInTouchMode=\"true\" />\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_username_create\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/lists_title_bottom_margin\"\n            app:hintTextAppearance=\"@style/HintText\">\n\n            <EditText\n                android:id=\"@+id/edit_text_username_create\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_username\"\n                android:imeOptions=\"flagNoExtractUi\"\n                android:inputType=\"textCapWords\"\n                android:padding=\"@dimen/edit_text_padding_land\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_email_create\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:hintTextAppearance=\"@style/HintText\"\n            android:layout_marginBottom=\"@dimen/margin_normal\">\n\n            <EditText\n                android:id=\"@+id/edit_text_email_create\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_email\"\n                android:imeOptions=\"flagNoExtractUi\"\n                android:inputType=\"textEmailAddress\"\n                android:padding=\"@dimen/edit_text_padding_land\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <Button\n            android:id=\"@+id/btn_create_account_final\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/input_field_height_land\"\n            android:background=\"@color/primary\"\n            android:onClick=\"onCreateAccountPressed\"\n            android:text=\"@string/title_activity_create_account\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"@dimen/login_buttons_text_size\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/edit_text_padding_land\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_already_have_account\"\n                style=\"@style/LoginCreateText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/text_already_have_account\" />\n\n            <TextView\n                android:id=\"@+id/tv_sign_in\"\n                style=\"@style/LoginCreateTextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onSignInPressed\"\n                android:text=\"@string/button_sign_in\" />\n        </LinearLayout>\n    </LinearLayout>\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/layout-land/activity_login.xml",
    "content": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    tools:context=\".ui.login.LoginActivity\">\n\n    <LinearLayout\n        android:id=\"@+id/linear_layout_login_activity\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n        android:paddingLeft=\"@dimen/activity_horizontal_margin_land\"\n        android:paddingRight=\"@dimen/activity_horizontal_margin_land\"\n        android:paddingTop=\"@dimen/activity_vertical_margin\">\n\n        <LinearLayout\n            android:layout_width=\"0px\"\n            android:layout_height=\"0px\"\n            android:focusable=\"true\"\n            android:focusableInTouchMode=\"true\" />\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_email\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/lists_title_bottom_margin\"\n            app:hintTextAppearance=\"@style/HintText\">\n\n            <EditText\n                android:id=\"@+id/edit_text_email\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_email\"\n                android:imeOptions=\"flagNoExtractUi\"\n                android:inputType=\"textEmailAddress\"\n                android:nextFocusDown=\"@+id/edit_text_password\"\n                android:padding=\"@dimen/edit_text_padding_land\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <android.support.design.widget.TextInputLayout\n            android:id=\"@+id/til_password\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"@dimen/margin_normal\"\n            app:hintTextAppearance=\"@style/HintText\">\n\n            <EditText\n                android:id=\"@+id/edit_text_password\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/white\"\n                android:hint=\"@string/hint_enter_password\"\n                android:imeOptions=\"actionDone|flagNoExtractUi\"\n                android:inputType=\"textPassword\"\n                android:padding=\"@dimen/edit_text_padding_land\"\n                android:textColor=\"@color/black\"\n                android:textColorHint=\"@color/dark_grey\"\n                android:textSize=\"@dimen/login_buttons_text_size\" />\n        </android.support.design.widget.TextInputLayout>\n\n        <Button\n            android:id=\"@+id/login_with_password\"\n            style=\"@style/Widget.Udacity.Button.Login\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"@dimen/input_field_height_land\"\n            android:layout_marginBottom=\"@dimen/lv_header_text_size\"\n            android:background=\"@color/primary\"\n            android:onClick=\"onSignInPressed\"\n            android:text=\"@string/button_signin_with_password\"\n            android:textColor=\"@android:color/white\" />\n\n        <com.google.android.gms.common.SignInButton\n            android:id=\"@+id/login_with_google\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"@dimen/margin_small\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_dont_have_account\"\n                style=\"@style/LoginCreateText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/text_dont_have_account\" />\n\n            <TextView\n                android:id=\"@+id/tv_sign_up\"\n                style=\"@style/LoginCreateTextButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:onClick=\"onSignUpPressed\"\n                android:text=\"@string/button_create_account\" />\n        </LinearLayout>\n    </LinearLayout>\n\n</ScrollView>"
  },
  {
    "path": "app/src/main/res/menu/menu_base.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\".ui.BaseActivity\">\n    <item\n        android:id=\"@+id/action_logout\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_logout\"\n        android:visible=\"true\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_list_details.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\".ui.activeListDetails.ActiveListDetailsActivity\">\n    <item\n        android:id=\"@+id/action_edit_list_name\"\n        android:animateLayoutChanges=\"true\"\n        android:icon=\"@drawable/ic_edit_white\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_edit_list_name\"\n        android:visible=\"false\"\n        app:showAsAction=\"ifRoom\" />\n    <item\n        android:id=\"@+id/action_share_list\"\n        android:animateLayoutChanges=\"true\"\n        android:icon=\"@drawable/abc_ic_menu_share_mtrl_alpha\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_share_list\"\n        android:visible=\"false\"\n        app:showAsAction=\"ifRoom\" />\n    <item\n        android:id=\"@+id/action_archive\"\n        android:title=\"@string/action_archive\"\n        app:showAsAction=\"withText\" />\n    <item\n        android:id=\"@+id/action_remove_list\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_remove_list\"\n        app:showAsAction=\"withText\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\".ui.MainActivity\">\n    <item\n        android:id=\"@+id/action_sort\"\n        android:icon=\"@drawable/ic_sort_white\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_sort\"\n        android:visible=\"true\"\n        app:showAsAction=\"ifRoom\" />\n    <item\n        android:id=\"@+id/action_logout\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_logout\"\n        android:visible=\"true\"\n        app:showAsAction=\"never\" />\n</menu>\n\n"
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"pref_sortType_entries_lists\">\n        <item>@string/pref_sort_list_label_publish_time_entry</item>\n        <item>@string/pref_sort_list_label_list_name_entry</item>\n        <item>@string/pref_sort_list_label_owner_entry</item>\n        <item>@string/pref_sort_list_label_most_recently_changed_entry</item>\n    </string-array>\n\n    <string-array name=\"pref_sortType_values_lists\">\n        <item>@string/sort_by_publish_time_value</item>\n        <item>@string/sort_by_list_name_value</item>\n        <item>@string/sort_by_owner_value</item>\n        <item>@string/sort_by_most_recently_changed_value</item>\n\n    </string-array>\n</resources>\n\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"grey\">#EDEEEF</color>\n    <color name=\"dark_green\">#388E3C</color>\n\n    <color name=\"dark_grey\">#AEAEAE</color>\n\n    <color name=\"accent\">#DF4A32</color>\n\n    <color name=\"primary\">#83CF1C</color>\n    <color name=\"primary_dark\">#5AB831</color>\n    <color name=\"primary_light\">#DCEDC8</color>\n\n    <color name=\"light_black\">#747474</color>\n    <color name=\"black\">#616161</color>\n    <color name=\"google\">#C6412C</color>\n\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n    <!-- Dimens for text -->\n    <dimen name=\"list_created_by_text_size\">12sp</dimen>\n    <dimen name=\"lv_header_text_size\">10sp</dimen>\n    <dimen name=\"list_item_text_size\">16.6sp</dimen>\n    <dimen name=\"login_buttons_text_size\">15.3sp</dimen>\n\n    <dimen name=\"edit_text_padding_land\">6dp</dimen>\n    <dimen name=\"lists_title_bottom_margin\">13dp</dimen>\n    <dimen name=\"edit_text_padding\">14dp</dimen>\n    <dimen name=\"margin_small\">16dp</dimen>\n    <dimen name=\"btn_shopping_height\">30dp</dimen>\n    <dimen name=\"input_field_height_land\">36dp</dimen>\n    <dimen name=\"margin_normal\">37dp</dimen>\n    <dimen name=\"input_field_height\">50dp</dimen>\n    <dimen name=\"activity_horizontal_margin_land\">64dp</dimen>\n    <dimen name=\"lists_row_height\">70dp</dimen>\n    <dimen name=\"empty_footer_height\">80dp</dimen>\n    <dimen name=\"logo_image_size\">100dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <!--\n\n    == Base Strings For the App ==\n\n     -->\n    <string name=\"app_name\">ShoppingList++</string>\n    <string name=\"pager_title_shopping_lists\">Shopping Lists</string>\n    <string name=\"pager_title_meals\">Meals</string>\n    <string name=\"remove_item_option\">Remove Item</string>\n    <string name=\"positive_button_create\">Create</string>\n    <string name=\"text_you\">You</string>\n\n\n    <!-- Base Error Strings-->\n    <string name=\"log_error_updating_data\">Error updating data: </string>\n    <string name=\"log_error_the_read_failed\">The read failed: </string>\n    <string name=\"log_error_occurred\">\"Error occurred: \"</string>\n\n    <!-- Menu Strings -->\n    <string name=\"action_logout\">Logout</string>\n    <string name=\"action_share_list\">Share List</string>\n    <string name=\"action_sort\">Sort</string>\n    <string name=\"action_remove_list\">Remove list</string>\n    <string name=\"action_archive\">Archive</string>\n\n\n    <!--\n\n    == Active List Strings ==\n\n     -->\n    <!-- Add List Dialog-->\n    <string name=\"hint_add_shopping_list\">Create a list</string>\n\n    <!-- List of Shopping Lists Strings -->\n    <string name=\"created_by\">Created by </string>\n    <string name=\"person_shopping\">%d person shopping</string>\n    <string name=\"people_shopping\">%d people shopping</string>\n\n\n    <!--\n\n     == Active List Details Strings ==\n\n      -->\n    <!-- Activity Title  -->\n    <string name=\"title_activity_list_details\">List Details</string>\n\n    <!-- Menu Items -->\n    <string name=\"action_edit_list_name\">Edit list name</string>\n\n    <!-- Edit List and Add Item Dialogs -->\n    <string name=\"positive_button_add_list_item\">Add item</string>\n    <string name=\"positive_button_edit_item\">Edit name</string>\n    <string name=\"negative_button_cancel\">Cancel</string>\n    <string name=\"dialog_message_are_you_sure_remove_list\">Are you sure you want to remove this list?</string>\n    <string name=\"dialog_message_are_you_sure_remove_item\">Are you sure you want to remove this item?</string>\n\n    <!-- Shopping Mode -->\n    <string name=\"button_stop_shopping\">Stop Shopping</string>\n    <string name=\"button_start_shopping\">Start Shopping</string>\n\n    <string name=\"text_you_are_shopping\">You are shopping</string>\n    <string name=\"text_you_and_other_are_shopping\">You and %s are shopping</string>\n    <string name=\"text_you_and_number_are_shopping\">You and %d others are shopping</string>\n\n    <string name=\"text_other_is_shopping\">%s is shopping</string>\n    <string name=\"text_other_and_other_are_shopping\">%1$s and %2$s are shopping</string>\n    <string name=\"text_other_and_number_are_shopping\">%1$s and %2$d others are shopping</string>\n\n    <string name=\"bought_by\">Bought by</string>\n\n    <!--\n\n     == Meal Strings ==\n\n      -->\n\n    <!-- Activity Title  -->\n    <string name=\"title_activity_meal_details\">Meal Details</string>\n    <string name=\"title_activity_add_items_from_meal\">Add Meal</string>\n\n    <!-- Add, Edit and Remove Meal Dialogs -->\n    <string name=\"hint_add_meal\">Create a meal</string>\n    <string name=\"dialog_title_remove_meal\">Remove meal</string>\n    <string name=\"dialog_message_are_you_sure_remove_meal\">Are you sure you want to remove this meal?</string>\n    <string name=\"hint_enter_meal_name\">Enter meal name</string>\n\n\n    <!--\n\n     == Archived List Strings ==\n\n      -->\n    <!-- Activity Titles  -->\n    <string name=\"title_activity_archived_lists\">Archived Lists</string>\n    <string name=\"title_activity_archived_list_details\">Archived List Details</string>\n\n    <!-- Archive Dialogs  -->\n    <string name=\"dialog_title_copy_list\">Copy List</string>\n    <string name=\"dialog_title_archive_list\">Archive list</string>\n    <string name=\"dialog_message_are_you_sure_archive_list\">Are you sure you want to archive this list?</string>\n    <string name=\"dialog_message_do_you_want_to_copy\">Do you want to copy this list?</string>\n    <string name=\"toast_shopping_list_was_archived\">Shopping List %s was archived.</string>\n\n\n    <!-- Archived Lists in Active Lists -->\n    <string name=\"button_footer_archived_lists\">Archived Lists</string>\n\n\n    <!--\n\n     == Login and Create Account Strings ==\n\n      -->\n    <!-- Activity Titles  -->\n    <string name=\"title_activity_create_account\">Create Account</string>\n\n    <!-- Login and Create Account Screen  -->\n    <string name=\"text_dont_have_account\">Don\\'t have an account?</string>\n    <string name=\"text_already_have_account\">Already have an account?</string>\n    <string name=\"button_create_account\">Sign Up</string>\n    <string name=\"button_sign_in\">Sign In</string>\n    <string name=\"button_signin_with_google\">SIGN IN WITH GOOGLE</string>\n    <string name=\"button_signin_with_password\">SIGN IN</string>\n    <string name=\"hint_enter_email\">Enter your email</string>\n    <string name=\"hint_enter_username\">Enter your name</string>\n    <string name=\"hint_enter_password\">Enter password</string>\n    <string name=\"image_view_logo_image\">Logo Image</string>\n\n\n    <!-- Instructions and Loading Text -->\n    <string name=\"progress_dialog_loading\">Loading…</string>\n    <string name=\"progress_dialog_authenticating_with_firebase\">Authenticating with Firebase…</string>\n    <string name=\"progress_dialog_creating_user_with_firebase\">Attempting to create account…</string>\n    <string name=\"progress_dialog_check_inbox\">Please check your inbox for your password</string>\n\n    <!-- Errors -->\n    <string name=\"dialog_title_error\">Error</string>\n    <string name=\"error_message_failed_sign_in_no_network\">There was a problem with the network connection.\\nAre you sure you\\'re connected to the internet?</string>\n    <string name=\"log_error_invalid_provider\">\"Invalid provider: \"</string>\n    <string name=\"log_error_failed_to_change_password\">\"Failed to change the password: \"</string>\n    <string name=\"error_invalid_email_not_valid\">%s is not a valid email</string>\n    <string name=\"error_message_email_issue\">There was an error with your email; please check that you entered it correctly.</string>\n    <string name=\"error_you_didnt_give_permissions\">You must give ShoppingList++ permission to access your Google account information if you want to use Google sign-in.</string>\n    <string name=\"error_email_taken\">The email is already taken.</string>\n    <string name=\"error_invalid_password_not_valid\">Please enter a password that is at least 6 characters long.</string>\n    <string name=\"error_cannot_be_empty\">This cannot be empty.</string>\n\n    <!-- Google Errors -->\n    <string name=\"google_error_recoverable_oauth_error\">\"Recoverable Google OAuth error: \"</string>\n    <string name=\"google_error_google_play_services_not_available\">Google Play Services are not available.</string>\n    <string name=\"google_error_trying_to_connect_google_api\">Trying to connect to Google API</string>\n    <string name=\"google_error_auth_with_google\">\"Error authenticating with Google: \"</string>\n    <string name=\"google_error_network_error\">\"Network error: \"</string>\n    <string name=\"google_error_cant_connect_to_api\">Are you sure that you set up a project for your app?\n        Check out : https://console.developers.google.com/project. Also make sure you have all the proper\n    gradle dependencies</string>\n\n\n    <!-- OAuth Format -->\n    <string name=\"oauth2_format\">oauth2:%s</string>\n\n    <!-- Log Messages -->\n    <string name=\"log_message_password_changed_successfully\">Password changed successfully from temporary password to </string>\n    <string name=\"log_message_auth_successful\">auth successful.</string>\n\n    <!--\n\n     == Sharing Strings ==\n\n      -->\n    <!-- Activity Titles -->\n    <string name=\"title_activity_share_list\">Share List</string>\n    <string name=\"title_activity_add_friend\">Add Friend</string>\n\n    <!-- Add a friend -->\n    <string name=\"hint_enter_friends_email\">Enter your friend\\'s email</string>\n    <string name=\"text_add_friend\">Add New Friend</string>\n\n    <!-- Toast Errors -->\n    <string name=\"toast_is_already_your_friend\">%s is already your friend!</string>\n    <string name=\"toast_you_cant_add_yourself\">You can\\'t add yourself as a friend.</string>\n\n\n    <!--\n\n    == Settings Strings ==\n\n    -->\n    <!-- Activity Titles -->\n    <string name=\"action_settings\">Settings</string>\n\n    <!-- Titles for Sort-->\n    <string name=\"pref_title_sort_lists\">Lists ordering</string>\n\n    <!-- Preference Name -->\n    <string name=\"pref_name_sort_order_lists\">pref_sort_order_lists</string>\n\n    <!-- Ordering Choices Labels : Lists -->\n    <string name=\"pref_sort_list_label_sortType\">Order by</string>\n    <string name=\"pref_sort_list_label_publish_time_entry\">Publish Time</string>\n    <string name=\"pref_sort_list_label_list_name_entry\">List Name</string>\n    <string name=\"pref_sort_list_label_owner_entry\">Owner Email</string>\n    <string name=\"pref_sort_list_label_most_recently_changed_entry\">Most Recently Changed</string>\n\n\n    <!-- Values -->\n    <string name=\"sort_by_publish_time_value\">orderByPushKey</string>\n    <string name=\"sort_by_list_name_value\">listName</string>\n    <string name=\"sort_by_owner_value\">owner</string>\n    <string name=\"sort_by_most_recently_changed_value\">timestampLastChangedReverse/timestamp</string>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <style name=\"ListItemText\">\n        <item name=\"android:textColor\">@color/light_black</item>\n        <item name=\"android:textSize\">@dimen/list_item_text_size</item>\n        <item name=\"android:layout_margin\">2dp</item>\n    </style>\n\n    <style name=\"HeaderText\">\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textColor\">@color/black</item>\n        <item name=\"android:textSize\">@dimen/login_buttons_text_size</item>\n        <item name=\"android:layout_margin\">2dp</item>\n    </style>\n\n    <style name=\"LoginCreateText\">\n        <item name=\"android:textSize\">@dimen/login_buttons_text_size</item>\n        <item name=\"android:textColor\">@android:color/white</item>\n        <item name=\"android:layout_margin\">2dp</item>\n    </style>\n\n    <style name=\"HintText\">\n        <item name=\"android:textSize\">0dp</item>\n    </style>\n\n    <style name=\"LoginCreateTextButton\">\n        <item name=\"android:textSize\">@dimen/login_buttons_text_size</item>\n        <item name=\"android:textStyle\">bold</item>\n        <item name=\"android:textColor\">@android:color/white</item>\n        <item name=\"android:layout_margin\">2dp</item>\n        <item name=\"android:clickable\">true</item>\n    </style>\n\n    <style name=\"FAB\">\n        <item name=\"android:layout_margin\">0dp</item>\n        <item name=\"fabSize\">normal</item>\n        <item name=\"rippleColor\">@android:color/white</item>\n        <item name=\"backgroundTint\">@color/primary</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppTheme\" parent=\"AppTheme.Base\">\n    </style>\n\n    <style name=\"AppTheme.Base\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/primary_dark</item>\n        <item name=\"colorAccent\">@color/dark_green</item>\n        <item name=\"android:colorBackground\">@color/primary</item>\n        <item name=\"android:textColorPrimary\">@android:color/white</item>\n        <item name=\"android:textColorSecondary\">#fcfcfc</item>\n    </style>\n\n\n    <style name=\"Toolbar\" parent=\"Widget.AppCompat.Light.ActionBar\">\n        <item name=\"android:background\">@color/primary</item>\n        <item name=\"android:textColorPrimary\">@android:color/white</item>\n    </style>\n\n    <style name=\"CustomTheme.Dialog\" parent=\"Theme.AppCompat.Light.Dialog\">\n    </style>\n\n    <!--<style name=\"CustomTheme.Dialog.Old\" parent=\"Theme.AppCompat.Light.Dialog\">-->\n        <!--<item name=\"android:windowBackground\">@android:color/transparent</item>-->\n        <!--<item name=\"android:windowContentOverlay\">@null</item>-->\n        <!--<item name=\"android:windowMinWidthMajor\">@android:dimen/dialog_min_width_major</item>-->\n        <!--<item name=\"android:windowMinWidthMinor\">@android:dimen/dialog_min_width_minor</item>-->\n    <!--</style>-->\n\n    <style name=\"PrefScreenTheme\">\n        <item name=\"android:textColorPrimary\">@color/black</item>\n        <item name=\"android:textColorSecondary\">@color/black</item>\n    </style>\n\n    <style name=\"Widget.Udacity.Button.Login\" parent=\"android:Widget.Button\">\n        <item name=\"android:paddingLeft\">18dp</item>\n        <item name=\"android:paddingRight\">16dp</item>\n        <item name=\"android:textSize\">@dimen/login_buttons_text_size</item>\n        <item name=\"android:layout_gravity\">center</item>\n    </style>\n\n    <style name=\"ShoppingButton\" parent=\"Widget.AppCompat.Button\">\n        <item name=\"android:textAllCaps\">false</item>\n        <item name=\"android:padding\">0dp</item>\n        <item name=\"android:textSize\">@dimen/list_created_by_text_size</item>\n        <item name=\"android:layout_height\">@dimen/btn_shopping_height</item>\n        <item name=\"android:layout_width\">@dimen/logo_image_size</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-sw480dp/dimens.xml",
    "content": "<resources>\n\n    <!-- Dimens for text -->\n    <dimen name=\"list_created_by_text_size\">18sp</dimen>\n    <dimen name=\"lv_header_text_size\">15sp</dimen>\n    <dimen name=\"list_item_text_size\">25sp</dimen>\n    <dimen name=\"login_buttons_text_size\">23sp</dimen>\n\n    <dimen name=\"edit_text_padding_land\">9dp</dimen>\n    <dimen name=\"lists_title_bottom_margin\">20dp</dimen>\n    <dimen name=\"edit_text_padding\">21dp</dimen>\n    <dimen name=\"margin_small\">24dp</dimen>\n    <dimen name=\"btn_shopping_height\">45dp</dimen>\n    <dimen name=\"margin_normal\">55dp</dimen>\n    <dimen name=\"input_field_height_land\">63dp</dimen>\n    <dimen name=\"input_field_height\">75dp</dimen>\n    <dimen name=\"activity_horizontal_margin_land\">96dp</dimen>\n    <dimen name=\"lists_row_height\">100dp</dimen>\n    <dimen name=\"empty_footer_height\">110dp</dimen>\n    <dimen name=\"logo_image_size\">150dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sw640dp/dimens.xml",
    "content": "<resources>\n    <!-- Dimens for text -->\n    <dimen name=\"list_created_by_text_size\">24sp</dimen>\n    <dimen name=\"lv_header_text_size\">20sp</dimen>\n    <dimen name=\"list_item_text_size\">33sp</dimen>\n    <dimen name=\"login_buttons_text_size\">30sp</dimen>\n\n    <dimen name=\"edit_text_padding_land\">12dp</dimen>\n    <dimen name=\"lists_title_bottom_margin\">26dp</dimen>\n    <dimen name=\"edit_text_padding\">28dp</dimen>\n    <dimen name=\"margin_small\">36dp</dimen>\n    <dimen name=\"btn_shopping_height\">60dp</dimen>\n    <dimen name=\"margin_normal\">72dp</dimen>\n    <dimen name=\"input_field_height_land\">84dp</dimen>\n    <dimen name=\"input_field_height\">100dp</dimen>\n    <dimen name=\"lists_row_height\">120dp</dimen>\n    <dimen name=\"activity_horizontal_margin_land\">128dp</dimen>\n    <dimen name=\"empty_footer_height\">130dp</dimen>\n    <dimen name=\"logo_image_size\">200dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "content": "<resources>\n    <style name=\"FAB\">\n        <item name=\"android:layout_margin\">16dp</item>\n        <item name=\"fabSize\">normal</item>\n        <item name=\"rippleColor\">@android:color/white</item>\n        <item name=\"backgroundTint\">@color/primary</item>\n    </style>\n</resources>"
  },
  {
    "path": "app/src/main/res/values-v21/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppTheme\" parent=\"AppTheme.Base\">\n    </style>\n\n    <style name=\"AppTheme.Base\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"android:colorBackground\">@color/background_material_light</item>\n        <item name=\"colorPrimary\">@color/primary</item>\n        <item name=\"colorPrimaryDark\">@color/dark_green</item>\n        <item name=\"colorAccent\">@color/dark_green</item>\n        <item name=\"android:textColorPrimary\">@android:color/white</item>\n        <item name=\"android:textColorSecondary\">#fcfcfc</item>\n    </style>\n\n    <style name=\"Toolbar\" parent=\"Widget.AppCompat.Light.ActionBar\">\n        <item name=\"android:background\">@color/primary</item>\n        <item name=\"android:textColorPrimary\">@android:color/white</item>\n    </style>\n\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values-w480dp/dimens.xml",
    "content": " <resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n      (such as screen margins) for screens with more than 820dp of available width. This\n      would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n\n </resources>"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/preference_screen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <ListPreference\n        android:key=\"pref_sort_order_lists\"\n        android:title=\"@string/pref_title_sort_lists\"\n        android:dialogTitle=\"@string/pref_sort_list_label_sortType\"\n        android:entries=\"@array/pref_sortType_entries_lists\"\n        android:summary=\"@string/pref_sort_list_label_publish_time_entry\"\n        android:entryValues=\"@array/pref_sortType_values_lists\"\n        android:defaultValue=\"@string/sort_by_publish_time_value\"\n        />\n</PreferenceScreen>"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.3.1'\n        classpath 'com.google.gms:google-services:1.5.0-beta2'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-all.zip\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "rules/git.log",
    "content": "commit f73d6708fe2217b1dd19cd0a088e45e0f2c15147 (HEAD, origin/master, master)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 23:17:43 2016 -0800\n\n    Final Commit Draft\n\ncommit 017ad9657501d79bd5625702c2e80f69f0ad0bf5\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 21:54:18 2016 -0800\n\n    5.14 Checking for List Access - Solution\n\ncommit f164b71c31cd08a18879524b74356eecd459d026\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 21:52:53 2016 -0800\n\n    5.13 Checking for List Access - Quiz\n\ncommit b195cce101d5715a33b121898476d4450fa5a4ee\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 17:40:27 2016 -0800\n\n    5.12 UID Email Mapping - Solution\n\ncommit 16b5c207f33f0b44aad6a3453aaa76e0b975b2bb\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 17:36:32 2016 -0800\n\n    5.11 UID Email Mapping - Quiz\n\ncommit ed5316c5265dc7c1d608e28253702a0786a04739\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 11:48:10 2016 -0800\n\n    5.10 Write the User Read Rule - Solution\n\ncommit c52715747f7572799df45988ccff8399d9760f44\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 11:18:24 2016 -0800\n\n    5.09 Write the User Read Rule - Quiz\n\ncommit 43fca4cae34f2275abb515fd7b9ad672c9a9546c\nAuthor: Lyla <lyla@udacity.com>\nDate:   Sat Jan 9 16:33:21 2016 -0800\n\n    5.08 Finish the Validate Rules - Solution\n\ncommit 8b24de971709be9c2d73373096a59d82d1bdc832\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 09:02:43 2016 -0800\n\n    5.07 Finish the Validate Rules - Quiz\n\ncommit 7aa5c8279276218d10b5d3c2373f97a9548abc8a\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Jan 7 08:49:52 2016 -0800\n\n    5.06 Validation for Shopping List Items - Solution\n\ncommit f6726fba279b867d5500f53568b750191949f661\nAuthor: Lyla <lyla@udacity.com>\nDate:   Sat Jan 9 16:32:03 2016 -0800\n\n    5.05 Validation for Shopping List Items - Quiz\n\ncommit 62e808cdb2daee7c9c7331d649b8453350f5993f\nAuthor: Lyla <lyla@udacity.com>\nDate:   Sat Jan 9 16:38:16 2016 -0800\n\n    5.04 Basic Rule Syntax - Solution\n\ncommit 513f4f4f87eeba5895a6e411bcab87342cce0af1\nAuthor: Lyla <lyla@udacity.com>\nDate:   Sat Jan 9 16:23:22 2016 -0800\n\n    5.03 Basic Rule Syntax - Quiz\n\ncommit 98ba481e2e4ea342c6ac0265a989c5d14bc651de\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Jan 6 16:19:26 2016 -0800\n\n    5.02 Simple Off-line - Solution\n\ncommit b2fb01391a8a4e7dd52418072ce99d8a866ab460\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Jan 6 16:18:38 2016 -0800\n\n    5.01 Simple Off-line - Quiz\n\ncommit 3f85620966090b050683a4ac40fc037857c319ed\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Jan 6 16:14:19 2016 -0800\n\n    5.00 Version 0.5 Start Code\n\ncommit dbf78669f51ed7dddc191c6e9b3796327d2736df (origin/4.16_oyo_sort_by_last_edited_timestamp, live_code/master, live_code/4.16_oyo_sort_by_last_edited_timestamp, 4.16_oyo_sort_by_last_edited_timestamp)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 16 21:44:20 2015 -0800\n\n    4.16 OYO Sort By Last Edited Timestamp\n\ncommit 157fbf8d2535ebdfef3522e9c47af1df164eeab3 (origin/4.15_oyo_cleanup_friendadapter, live_code/4.15_oyo_cleanup_friendadapter, 4.15_oyo_cleanup_friendadapter)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 16 21:30:54 2015 -0800\n\n    4.15 OYO Cleanup FriendAdapter\n\ncommit 9ed73f8e699e7e1293c120cce75722f9aaacddb7 (origin/4.14_searching-solution, live_code/4.14_searching-solution, 4.14_searching-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 3 19:23:58 2015 -0800\n\n    4.14 Searching - Solution\n\ncommit 2dc7e1b492884bcee683f2cdfad2097660441f8c (origin/4.13_searching-quiz, live_code/4.13_searching-quiz, 4.13_searching-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 16 21:18:11 2015 -0800\n\n    4.13 Searching - Quiz\n\ncommit eb03b065094ec616f3f4636b2705b915a6f13133 (origin/4.12_implementing_sharing-solution, live_code/4.12_implementing_sharing-solution, 4.12_implementing_sharing-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Dec 4 15:44:08 2015 -0800\n\n    4.12 Implementing Sharing - Solution\n\ncommit c50d2fd59957e4cca7c66d48669b65ff0f6bfd04 (origin/4.11_implementing_sharing-quiz, live_code/4.11_implementing_sharing-quiz, 4.11_implementing_sharing-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 16 20:22:10 2015 -0800\n\n    4.11 Implementing Sharing - Quiz\n\ncommit 818ad56c4ba566dd16c64fbd2cd30d5d99d661ef (origin/4.10_adding_to_the_share_list-solution, live_code/4.10_adding_to_the_share_list-solution, 4.10_adding_to_the_share_list-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 16 19:37:15 2015 -0800\n\n    4.10 Adding to the Share List - Solution\n\ncommit a34cd1d46c59966c61f68daa6b7c674c732165c9 (origin/4.09_adding_to_the_share_list-quiz, live_code/4.09_adding_to_the_share_list-quiz, 4.09_adding_to_the_share_list-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 16 17:35:55 2015 -0800\n\n    4.09 Adding to the Share List - Quiz\n\ncommit 0d3e72f571009bd6f27e419d13c9e719a4b46263 (origin/4.08_user_friends-solution, live_code/4.08_user_friends-solution, 4.08_user_friends-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Nov 6 15:23:42 2015 -0800\n\n    4.08 User Friends - Solution\n\ncommit bfbcb530b2e6f24d456b192e8f53b8a5d5f76426 (origin/4.07_user_friends-quiz, live_code/4.07_user_friends-quiz, 4.07_user_friends-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 3 15:59:43 2015 -0800\n\n    4.07 User Friends - Quiz\n\ncommit 11dd8b0d5008cde5ee8aa72fe5d34d085112b2f5 (origin/4.06_efficiency_at_scale_refactor-solution, live_code/4.06_efficiency_at_scale_refactor-solution, 4.06_efficiency_at_scale_refactor-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Dec 4 15:40:54 2015 -0800\n\n    4.06 Efficiency at Scale Refactor - Solution\n\ncommit dd279a16c110a448a2ac44c0ff1827edd6aa1134 (origin/4.05_efficiency_at_scale_refactor-quiz, live_code/4.05_efficiency_at_scale_refactor-quiz, 4.05_efficiency_at_scale_refactor-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 3 14:30:07 2015 -0800\n\n    4.05 Efficiency at Scale Refactor - Quiz\n\ncommit 45f0a4ed1fc4b52a14101c6ee5b1db97c5814100 (origin/4.04_separate_bought_and_unbought-solution, live_code/4.04_separate_bought_and_unbought-solution, 4.04_separate_bought_and_unbought-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 3 11:25:23 2015 -0800\n\n    4.04 Separate Bought And Unbought - Solution\n\ncommit 9f363276b2c86461f5bb815f511101a40b4d4408 (origin/4.03_separate_bought_and_unbought-quiz, live_code/4.03_separate_bought_and_unbought-quiz, 4.03_separate_bought_and_unbought-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 3 11:27:52 2015 -0800\n\n    4.03 Separate Bought And Unbought - Quiz\n\ncommit 09aaeef46fb0abde03d8b8186102f32625f4057f (origin/4.02_sorting_with_orderby-solution, live_code/4.02_sorting_with_orderby-solution, 4.02_sorting_with_orderby-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Dec 9 17:16:39 2015 -0800\n\n    4.02 Sorting with orderBy - Solution\n\ncommit b887ebb60c2da849e07f3188166b47c09f7a0b16 (origin/4.01_sorting_with_orderby-quiz, live_code/4.01_sorting_with_orderby-quiz, 4.01_sorting_with_orderby-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Nov 13 13:09:05 2015 -0800\n\n    4.01 Sorting with orderBy - Quiz\n\ncommit 730bfdb9bda622778f4eebab78bb1b1244d3128c (origin/4.00_version_0.4_start_code, live_code/4.00_version_0.4_start_code, 4.00_version_0.4_start_code)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 2 18:05:21 2015 -0800\n\n    4.00 Version 0.4 Start Code\n\ncommit 65fc9bbaa16c692e19a9ce2d69db64c02c6e56fc (origin/3.27_oyo_restore_signed_in_session, live_code/3.27_oyo_restore_signed_in_session, 3.27_oyo_restore_signed_in_session)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 10 13:16:58 2015 -0800\n\n    3.27 OYO Restore Signed In Session\n\ncommit 927144e35183276422568663c0cf2af45a1cd2a9 (origin/3.26_oyo_permissions_for_items, live_code/3.26_oyo_permissions_for_items, 3.26_oyo_permissions_for_items)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Dec 9 17:15:21 2015 -0800\n\n    3.26 OYO Permissions for Items\n\ncommit 012d7d95ac6172f38a5e2e88aecb06a0905d016d (origin/3.25_oyo_list_fragment_who_is_shopping, live_code/3.25_oyo_list_fragment_who_is_shopping, 3.25_oyo_list_fragment_who_is_shopping)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Nov 4 23:03:53 2015 -0800\n\n    3.25 OYO List Fragment Who is Shopping\n\ncommit bbb77aef88f92ae6282f477a9efe76e9f27c4ab1 (origin/3.24_who_is_shopping-solution, live_code/3.24_who_is_shopping-solution, 3.24_who_is_shopping-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 18:11:23 2015 -0700\n\n    3.24 Who is Shopping - Solution\n\ncommit fc0abef44ae84319ec0849d4dfbea411a54d345c (origin/3.23_who_is_shopping-quiz, live_code/3.23_who_is_shopping-quiz, 3.23_who_is_shopping-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 18:10:50 2015 -0700\n\n    3.23 Who is Shopping - Quiz\n\ncommit 5689bf6024bdde9ae99f19b9d7d9a668f4de4e02 (origin/3.22_shopping_mode-solution, live_code/3.22_shopping_mode-solution, 3.22_shopping_mode-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Nov 6 15:18:42 2015 -0800\n\n    3.22 Shopping Mode - Solution\n\ncommit 77416aa254038680d6f91655d490aa15f4f598d1 (origin/3.21_shopping_mode-quiz, live_code/3.21_shopping_mode-quiz, 3.21_shopping_mode-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 17:51:17 2015 -0700\n\n    3.21 Shopping Mode - Quiz\n\ncommit edd2d93fc4809a937fdb37315e25c62c0ac42341 (origin/3.20_check_off_items-solution, live_code/3.20_check_off_items-solution, 3.20_check_off_items-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 15:55:21 2015 -0700\n\n    3.20 Check Off Items - Solution\n\ncommit fe5437ddbbe1daed00cb1b343853c5393b0df6a3 (origin/3.19_check_off_items-quiz, live_code/3.19_check_off_items-quiz, 3.19_check_off_items-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 15:50:34 2015 -0700\n\n    3.19 Check Off Items - Quiz\n\ncommit f559f7373316a3ac338b9049483b7448142d020c (origin/3.18_reset_password_login_flow-solution, live_code/3.18_reset_password_login_flow-solution, 3.18_reset_password_login_flow-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 9 23:23:35 2015 -0800\n\n    3.18 Reset Password Login Flow - Solution\n\ncommit 777f4bf9c5d0736609cc08ebb487d3dc24263a37 (origin/3.17_reset_password_login_flow-quiz, live_code/3.17_reset_password_login_flow-quiz, 3.17_reset_password_login_flow-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 15:08:14 2015 -0700\n\n    3.17 Reset Password Login Flow - Quiz\n\ncommit 337a9c69bd4272d9b7aa43faa2e53d402d07954e (origin/3.16_owner_list_permissions-solution, live_code/3.16_owner_list_permissions-solution, 3.16_owner_list_permissions-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 12:53:42 2015 -0700\n\n    3.16 Owner List Permissions - Solution\n\ncommit ca76319459ce133e0e2027ee300dfd876b1e8957 (origin/3.15_owner_list_permissions-quiz, live_code/3.15_owner_list_permissions-quiz, 3.15_owner_list_permissions-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 12:52:14 2015 -0700\n\n    3.15 Owner List Permissions - Quiz\n\ncommit 85d3a19c3f73da5fc5e6098c3ae9bc23354aeb7d (origin/3.14_logging_out_and_kicking_off-solution, live_code/3.14_logging_out_and_kicking_off-solution, 3.14_logging_out_and_kicking_off-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Dec 9 17:09:26 2015 -0800\n\n    3.14 Logging Out and Kicking Off - Solution\n\ncommit 7ef2cb453da6eaf6e80060bd52f803fe72e12869 (origin/3.13_logging_out_and_kicking_off-quiz, live_code/3.13_logging_out_and_kicking_off-quiz, 3.13_logging_out_and_kicking_off-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 10 11:33:48 2015 -0800\n\n    3.13 Logging Out and Kicking Off - Quiz\n\ncommit 680c539a0a4a52e8ac758fdcb5530342c29775ba (origin/3.12_use_owner_data-solution, live_code/3.12_use_owner_data-solution, 3.12_use_owner_data-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 10 11:30:54 2015 -0800\n\n    3.12 Use Owner Data - Solution\n\ncommit 0df137b42afac31e902205e4fdcafda52710b3a4 (origin/3.11_use_owner_data-quiz, live_code/3.11_use_owner_data-quiz, 3.11_use_owner_data-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 10 11:23:21 2015 -0800\n\n    3.11 Use Owner Data - Quiz\n\ncommit 934addabb5b0f370a255e0567237757093f2cac3 (origin/3.10_user_data_with_google-solution, live_code/3.10_user_data_with_google-solution, 3.10_user_data_with_google-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 10 11:21:47 2015 -0800\n\n    3.10 User Data with Google - Solution\n\ncommit 71ffeec233fca423f2cdabdb43b655e7b1e857d3 (origin/3.09_user_data_with_google-quiz, live_code/3.09_user_data_with_google-quiz, 3.09_user_data_with_google-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 9 15:27:19 2015 -0800\n\n    3.09 User Data with Google - Quiz\n\ncommit c67c80adf61c5fdd14b10255bf88818bf6f4ef23 (origin/3.08_user_data_with_email/password-solution, live_code/3.08_user_data_with_email/password-solution, 3.08_user_data_with_email/password-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 9 23:09:02 2015 -0800\n\n    3.08 User Data with Email/Password - Solution\n\ncommit 3250a904b35490d9bff585dfe1f5b501174ea40d (origin/3.07_user_data_with_email/password-quiz, live_code/3.07_user_data_with_email/password-quiz, 3.07_user_data_with_email/password-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Nov 9 15:37:33 2015 -0800\n\n    3.07 User Data with Email/Password - Quiz\n\ncommit 9d3a605f4ded28ddc023e2996a29bbc2efca832b (origin/3.06_add_google_login-solution, live_code/3.06_add_google_login-solution, 3.06_add_google_login-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 01:57:12 2015 -0700\n\n    3.06 Add Google Login - Solution\n\ncommit b20f35c88427d43b2110343e7966dbee49190681 (origin/3.05_add_google_login-quiz, live_code/3.05_add_google_login-quiz, 3.05_add_google_login-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 01:52:26 2015 -0700\n\n    3.05 Add Google Login - Quiz\n\ncommit 143a3675f6afb5134bcac87a69d84a2589622beb (origin/3.04_logging_in-solution, live_code/3.04_logging_in-solution, 3.04_logging_in-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 12:02:27 2015 -0700\n\n    3.04 Logging In - Solution\n\ncommit 1f847cc1ca2e7e86ecfc66d5a6086fc2be238682 (origin/3.03_logging_in-quiz, live_code/3.03_logging_in-quiz, 3.03_logging_in-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 10:03:19 2015 -0700\n\n    3.03 Logging In - Quiz\n\ncommit a6714c8e97feef0df1e596734031060d898327f7 (origin/3.02_creating_a_user-solution, live_code/3.02_creating_a_user-solution, 3.02_creating_a_user-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Oct 28 23:49:01 2015 -0700\n\n    3.02 Creating a User - Solution\n\ncommit ca26c900e6e237d7c9d59be6a459426eaadd8ba0 (origin/3.01_creating_a_user-quiz, live_code/3.01_creating_a_user-quiz, 3.01_creating_a_user-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Oct 28 23:47:07 2015 -0700\n\n    3.01 Creating a User - Quiz\n\ncommit e603c52f4c0a70febb7394793b6011fc63270cc8 (origin/3.00_version_0.3_start_code, live_code/3.00_version_0.3_start_code, 3.00_version_0.3_start_code)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 29 01:19:02 2015 -0700\n\n    3.00 Version 0.3 Start Code\n\ncommit ba24e90cd2b58b0b2fa71914e3420bbe74d53017 (origin/2.19_oyo_edit_and_remove_list, live_code/2.19_oyo_edit_and_remove_list, 2.19_oyo_edit_and_remove_list)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Dec 9 17:05:29 2015 -0800\n\n    2.19 OYO Edit and Remove List\n\ncommit 5a53a2ff69344d1971b590226a537e44eeac2789 (origin/2.18_remove_event_listeners-solution, live_code/2.18_remove_event_listeners-solution, 2.18_remove_event_listeners-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 22 15:45:29 2015 -0700\n\n    2.18 Remove Event Listeners - Solution\n\ncommit 85645dbae9de1bddc391037e0ae949f243a28902 (origin/2.17_remove_event_listeners-quiz, live_code/2.17_remove_event_listeners-quiz, 2.17_remove_event_listeners-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 22 15:43:52 2015 -0700\n\n    2.17 Remove Event Listeners - Quiz\n\ncommit 799b400c1fc60f808c4a64ecac7207e6512c1d70 (origin/2.16_add_items_to_the_list_detail-solution, live_code/2.16_add_items_to_the_list_detail-solution, 2.16_add_items_to_the_list_detail-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Oct 23 20:27:05 2015 -0700\n\n    2.16 Add Items to the List Detail - Solution\n\ncommit fa14466776449477019ac31eafe21ae6e1bd3c9c (origin/2.15_add_items_to_the_list_detail-quiz, live_code/2.15_add_items_to_the_list_detail-quiz, 2.15_add_items_to_the_list_detail-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Nov 6 15:11:01 2015 -0800\n\n    2.15 Add Items to the List Detail - Quiz\n\ncommit 07753fbcade3356a57dafddf9d80166195a0fd2a (origin/2.14_items-solution, live_code/2.14_items-solution, 2.14_items-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Nov 6 15:10:21 2015 -0800\n\n    2.14 Items - Solution\n\ncommit 6d1caf83e788a6f37f7dba750a0a3f44ad0a27db (origin/2.13_items-quiz, live_code/2.13_items-quiz, 2.13_items-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Oct 21 14:53:12 2015 -0700\n\n    2.13 Items - Quiz\n\ncommit ce0217d26986e60d30a2ac27f186173a9d4610c8 (origin/2.12_removing_a_shopping_list-solution, live_code/2.12_removing_a_shopping_list-solution, 2.12_removing_a_shopping_list-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 22 00:25:13 2015 -0700\n\n    2.12 Removing A Shopping List - Solution\n\ncommit d2f5a8395d5139acb5f885fd9a88a54e5ca36335 (origin/2.11_removing_a_shopping_list-quiz, live_code/2.11_removing_a_shopping_list-quiz, 2.11_removing_a_shopping_list-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 22 00:22:08 2015 -0700\n\n    2.11 Removing A Shopping List - Quiz\n\ncommit a545d3c7eb6e3e3747d7437a4f2424f719f05757 (origin/2.10_send_over_the_push_id-solution, live_code/2.10_send_over_the_push_id-solution, 2.10_send_over_the_push_id-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Nov 6 15:03:11 2015 -0800\n\n    2.10 Send Over The Push ID - Solution\n\ncommit 326da1ce5522d1802905c35d597dbf16c4287f0d (origin/2.09_send_over_the_push_id_spec-quiz, live_code/2.09_send_over_the_push_id_spec-quiz, 2.09_send_over_the_push_id_spec-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Nov 4 19:59:04 2015 -0800\n\n    2.09 Send Over The Push ID Spec - Quiz\n\ncommit d2fd896026b7322251f744ebb7155e8b263e25e4 (origin/2.08_the_firebase_list_adapter_repo-solution, live_code/2.08_the_firebase_list_adapter_repo-solution, 2.08_the_firebase_list_adapter_repo-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Nov 4 23:24:09 2015 -0800\n\n    2.08 The Firebase List Adapter Repo - Solution\n\ncommit c2bf3f0a50fd727aa68a8bc45977872335bdc82d (origin/2.07_the_firebase_list_adapter_repo-quiz, live_code/2.07_the_firebase_list_adapter_repo-quiz, 2.07_the_firebase_list_adapter_repo-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Dec 9 16:59:51 2015 -0800\n\n    2.07 The Firebase List Adapter Repo - Quiz\n\ncommit 926a3a3d58cb5a7d20b65e9dda8f102a304756a7 (origin/2.06_add_a_new_shopping_list-solution, live_code/2.06_add_a_new_shopping_list-solution, 2.06_add_a_new_shopping_list-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 22 00:14:41 2015 -0700\n\n    2.06 Add a New Shopping List - Solution\n\ncommit ed275c163308fea4e74add0fe18fab2b2e93328f (origin/2.05_add_a_new_shopping_list-quiz, live_code/2.05_add_a_new_shopping_list-quiz, 2.05_add_a_new_shopping_list-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 22 00:11:37 2015 -0700\n\n    2.05 Add a New Shopping List - Quiz\n\ncommit d4ef4d451e65bda232ade2cecf6653ed346a4866 (origin/2.04_edit_shopping_list_name-solution, live_code/2.04_edit_shopping_list_name-solution, 2.04_edit_shopping_list_name-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Fri Oct 23 19:53:06 2015 -0700\n\n    2.04 Edit Shopping List Name - Solution\n\ncommit 105337c7f73f625708644bd1015845e1579a214d (origin/2.03_edit_shopping_list_name-quiz, live_code/2.03_edit_shopping_list_name-quiz, 2.03_edit_shopping_list_name-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Oct 20 23:30:00 2015 -0700\n\n    2.03 Edit Shopping List Name - Quiz\n\ncommit 5f7375eb924970458da19903cf2871bc7e4c10e4 (origin/2.02_the_detail_screen-solution, live_code/2.02_the_detail_screen-solution, 2.02_the_detail_screen-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Oct 20 22:57:15 2015 -0700\n\n    2.02 The Detail Screen - Solution\n\ncommit 6fc66ce3bf1aa6c5ee4f75cc58a1a6b5e6bcacbc (origin/2.01_the_detail_screen-quiz, live_code/2.01_the_detail_screen-quiz, 2.01_the_detail_screen-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Oct 20 22:55:00 2015 -0700\n\n    2.01 The Detail Screen - Quiz\n\ncommit 26891fcdff927e23c9decbb89354b71891fc3b68 (origin/2.00_version_0.2_start_code, live_code/2.00_version_0.2_start_code, 2.00_version_0.2_start_code)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Oct 20 21:12:02 2015 -0700\n\n    2.00 Version 0.2 Start Code\n\ncommit 762edecb9ea16fbca78d8e61b9b5aebb9ad6cd1e (origin/1.09_oyo_add_an_edit_time_field_using_servervalue.timestamp, live_code/1.09_oyo_add_an_edit_time_field_using_servervalue.timestamp, 1.09_oyo_add_an_edit_time_field_using_servervalue.timestamp)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Thu Oct 15 22:31:39 2015 -0700\n\n    1.09 OYO Add an edit time field using ServerValue.TIMESTAMP\n\ncommit ebe9689ecf42afc595860392beaa0adb948dd418 (origin/1.08_plain_old_java_objects_in_firebase-solution, live_code/1.08_plain_old_java_objects_in_firebase-solution, 1.08_plain_old_java_objects_in_firebase-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 23:28:35 2015 -0700\n\n    1.08 Plain Old Java Objects in Firebase - Solution\n\ncommit 05d3f80c426f8a94ba4285ff21630cf28729dd2c (origin/1.07_plain_old_java_objects_in_firebase-quiz, live_code/1.07_plain_old_java_objects_in_firebase-quiz, 1.07_plain_old_java_objects_in_firebase-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 23:12:05 2015 -0700\n\n    1.07 Plain Old Java Objects in Firebase - Quiz\n\ncommit 446d47b7337652ec41ddac7fa22d443728bb8ec7 (origin/1.06_read_your_first_firebase_data-solution, live_code/1.06_read_your_first_firebase_data-solution, 1.06_read_your_first_firebase_data-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 22:59:22 2015 -0700\n\n    1.06 Read Your First Firebase Data - Solution\n\ncommit 70c98e063933f978866d1c3958a2b2d43367f1c4 (origin/1.05_read_your_first_firebase_data-quiz, live_code/1.05_read_your_first_firebase_data-quiz, 1.05_read_your_first_firebase_data-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 22:47:05 2015 -0700\n\n    1.05 Read Your First Firebase Data - Quiz\n\ncommit ba6185565975fade22516c79bcbab2db3ee36976 (origin/1.04_write_your_first_firebase_data-solution, live_code/1.04_write_your_first_firebase_data-solution, 1.04_write_your_first_firebase_data-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 19:27:21 2015 -0700\n\n     1.04 Write Your First Firebase Data - Solution\n\ncommit 8b3da2b71dae0ac203e990331b8c1cedf2b33eae (origin/1.03_write_your_first_firebase_data-quiz, live_code/1.03_write_your_first_firebase_data-quiz, 1.03_write_your_first_firebase_data-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 18:41:37 2015 -0700\n\n    1.03 Write Your First Firebase Data - Quiz\n\ncommit 70d1011873f8df57df8655c0ca93f0650aed5a17 (origin/1.02_connect_firebase_to_your_app-solution, live_code/1.02_connect_firebase_to_your_app-solution, 1.02_connect_firebase_to_your_app-solution)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Tue Nov 10 10:38:07 2015 -0800\n\n    1.02 Connect Firebase to Your App - Solution\n\ncommit 08e22d21529dfb9b863e117d0550bbfb3877a3b8 (origin/1.01_connect_firebase_to_your_app-quiz, live_code/1.01_connect_firebase_to_your_app-quiz, 1.01_connect_firebase_to_your_app-quiz)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Wed Oct 28 21:59:36 2015 -0700\n\n    1.01 Connect Firebase to Your App - Quiz\n\ncommit 53647b4a442820a64479b9597493190929a7ae3f (origin/1.00_version_0.1_start_code, live_code/1.00_version_0.1_start_code, 1.00_version_0.1_start_code)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Mon Oct 12 17:56:12 2015 -0700\n\n    1.00 Version 0.1 Start Code\n\ncommit d65940e15a745a86e291e9289c4de206cd3dc103\nAuthor: Lyla <lyla@udacity.com>\nDate:   Sat Oct 10 19:31:04 2015 -0700\n\n    Write the README\n\ncommit 9344ea48d8f75c618c53814fc49120daee5d3d5e (origin/initial_commit)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Sat Oct 10 19:27:47 2015 -0700\n\n    Initial commit\n"
  },
  {
    "path": "rules/name_branches_commits.py",
    "content": "import re\nimport subprocess\n\ngit_message_to_branch = {}\n\n#BRANCH_REGEX = re.compile('origin/(\\d[^,]*)[\\),]')\n\nBRANCH_NAME_REGEX = re.compile('\\d\\.\\d\\d') #find the commit names\nCOMMIT_REGEX = re.compile('commit ([0-9a-f]{5,40})')\n\ndef sanitize(message):\n\treturn message.strip().replace(' - ', '-').replace(' ', '_').lower()\n\t#return message.strip().replace('(', '\\(').replace(')', '\\)')\n\n# git.log is the output from git log before rebasing, with all the branch labels\n# we look for all the branch labels in it and grab the commit messages associated\n# with each of them, so that we can look up branch by message name later\nwith open(\"git.log\") as git_log:\n\tcommitline = git_log.readline()\n\twhile commitline:\n\t\tmatchCommit = re.search(COMMIT_REGEX, commitline)\n\t\tif matchCommit != None:\n\t\t\tcommit = matchCommit.group(1)\n\t\t\tprint commit\n\t\t\tfor i in range(3): git_log.readline()\n\t\t\tbranchline = git_log.readline()\n\t\t\tbranchName = sanitize(branchline)\n\t\t\t#branchName = matchBranchName.group(0)\n\t\t\tprint branchline\n\t\t\tgit_message_to_branch[commit] = branchName\n\t\tcommitline = git_log.readline()\n\nprint git_message_to_branch\n\n# Now take the current state of the log, which should have the same commit messages\n# for the most part, but the commit SHAs will have changed\n# So, we find the new SHAs associated with each message, and re-attach the branches\n# to those commits!\n# p = subprocess.Popen(['git','log'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n# output, err = p.communicate()\n\nfor commit in git_message_to_branch:\n  #commit_regex = re.compile('commit ([^ ]*)([^\\n]*\\n){4}    ' + message)\n  #match = re.search(commit_regex, output)\n  #if match != None and message in git_message_to_branch:\n  #commit = match.group(1)\n  #subprocess.call(['git','branch', '-D', git_message_to_branch[commit]])\n  subprocess.call(['git','branch', git_message_to_branch[commit],commit])\n  # else: \n  #     print \"Couldn't find \" + message\n"
  },
  {
    "path": "rules/rules.bolt",
    "content": "/** NOTE: This is just an example file for what Bolt code looks like. The rules.json file does not\n mirror the output of this file **/\n\n// Objects for ShoppingList++\ntype Timestamp {\n\ttimestamp : Number\n}\n\ntype EncodedEmail extends String {\n\tvalidate() = this.test(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i);\n}\n\ntype User {\n\tname : String,\n\temail : EncodedEmail,\n\ttimestampJoined : Timestamp,\n\thasLoggedInWithPassword : Boolean\n}\n\ntype ShoppingListItem {\n    itemName : String,\n    owner : String,\n    boughtBy : EncodedEmail | Null,\n    bought : Boolean | Null\n}\n\ntype ShoppingList {\n\tlistName : String,\n\towner : EncodedEmail,\n\ttimestampCreated : Timestamp,\n\ttimestampLastChanged : Timestamp,\n\ttimestampLastChangedReverse : Timestamp | Null,\n\tusersShopping : Map<String, User>\n}\n\n//Functions\n\nfunction isOwner(ownerEmail) {\n\t(isLoggedIn() && getEncodedEmail() == ownerEmail);\n}\n\nfunction isOwnerOrShared(ownerId, listId) {\n\t(isOwner(ownerId) || (prior(root).sharedWith[listId][getEncodedEmail()] != null)); //getEncodedEmail() != null && \n}\n\nfunction isLoggedIn() {\n\tauth != null;\n}\n\nfunction isShopping(listId) {\n\tprior(root).userLists[getOwnerEmail(listId)][listId].usersShopping[getEncodedEmail()] != null;\n}\n\nfunction getEncodedEmail() {\n\tprior(root).uidMappings[auth.uid];\n}\n\nfunction getOwnerEmail(listId) {\n\tprior(root).ownerMappings[listId];\n}\n\n//Paths\n\n/** SharedWith **/\n\n// Read: List owners and shared\n// Write: Add/Remove for list owner\n\npath /sharedWith/$listId {\n\tread() = isOwnerOrShared(getOwnerEmail($listId), $listId);\n\twrite() = isOwner(getOwnerEmail($listId));\n}\n\npath /sharedWith/$listId/$userId is User {\n}\n\n\n/** Shopping List Items **/\n\n\n// Read: List owner and shared\n// Write: Owner can remove all items\npath /shoppingListItems/$listId {\n\tread() = isOwnerOrShared(getOwnerEmail($listId), $listId);\n\twrite() = isOwner(getOwnerEmail($listId)) && this == null;\n}\n\n// Write: Add for owner or shared; Remove/Edit for list owner or item's owner and if it's not bought\npath /shoppingListItems/$listId/$itemId is ShoppingListItem {\n\twrite() = (isOwnerOrShared(getOwnerEmail($listId), $listId) && prior(this) == null) || ((isOwner(getOwnerEmail($listId)) || isOwner(prior(this.owner))) && !prior(this.bought));\n}\n\n//Write: List owner or shared\n//Validate: If this item is true, there must be a boughtBy. If this item is false, there must be no bought by.\npath /shoppingListItems/$listId/$itemId/bought {\n\twrite() = isOwnerOrShared(getOwnerEmail($listId), $listId); //&& isShopping($listId);\n\tvalidate() = (this && this.parent().boughtBy != null) || (!this && this.parent().boughtBy == null);\n}\n\n//Write: List owner or shared\n//Validate: The person must be the user\npath /shoppingListItems/$listId/$itemId/boughtBy {\n\twrite() = isOwnerOrShared(getOwnerEmail($listId), $listId);\n\tvalidate() = this == getEncodedEmail();\n}\n\n//Validate: Cannot change\npath /shoppingListItems/$listId/$itemId/owner {\n\tvalidate() = prior(this) == null;\n}\n\n/** UserFriends **/\n\n//Read : The owner\n//Write : The owner\n\npath /userFriends/$userEncodedEmail {\n\tread() = isOwner($userEncodedEmail);\n\twrite() = isOwner($userEncodedEmail);\n}\n\npath /userFriends/$userEncodedEmail/$friendEncodedEmail is User {\n\n}\n\n/** UserLists **/\n//Read: The owner\npath /userLists/$userEncodedEmail {\n\tread() = isOwner($userEncodedEmail);\n}\n\n\n//Write: Add/Delete only the owner\n//Validate: List owner and shared\n\npath /userLists/$userEncodedEmail/$listId is ShoppingList{\n\twrite() = (prior(this) == null && isOwner(this.owner)) || (this == null && isOwner(prior(this.owner)));\n\tvalidate() = isOwnerOrShared(this.owner, $listId);\n}\n\n//Write: The owner\npath /userLists/$userEncodedEmail/$listId/listName {\n\twrite() = isOwner(prior(this.parent().owner));\n}\n\n//Write: The owner or shared\n//Validate: Must be now\npath /userLists/$userEncodedEmail/$listId/timestampLastChanged {\n\twrite() = isOwnerOrShared(prior(this.parent().owner), $listId);\n\tvalidate() = this.timestamp == now;\n}\n\n//Write: The owner or shared\npath /userLists/$userEncodedEmail/$listId/timestampLastChangedReverse {\n\twrite() = isOwnerOrShared(prior(this.parent().owner), $listId);\n}\n\n//Write: The owner or shared\npath /userLists/$userEncodedEmail/$listId/usersShopping {\n\twrite() = isOwnerOrShared(prior(this.parent().owner), $listId);\n}\n\n//Validate: User must add themselves\npath /userLists/$userEncodedEmail/$listId/usersShopping/$key1 {\n\tvalidate() = key() == getEncodedEmail();\n}\n\n/** Users **/\n\n//Read: Logged in\npath /users {\n\tread() = isLoggedIn();\n}\n\n//Write: The user can add themselves\npath /users/$userEncodedEmail is User {\n\twrite() = isLoggedIn() && root.uidMappings[auth.uid] == $userEncodedEmail && prior(this) == null;\n}\n\n//Write: The user can change if it's false\npath /users/$userEncodedEmail/hasLoggedInWithPassword {\n\twrite() = isOwner($userEncodedEmail) && !prior(this);\n}\n\n/** uidMappings **/\n//Read: Anyone logged in\n//Write: The user can add themselves \npath /uidMappings/$uid is EncodedEmail{\n\tread() = isLoggedIn();\n\twrite() = auth.uid == $uid && prior(this) == null;\n}\n\n/** ownerMappings **/\n//Read: Anyone logged in\n//Write: The owner\npath /ownerMappings/$listId {\n\tread() = isLoggedIn();\n\twrite() = isOwner(prior(this)) || isOwner(this);\n}"
  },
  {
    "path": "rules/rules.json",
    "content": "{\n  \"rules\": {\n    \"sharedWith\": {\n      \"$listId\": {\n        \".read\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\",\n        \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val()\",\n        \"$userId\": {\n          \".validate\": \"newData.hasChildren(['name', 'email', 'timestampJoined', 'hasLoggedInWithPassword'])\",\n          \"name\": {\n            \".validate\": \"newData.isString()\"\n          },\n          \"email\": {\n            \".validate\": \"newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i)\"\n          },\n          \"timestampJoined\": {\n            \".validate\": \"newData.hasChildren(['timestamp'])\",\n            \"timestamp\": {\n              \".validate\": \"newData.isNumber()\"\n            },\n            \"$other\": {\n              \".validate\": \"false\"\n            }\n          },\n          \"hasLoggedInWithPassword\": {\n            \".validate\": \"newData.isBoolean()\"\n          },\n          \"$other\": {\n            \".validate\": \"false\"\n          }\n        }\n      }\n    },\n    \"shoppingListItems\": {\n      \"$listId\": {\n        \".read\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\",\n        \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() && newData.val() == null\",\n        \"$itemId\": {\n          \".write\": \"(auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null) && data.val() == null || (auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() || auth != null && root.child('uidMappings').child(auth.uid).val() == data.child('owner').val()) && !(data.child('bought').val() == true)\",          \n          \".validate\": \"newData.hasChildren(['itemName', 'owner'])\",\n          \"itemName\": {\n            \".validate\": \"newData.isString()\"\n          },\n          \"owner\": {\n            \".validate\": \"newData.isString() && data.val() == null\"\n          },\n          \"boughtBy\": {\n            \".validate\": \"(newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i) || newData.val() == null) && newData.val() == root.child('uidMappings').child(auth.uid).val()\",\n            \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\"\n          },\n          \"bought\": {\n            \".validate\": \"(newData.isBoolean() || newData.val() == null) && (newData.val() == true && newData.parent().child('boughtBy').val() != null || !(newData.val() == true) && newData.parent().child('boughtBy').val() == null)\",\n            \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == root.child('ownerMappings').child($listId).val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\"\n          },\n          \"$other\": {\n            \".validate\": \"false\"\n          }\n        }\n      }\n    },\n    \"userFriends\": {\n      \"$userEncodedEmail\": {\n        \".read\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == $userEncodedEmail\",\n        \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == $userEncodedEmail\",\n        \"$friendEncodedEmail\": {\n          \".validate\": \"newData.hasChildren(['name', 'email', 'timestampJoined', 'hasLoggedInWithPassword'])\",\n          \"name\": {\n            \".validate\": \"newData.isString()\"\n          },\n          \"email\": {\n            \".validate\": \"newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i)\"\n          },\n          \"timestampJoined\": {\n            \".validate\": \"newData.hasChildren(['timestamp'])\",\n            \"timestamp\": {\n              \".validate\": \"newData.isNumber()\"\n            },\n            \"$other\": {\n              \".validate\": \"false\"\n            }\n          },\n          \"hasLoggedInWithPassword\": {\n            \".validate\": \"newData.isBoolean()\"\n          },\n          \"$other\": {\n            \".validate\": \"false\"\n          }\n        }\n      }\n    },\n    \"userLists\": {\n      \"$userEncodedEmail\": {\n        \".read\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == $userEncodedEmail\",\n        \"$listId\": {\n          \".write\": \"data.val() == null && (auth != null && root.child('uidMappings').child(auth.uid).val() == newData.child('owner').val()) || newData.val() == null && (auth != null && root.child('uidMappings').child(auth.uid).val() == data.child('owner').val())\",\n          \".validate\": \"newData.hasChildren(['listName', 'owner', 'timestampCreated', 'timestampLastChanged']) && (auth != null && root.child('uidMappings').child(auth.uid).val() == newData.child('owner').val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null)\",\n          \"listName\": {\n            \".validate\": \"newData.isString()\",\n            \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == data.parent().child('owner').val()\"\n          },\n          \"owner\": {\n            \".validate\": \"newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i)\"\n          },\n          \"timestampCreated\": {\n            \".validate\": \"newData.hasChildren(['timestamp'])\",\n            \"timestamp\": {\n              \".validate\": \"newData.isNumber()\"\n            },\n            \"$other\": {\n              \".validate\": \"false\"\n            }\n          },\n          \"timestampLastChanged\": {\n            \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == data.parent().child('owner').val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\",\n            \".validate\": \"newData.hasChildren(['timestamp']) && newData.child('timestamp').val() == now\",\n            \"timestamp\": {\n              \".validate\": \"newData.isNumber()\"\n            },\n            \"$other\": {\n              \".validate\": \"false\"\n            }\n          },\n          \"timestampLastChangedReverse\": {\n            \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == data.parent().child('owner').val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\",\n            \".validate\": \"newData.hasChildren() && newData.hasChildren(['timestamp']) || newData.val() == null\",\n            \"timestamp\": {\n              \".validate\": \"newData.isNumber()\"\n            },\n            \"$other\": {\n              \".validate\": \"false\"\n            }\n          },\n          \"usersShopping\": {\n            \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == data.parent().child('owner').val() || root.child('sharedWith').child($listId).child(root.child('uidMappings').child(auth.uid).val()).val() != null\",            \n            \"$shoppingUser\": {\n              \".validate\": \"newData.hasChildren(['name', 'email', 'timestampJoined', 'hasLoggedInWithPassword']) && $shoppingUser == root.child('uidMappings').child(auth.uid).val()\",\n              \"name\": {\n                \".validate\": \"newData.isString()\"\n              },\n              \"email\": {\n                \".validate\": \"newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i)\"\n              },\n              \"timestampJoined\": {\n                \".validate\": \"newData.hasChildren(['timestamp'])\",\n                \"timestamp\": {\n                  \".validate\": \"newData.isNumber()\"\n                },\n                \"$other\": {\n                  \".validate\": \"false\"\n                }\n              },\n              \"hasLoggedInWithPassword\": {\n                \".validate\": \"newData.isBoolean()\"\n              },\n              \"$other\": {\n                \".validate\": \"false\"\n              }\n            }\n          },\n          \"$other\": {\n            \".validate\": \"false\"\n          }\n        }\n      }\n    },\n    \"users\": {\n      \".read\": \"auth != null\",\n      \"$userEncodedEmail\": {\n        \".write\": \"auth != null && newData.parent().parent().child('uidMappings').child(auth.uid).val() == $userEncodedEmail && data.val() == null\",        \n        \".validate\": \"newData.hasChildren(['name', 'email', 'timestampJoined', 'hasLoggedInWithPassword'])\",\n        \"name\": {\n          \".validate\": \"newData.isString()\"\n        },\n        \"email\": {\n          \".validate\": \"newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i)\"\n        },\n        \"timestampJoined\": {\n          \".validate\": \"newData.hasChildren(['timestamp'])\",\n          \"timestamp\": {\n            \".validate\": \"newData.isNumber()\"\n          },\n          \"$other\": {\n            \".validate\": \"false\"\n          }\n        },\n        \"hasLoggedInWithPassword\": {\n          \".validate\": \"newData.isBoolean()\",\n          \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == $userEncodedEmail && !(data.val() == true)\"\n        },\n        \"$other\": {\n          \".validate\": \"false\"\n        }\n      }\n    },\n    \"uidMappings\": {\n      \"$uid\": {\n        \".validate\": \"newData.isString() && newData.val().matches(/^[A-Z0-9,_%+-]+@[A-Z0-9,-]+,[A-Z]{2,4}$/i)\",\n        \".read\": \"auth != null\",\n        \".write\": \"auth.uid == $uid && data.val() == null\"\n      }\n    },\n    \"ownerMappings\": {\n      \"$listId\": {\n        \".read\": \"auth != null\",\n        \".write\": \"auth != null && root.child('uidMappings').child(auth.uid).val() == data.val() || auth != null && root.child('uidMappings').child(auth.uid).val() == newData.val()\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]