Full Code of udacity/ShoppingListPlusPlus for AI

master 46d3e25c28d7 cached
82 files
287.6 KB
68.3k tokens
206 symbols
1 requests
Download .txt
Showing preview only (314K chars total). Download the full file or copy to clipboard to get everything.
Repository: udacity/ShoppingListPlusPlus
Branch: master
Commit: 46d3e25c28d7
Files: 82
Total size: 287.6 KB

Directory structure:
gitextract_41bacwm3/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── keystores/
│   │   ├── debug.jks
│   │   └── myDebugKey.jks
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── udacity/
│       │               └── firebase/
│       │                   └── shoppinglistplusplus/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── udacity/
│           │           └── firebase/
│           │               └── shoppinglistplusplus/
│           │                   ├── ShoppingListApplication.java
│           │                   ├── model/
│           │                   │   ├── ShoppingList.java
│           │                   │   ├── ShoppingListItem.java
│           │                   │   └── User.java
│           │                   ├── ui/
│           │                   │   ├── BaseActivity.java
│           │                   │   ├── MainActivity.java
│           │                   │   ├── SettingsActivity.java
│           │                   │   ├── activeListDetails/
│           │                   │   │   ├── ActiveListDetailsActivity.java
│           │                   │   │   ├── ActiveListItemAdapter.java
│           │                   │   │   ├── AddListItemDialogFragment.java
│           │                   │   │   ├── EditListDialogFragment.java
│           │                   │   │   ├── EditListItemNameDialogFragment.java
│           │                   │   │   ├── EditListNameDialogFragment.java
│           │                   │   │   └── RemoveListDialogFragment.java
│           │                   │   ├── activeLists/
│           │                   │   │   ├── ActiveListAdapter.java
│           │                   │   │   ├── AddListDialogFragment.java
│           │                   │   │   └── ShoppingListsFragment.java
│           │                   │   ├── login/
│           │                   │   │   ├── CreateAccountActivity.java
│           │                   │   │   └── LoginActivity.java
│           │                   │   ├── meals/
│           │                   │   │   ├── AddMealDialogFragment.java
│           │                   │   │   └── MealsFragment.java
│           │                   │   └── sharing/
│           │                   │       ├── AddFriendActivity.java
│           │                   │       ├── AutocompleteFriendAdapter.java
│           │                   │       ├── FriendAdapter.java
│           │                   │       └── ShareListActivity.java
│           │                   └── utils/
│           │                       ├── Constants.java
│           │                       └── Utils.java
│           └── res/
│               ├── layout/
│               │   ├── activity_active_list_details.xml
│               │   ├── activity_add_friend.xml
│               │   ├── activity_create_account.xml
│               │   ├── activity_login.xml
│               │   ├── activity_main.xml
│               │   ├── activity_share_list.xml
│               │   ├── dialog_add_item.xml
│               │   ├── dialog_add_list.xml
│               │   ├── dialog_add_meal.xml
│               │   ├── dialog_edit_item.xml
│               │   ├── dialog_edit_list.xml
│               │   ├── footer_empty.xml
│               │   ├── fragment_meals.xml
│               │   ├── fragment_shopping_lists.xml
│               │   ├── single_active_list.xml
│               │   ├── single_active_list_item.xml
│               │   ├── single_autocomplete_item.xml
│               │   └── single_user_item.xml
│               ├── layout-land/
│               │   ├── activity_create_account.xml
│               │   └── activity_login.xml
│               ├── menu/
│               │   ├── menu_base.xml
│               │   ├── menu_list_details.xml
│               │   └── menu_main.xml
│               ├── values/
│               │   ├── arrays.xml
│               │   ├── colors.xml
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   ├── styles.xml
│               │   └── themes.xml
│               ├── values-sw480dp/
│               │   └── dimens.xml
│               ├── values-sw640dp/
│               │   └── dimens.xml
│               ├── values-v21/
│               │   ├── styles.xml
│               │   └── themes.xml
│               ├── values-w480dp/
│               │   └── dimens.xml
│               ├── values-w820dp/
│               │   └── dimens.xml
│               └── xml/
│                   └── preference_screen.xml
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── rules/
│   ├── git.log
│   ├── name_branches_commits.py
│   ├── rules.bolt
│   └── rules.json
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
#built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Eclipse project files
.classpath
.project
# Android Studio
*.iml
.idea
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
.gradle
build/
#.idea/vcs.xml
gradle.properties
#don't store google-services.json
app/google-services.json



================================================
FILE: README.md
================================================
ShoppingList++
========

ShoppingList++ 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). 

The course covers everything you need to know to incorporate the [Firebase](https://www.firebase.com) into an Android App.

For 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).

##Seeing Errors?

![Conflict errors](http://lh3.googleusercontent.com/zQxJRMJIEnWEQb7csOy3AiJoIJiOnY_dqf4D-seEEpJQFBMwmdpCF_JeszhB96K57tFxW2_FtOQvmIhUvQ=s0#w=1014&h=359)

As 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.

##Changelogs

- **1/26/2016** : [Changelog](https://docs.google.com/document/d/1SgBmUu7COQQT5maqKVvIV4Iv0Oyva9-9-YRnpQ88XuY/pub)
- **12/10/2015** : [Changelog](https://docs.google.com/document/d/1A5BSoLyEHkXrcBC50lNXqrl1Rkh0G2nM-h4ER8lKovw/pub)
- **11/30/2015** : Lesson 1 and 2 launch
 # Archival Note 
 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: 
- Utilize the https://knowledge.udacity.com/ forum to seek help on content-specific issues. 
- 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).

================================================
FILE: app/.gitignore
================================================
#built application files
*.apk
*.ap_
# files for the dex VM
*.dex
# Java class files
*.class
# generated files
bin/
gen/
# Local configuration file (sdk path, etc)
local.properties
# Windows thumbnail db
Thumbs.db
# OSX files
.DS_Store
# Eclipse project files
.classpath
.project
# Android Studio
*.iml
.idea
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
.gradle
build/
#.idea/vcs.xml

================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.udacity.firebase.shoppinglistplusplus"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
        }
    }

    /* This for anyone following along with the repo. Since you will have a different
     * root URL, this code loads up a value from your gradle.properties file.
     */

    buildTypes.each {
        it.buildConfigField 'String', 'UNIQUE_FIREBASE_ROOT_URL', UniqueFirebaseRootUrl
    }

    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE-FIREBASE.txt'
        exclude 'META-INF/NOTICE'
    }

}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.android.support:support-v4:23.0.1'
    compile 'com.android.support:cardview-v7:23.0.1'

    /* Firebase SDK */
    compile 'com.firebase:firebase-client-android:2.4.0'

    /* Firebase UI */
    compile 'com.firebaseui:firebase-ui:0.2.2'

    /* For Google Play Services */
    compile 'com.google.android.gms:play-services-safetynet:8.3.0'
    compile 'com.google.android.gms:play-services-auth:8.3.0'
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/admin/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}


================================================
FILE: app/src/androidTest/java/com/udacity/firebase/shoppinglistplusplus/ApplicationTest.java
================================================
package com.udacity.firebase.shoppinglistplusplus;

import android.app.Application;
import android.test.ApplicationTestCase;

/**
 * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
 */
public class ApplicationTest extends ApplicationTestCase<Application> {
    public ApplicationTest() {
        super(Application.class);
    }
}

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.udacity.firebase.shoppinglistplusplus">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />

    <application
        android:name="com.udacity.firebase.shoppinglistplusplus.ShoppingListApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_shopping_list"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.udacity.firebase.shoppinglistplusplus.ui.MainActivity"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustPan">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.udacity.firebase.shoppinglistplusplus.ui.MainActivity" />
        </activity>

        <activity
            android:name=".ui.activeListDetails.ActiveListDetailsActivity"
            android:label="@string/title_activity_list_details"
            android:parentActivityName=".ui.MainActivity"
            android:windowSoftInputMode="adjustPan">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".ui.MainActivity" />
        </activity>

        <activity android:name=".ui.login.LoginActivity">
            <intent-filter android:label="@string/app_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".ui.login.CreateAccountActivity"
            android:label="@string/title_activity_create_account"
            android:parentActivityName=".ui.login.LoginActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".ui.login.LoginActivity" />
        </activity>
        <activity
            android:name=".ui.SettingsActivity"
            android:label="@string/action_settings" />
        <activity
            android:name=".ui.sharing.ShareListActivity"
            android:label="@string/title_activity_share_list"
            android:parentActivityName=".ui.activeListDetails.ActiveListDetailsActivity"
            android:windowSoftInputMode="adjustPan">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".ui.activeListDetails.ActiveListDetailsActivity" />
        </activity>
        <activity
            android:name=".ui.sharing.AddFriendActivity"
            android:label="@string/title_activity_add_friend"
            android:parentActivityName=".ui.sharing.ShareListActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".ui.sharing.ShareListActivity" />
        </activity>
    </application>
</manifest>


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ShoppingListApplication.java
================================================
package com.udacity.firebase.shoppinglistplusplus;

import com.firebase.client.Firebase;

/**
 * Includes one-time initialization of Firebase related code
 */
public class ShoppingListApplication extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();
        /* Initialize Firebase */
        Firebase.setAndroidContext(this);
        /* Enable disk persistence  */
        Firebase.getDefaultConfig().setPersistenceEnabled(true);
    }

}

================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java
================================================
package com.udacity.firebase.shoppinglistplusplus.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.firebase.client.ServerValue;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

import java.util.HashMap;

/**
 * Defines the data structure for both Active and Archived ShoppingList objects.
 */

public class ShoppingList {
    private String listName;
    private String owner;
    private HashMap<String, Object> timestampLastChanged;
    private HashMap<String, Object> timestampCreated;
    private HashMap<String, Object> timestampLastChangedReverse;
    private HashMap<String, User> usersShopping;

    /**
     * Required public constructor
     */
    public ShoppingList() {
    }

    /**
     * Use this constructor to create new ShoppingLists.
     * Takes shopping list listName and owner. Set's the last
     * changed time to what is stored in ServerValue.TIMESTAMP
     *
     * @param listName
     * @param owner
     */
    public ShoppingList(String listName, String owner, HashMap<String, Object> timestampCreated) {
        this.listName = listName;
        this.owner = owner;
        this.timestampCreated = timestampCreated;
        HashMap<String, Object> timestampNowObject = new HashMap<String, Object>();
        timestampNowObject.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);
        this.timestampLastChanged = timestampNowObject;
        this.timestampLastChangedReverse = null;
        this.usersShopping = new HashMap<>();
    }

    public String getListName() {
        return listName;
    }

    public String getOwner() {
        return owner;
    }

    public HashMap<String, Object> getTimestampLastChanged() {
        return timestampLastChanged;
    }

    public HashMap<String, Object> getTimestampCreated() {
        return timestampCreated;
    }

    public HashMap<String, Object> getTimestampLastChangedReverse() {
        return timestampLastChangedReverse;
    }

    @JsonIgnore
    public long getTimestampLastChangedLong() {

        return (long) timestampLastChanged.get(Constants.FIREBASE_PROPERTY_TIMESTAMP);
    }

    @JsonIgnore
    public long getTimestampCreatedLong() {
        return (long) timestampLastChanged.get(Constants.FIREBASE_PROPERTY_TIMESTAMP);
    }

    @JsonIgnore
    public long getTimestampLastChangedReverseLong() {

        return (long) timestampLastChangedReverse.get(Constants.FIREBASE_PROPERTY_TIMESTAMP);
    }

    public HashMap getUsersShopping() {
        return usersShopping;
    }

    public void setTimestampLastChangedToNow() {
        HashMap<String, Object> timestampNowObject = new HashMap<String, Object>();
        timestampNowObject.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);
        this.timestampLastChanged = timestampNowObject;
    }


}



================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java
================================================
package com.udacity.firebase.shoppinglistplusplus.model;

/**
 * Defines the data structure for ShoppingListItem objects.
 */
public class ShoppingListItem {
    private String itemName;
    private String owner;
    private String boughtBy;
    private boolean bought;

    /**
     * Required public constructor
     */
    public ShoppingListItem() {
    }

    /**
     * Use this constructor to create new ShoppingListItem.
     * Takes shopping list item name and list item owner email as params
     *
     * @param itemName
     * @param owner
     */
    public ShoppingListItem(String itemName, String owner) {
        this.itemName = itemName;
        this.owner = owner;
        this.boughtBy = null;
        this.bought = false;

    }

    public String getItemName() { return itemName; }

    public String getOwner() {
        return owner;
    }

    public String getBoughtBy() {
        return boughtBy;
    }

    public boolean isBought() {
        return bought;
    }

}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/User.java
================================================
package com.udacity.firebase.shoppinglistplusplus.model;

import java.util.HashMap;

/**
 * Defines the data structure for User objects.
 */
public class User {
    private String name;
    private String email;
    private HashMap<String, Object> timestampJoined;
    private boolean hasLoggedInWithPassword;


    /**
     * Required public constructor
     */
    public User() {
    }

    /**
     * Use this constructor to create new User.
     * Takes user name, email and timestampJoined as params
     *
     * @param name
     * @param email
     * @param timestampJoined
     */
    public User(String name, String email, HashMap<String, Object> timestampJoined) {
        this.name = name;
        this.email = email;
        this.timestampJoined = timestampJoined;
        this.hasLoggedInWithPassword = false;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public HashMap<String, Object> getTimestampJoined() {
        return timestampJoined;
    }

    public boolean isHasLoggedInWithPassword() {
        return hasLoggedInWithPassword;
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/BaseActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui;

import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;

import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.ui.login.CreateAccountActivity;
import com.udacity.firebase.shoppinglistplusplus.ui.login.LoginActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

/**
 * BaseActivity class is used as a base class for all activities in the app
 * It implements GoogleApiClient callbacks to enable "Logout" in all activities
 * and defines variables that are being shared across all activities
 */
public abstract class BaseActivity extends AppCompatActivity implements
        GoogleApiClient.OnConnectionFailedListener {
    protected String mProvider, mEncodedEmail;
    /* Client used to interact with Google APIs. */
    protected GoogleApiClient mGoogleApiClient;
    protected Firebase.AuthStateListener mAuthListener;
    protected Firebase mFirebaseRef;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /* Setup the Google API object to allow Google logins */
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestEmail()
                .build();

        /**
         * Build a GoogleApiClient with access to the Google Sign-In API and the
         * options specified by gso.
         */

        /* Setup the Google API object to allow Google+ logins */
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
                .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .build();

        /**
         * Getting mProvider and mEncodedEmail from SharedPreferences
         */
        final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(BaseActivity.this);
        /* Get mEncodedEmail and mProvider from SharedPreferences, use null as default value */
        mEncodedEmail = sp.getString(Constants.KEY_ENCODED_EMAIL, null);
        mProvider = sp.getString(Constants.KEY_PROVIDER, null);


        if (!((this instanceof LoginActivity) || (this instanceof CreateAccountActivity))) {
            mFirebaseRef = new Firebase(Constants.FIREBASE_URL);
            mAuthListener = new Firebase.AuthStateListener() {
                @Override
                public void onAuthStateChanged(AuthData authData) {
                     /* The user has been logged out */
                    if (authData == null) {
                        /* Clear out shared preferences */
                        SharedPreferences.Editor spe = sp.edit();
                        spe.putString(Constants.KEY_ENCODED_EMAIL, null);
                        spe.putString(Constants.KEY_PROVIDER, null);
                        
                        takeUserToLoginScreenOnUnAuth();
                    }
                }
            };
            mFirebaseRef.addAuthStateListener(mAuthListener);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        /* Cleanup the AuthStateListener */
        if (!((this instanceof LoginActivity) || (this instanceof CreateAccountActivity))) {
            mFirebaseRef.removeAuthStateListener(mAuthListener);
        }

    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        /* Inflate the menu; this adds items to the action bar if it is present. */
        getMenuInflater().inflate(R.menu.menu_base, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == android.R.id.home) {
            super.onBackPressed();
            return true;
        }

        if (id == R.id.action_logout) {
            logout();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    protected void initializeBackground(LinearLayout linearLayout) {

        /**
         * Set different background image for landscape and portrait layouts
         */
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            linearLayout.setBackgroundResource(R.drawable.background_loginscreen_land);
        } else {
            linearLayout.setBackgroundResource(R.drawable.background_loginscreen);
        }
    }

    /**
     * Logs out the user from their current session and starts LoginActivity.
     * Also disconnects the mGoogleApiClient if connected and provider is Google
     */
    protected void logout() {

        /* Logout if mProvider is not null */
        if (mProvider != null) {
            mFirebaseRef.unauth();

            if (mProvider.equals(Constants.GOOGLE_PROVIDER)) {

                /* Logout from Google+ */
                Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
                        new ResultCallback<Status>() {
                            @Override
                            public void onResult(Status status) {
                                //nothing
                            }
                        });
            }
        }
    }

    private void takeUserToLoginScreenOnUnAuth() {
        /* Move user to LoginActivity, and remove the backstack */
        Intent intent = new Intent(BaseActivity.this, LoginActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
        finish();
    }
    
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/MainActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui;

import android.app.DialogFragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.ui.activeLists.AddListDialogFragment;
import com.udacity.firebase.shoppinglistplusplus.ui.activeLists.ShoppingListsFragment;
import com.udacity.firebase.shoppinglistplusplus.ui.meals.AddMealDialogFragment;
import com.udacity.firebase.shoppinglistplusplus.ui.meals.MealsFragment;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

/**
 * Represents the home screen of the app which
 * has a {@link ViewPager} with {@link ShoppingListsFragment} and {@link MealsFragment}
 */
public class MainActivity extends BaseActivity {
    private Firebase mUserRef;
    private static final String LOG_TAG = MainActivity.class.getSimpleName();
    private ValueEventListener mUserRefListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * Create Firebase references
         */
        mUserRef = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);

        /**
         * Link layout elements from XML and setup the toolbar
         */
        initializeScreen();

        /**
         * Add ValueEventListeners to Firebase references
         * to control get data and control behavior and visibility of elements
         */
        mUserRefListener = mUserRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot snapshot) {
                User user = snapshot.getValue(User.class);

                /**
                 * Set the activity title to current user name if user is not null
                 */
                if (user != null) {
                    /* Assumes that the first word in the user's name is the user's first name. */
                    String firstName = user.getName().split("\\s+")[0];
                    String title = firstName + "'s Lists";
                    setTitle(title);
                }
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });

    }


    /**
     * Override onOptionsItemSelected to use main_menu instead of BaseActivity menu
     *
     * @param menu
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        /* Inflate the menu; this adds items to the action bar if it is present. */
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    /**
     * Override onOptionsItemSelected to add action_settings only to the MainActivity
     *
     * @param item
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        /**
         * Open SettingsActivity with sort options when Sort icon was clicked
         */
        if (id == R.id.action_sort) {
            startActivity(new Intent(MainActivity.this, SettingsActivity.class));
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        mUserRef.removeEventListener(mUserRefListener);
    }

    /**
     * Link layout elements from XML and setup the toolbar
     */
    public void initializeScreen() {
        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);
        /**
         * Create SectionPagerAdapter, set it as adapter to viewPager with setOffscreenPageLimit(2)
         **/
        SectionPagerAdapter adapter = new SectionPagerAdapter(getSupportFragmentManager());
        viewPager.setOffscreenPageLimit(2);
        viewPager.setAdapter(adapter);
        /**
         * Setup the mTabLayout with view pager
         */
        tabLayout.setupWithViewPager(viewPager);
    }

    /**
     * Create an instance of the AddList dialog fragment and show it
     */
    public void showAddListDialog(View view) {
        /* Create an instance of the dialog fragment and show it */
        DialogFragment dialog = AddListDialogFragment.newInstance(mEncodedEmail);
        dialog.show(MainActivity.this.getFragmentManager(), "AddListDialogFragment");
    }

    /**
     * Create an instance of the AddMeal dialog fragment and show it
     */
    public void showAddMealDialog(View view) {
        /* Create an instance of the dialog fragment and show it */
        DialogFragment dialog = AddMealDialogFragment.newInstance();
        dialog.show(MainActivity.this.getFragmentManager(), "AddMealDialogFragment");
    }

    /**
     * SectionPagerAdapter class that extends FragmentStatePagerAdapter to save fragments state
     */
    public class SectionPagerAdapter extends FragmentStatePagerAdapter {

        public SectionPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        /**
         * Use positions (0 and 1) to find and instantiate fragments with newInstance()
         *
         * @param position
         */
        @Override
        public Fragment getItem(int position) {

            Fragment fragment = null;

            /**
             * Set fragment to different fragments depending on position in ViewPager
             */
            switch (position) {
                case 0:
                    fragment = ShoppingListsFragment.newInstance(mEncodedEmail);
                    break;
                case 1:
                    fragment = MealsFragment.newInstance();
                    break;
                default:
                    fragment = ShoppingListsFragment.newInstance(mEncodedEmail);
                    break;
            }

            return fragment;
        }


        @Override
        public int getCount() {
            return 2;
        }

        /**
         * Set string resources as titles for each fragment by it's position
         *
         * @param position
         */
        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return getString(R.string.pager_title_shopping_lists);
                case 1:
                default:
                    return getString(R.string.pager_title_meals);
            }
        }
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/SettingsActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;

import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

/**
 * SettingsActivity represents preference screen and functionality
 */
public class SettingsActivity extends PreferenceActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTheme(R.style.PrefScreenTheme);

        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SortPreferenceFragment())
                .commit();
    }

    /**
     * This fragment shows the preferences for the first header.
     */
    public static class SortPreferenceFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            /* Load the preferences from an XML resource */
            addPreferencesFromResource(R.xml.preference_screen);

            /**
             * Bind preference summary to value for lists and meals sorting list preferences
             */
            bindPreferenceSummaryToValue(findPreference(getString(R.string.pref_name_sort_order_lists)));
        }

        /**
         * When preference is changed, save it's new value to default shared preferences
         *
         * @param preference
         * @param newValue
         */
        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            setPreferenceSummary(preference, newValue);
            SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
            SharedPreferences.Editor spe = sharedPref.edit();
            spe.putString(Constants.KEY_PREF_SORT_ORDER_LISTS, newValue.toString()).apply();
            return true;
        }

        private void bindPreferenceSummaryToValue(Preference preference) {
        /* Set the listener to watch for value changes. */
            preference.setOnPreferenceChangeListener(this);
            /* Trigger the listener immediately with the preference's current value. */
            setPreferenceSummary(preference,
                    PreferenceManager
                            .getDefaultSharedPreferences(preference.getContext())
                            .getString(preference.getKey(), ""));
        }

        /**
         * Sets preference summary to appropriate value
         *
         * @param preference
         * @param value
         */
        private void setPreferenceSummary(Preference preference, Object value) {
            String stringValue = value.toString();

            if (preference instanceof ListPreference) {
                ListPreference listPreference = (ListPreference) preference;
                int prefIndex = listPreference.findIndexOfValue(stringValue);

                if (prefIndex >= 0) {
                    preference.setSummary(listPreference.getEntries()[prefIndex]);
                }
            }
        }
    }
}

================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Activity;
import android.app.DialogFragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;
import com.udacity.firebase.shoppinglistplusplus.ui.sharing.ShareListActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Represents the details screen for the selected shopping list
 */
public class ActiveListDetailsActivity extends BaseActivity {
    private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName();
    private Firebase mCurrentListRef, mCurrentUserRef, mSharedWithRef;
    private ActiveListItemAdapter mActiveListItemAdapter;
    private Button mButtonShopping;
    private TextView mTextViewPeopleShopping;
    private ListView mListView;
    private String mListId;
    private User mCurrentUser;
    /* Stores whether the current user is shopping */
    private boolean mShopping = false;
    /* Stores whether the current user is the owner */
    private boolean mCurrentUserIsOwner = false;
    private ShoppingList mShoppingList;
    private ValueEventListener mCurrentUserRefListener, mCurrentListRefListener, mSharedWithListener;
    private HashMap<String, User> mSharedWithUsers;

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

        /* Get the push ID from the extra passed by ShoppingListFragment */
        Intent intent = this.getIntent();
        mListId = intent.getStringExtra(Constants.KEY_LIST_ID);
        if (mListId == null) {
            /* No point in continuing without a valid ID. */
            finish();
            return;
        }

        /**
         * Create Firebase references
         */
        mCurrentListRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS).child(mEncodedEmail).child(mListId);
        mCurrentUserRef = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);
        mSharedWithRef = new Firebase (Constants.FIREBASE_URL_LISTS_SHARED_WITH).child(mListId);
        Firebase listItemsRef = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS).child(mListId);


        /**
         * Link layout elements from XML and setup the toolbar
         */
        initializeScreen();


        /**
         * Setup the adapter
         */
        mActiveListItemAdapter = new ActiveListItemAdapter(this, ShoppingListItem.class,
                R.layout.single_active_list_item, listItemsRef.orderByChild(Constants.FIREBASE_PROPERTY_BOUGHT_BY),
                mListId, mEncodedEmail);
        /* Create ActiveListItemAdapter and set to listView */
        mListView.setAdapter(mActiveListItemAdapter);


        /**
         * Add ValueEventListeners to Firebase references
         * to control get data and control behavior and visibility of elements
         */

        /* Save the most up-to-date version of current user in mCurrentUser */
        mCurrentUserRefListener = mCurrentUserRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                User currentUser = dataSnapshot.getValue(User.class);
                if (currentUser != null) mCurrentUser = currentUser;
                else finish();
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });

        final Activity thisActivity = this;


        /**
         * Save the most recent version of current shopping list into mShoppingList instance
         * variable an update the UI to match the current list.
         */
        mCurrentListRefListener = mCurrentListRef.addValueEventListener(new ValueEventListener() {

            @Override
            public void onDataChange(DataSnapshot snapshot) {

                /**
                 * Saving the most recent version of current shopping list into mShoppingList if present
                 * finish() the activity if the list is null (list was removed or unshared by it's owner
                 * while current user is in the list details activity)
                 */
                ShoppingList shoppingList = snapshot.getValue(ShoppingList.class);

                if (shoppingList == null) {
                    finish();
                    /**
                     * Make sure to call return, otherwise the rest of the method will execute,
                     * even after calling finish.
                     */
                    return;
                }
                mShoppingList = shoppingList;
                /**
                 * Pass the shopping list to the adapter if it is not null.
                 * We do this here because mShoppingList is null when first created.
                 */
                mActiveListItemAdapter.setShoppingList(mShoppingList);

                /* Check if the current user is owner */
                mCurrentUserIsOwner = Utils.checkIfOwner(shoppingList, mEncodedEmail);


                /* Calling invalidateOptionsMenu causes onCreateOptionsMenu to be called */
                invalidateOptionsMenu();

                /* Set title appropriately. */
                setTitle(shoppingList.getListName());

                HashMap<String, User> usersShopping = mShoppingList.getUsersShopping();
                if (usersShopping != null && usersShopping.size() != 0 &&
                        usersShopping.containsKey(mEncodedEmail)) {
                    mShopping = true;
                    mButtonShopping.setText(getString(R.string.button_stop_shopping));
                    mButtonShopping.setBackgroundColor(ContextCompat.getColor(ActiveListDetailsActivity.this, R.color.dark_grey));
                } else {
                    mButtonShopping.setText(getString(R.string.button_start_shopping));
                    mButtonShopping.setBackgroundColor(ContextCompat.getColor(ActiveListDetailsActivity.this, R.color.primary_dark));
                    mShopping = false;

                }

                setWhosShoppingText(mShoppingList.getUsersShopping());

            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });

        mSharedWithListener = mSharedWithRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                mSharedWithUsers = new HashMap<String, User>();
                for (DataSnapshot currentUser : dataSnapshot.getChildren()) {
                    mSharedWithUsers.put(currentUser.getKey(), currentUser.getValue(User.class));
                }
                mActiveListItemAdapter.setSharedWithUsers(mSharedWithUsers);
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });

        /**
         * Set up click listeners for interaction.
         */

        /* Show edit list item name dialog on listView item long click event */
        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {

            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                /* Check that the view is not the empty footer item */
                if (view.getId() != R.id.list_view_footer_empty) {
                    ShoppingListItem shoppingListItem = mActiveListItemAdapter.getItem(position);

                    if (shoppingListItem != null) {
                        /*
                        If the person is the owner and not shopping and the item is not bought, then
                        they can edit it.
                         */
                        if (shoppingListItem.getOwner().equals(mEncodedEmail) && !mShopping && !shoppingListItem.isBought()) {
                            String itemName = shoppingListItem.getItemName();
                            String itemId = mActiveListItemAdapter.getRef(position).getKey();
                            showEditListItemNameDialog(itemName, itemId);
                            return true;
                        }
                    }
                }
                return false;
            }
        });

        /* Perform buy/return action on listView item click event if current user is shopping. */
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                /* Check that the view is not the empty footer item */
                if (view.getId() != R.id.list_view_footer_empty) {
                    final ShoppingListItem selectedListItem = mActiveListItemAdapter.getItem(position);
                    String itemId = mActiveListItemAdapter.getRef(position).getKey();

                    if (selectedListItem != null) {
                        /* If current user is shopping */
                        if (mShopping) {

                            /* Create map and fill it in with deep path multi write operations list */
                            HashMap<String, Object> updatedItemBoughtData = new HashMap<String, Object>();

                            /* Buy selected item if it is NOT already bought */
                            if (!selectedListItem.isBought()) {
                                updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT, true);
                                updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT_BY, mEncodedEmail);
                            } else {
                                /* Return selected item only if it was bought by current user */
                                if (selectedListItem.getBoughtBy().equals(mEncodedEmail)) {
                                    updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT, false);
                                    updatedItemBoughtData.put(Constants.FIREBASE_PROPERTY_BOUGHT_BY, null);
                                }
                            }

                            /* Do update */
                            Firebase firebaseItemLocation = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS)
                                    .child(mListId).child(itemId);
                            firebaseItemLocation.updateChildren(updatedItemBoughtData, new Firebase.CompletionListener() {
                                @Override
                                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                                    if (firebaseError != null) {
                                        Log.d(LOG_TAG, getString(R.string.log_error_updating_data) +
                                                firebaseError.getMessage());
                                    }
                                }
                            });
                        }
                    }
                }
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        /* Inflate the menu; this adds items to the action bar if it is present. */
        getMenuInflater().inflate(R.menu.menu_list_details, menu);

        /**
         * Get menu items
         */
        MenuItem remove = menu.findItem(R.id.action_remove_list);
        MenuItem edit = menu.findItem(R.id.action_edit_list_name);
        MenuItem share = menu.findItem(R.id.action_share_list);
        MenuItem archive = menu.findItem(R.id.action_archive);

        /* Only the edit and remove options are implemented */
        remove.setVisible(mCurrentUserIsOwner);
        edit.setVisible(mCurrentUserIsOwner);
        share.setVisible(mCurrentUserIsOwner);
        archive.setVisible(false);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        /**
         * Show edit list dialog when the edit action is selected
         */
        if (id == R.id.action_edit_list_name) {
            showEditListNameDialog();
            return true;
        }

        /**
         * removeList() when the remove action is selected
         */
        if (id == R.id.action_remove_list) {
            removeList();
            return true;
        }

        /**
         * Eventually we'll add this
         */
        if (id == R.id.action_share_list) {
            Intent intent = new Intent(ActiveListDetailsActivity.this, ShareListActivity.class);
            intent.putExtra(Constants.KEY_LIST_ID, mListId);
            /* Starts an active showing the details for the selected list */
            startActivity(intent);
            return true;
        }

        /**
         * archiveList() when the archive action is selected
         */
        if (id == R.id.action_archive) {
            archiveList();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }


    /**
     * Cleanup when the activity is destroyed.
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        mActiveListItemAdapter.cleanup();
        mCurrentListRef.removeEventListener(mCurrentListRefListener);
        mCurrentUserRef.removeEventListener(mCurrentUserRefListener);
        mSharedWithRef.removeEventListener(mSharedWithListener);
    }

    /**
     * Link layout elements from XML and setup the toolbar
     */
    private void initializeScreen() {
        mListView = (ListView) findViewById(R.id.list_view_shopping_list_items);
        mTextViewPeopleShopping = (TextView) findViewById(R.id.text_view_people_shopping);
        mButtonShopping = (Button) findViewById(R.id.button_shopping);
        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
        /* Common toolbar setup */
        setSupportActionBar(toolbar);
        /* Add back button to the action bar */
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
        /* Inflate the footer, set root layout to null*/
        View footer = getLayoutInflater().inflate(R.layout.footer_empty, null);
        mListView.addFooterView(footer);
    }

    /**
     * Set appropriate text for Start/Stop shopping button and Who's shopping textView
     * depending on the current user shopping status
     */
    private void setWhosShoppingText(HashMap<String, User> usersShopping) {

        if (usersShopping != null) {
            ArrayList<String> usersWhoAreNotYou = new ArrayList<>();
            /**
             * If at least one user is shopping
             * Add userName to the list of users shopping if this user is not current user
             */
            for (User user : usersShopping.values()) {
                if (user != null && !(user.getEmail().equals(mEncodedEmail))) {
                    usersWhoAreNotYou.add(user.getName());
                }
            }

            int numberOfUsersShopping = usersShopping.size();
            String usersShoppingText;

            /**
             * If current user is shopping...
             * If current user is the only person shopping, set text to "You are shopping"
             * If current user and one user are shopping, set text "You and userName are shopping"
             * Else set text "You and N others shopping"
             */
            if (mShopping) {
                switch (numberOfUsersShopping) {
                    case 1:
                        usersShoppingText = getString(R.string.text_you_are_shopping);
                        break;
                    case 2:
                        usersShoppingText = String.format(
                                getString(R.string.text_you_and_other_are_shopping),
                                usersWhoAreNotYou.get(0));
                        break;
                    default:
                        usersShoppingText = String.format(
                                getString(R.string.text_you_and_number_are_shopping),
                                usersWhoAreNotYou.size());
                }
                /**
                 * If current user is not shopping..
                 * If there is only one person shopping, set text to "userName is shopping"
                 * If there are two users shopping, set text "userName1 and userName2 are shopping"
                 * Else set text "userName and N others shopping"
                 */
            } else {
                switch (numberOfUsersShopping) {
                    case 1:
                        usersShoppingText = String.format(
                                getString(R.string.text_other_is_shopping),
                                usersWhoAreNotYou.get(0));
                        break;
                    case 2:
                        usersShoppingText = String.format(
                                getString(R.string.text_other_and_other_are_shopping),
                                usersWhoAreNotYou.get(0),
                                usersWhoAreNotYou.get(1));
                        break;
                    default:
                        usersShoppingText = String.format(
                                getString(R.string.text_other_and_number_are_shopping),
                                usersWhoAreNotYou.get(0),
                                usersWhoAreNotYou.size() - 1);
                }
            }
            mTextViewPeopleShopping.setText(usersShoppingText);
        } else {
            mTextViewPeopleShopping.setText("");
        }
    }


    /**
     * Archive current list when user selects "Archive" menu item
     */
    public void archiveList() {
    }


    /**
     * Start AddItemsFromMealActivity to add meal ingredients into the shopping list
     * when the user taps on "add meal" fab
     */
    public void addMeal(View view) {
    }

    /**
     * Remove current shopping list and its items from all nodes
     */
    public void removeList() {
        /* Create an instance of the dialog fragment and show it */
        DialogFragment dialog = RemoveListDialogFragment.newInstance(mShoppingList, mListId,
                mSharedWithUsers);
        dialog.show(getFragmentManager(), "RemoveListDialogFragment");
    }

    /**
     * Show the add list item dialog when user taps "Add list item" fab
     */
    public void showAddListItemDialog(View view) {
        /* Create an instance of the dialog fragment and show it */
        DialogFragment dialog = AddListItemDialogFragment.newInstance(mShoppingList, mListId,
                mEncodedEmail, mSharedWithUsers);
        dialog.show(getFragmentManager(), "AddListItemDialogFragment");
    }

    /**
     * Show edit list name dialog when user selects "Edit list name" menu item
     */
    public void showEditListNameDialog() {
        /* Create an instance of the dialog fragment and show it */
        DialogFragment dialog = EditListNameDialogFragment.newInstance(mShoppingList, mListId,
                mEncodedEmail, mSharedWithUsers);
        dialog.show(this.getFragmentManager(), "EditListNameDialogFragment");
    }

    /**
     * Show the edit list item name dialog after longClick on the particular item
     *
     * @param itemName
     * @param itemId
     */
    public void showEditListItemNameDialog(String itemName, String itemId) {
        /* Create an instance of the dialog fragment and show it */
        DialogFragment dialog = EditListItemNameDialogFragment.newInstance(mShoppingList, itemName,
                itemId, mListId, mEncodedEmail, mSharedWithUsers);

        dialog.show(this.getFragmentManager(), "EditListItemNameDialogFragment");
    }

    /**
     * This method is called when user taps "Start/Stop shopping" button
     */
    public void toggleShopping(View view) {
        /**
         * Create map and fill it in with deep path multi write operations list
         */
        HashMap<String, Object> updatedUserData = new HashMap<String, Object>();
        String propertyToUpdate = Constants.FIREBASE_PROPERTY_USERS_SHOPPING + "/" + mEncodedEmail;

        /**
         * If current user is already shopping, remove current user from usersShopping map
         */
        if (mShopping) {

            /* Add the value to update at the specified property for all lists */
            Utils.updateMapForAllWithValue(mSharedWithUsers,
                    mListId, mShoppingList.getOwner(), updatedUserData,
                    propertyToUpdate, null);

            /* Appends the timestamp changes for all lists */
            Utils.updateMapWithTimestampLastChanged(mSharedWithUsers,
                    mListId, mShoppingList.getOwner(), updatedUserData);


            /* Do a deep-path update */
            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    /* Updates the reversed timestamp */
                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId, mSharedWithUsers,
                            mShoppingList.getOwner());
                }
            });
        } else {
            /**
             * If current user is not shopping, create map to represent User model add to usersShopping map
             */
            HashMap<String, Object> currentUser = (HashMap<String, Object>)
                    new ObjectMapper().convertValue(mCurrentUser, Map.class);

            /* Add the value to update at the specified property for all lists */
            Utils.updateMapForAllWithValue(mSharedWithUsers,
                    mListId, mShoppingList.getOwner(), updatedUserData, propertyToUpdate, currentUser);

            /* Appends the timestamp changes for all lists */
            Utils.updateMapWithTimestampLastChanged(mSharedWithUsers,
                    mListId, mShoppingList.getOwner(), updatedUserData);

            /* Do a deep-path update */
            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    /* Updates the reversed timestamp */
                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId, mSharedWithUsers,
                            mShoppingList.getOwner());
                }
            });

        }
    }

}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Activity;
import android.content.DialogInterface;
import android.graphics.Paint;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;

import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.Query;
import com.firebase.client.ValueEventListener;
import com.firebase.ui.FirebaseListAdapter;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;


/**
 * Populates list_view_shopping_list_items inside ActiveListDetailsActivity
 */
public class ActiveListItemAdapter extends FirebaseListAdapter<ShoppingListItem> {
    private ShoppingList mShoppingList;
    private String mListId;
    private String mEncodedEmail;
    private HashMap<String, User> mSharedWithUsers;

    /**
     * Public constructor that initializes private instance variables when adapter is created
     */
    public ActiveListItemAdapter(Activity activity, Class<ShoppingListItem> modelClass, int modelLayout,
                                 Query ref, String listId, String encodedEmail) {
        super(activity, modelClass, modelLayout, ref);
        this.mActivity = activity;
        this.mListId = listId;
        this.mEncodedEmail = encodedEmail;
    }

    /**
     * Public method that is used to pass shoppingList object when it is loaded in ValueEventListener
     */
    public void setShoppingList(ShoppingList shoppingList) {
        this.mShoppingList = shoppingList;
        this.notifyDataSetChanged();
    }

    public void setSharedWithUsers(HashMap<String, User> sharedWithUsers) {
        this.mSharedWithUsers = sharedWithUsers;
        this.notifyDataSetChanged();
    }

    /**
     * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete)
     * with items inflated from single_active_list_item.xml
     * populateView also handles data changes and updates the listView accordingly
     */
    @Override
    protected void populateView(View view, final ShoppingListItem item, int position) {

        ImageButton buttonRemoveItem = (ImageButton) view.findViewById(R.id.button_remove_item);
        TextView textViewItemName = (TextView) view.findViewById(R.id.text_view_active_list_item_name);
        final TextView textViewBoughtByUser = (TextView) view.findViewById(R.id.text_view_bought_by_user);
        TextView textViewBoughtBy = (TextView) view.findViewById(R.id.text_view_bought_by);

        String owner = item.getOwner();

        textViewItemName.setText(item.getItemName());


        setItemAppearanceBaseOnBoughtStatus(owner, textViewBoughtByUser, textViewBoughtBy, buttonRemoveItem,
                textViewItemName, item);


        /* Gets the id of the item to remove */
        final String itemToRemoveId = this.getRef(position).getKey();

        /**
         * Set the on click listener for "Remove list item" button
         */
        buttonRemoveItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mActivity, R.style.CustomTheme_Dialog)
                        .setTitle(mActivity.getString(R.string.remove_item_option))
                        .setMessage(mActivity.getString(R.string.dialog_message_are_you_sure_remove_item))
                        .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {

                                removeItem(itemToRemoveId);
                                /* Dismiss the dialog */
                                dialog.dismiss();
                            }
                        })
                        .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                /* Dismiss the dialog */
                                dialog.dismiss();
                            }
                        })
                        .setIcon(android.R.drawable.ic_dialog_alert);

                AlertDialog alertDialog = alertDialogBuilder.create();
                alertDialog.show();
            }
        });
    }

    private void removeItem(String itemId) {
        Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);

        /* Make a map for the removal */
        HashMap<String, Object> updatedRemoveItemMap = new HashMap<String, Object>();

        /* Remove the item by passing null */
        updatedRemoveItemMap.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/"
                + mListId + "/" + itemId, null);

        /* Add the updated timestamp */
        Utils.updateMapWithTimestampLastChanged(mSharedWithUsers,
                mListId, mShoppingList.getOwner(), updatedRemoveItemMap);

        /* Do the update */
        firebaseRef.updateChildren(updatedRemoveItemMap, new Firebase.CompletionListener() {
            @Override
            public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                Utils.updateTimestampReversed(firebaseError, "ActListItemAdap", mListId,
                        mSharedWithUsers, mShoppingList.getOwner());
            }
        });
    }

    private void setItemAppearanceBaseOnBoughtStatus(String owner, final TextView textViewBoughtByUser,
                                                     TextView textViewBoughtBy, ImageButton buttonRemoveItem,
                                                     TextView textViewItemName, ShoppingListItem item) {
        /**
         * If selected item is bought
         * Set "Bought by" text to "You" if current user is owner of the list
         * Set "Bought by" text to userName if current user is NOT owner of the list
         * Set the remove item button invisible if current user is NOT list or item owner
         */
        if (item.isBought() && item.getBoughtBy() != null) {

            textViewBoughtBy.setVisibility(View.VISIBLE);
            textViewBoughtByUser.setVisibility(View.VISIBLE);
            buttonRemoveItem.setVisibility(View.INVISIBLE);

            /* Add a strike-through */
            textViewItemName.setPaintFlags(textViewItemName.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);

            if (item.getBoughtBy().equals(mEncodedEmail)) {
                textViewBoughtByUser.setText(mActivity.getString(R.string.text_you));
            } else {

                Firebase boughtByUserRef = new Firebase(Constants.FIREBASE_URL_USERS).child(item.getBoughtBy());
                /* Get the item's owner's name; use a SingleValueEvent listener for memory efficiency */
                boughtByUserRef.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        User user = dataSnapshot.getValue(User.class);
                        if (user != null) {
                            textViewBoughtByUser.setText(user.getName());
                        }
                    }

                    @Override
                    public void onCancelled(FirebaseError firebaseError) {
                        Log.e(mActivity.getClass().getSimpleName(),
                                mActivity.getString(R.string.log_error_the_read_failed) +
                                        firebaseError.getMessage());
                    }
                });
            }
        } else {
            /**
             * If selected item is NOT bought
             * Set "Bought by" text to be empty and invisible
             * Set the remove item button visible if current user is owner of the list or selected item
             */

            /* Remove the strike-through */
            textViewItemName.setPaintFlags(textViewItemName.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));

            textViewBoughtBy.setVisibility(View.INVISIBLE);
            textViewBoughtByUser.setVisibility(View.INVISIBLE);
            textViewBoughtByUser.setText("");
            /**
             * If you are the owner of the item or the owner of the list, then the remove icon
             * is visible.
             */
            if (owner.equals(mEncodedEmail) || (mShoppingList != null && mShoppingList.getOwner().equals(mEncodedEmail))) {
                buttonRemoveItem.setVisibility(View.VISIBLE);
            } else {
                buttonRemoveItem.setVisibility(View.INVISIBLE);
            }
        }
    }
}

================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Dialog;
import android.os.Bundle;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingListItem;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;
import java.util.Map;

/**
 * Lets user add new list item.
 */
public class AddListItemDialogFragment extends EditListDialogFragment {

    /**
     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created
     */
    public static AddListItemDialogFragment newInstance(ShoppingList shoppingList, String listId,
                                                        String encodedEmail,
                                                        HashMap<String, User> sharedWithUsers) {
        AddListItemDialogFragment addListItemDialogFragment = new AddListItemDialogFragment();
        Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList,
                R.layout.dialog_add_item, listId, encodedEmail, sharedWithUsers);
        addListItemDialogFragment.setArguments(bundle);

        return addListItemDialogFragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        /** {@link EditListDialogFragment#createDialogHelper(int)} is a
         * superclass method that creates the dialog
         **/
        return super.createDialogHelper(R.string.positive_button_add_list_item);
    }

    /**
     * Adds new item to the current shopping list
     */
    @Override
    protected void doListEdit() {
        String mItemName = mEditTextForList.getText().toString();
        /**
         * Adds list item if the input name is not empty
         */
        if (!mItemName.equals("")) {

            Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);
            Firebase itemsRef = new Firebase(Constants.FIREBASE_URL_SHOPPING_LIST_ITEMS).child(mListId);

            /* Make a map for the item you are adding */
            HashMap<String, Object> updatedItemToAddMap = new HashMap<String, Object>();

            /* Save push() to maintain same random Id */
            Firebase newRef = itemsRef.push();
            String itemId = newRef.getKey();

            /* Make a POJO for the item and immediately turn it into a HashMap */
            ShoppingListItem itemToAddObject = new ShoppingListItem(mItemName, mEncodedEmail);
            HashMap<String, Object> itemToAdd =
                    (HashMap<String, Object>) new ObjectMapper().convertValue(itemToAddObject, Map.class);


            /* Add the item to the update map*/
            updatedItemToAddMap.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/"
                    + mListId + "/" + itemId, itemToAdd);

            /* Update affected lists timestamps */
            Utils.updateMapWithTimestampLastChanged(mSharedWith,
                    mListId, mOwner, updatedItemToAddMap);

            /* Do the update */
            firebaseRef.updateChildren(updatedItemToAddMap, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    /* Now that we have the timestamp, update the reversed timestamp */
                    Utils.updateTimestampReversed(firebaseError, "AddListItem", mListId,
                            mSharedWith, mOwner);
                }
            });

            /**
             * Close the dialog fragment when done
             */
            AddListItemDialogFragment.this.getDialog().cancel();
        }
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;

import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

import java.util.HashMap;

/**
 * Base class for {@link DialogFragment}s involved with editing a shopping list.
 */
public abstract class EditListDialogFragment extends DialogFragment {
    String mListId, mOwner, mEncodedEmail;
    EditText mEditTextForList;
    int mResource;
    HashMap mSharedWith;

    /**
     * Helper method that creates a basic bundle of all of the information needed to change
     * values in a shopping list.
     *
     * @param shoppingList The shopping list that the dialog is editing
     * @param resource The xml layout file associated with the dialog
     * @param listId The id of the shopping list the dialog is editing
     * @param encodedEmail The encoded email of the current user
     * @param sharedWithUsers The HashMap containing all users that the current shopping list
     *                        is shared with
     * @return The bundle containing all the arguments.
     */
    protected static Bundle newInstanceHelper(ShoppingList shoppingList, int resource, String listId,
                                              String encodedEmail, HashMap<String, User> sharedWithUsers) {
        Bundle bundle = new Bundle();
        bundle.putSerializable(Constants.KEY_SHARED_WITH_USERS, sharedWithUsers);
        bundle.putString(Constants.KEY_LIST_ID, listId);
        bundle.putInt(Constants.KEY_LAYOUT_RESOURCE, resource);
        bundle.putString(Constants.KEY_LIST_OWNER, shoppingList.getOwner());
        bundle.putString(Constants.KEY_ENCODED_EMAIL, encodedEmail);
        return bundle;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSharedWith = (HashMap) getArguments().getSerializable(Constants.KEY_SHARED_WITH_USERS);
        mListId = getArguments().getString(Constants.KEY_LIST_ID);
        mResource = getArguments().getInt(Constants.KEY_LAYOUT_RESOURCE);
        mOwner = getArguments().getString(Constants.KEY_LIST_OWNER);
        mEncodedEmail = getArguments().getString(Constants.KEY_ENCODED_EMAIL);
    }

    /**
     * Open the keyboard automatically when the dialog fragment is opened
     */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    }

    protected Dialog createDialogHelper(int stringResourceForPositiveButton) {
        /* Use the Builder class for convenient dialog construction */
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog);
        /* Get the layout inflater */
        LayoutInflater inflater = getActivity().getLayoutInflater();
        /* Inflate the layout, set root ViewGroup to null*/
        View rootView = inflater.inflate(mResource, null);
        mEditTextForList = (EditText) rootView.findViewById(R.id.edit_text_list_dialog);

        /**
         * Call doListEdit() when user taps "Done" keyboard action
         */
        mEditTextForList.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                    doListEdit();

                    /**
                     * Close the dialog fragment when done
                     */
                    EditListDialogFragment.this.getDialog().cancel();
                }
                return true;
            }
        });
        /* Inflate and set the layout for the dialog */
        /* Pass null as the parent view because its going in the dialog layout */
        builder.setView(rootView)
                /* Add action buttons */
                .setPositiveButton(stringResourceForPositiveButton, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        doListEdit();

                        /**
                         * Close the dialog fragment
                         */
                        EditListDialogFragment.this.getDialog().cancel();
                    }
                })
                .setNegativeButton(R.string.negative_button_cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {

                        /**
                         * Close the dialog fragment
                         */
                        EditListDialogFragment.this.getDialog().cancel();
                    }
                });

        return builder.create();
    }

    /**
     * Set the EditText text to be the inputted text
     * and put the pointer at the end of the input
     *
     * @param defaultText
     */
    protected void helpSetDefaultValueEditText(String defaultText) {
        mEditTextForList.setText(defaultText);
        mEditTextForList.setSelection(defaultText.length());
    }

    /**
     * Method to be overriden with whatever edit is supposed to happen to the list
     */
    protected abstract void doListEdit();
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Dialog;
import android.os.Bundle;

import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;

/**
 * Lets user edit list item name for all copies of the current list
 */
public class EditListItemNameDialogFragment extends EditListDialogFragment {
    String mItemName, mItemId;

    /**
     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created
     */
    public static EditListItemNameDialogFragment newInstance(ShoppingList shoppingList, String itemName,
                                                             String itemId, String listId, String encodedEmail,
                                                             HashMap<String, User> sharedWithUsers) {
        EditListItemNameDialogFragment editListItemNameDialogFragment = new EditListItemNameDialogFragment();

        Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList, R.layout.dialog_edit_item,
                listId, encodedEmail, sharedWithUsers);
        bundle.putString(Constants.KEY_LIST_ITEM_NAME, itemName);
        bundle.putString(Constants.KEY_LIST_ITEM_ID, itemId);
        editListItemNameDialogFragment.setArguments(bundle);

        return editListItemNameDialogFragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mItemName = getArguments().getString(Constants.KEY_LIST_ITEM_NAME);
        mItemId = getArguments().getString(Constants.KEY_LIST_ITEM_ID);
    }


    @Override

    public Dialog onCreateDialog(Bundle savedInstanceState) {
        /** {@link EditListDialogFragment#createDialogHelper(int)} is a
         * superclass method that creates the dialog
         */
        Dialog dialog = super.createDialogHelper(R.string.positive_button_edit_item);
        /**
         * {@link EditListDialogFragment#helpSetDefaultValueEditText(String)} is a superclass
         * method that sets the default text of the TextView
         */
        super.helpSetDefaultValueEditText(mItemName);

        return dialog;
    }

    /**
     * Change selected list item name to the editText input if it is not empty
     */
    protected void doListEdit() {
        String nameInput = mEditTextForList.getText().toString();

        /**
         * Set input text to be the current list item name if it is not empty and is not the
         * previous name.
         */
        if (!nameInput.equals("") && !nameInput.equals(mItemName)) {
            Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);

            /* Make a map for the item you are changing the name of */
            HashMap<String, Object> updatedDataItemToEditMap = new HashMap<String, Object>();

            /* Add the new name to the update map*/
            updatedDataItemToEditMap.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/"
                            + mListId + "/" + mItemId + "/" + Constants.FIREBASE_PROPERTY_ITEM_NAME,
                    nameInput);

            /* Update affected lists timestamps */
            Utils.updateMapWithTimestampLastChanged(mSharedWith, mListId, mOwner, updatedDataItemToEditMap);

            /* Do the update */
            firebaseRef.updateChildren(updatedDataItemToEditMap, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                        /* Now that we have the timestamp, update the reversed timestamp */
                    Utils.updateTimestampReversed(firebaseError, "EditListItem", mListId,
                            mSharedWith, mOwner);
                }
            });


        }
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Dialog;
import android.os.Bundle;

import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;

/**
 * Lets user edit the list name for all copies of the current list
 */
public class EditListNameDialogFragment extends EditListDialogFragment {
    private static final String LOG_TAG = ActiveListDetailsActivity.class.getSimpleName();
    String mListName;

    /**
     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created
     */
    public static EditListNameDialogFragment newInstance(ShoppingList shoppingList, String listId,
                                                         String encodedEmail,
                                                         HashMap<String, User> sharedWithUsers) {
        EditListNameDialogFragment editListNameDialogFragment = new EditListNameDialogFragment();
        Bundle bundle = EditListDialogFragment.newInstanceHelper(shoppingList,
                R.layout.dialog_edit_list, listId, encodedEmail, sharedWithUsers);
        bundle.putString(Constants.KEY_LIST_NAME, shoppingList.getListName());
        editListNameDialogFragment.setArguments(bundle);
        return editListNameDialogFragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mListName = getArguments().getString(Constants.KEY_LIST_NAME);
    }


    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        /** {@link EditListDialogFragment#createDialogHelper(int)} is a
         * superclass method that creates the dialog
         **/
        Dialog dialog = super.createDialogHelper(R.string.positive_button_edit_item);
        /**
         * {@link EditListDialogFragment#helpSetDefaultValueEditText(String)} is a superclass
         * method that sets the default text of the TextView
         */
        helpSetDefaultValueEditText(mListName);
        return dialog;
    }

    /**
     * Changes the list name in all copies of the current list
     */
    protected void doListEdit() {
        final String inputListName = mEditTextForList.getText().toString();
        /**
         * Check that the user inputted list name is not empty, has changed the original name
         * and that the dialog was properly initialized with the current name and id of the list.
         */
        if (!inputListName.equals("") && mListName != null &&
                mListId != null && !inputListName.equals(mListName)) {

            Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);

            /**
             * Create map and fill it in with deep path multi write operations list
             */
            HashMap<String, Object> updatedListData = new HashMap<String, Object>();

            /* Add the value to update at the specified property for all lists */
            Utils.updateMapForAllWithValue(mSharedWith, mListId, mOwner, updatedListData,
                    Constants.FIREBASE_PROPERTY_LIST_NAME, inputListName);

            /* Update affected lists timestamps */
            Utils.updateMapWithTimestampLastChanged(mSharedWith, mListId, mOwner, updatedListData);

            /* Do a deep-path update */
            firebaseRef.updateChildren(updatedListData, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    /* Now that we have the timestamp, update the reversed timestamp */
                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId,
                            mSharedWith, mOwner);
                }
            });

        }
    }
}



================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;

import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.util.Log;

import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;

/**
 * Lets the user remove active shopping list
 */
public class RemoveListDialogFragment extends DialogFragment {
    String mListId;
    String mListOwner;
    HashMap mSharedWith;

    final static String LOG_TAG = RemoveListDialogFragment.class.getSimpleName();

    /**
     * Public static constructor that creates fragment and passes a bundle with data into it when adapter is created
     */
    public static RemoveListDialogFragment newInstance(ShoppingList shoppingList, String listId,
                                                       HashMap<String, User> sharedWithUsers) {
        RemoveListDialogFragment removeListDialogFragment = new RemoveListDialogFragment();
        Bundle bundle = new Bundle();
        bundle.putString(Constants.KEY_LIST_ID, listId);
        bundle.putString(Constants.KEY_LIST_OWNER, shoppingList.getOwner());
        bundle.putSerializable(Constants.KEY_SHARED_WITH_USERS, sharedWithUsers);
        removeListDialogFragment.setArguments(bundle);
        return removeListDialogFragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mListId = getArguments().getString(Constants.KEY_LIST_ID);
        mListOwner = getArguments().getString(Constants.KEY_LIST_OWNER);
        mSharedWith = (HashMap) getArguments().getSerializable(Constants.KEY_SHARED_WITH_USERS);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog)
                .setTitle(getActivity().getResources().getString(R.string.action_remove_list))
                .setMessage(getString(R.string.dialog_message_are_you_sure_remove_list))
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        removeList();
                        /* Dismiss the dialog */
                        dialog.dismiss();
                    }
                })
                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        /* Dismiss the dialog */
                        dialog.dismiss();
                    }
                })
                .setIcon(android.R.drawable.ic_dialog_alert);

        return builder.create();
    }

    private void removeList() {
        Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);

        /**
         * Create map and fill it in with deep path multi write operations list
         */
        HashMap<String, Object> removeListData = new HashMap<String, Object>();

        /* Remove the ShoppingLists from both user lists and active lists */
        Utils.updateMapForAllWithValue(mSharedWith, mListId, mListOwner, removeListData, "", null);

        /* Remove the associated list items */
        removeListData.put("/" + Constants.FIREBASE_LOCATION_SHOPPING_LIST_ITEMS + "/" + mListId,
                null);

        removeListData.put("/" + Constants.FIREBASE_LOCATION_OWNER_MAPPINGS + "/" + mListId,
                null);
        /* Do a deep-path update */
        firebaseRef.updateChildren(removeListData, new Firebase.CompletionListener() {
            @Override
            public void onComplete(FirebaseError firebaseError, Firebase firebase) {

                if (firebaseError != null) {
                    Log.e(LOG_TAG, getString(R.string.log_error_updating_data) + firebaseError.getMessage());
                }
            }
        });
    }

}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.Query;
import com.firebase.client.ValueEventListener;
import com.firebase.ui.FirebaseListAdapter;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

/**
 * Populates the list_view_active_lists inside ShoppingListsFragment
 */
public class ActiveListAdapter extends FirebaseListAdapter<ShoppingList> {
    private String mEncodedEmail;

    /**
     * Public constructor that initializes private instance variables when adapter is created
     */
    public ActiveListAdapter(Activity activity, Class<ShoppingList> modelClass, int modelLayout,
                             Query ref, String encodedEmail) {
        super(activity, modelClass, modelLayout, ref);
        this.mEncodedEmail = encodedEmail;
        this.mActivity = activity;
    }

    /**
     * Protected method that populates the view attached to the adapter (list_view_active_lists)
     * with items inflated from single_active_list.xml
     * populateView also handles data changes and updates the listView accordingly
     */
    @Override
    protected void populateView(View view, ShoppingList list) {

        /**
         * Grab the needed Textivews and strings
         */
        TextView textViewListName = (TextView) view.findViewById(R.id.text_view_list_name);
        final TextView textViewCreatedByUser = (TextView) view.findViewById(R.id.text_view_created_by_user);
        final TextView textViewUsersShopping = (TextView) view.findViewById(R.id.text_view_people_shopping_count);

        String ownerEmail = list.getOwner();

        /* Set the list name and owner */
        textViewListName.setText(list.getListName());

        /**
         * Show "1 person is shopping" if one person is shopping
         * Show "N people shopping" if two or more users are shopping
         * Show nothing if nobody is shopping
         */
        if (list.getUsersShopping() != null) {
            int usersShopping = list.getUsersShopping().size();
            if (usersShopping == 1) {
                textViewUsersShopping.setText(String.format(
                        mActivity.getResources().getString(R.string.person_shopping),
                        usersShopping));
            } else {
                textViewUsersShopping.setText(String.format(
                        mActivity.getResources().getString(R.string.people_shopping),
                        usersShopping));
            }
        } else {
            /* otherwise show nothing */
            textViewUsersShopping.setText("");
        }

        /**
         * Set "Created by" text to "You" if current user is owner of the list
         * Set "Created by" text to userName if current user is NOT owner of the list
         */
        if (ownerEmail != null) {
            if (ownerEmail.equals(mEncodedEmail)) {
                textViewCreatedByUser.setText(mActivity.getResources().getString(R.string.text_you));
            } else {
                Firebase userRef = new Firebase(Constants.FIREBASE_URL_USERS).child(ownerEmail);
                /* Get the user's name */
                userRef.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        User user = dataSnapshot.getValue(User.class);

                        if (user != null) {
                            textViewCreatedByUser.setText(user.getName());
                        }
                    }

                    @Override
                    public void onCancelled(FirebaseError firebaseError) {
                        Log.e(mActivity.getClass().getSimpleName(),
                                mActivity.getString(R.string.log_error_the_read_failed) +
                                        firebaseError.getMessage());
                    }
                });
            }
        }

    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;

import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ServerValue;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;
import java.util.Map;

/**
 * Adds a new shopping list
 */
public class AddListDialogFragment extends DialogFragment {
    String mEncodedEmail;
    EditText mEditTextListName;

    /**
     * Public static constructor that creates fragment and
     * passes a bundle with data into it when adapter is created
     */
    public static AddListDialogFragment newInstance(String encodedEmail) {
        AddListDialogFragment addListDialogFragment = new AddListDialogFragment();
        Bundle bundle = new Bundle();
        bundle.putString(Constants.KEY_ENCODED_EMAIL, encodedEmail);
        addListDialogFragment.setArguments(bundle);
        return addListDialogFragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mEncodedEmail = getArguments().getString(Constants.KEY_ENCODED_EMAIL);
    }

    /**
     * Open the keyboard automatically when the dialog fragment is opened
     */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog);
        // Get the layout inflater
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View rootView = inflater.inflate(R.layout.dialog_add_list, null);
        mEditTextListName = (EditText) rootView.findViewById(R.id.edit_text_list_name);

        /**
         * Call addShoppingList() when user taps "Done" keyboard action
         */
        mEditTextListName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                    addShoppingList();
                }
                return true;
            }
        });

        /* Inflate and set the layout for the dialog */
        /* Pass null as the parent view because its going in the dialog layout*/
        builder.setView(rootView)
                /* Add action buttons */
                .setPositiveButton(R.string.positive_button_create, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        addShoppingList();
                    }
                });

        return builder.create();
    }

    /**
     * Add new active list
     */
    public void addShoppingList() {
        String userEnteredName = mEditTextListName.getText().toString();

        /**
         * If EditText input is not empty
         */
        if (!userEnteredName.equals("")) {

            /**
             * Create Firebase references
             */
            Firebase userListsRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS).
                    child(mEncodedEmail);
            final Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);

            Firebase newListRef = userListsRef.push();

            /* Save listsRef.push() to maintain same random Id */
            final String listId = newListRef.getKey();

            /* HashMap for data to update */
            HashMap<String, Object> updateShoppingListData = new HashMap<>();

            /**
             * Set raw version of date to the ServerValue.TIMESTAMP value and save into
             * timestampCreatedMap
             */
            HashMap<String, Object> timestampCreated = new HashMap<>();
            timestampCreated.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);

            /* Build the shopping list */
            ShoppingList newShoppingList = new ShoppingList(userEnteredName, mEncodedEmail,
                    timestampCreated);

            HashMap<String, Object> shoppingListMap = (HashMap<String, Object>)
                    new ObjectMapper().convertValue(newShoppingList, Map.class);

            Utils.updateMapForAllWithValue(null, listId, mEncodedEmail,
                    updateShoppingListData, "", shoppingListMap);

            updateShoppingListData.put("/" + Constants.FIREBASE_LOCATION_OWNER_MAPPINGS + "/" + listId,
                    mEncodedEmail);

            /* Do the update */
            firebaseRef.updateChildren(updateShoppingListData, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    /* Now that we have the timestamp, update the reversed timestamp */
                    Utils.updateTimestampReversed(firebaseError, "AddList", listId,
                            null, mEncodedEmail);
                }
            });

            /* Close the dialog fragment */
            AddListDialogFragment.this.getDialog().cancel();
        }
    }
}



================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;


import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;

import com.firebase.client.Firebase;
import com.firebase.client.Query;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails.ActiveListDetailsActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;


/**
 * A simple {@link Fragment} subclass that shows a list of all shopping lists a user can see.
 * Use the {@link ShoppingListsFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class ShoppingListsFragment extends Fragment {
    private String mEncodedEmail;
    private ActiveListAdapter mActiveListAdapter;
    private ListView mListView;

    public ShoppingListsFragment() {
        /* Required empty public constructor */
    }

    /**
     * Create fragment and pass bundle with data as it's arguments
     * Right now there are not arguments...but eventually there will be.
     */
    public static ShoppingListsFragment newInstance(String encodedEmail) {
        ShoppingListsFragment fragment = new ShoppingListsFragment();
        Bundle args = new Bundle();
        args.putString(Constants.KEY_ENCODED_EMAIL, encodedEmail);
        fragment.setArguments(args);
        return fragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mEncodedEmail = getArguments().getString(Constants.KEY_ENCODED_EMAIL);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        /**
         * Initialize UI elements
         */
        View rootView = inflater.inflate(R.layout.fragment_shopping_lists, container, false);
        initializeScreen(rootView);

        /**
         * Set interactive bits, such as click events and adapters
         */
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                ShoppingList selectedList = mActiveListAdapter.getItem(position);
                if (selectedList != null) {
                    Intent intent = new Intent(getActivity(), ActiveListDetailsActivity.class);
                    /* Get the list ID using the adapter's get ref method to get the Firebase
                     * ref and then grab the key.
                     */
                    String listId = mActiveListAdapter.getRef(position).getKey();
                    intent.putExtra(Constants.KEY_LIST_ID, listId);
                    /* Starts an active showing the details for the selected list */
                    startActivity(intent);
                }
            }
        });


        return rootView;
    }
    /**
     * Updates the order of mListView onResume to handle sortOrderChanges properly
     */
    @Override
    public void onResume() {
        super.onResume();
        final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
        String sortOrder = sharedPref.getString(Constants.KEY_PREF_SORT_ORDER_LISTS, Constants.ORDER_BY_KEY);

        Query orderedActiveUserListsRef;
        Firebase activeListsRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS)
                .child(mEncodedEmail);
        /**
         * Sort active lists by "date created"
         * if it's been selected in the SettingsActivity
         */
        if (sortOrder.equals(Constants.ORDER_BY_KEY)) {
            orderedActiveUserListsRef = activeListsRef.orderByKey();
        } else {

            /**
             * Sort active by lists by name or datelastChanged. Otherwise
             * depending on what's been selected in SettingsActivity
             */

            orderedActiveUserListsRef = activeListsRef.orderByChild(sortOrder);
        }

        /**
         * Create the adapter with selected sort order
         */
        mActiveListAdapter = new ActiveListAdapter(getActivity(), ShoppingList.class,
                R.layout.single_active_list, orderedActiveUserListsRef,
                mEncodedEmail);

        /**
         * Set the adapter to the mListView
         */
        mListView.setAdapter(mActiveListAdapter);
    }

    /**
     * Cleanup the adapter when activity is paused.
     */
    @Override
    public void onPause() {
        super.onPause();
        mActiveListAdapter.cleanup();
    }

    /**
     * Link list view from XML
     */
    private void initializeScreen(View rootView) {
        mListView = (ListView) rootView.findViewById(R.id.list_view_active_lists);
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.login;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ServerValue;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

/**
 * Represents Sign up screen and functionality of the app
 */
public class CreateAccountActivity extends BaseActivity {
    private static final String LOG_TAG = CreateAccountActivity.class.getSimpleName();
    private ProgressDialog mAuthProgressDialog;
    private Firebase mFirebaseRef;
    private EditText mEditTextUsernameCreate, mEditTextEmailCreate;
    private String mUserName, mUserEmail, mPassword;
    private SecureRandom mRandom = new SecureRandom();


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

        /**
         * Create Firebase references
         */
        mFirebaseRef = new Firebase(Constants.FIREBASE_URL);

        /**
         * Link layout elements from XML and setup the progress dialog
         */
        initializeScreen();
    }

    /**
     * Override onCreateOptionsMenu to inflate nothing
     *
     * @param menu The menu with which nothing will happen
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return true;
    }


    /**
     * Link layout elements from XML and setup the progress dialog
     */
    public void initializeScreen() {
        mEditTextUsernameCreate = (EditText) findViewById(R.id.edit_text_username_create);
        mEditTextEmailCreate = (EditText) findViewById(R.id.edit_text_email_create);
        LinearLayout linearLayoutCreateAccountActivity = (LinearLayout) findViewById(R.id.linear_layout_create_account_activity);
        initializeBackground(linearLayoutCreateAccountActivity);

        /* Setup the progress dialog that is displayed later when authenticating with Firebase */
        mAuthProgressDialog = new ProgressDialog(this);
        mAuthProgressDialog.setTitle(getResources().getString(R.string.progress_dialog_loading));
        mAuthProgressDialog.setMessage(getResources().getString(R.string.progress_dialog_check_inbox));
        mAuthProgressDialog.setCancelable(false);
    }

    /**
     * Open LoginActivity when user taps on "Sign in" textView
     */
    public void onSignInPressed(View view) {
        Intent intent = new Intent(CreateAccountActivity.this, LoginActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
        finish();
    }

    /**
     * Create new account using Firebase email/password provider
     */
    public void onCreateAccountPressed(View view) {
        mUserName = mEditTextUsernameCreate.getText().toString();
        mUserEmail = mEditTextEmailCreate.getText().toString().toLowerCase();
        mPassword = new BigInteger(130, mRandom).toString(32);

        /**
         * Check that email and user name are okay
         */
        boolean validEmail = isEmailValid(mUserEmail);
        boolean validUserName = isUserNameValid(mUserName);
        if (!validEmail || !validUserName) return;

        /**
         * If everything was valid show the progress dialog to indicate that
         * account creation has started
         */
        mAuthProgressDialog.show();

        /**
         * Create new user with specified email and password
         */
        mFirebaseRef.createUser(mUserEmail, mPassword, new Firebase.ValueResultHandler<Map<String, Object>>() {
            @Override
            public void onSuccess(final Map<String, Object> result) {
                /**
                 * If user was successfully created, run resetPassword() to send temporary 24h
                 * password to the user's email and make sure that user owns specified email
                 */
                mFirebaseRef.resetPassword(mUserEmail, new Firebase.ResultHandler() {
                    @Override
                    public void onSuccess() {

                        mFirebaseRef.authWithPassword(mUserEmail, mPassword, new Firebase.AuthResultHandler() {
                            @Override
                            public void onAuthenticated(AuthData authData) {
                                mAuthProgressDialog.dismiss();
                                Log.i(LOG_TAG, getString(R.string.log_message_auth_successful));

                                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(CreateAccountActivity.this);
                                SharedPreferences.Editor spe = sp.edit();

                                /**
                                 * Save name and email to sharedPreferences to create User database record
                                 * when the registered user will sign in for the first time
                                 */
                                spe.putString(Constants.KEY_SIGNUP_EMAIL, mUserEmail).apply();

                                /**
                                 * Encode user email replacing "." with ","
                                 * to be able to use it as a Firebase db key
                                 */
                                createUserInFirebaseHelper((String) result.get("uid"));

                                /**
                                 *  Password reset email sent, open app chooser to pick app
                                 *  for handling inbox email intent
                                 */
                                Intent intent = new Intent(Intent.ACTION_MAIN);
                                intent.addCategory(Intent.CATEGORY_APP_EMAIL);
                                try {
                                    startActivity(intent);
                                    finish();
                                } catch (android.content.ActivityNotFoundException ex) {
                                    /* User does not have any app to handle email */
                                }
                            }

                            @Override
                            public void onAuthenticationError(FirebaseError firebaseError) {
                                Log.e(LOG_TAG, firebaseError.getMessage());
                            }
                        });

                    }

                    @Override
                    public void onError(FirebaseError firebaseError) {
                        /* Error occurred, log the error and dismiss the progress dialog */
                        Log.d(LOG_TAG, getString(R.string.log_error_occurred) +
                                firebaseError);
                        mAuthProgressDialog.dismiss();
                    }
                });


            }

            @Override
            public void onError(FirebaseError firebaseError) {
                /* Error occurred, log the error and dismiss the progress dialog */
                Log.d(LOG_TAG, getString(R.string.log_error_occurred) +
                        firebaseError);
                mAuthProgressDialog.dismiss();
                /* Display the appropriate error message */
                if (firebaseError.getCode() == FirebaseError.EMAIL_TAKEN) {
                    mEditTextEmailCreate.setError(getString(R.string.error_email_taken));
                } else {
                    showErrorToast(firebaseError.getMessage());
                }

            }
        });


    }

    /**
     * Creates a new user in Firebase from the Java POJO
     */
    private void createUserInFirebaseHelper(final String authUserId) {
        final String encodedEmail = Utils.encodeEmail(mUserEmail);

        /**
         * Create the user and uid mapping
         */
        HashMap<String, Object> userAndUidMapping = new HashMap<String, Object>();

        /* Set raw version of date to the ServerValue.TIMESTAMP value and save into dateCreatedMap */
        HashMap<String, Object> timestampJoined = new HashMap<>();
        timestampJoined.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);

        /* Create a HashMap version of the user to add */
        User newUser = new User(mUserName, encodedEmail, timestampJoined);
        HashMap<String, Object> newUserMap = (HashMap<String, Object>)
                new ObjectMapper().convertValue(newUser, Map.class);

        /* Add the user and UID to the update map */
        userAndUidMapping.put("/" + Constants.FIREBASE_LOCATION_USERS + "/" + encodedEmail,
                newUserMap);
        userAndUidMapping.put("/" + Constants.FIREBASE_LOCATION_UID_MAPPINGS + "/"
                + authUserId, encodedEmail);

        /* Try to update the database; if there is already a user, this will fail */
        mFirebaseRef.updateChildren(userAndUidMapping, new Firebase.CompletionListener() {
            @Override
            public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                if (firebaseError != null) {
                    /* Try just making a uid mapping */
                    mFirebaseRef.child(Constants.FIREBASE_LOCATION_UID_MAPPINGS)
                            .child(authUserId).setValue(encodedEmail);
                }
                /**
                 *  The value has been set or it failed; either way, log out the user since
                 *  they were only logged in with a temp password
                 **/
                mFirebaseRef.unauth();
            }
        });
    }

    private boolean isEmailValid(String email) {
        boolean isGoodEmail =
                (email != null && android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches());
        if (!isGoodEmail) {
            mEditTextEmailCreate.setError(String.format(getString(R.string.error_invalid_email_not_valid),
                    email));
            return false;
        }
        return isGoodEmail;
    }

    private boolean isUserNameValid(String userName) {
        if (userName.equals("")) {
            mEditTextUsernameCreate.setError(getResources().getString(R.string.error_cannot_be_empty));
            return false;
        }
        return true;
    }


    /**
     * Show error toast to users
     */
    private void showErrorToast(String message) {
        Toast.makeText(CreateAccountActivity.this, message, Toast.LENGTH_LONG).show();
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.login;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.firebase.client.AuthData;
import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ServerValue;
import com.firebase.client.ValueEventListener;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.Scope;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;
import com.udacity.firebase.shoppinglistplusplus.ui.MainActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Represents Sign in screen and functionality of the app
 */
public class LoginActivity extends BaseActivity {

    private static final String LOG_TAG = LoginActivity.class.getSimpleName();
    /* A dialog that is presented until the Firebase authentication finished. */
    private ProgressDialog mAuthProgressDialog;
    /* References to the Firebase */
    private Firebase mFirebaseRef;
    /* Listener for Firebase session changes */
    private Firebase.AuthStateListener mAuthStateListener;
    private EditText mEditTextEmailInput, mEditTextPasswordInput;

    private SharedPreferences mSharedPref;
    private SharedPreferences.Editor mSharedPrefEditor;

    /**
     * Variables related to Google Login
     */
    /* A flag indicating that a PendingIntent is in progress and prevents us from starting further intents. */
    private boolean mGoogleIntentInProgress;
    /* Request code used to invoke sign in user interactions for Google+ */
    public static final int RC_GOOGLE_LOGIN = 1;
    /* A Google account object that is populated if the user signs in with Google */
    GoogleSignInAccount mGoogleAccount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        mSharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        mSharedPrefEditor = mSharedPref.edit();

        /**
         * Create Firebase references
         */
        mFirebaseRef = new Firebase(Constants.FIREBASE_URL);

        /**
         * Link layout elements from XML and setup progress dialog
         */
        initializeScreen();

        /**
         * Call signInPassword() when user taps "Done" keyboard action
         */
        mEditTextPasswordInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {

                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                    signInPassword();
                }
                return true;
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();

        /**
         * This is the authentication listener that maintains the current user session
         * and signs in automatically on application launch
         */
        mAuthStateListener = new Firebase.AuthStateListener() {
            @Override
            public void onAuthStateChanged(AuthData authData) {
                mAuthProgressDialog.dismiss();

                /**
                 * If there is a valid session to be restored, start MainActivity.
                 * No need to pass data via SharedPreferences because app
                 * already holds userName/provider data from the latest session
                 */
                if (authData != null) {
                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(intent);
                    finish();
                }
            }
        };
        /* Add auth listener to Firebase ref */
        mFirebaseRef.addAuthStateListener(mAuthStateListener);

        /**
         * Get the newly registered user email if present, use null as default value
         */
        String signupEmail = mSharedPref.getString(Constants.KEY_SIGNUP_EMAIL, null);

        /**
         * Fill in the email editText and remove value from SharedPreferences if email is present
         */
        if (signupEmail != null) {
            mEditTextEmailInput.setText(signupEmail);

            /**
             * Clear signupEmail sharedPreferences to make sure that they are used just once
             */
            mSharedPrefEditor.putString(Constants.KEY_SIGNUP_EMAIL, null).apply();
        }
    }

    /**
     * Cleans up listeners tied to the user's authentication state
     */
    @Override
    public void onPause() {
        super.onPause();
        mFirebaseRef.removeAuthStateListener(mAuthStateListener);
    }

    /**
     * Override onCreateOptionsMenu to inflate nothing
     *
     * @param menu The menu with which nothing will happen
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        return true;
    }


    /**
     * Sign in with Password provider when user clicks sign in button
     */
    public void onSignInPressed(View view) {
        signInPassword();
    }

    /**
     * Open CreateAccountActivity when user taps on "Sign up" TextView
     */
    public void onSignUpPressed(View view) {
        Intent intent = new Intent(LoginActivity.this, CreateAccountActivity.class);
        startActivity(intent);
    }

    /**
     * Link layout elements from XML and setup the progress dialog
     */
    public void initializeScreen() {
        mEditTextEmailInput = (EditText) findViewById(R.id.edit_text_email);
        mEditTextPasswordInput = (EditText) findViewById(R.id.edit_text_password);
        LinearLayout linearLayoutLoginActivity = (LinearLayout) findViewById(R.id.linear_layout_login_activity);
        initializeBackground(linearLayoutLoginActivity);
        /* Setup the progress dialog that is displayed later when authenticating with Firebase */
        mAuthProgressDialog = new ProgressDialog(this);
        mAuthProgressDialog.setTitle(getString(R.string.progress_dialog_loading));
        mAuthProgressDialog.setMessage(getString(R.string.progress_dialog_authenticating_with_firebase));
        mAuthProgressDialog.setCancelable(false);
        /* Setup Google Sign In */
        setupGoogleSignIn();
    }

    /**
     * Sign in with Password provider (used when user taps "Done" action on keyboard)
     */
    public void signInPassword() {
        String email = mEditTextEmailInput.getText().toString();
        String password = mEditTextPasswordInput.getText().toString();

        /**
         * If email and password are not empty show progress dialog and try to authenticate
         */
        if (email.equals("")) {
            mEditTextEmailInput.setError(getString(R.string.error_cannot_be_empty));
            return;
        }

        if (password.equals("")) {
            mEditTextPasswordInput.setError(getString(R.string.error_cannot_be_empty));
            return;
        }
        mAuthProgressDialog.show();
        mFirebaseRef.authWithPassword(email, password, new MyAuthResultHandler(Constants.PASSWORD_PROVIDER));
    }

    /**
     * Handle user authentication that was initiated with mFirebaseRef.authWithPassword
     * or mFirebaseRef.authWithOAuthToken
     */
    private class MyAuthResultHandler implements Firebase.AuthResultHandler {

        private final String provider;

        public MyAuthResultHandler(String provider) {
            this.provider = provider;
        }

        /**
         * On successful authentication call setAuthenticatedUser if it was not already
         * called in
         */
        @Override
        public void onAuthenticated(AuthData authData) {
            mAuthProgressDialog.dismiss();
            Log.i(LOG_TAG, provider + " " + getString(R.string.log_message_auth_successful));

            if (authData != null) {
                /**
                 * If user has logged in with Google provider
                 */
                if (authData.getProvider().equals(Constants.PASSWORD_PROVIDER)) {
                    setAuthenticatedUserPasswordProvider(authData);
                } else
                /**
                 * If user has logged in with Password provider
                 */
                    if (authData.getProvider().equals(Constants.GOOGLE_PROVIDER)) {
                        setAuthenticatedUserGoogle(authData);
                    } else {
                        Log.e(LOG_TAG, getString(R.string.log_error_invalid_provider) + authData.getProvider());
                    }

                /* Save provider name and encodedEmail for later use and start MainActivity */
                mSharedPrefEditor.putString(Constants.KEY_PROVIDER, authData.getProvider()).apply();
                mSharedPrefEditor.putString(Constants.KEY_ENCODED_EMAIL, mEncodedEmail).apply();

                /* Go to main activity */
                Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(intent);
                finish();
            }
        }

        @Override
        public void onAuthenticationError(FirebaseError firebaseError) {
            mAuthProgressDialog.dismiss();

            /**
             * Use utility method to check the network connection state
             * Show "No network connection" if there is no connection
             * Show Firebase specific error message otherwise
             */
            switch (firebaseError.getCode()) {
                case FirebaseError.INVALID_EMAIL:
                case FirebaseError.USER_DOES_NOT_EXIST:
                    mEditTextEmailInput.setError(getString(R.string.error_message_email_issue));
                    break;
                case FirebaseError.INVALID_PASSWORD:
                    mEditTextPasswordInput.setError(firebaseError.getMessage());
                    break;
                case FirebaseError.NETWORK_ERROR:
                    showErrorToast(getString(R.string.error_message_failed_sign_in_no_network));
                    break;
                default:
                    showErrorToast(firebaseError.toString());
            }
        }
    }

    /**
     * Helper method that makes sure a user is created if the user
     * logs in with Firebase's email/password provider.
     *
     * @param authData AuthData object returned from onAuthenticated
     */
    private void setAuthenticatedUserPasswordProvider(AuthData authData) {
        final String unprocessedEmail = authData.getProviderData().get(Constants.FIREBASE_PROPERTY_EMAIL).toString().toLowerCase();
        /**
         * Encode user email replacing "." with ","
         * to be able to use it as a Firebase db key
         */
        mEncodedEmail = Utils.encodeEmail(unprocessedEmail);

        final Firebase userRef = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);

        /**
         * Check if current user has logged in at least once
         */
        userRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                User user = dataSnapshot.getValue(User.class);

                if (user != null) {

                    /**
                     * If recently registered user has hasLoggedInWithPassword = "false"
                     * (never logged in using password provider)
                     */
                    if (!user.isHasLoggedInWithPassword()) {

                        /**
                         * Change password if user that just signed in signed up recently
                         * to make sure that user will be able to use temporary password
                         * from the email more than 24 hours
                         */
                        mFirebaseRef.changePassword(unprocessedEmail, mEditTextPasswordInput.getText().toString(), mEditTextPasswordInput.getText().toString(), new Firebase.ResultHandler() {
                            @Override
                            public void onSuccess() {
                                userRef.child(Constants.FIREBASE_PROPERTY_USER_HAS_LOGGED_IN_WITH_PASSWORD).setValue(true);
                                        /* The password was changed */
                                Log.d(LOG_TAG, getString(R.string.log_message_password_changed_successfully) + mEditTextPasswordInput.getText().toString());
                            }

                            @Override
                            public void onError(FirebaseError firebaseError) {
                                Log.d(LOG_TAG, getString(R.string.log_error_failed_to_change_password) + firebaseError);
                            }
                        });
                    }
                }
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });

    }

    /**
     * Helper method that makes sure a user is created if the user
     * logs in with Firebase's Google login provider.
     *
     * @param authData AuthData object returned from onAuthenticated
     */
    private void setAuthenticatedUserGoogle(final AuthData authData) {
        /**
         * If google api client is connected, get the lowerCase user email
         * and save in sharedPreferences
         */
        String unprocessedEmail;
        if (mGoogleApiClient.isConnected()) {
            unprocessedEmail = mGoogleAccount.getEmail().toLowerCase();
            mSharedPrefEditor.putString(Constants.KEY_GOOGLE_EMAIL, unprocessedEmail).apply();
        } else {

            /**
             * Otherwise get email from sharedPreferences, use null as default value
             * (this mean that user resumes his session)
             */
            unprocessedEmail = mSharedPref.getString(Constants.KEY_GOOGLE_EMAIL, null);
        }

        /**
         * Encode user email replacing "." with "," to be able to use it
         * as a Firebase db key
         */
        mEncodedEmail = Utils.encodeEmail(unprocessedEmail);

        /* Get username from authData */
        final String userName = (String) authData.getProviderData().get(Constants.PROVIDER_DATA_DISPLAY_NAME);

        /* Make a user */
        final Firebase userLocation = new Firebase(Constants.FIREBASE_URL_USERS).child(mEncodedEmail);

        HashMap<String, Object> userAndUidMapping = new HashMap<String, Object>();

        HashMap<String, Object> timestampJoined = new HashMap<>();
        timestampJoined.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);

        /* Create a HashMap version of the user to add */
        User newUser = new User(userName, mEncodedEmail, timestampJoined);
        HashMap<String, Object> newUserMap = (HashMap<String, Object>)
                new ObjectMapper().convertValue(newUser, Map.class);

        /* Add the user and UID to the update map */
        userAndUidMapping.put("/" + Constants.FIREBASE_LOCATION_USERS + "/" + mEncodedEmail,
                newUserMap);
        userAndUidMapping.put("/" + Constants.FIREBASE_LOCATION_UID_MAPPINGS + "/"
                + authData.getUid(), mEncodedEmail);

        /* Update the database; it will fail if a user already exists */
        mFirebaseRef.updateChildren(userAndUidMapping, new Firebase.CompletionListener() {
            @Override
            public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                if (firebaseError != null) {
                    /* Try just making a uid mapping */
                    mFirebaseRef.child(Constants.FIREBASE_LOCATION_UID_MAPPINGS)
                            .child(authData.getUid()).setValue(mEncodedEmail);
                }
            }
        });
    }

    /**
     * Show error toast to users
     */
    private void showErrorToast(String message) {
        Toast.makeText(LoginActivity.this, message, Toast.LENGTH_LONG).show();
    }


    /**
     * Signs you into ShoppingList++ using the Google Login Provider
     *
     * @param token A Google OAuth access token returned from Google
     */
    private void loginWithGoogle(String token) {
        mFirebaseRef.authWithOAuthToken(Constants.GOOGLE_PROVIDER, token, new MyAuthResultHandler(Constants.GOOGLE_PROVIDER));
    }

    /**
     * GOOGLE SIGN IN CODE
     * <p/>
     * This code is mostly boiler plate from
     * https://developers.google.com/identity/sign-in/android/start-integrating
     * and
     * https://github.com/googlesamples/google-services/blob/master/android/signin/app/src/main/java/com/google/samples/quickstart/signin/SignInActivity.java
     * <p/>
     * The big picture steps are:
     * 1. User clicks the sign in with Google button
     * 2. An intent is started for sign in.
     * - If the connection fails it is caught in the onConnectionFailed callback
     * - If it finishes, onActivityResult is called with the correct request code.
     * 3. If the sign in was successful, set the mGoogleAccount to the current account and
     * then call get GoogleOAuthTokenAndLogin
     * 4. getGoogleOAuthTokenAndLogin launches an AsyncTask to get an OAuth2 token from Google.
     * 5. Once this token is retrieved it is available to you in the onPostExecute method of
     * the AsyncTask. **This is the token required by Firebase**
     */


    /* Sets up the Google Sign In Button : https://developers.google.com/android/reference/com/google/android/gms/common/SignInButton */
    private void setupGoogleSignIn() {
        SignInButton signInButton = (SignInButton) findViewById(R.id.login_with_google);
        signInButton.setSize(SignInButton.SIZE_WIDE);
        signInButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onSignInGooglePressed(v);
            }
        });
    }

    /**
     * Sign in with Google plus when user clicks "Sign in with Google" textView (button)
     */
    public void onSignInGooglePressed(View view) {
        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
        startActivityForResult(signInIntent, RC_GOOGLE_LOGIN);
        mAuthProgressDialog.show();

    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        /**
         * An unresolvable error has occurred and Google APIs (including Sign-In) will not
         * be available.
         */
        mAuthProgressDialog.dismiss();
        showErrorToast(result.toString());
    }


    /**
     * This callback is triggered when any startActivityForResult finishes. The requestCode maps to
     * the value passed into startActivityForResult.
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        /* Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); */
        if (requestCode == RC_GOOGLE_LOGIN) {
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            handleSignInResult(result);
        }

    }

    private void handleSignInResult(GoogleSignInResult result) {
        Log.d(LOG_TAG, "handleSignInResult:" + result.isSuccess());
        if (result.isSuccess()) {
            /* Signed in successfully, get the OAuth token */
            mGoogleAccount = result.getSignInAccount();
            getGoogleOAuthTokenAndLogin();


        } else {
            if (result.getStatus().getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) {
                showErrorToast("The sign in was cancelled. Make sure you're connected to the internet and try again.");
            } else {
                showErrorToast("Error handling the sign in: " + result.getStatus().getStatusMessage());
            }
            mAuthProgressDialog.dismiss();
        }
    }

    /**
     * Gets the GoogleAuthToken and logs in.
     */
    private void getGoogleOAuthTokenAndLogin() {
        /* Get OAuth token in Background */
        AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
            String mErrorMessage = null;

            @Override
            protected String doInBackground(Void... params) {
                String token = null;

                try {
                    String scope = String.format(getString(R.string.oauth2_format), new Scope(Scopes.PROFILE)) + " email";

                    token = GoogleAuthUtil.getToken(LoginActivity.this, mGoogleAccount.getEmail(), scope);
                } catch (IOException transientEx) {
                    /* Network or server error */
                    Log.e(LOG_TAG, getString(R.string.google_error_auth_with_google) + transientEx);
                    mErrorMessage = getString(R.string.google_error_network_error) + transientEx.getMessage();
                } catch (UserRecoverableAuthException e) {
                    Log.w(LOG_TAG, getString(R.string.google_error_recoverable_oauth_error) + e.toString());

                    /* We probably need to ask for permissions, so start the intent if there is none pending */
                    if (!mGoogleIntentInProgress) {
                        mGoogleIntentInProgress = true;
                        Intent recover = e.getIntent();
                        startActivityForResult(recover, RC_GOOGLE_LOGIN);
                    }
                } catch (GoogleAuthException authEx) {
                    /* The call is not ever expected to succeed assuming you have already verified that
                     * Google Play services is installed. */
                    Log.e(LOG_TAG, " " + authEx.getMessage(), authEx);
                    mErrorMessage = getString(R.string.google_error_auth_with_google) + authEx.getMessage();
                }
                return token;
            }

            @Override
            protected void onPostExecute(String token) {
                mAuthProgressDialog.dismiss();
                if (token != null) {
                    /* Successfully got OAuth token, now login with Google */
                    loginWithGoogle(token);
                } else if (mErrorMessage != null) {
                    showErrorToast(mErrorMessage);
                }
            }
        };

        task.execute();
    }
}

================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/AddMealDialogFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.meals;

import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;

import com.udacity.firebase.shoppinglistplusplus.R;

/**
 * Adds a new meal
 */
public class AddMealDialogFragment extends DialogFragment {
    EditText editTextMealName;

    /**
     * Public static constructor that creates fragment and
     * passes a bundle with data into it when adapter is created
     */
    public static AddMealDialogFragment newInstance() {
        AddMealDialogFragment addMealDialogFragment = new AddMealDialogFragment();
        Bundle bundle = new Bundle();
        addMealDialogFragment.setArguments(bundle);
        return addMealDialogFragment;
    }

    /**
     * Initialize instance variables with data from bundle
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    /**
     * Open the keyboard automatically when the dialog fragment is opened
     */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        /* Use the Builder class for convenient dialog construction */
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomTheme_Dialog);
        /* Get the layout inflater */
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View rootView = inflater.inflate(R.layout.dialog_add_meal, null);
        editTextMealName = (EditText) rootView.findViewById(R.id.edit_text_meal_name);

        /**
         * Call addMeal() when user taps "Done" keyboard action
         */
        editTextMealName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
                if (actionId == EditorInfo.IME_ACTION_DONE || keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                    addMeal();
                }
                return true;
            }
        });

        /* Inflate and set the layout for the dialog */
        /* Pass null as the parent view because its going in the dialog layout */
        builder.setView(rootView)
                /* Add action buttons */
                .setPositiveButton(R.string.positive_button_create, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        addMeal();
                    }
                });

        return builder.create();
    }

    /**
     * Add new meal
     */
    public void addMeal() {
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/MealsFragment.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.meals;


import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;

import com.udacity.firebase.shoppinglistplusplus.R;


/**
 * A simple {@link Fragment} subclass which shows all of the meals in the Firebase database
 * Use the {@link MealsFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class MealsFragment extends Fragment {
    private ListView mListView;

    /**
     * Create fragment and pass bundle with data as its' arguments
     */
    public static MealsFragment newInstance() {
        MealsFragment fragment = new MealsFragment();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    public MealsFragment() {
        /* Required empty public constructor*/
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        /* Inflate the layout for this fragment */
        View rootView = inflater.inflate(R.layout.fragment_meals, container, false);

        /**
         * Link layout elements from XML and setup the toolbar
         */
        initializeScreen(rootView);

        /**
         * Set interactive bits, such as click events/adapters
         */
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

            }
        });

        return rootView;
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    private void initializeScreen(View rootView) {
        mListView = (ListView) rootView.findViewById(R.id.list_view_meals_list);
        View footer = getActivity().getLayoutInflater().inflate(R.layout.footer_empty, null);
        mListView.addFooterView(footer);
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AddFriendActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.sharing;

import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.ListView;

import com.firebase.client.Firebase;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

/**
 * Represents the Add Friend screen and functionality
 */
public class AddFriendActivity extends BaseActivity {
    private EditText mEditTextAddFriendEmail;
    private AutocompleteFriendAdapter mFriendsAutocompleteAdapter;
    private String mInput;
    private ListView mListViewAutocomplete;
    private Firebase mUsersRef;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_friend);
        /**
         * Create Firebase references
         */
        mUsersRef = new Firebase(Constants.FIREBASE_URL_USERS);

        /**
         * Link layout elements from XML and setup the toolbar
         */
        initializeScreen();

        /**
         * Set interactive bits, such as click events/adapters
         */

        mEditTextAddFriendEmail.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void afterTextChanged(Editable s) {
                /* Get the input after every textChanged event and transform it to lowercase */
            mInput = mEditTextAddFriendEmail.getText().toString().toLowerCase();

            /* Clean up the old adapter */
            if (mFriendsAutocompleteAdapter != null) mFriendsAutocompleteAdapter.cleanup();
            /* Nullify the adapter data if the input length is less than 2 characters */
            if (mInput.equals("") || mInput.length() < 2) {
                mListViewAutocomplete.setAdapter(null);

            /* Define and set the adapter otherwise. */
            } else {
                mFriendsAutocompleteAdapter = new AutocompleteFriendAdapter(AddFriendActivity.this, User.class,
                        R.layout.single_autocomplete_item, mUsersRef.orderByChild(Constants.FIREBASE_PROPERTY_EMAIL)
                        .startAt(mInput).endAt(mInput + "~").limitToFirst(5), mEncodedEmail);

                mListViewAutocomplete.setAdapter(mFriendsAutocompleteAdapter);
            }

        }
        });

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mFriendsAutocompleteAdapter != null) {
            mFriendsAutocompleteAdapter.cleanup();
        }
    }

    /**
     * Link layout elements from XML and setup the toolbar
     */
    public void initializeScreen() {
        mListViewAutocomplete = (ListView) findViewById(R.id.list_view_friends_autocomplete);
        mEditTextAddFriendEmail = (EditText) findViewById(R.id.edit_text_add_friend_email);
        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);
        /* Add back button to the action bar */
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AutocompleteFriendAdapter.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.sharing;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.Query;
import com.firebase.client.ValueEventListener;
import com.firebase.ui.FirebaseListAdapter;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

/**
 * Populates the list_view_friends_autocomplete inside AddFriendActivity
 */
public class AutocompleteFriendAdapter extends FirebaseListAdapter<User> {
    private String mEncodedEmail;

    /**
     * Public constructor that initializes private instance variables when adapter is created
     */
    public AutocompleteFriendAdapter(Activity activity, Class<User> modelClass, int modelLayout,
                                     Query ref, String encodedEmail) {
        super(activity, modelClass, modelLayout, ref);
        this.mActivity = activity;
        this.mEncodedEmail = encodedEmail;
    }

    /**
     * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete)
     * with items inflated from single_autocomplete_item.xml
     * populateView also handles data changes and updates the listView accordingly
     */
    @Override
    protected void populateView(View view, final User user) {
         /* Get friends email textview and set it's text to user.email() */
        TextView textViewFriendEmail = (TextView) view.findViewById(R.id.text_view_autocomplete_item);
        textViewFriendEmail.setText(Utils.decodeEmail(user.getEmail()));

        /**
         * Set the onClickListener to a single list item
         * If selected email is not friend already and if it is not the
         * current user's email, we add selected user to current user's friends
         */
        textViewFriendEmail.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                /**
                 * If selected user is not current user proceed
                 */
                if (isNotCurrentUser(user)) {

                    Firebase currentUserFriendsRef = new Firebase(Constants.FIREBASE_URL_USER_FRIENDS).child(mEncodedEmail);
                    final Firebase friendRef = currentUserFriendsRef.child(user.getEmail());

                    /**
                     * Add listener for single value event to perform a one time operation
                     */
                    friendRef.addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(DataSnapshot dataSnapshot) {

                            /**
                             * Add selected user to current user's friends if not in friends yet
                             */
                            if (isNotAlreadyAdded(dataSnapshot, user)) {
                                friendRef.setValue(user);
                                mActivity.finish();
                            }
                        }

                        @Override
                        public void onCancelled(FirebaseError firebaseError) {
                            Log.e(mActivity.getClass().getSimpleName(),
                                    mActivity.getString(R.string.log_error_the_read_failed) +
                                            firebaseError.getMessage());
                        }
                    });

                }
            }
        });

    }

    private boolean isNotCurrentUser(User user) {
        if (user.getEmail().equals(mEncodedEmail)) {
            /* Toast appropriate error message if the user is trying to add themselves  */
            Toast.makeText(mActivity,
                    mActivity.getResources().getString(R.string.toast_you_cant_add_yourself),
                    Toast.LENGTH_LONG).show();
            return false;
        }
        return true;
    }

    private boolean isNotAlreadyAdded(DataSnapshot dataSnapshot, User user) {
        if (dataSnapshot.getValue(User.class) != null) {
            /* Toast appropriate error message if the user is already a friend of the user */
            String friendError = String.format(mActivity.getResources().
                            getString(R.string.toast_is_already_your_friend),
                    user.getName());

            Toast.makeText(mActivity,
                    friendError,
                    Toast.LENGTH_LONG).show();
            return false;
        }
        return true;
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/FriendAdapter.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.sharing;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.Query;
import com.firebase.client.ValueEventListener;
import com.firebase.ui.FirebaseListAdapter;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;
import com.udacity.firebase.shoppinglistplusplus.utils.Utils;

import java.util.HashMap;
import java.util.Map;

/**
 * Populates the list_view_friends_share inside ShareListActivity
 */
public class FriendAdapter extends FirebaseListAdapter<User> {
    private ShoppingList mShoppingList;
    private static final String LOG_TAG = FriendAdapter.class.getSimpleName();
    private String mListId;
    private Firebase mFirebaseRef;
    private HashMap<String, User> mSharedUsersList;
    private HashMap <Firebase, ValueEventListener> mLocationListenerMap;


    /**
     * Public constructor that initializes private instance variables when adapter is created
     */
    public FriendAdapter(Activity activity, Class<User> modelClass, int modelLayout,
                         Query ref, String listId) {
        super(activity, modelClass, modelLayout, ref);
        this.mActivity = activity;
        this.mListId = listId;
        mFirebaseRef = new Firebase(Constants.FIREBASE_URL);
        mLocationListenerMap = new HashMap<Firebase, ValueEventListener>();
    }

    /**
     * Protected method that populates the view attached to the adapter (list_view_friends_autocomplete)
     * with items inflated from single_user_item.xml
     * populateView also handles data changes and updates the listView accordingly
     */
    @Override
    protected void populateView(View view, final User friend) {
        ((TextView) view.findViewById(R.id.user_name)).setText(friend.getName());
        final ImageButton buttonToggleShare = (ImageButton) view.findViewById(R.id.button_toggle_share);

        final Firebase sharedFriendInShoppingListRef = new Firebase(Constants.FIREBASE_URL_LISTS_SHARED_WITH)
                .child(mListId).child(friend.getEmail());

        /**
         * Gets the value of the friend from the ShoppingList's sharedWith list of users
         * and then allows the friend to be toggled as shared with or not.
         *
         * The friend in the snapshot (sharedFriendInShoppingList) will either be a User object
         * (if they are in the the sharedWith list) or null (if they are not in the sharedWith
         * list)
         */

        ValueEventListener listener = sharedFriendInShoppingListRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot snapshot) {
                final User sharedFriendInShoppingList = snapshot.getValue(User.class);

                /**
                 * If list is already being shared with this friend, set the buttonToggleShare
                 * to remove selected friend from sharedWith onClick and change the
                 * buttonToggleShare image to green
                 */
                if (sharedFriendInShoppingList != null) {
                    buttonToggleShare.setImageResource(R.drawable.ic_shared_check);
                    buttonToggleShare.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {

                            /**
                             * Create map and fill it in with deep path multi write operations list.
                             * Use false to mark that you are removing this friend
                             */
                            HashMap<String, Object> updatedUserData = updateFriendInSharedWith(false, friend);

                            /* Do a deep-path update */
                            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {
                                @Override
                                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId,
                                            mSharedUsersList, mShoppingList.getOwner());
                                }
                            });
                        }
                    });
                } else {

                    /**
                     * Set the buttonToggleShare onClickListener to add selected friend to sharedWith
                     * and change the buttonToggleShare image to grey otherwise
                     */
                    buttonToggleShare.setImageResource(R.drawable.icon_add_friend);
                    buttonToggleShare.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {

                            /**
                             * Create map and fill it in with deep path multi write operations list
                             */
                            HashMap<String, Object> updatedUserData = updateFriendInSharedWith(true, friend);

                            /* Do a deep-path update */
                            mFirebaseRef.updateChildren(updatedUserData, new Firebase.CompletionListener() {
                                @Override
                                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                                    Utils.updateTimestampReversed(firebaseError, LOG_TAG, mListId,
                                            mSharedUsersList, mShoppingList.getOwner());
                                }
                            });
                        }
                    });
                }
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        mActivity.getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });
        /* Add the listener to the HashMap so that it can be removed on cleanup */
        mLocationListenerMap.put(sharedFriendInShoppingListRef, listener);

    }

    /**
     * Public method that is used to pass ShoppingList object when it is loaded in ValueEventListener
     */
    public void setShoppingList(ShoppingList shoppingList) {
        this.mShoppingList = shoppingList;
        this.notifyDataSetChanged();
    }

    /**
     * Public method that is used to pass SharedUsers when they are loaded in ValueEventListener
     */
    public void setSharedWithUsers(HashMap<String, User> sharedUsersList) {
        this.mSharedUsersList = sharedUsersList;
        this.notifyDataSetChanged();
    }

    /**
     * This method does the tricky job of adding or removing a friend from the sharedWith list.
     * @param addFriend This is true if the friend is being added, false is the friend is being removed.
     * @param friendToAddOrRemove This is the friend to either add or remove
     * @return
     */
    private HashMap<String, Object> updateFriendInSharedWith(Boolean addFriend, User friendToAddOrRemove) {
        HashMap<String, Object> updatedUserData = new HashMap<String, Object>();

        /* The newSharedWith lists contains all users who need their last time changed updated */
        HashMap<String, User> newSharedWith = new HashMap<String, User>(mSharedUsersList);

        if (addFriend) {
            /**
             * Changes the timestamp changed to now; Because of ancestry issues, we cannot
             * have one updateChildren call that both creates data and then updates that same data
             * because updateChildren has no way of knowing what was the intended update
             */
            mShoppingList.setTimestampLastChangedToNow();
            /* Make it a HashMap of the shopping list and user */
            final HashMap<String, Object> shoppingListForFirebase = (HashMap<String, Object>)
                    new ObjectMapper().convertValue(mShoppingList, Map.class);

            final HashMap<String, Object> friendForFirebase = (HashMap<String, Object>)
                    new ObjectMapper().convertValue(friendToAddOrRemove, Map.class);

            /* Add the friend to the shared list */
            updatedUserData.put("/" + Constants.FIREBASE_LOCATION_LISTS_SHARED_WITH + "/" + mListId +
                    "/" + friendToAddOrRemove.getEmail(), friendForFirebase);

            /* Add that shopping list hashmap to the new user's active lists */
            updatedUserData.put("/" + Constants.FIREBASE_LOCATION_USER_LISTS + "/" + friendToAddOrRemove.getEmail()
                    + "/" + mListId, shoppingListForFirebase);

        } else {
            /* Remove the friend from the shared list */
            updatedUserData.put("/" + Constants.FIREBASE_LOCATION_LISTS_SHARED_WITH + "/" + mListId +
                    "/" + friendToAddOrRemove.getEmail(), null);

            /* Remove the list from the shared friend */
            updatedUserData.put("/" + Constants.FIREBASE_LOCATION_USER_LISTS + "/" + friendToAddOrRemove.getEmail()
                    + "/" + mListId, null);

            newSharedWith.remove(friendToAddOrRemove.getEmail());

        }

        Utils.updateMapWithTimestampLastChanged(newSharedWith,
                mListId, mShoppingList.getOwner(), updatedUserData);

        return updatedUserData;
    }

    @Override
    public void cleanup() {
        super.cleanup();
        /* Clean up the event listeners */
        for (HashMap.Entry<Firebase, ValueEventListener> listenerToClean : mLocationListenerMap.entrySet())
        {
            listenerToClean.getKey().removeEventListener(listenerToClean.getValue());
        }

    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/ShareListActivity.java
================================================
package com.udacity.firebase.shoppinglistplusplus.ui.sharing;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.ListView;

import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;
import com.udacity.firebase.shoppinglistplusplus.R;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;
import com.udacity.firebase.shoppinglistplusplus.ui.BaseActivity;
import com.udacity.firebase.shoppinglistplusplus.utils.Constants;

import java.util.HashMap;

/**
 * Allows for you to check and un-check friends that you share the current list with
 */
public class ShareListActivity extends BaseActivity {
    private static final String LOG_TAG = ShareListActivity.class.getSimpleName();
    private FriendAdapter mFriendAdapter;
    private ListView mListView;
    private ShoppingList mShoppingList;
    private String mListId;
    private Firebase mActiveListRef, mSharedWithRef;
    private ValueEventListener mActiveListRefListener, mSharedWithListener;
    private HashMap<String, User> mSharedWithUsers;


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

        /* Get the push ID from the extra passed by ActiveListDetailsActivity */
        Intent intent = this.getIntent();
        mListId = intent.getStringExtra(Constants.KEY_LIST_ID);
        if (mListId == null) {
            /* No point in continuing without a valid ID. */
            finish();
            return;
        }

        /**
         * Link layout elements from XML and setup the toolbar
         */
        initializeScreen();

        /**
         * Create Firebase references
         */
        Firebase currentUserFriendsRef = new Firebase(Constants.FIREBASE_URL_USER_FRIENDS).child(mEncodedEmail);
        mActiveListRef = new Firebase(Constants.FIREBASE_URL_USER_LISTS).child(mEncodedEmail).child(mListId);
        mSharedWithRef = new Firebase (Constants.FIREBASE_URL_LISTS_SHARED_WITH).child(mListId);

        /**
         * Add ValueEventListeners to Firebase references
         * to control get data and control behavior and visibility of elements
         */

        mActiveListRefListener = mActiveListRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                ShoppingList shoppingList = dataSnapshot.getValue(ShoppingList.class);

                /**
                 * Saving the most recent version of current shopping list into mShoppingList
                 * and pass it to setShoppingList() if present
                 * finish() the activity otherwise
                 */
                if (shoppingList != null) {
                    mShoppingList = shoppingList;
                    mFriendAdapter.setShoppingList(mShoppingList);
                } else {
                    finish();
                }
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });


        mSharedWithListener = mSharedWithRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                mSharedWithUsers = new HashMap<String, User>();
                for (DataSnapshot currentUser : dataSnapshot.getChildren()) {
                    mSharedWithUsers.put(currentUser.getKey(), currentUser.getValue(User.class));
                }
                mFriendAdapter.setSharedWithUsers(mSharedWithUsers);
            }

            @Override
            public void onCancelled(FirebaseError firebaseError) {
                Log.e(LOG_TAG,
                        getString(R.string.log_error_the_read_failed) +
                                firebaseError.getMessage());
            }
        });


        /**
         * Set interactive bits, such as click events/adapters
         */
        mFriendAdapter = new FriendAdapter(ShareListActivity.this, User.class,
                R.layout.single_user_item, currentUserFriendsRef, mListId);

        /* Set adapter for the listView */
        mListView.setAdapter(mFriendAdapter);
    }

    /**
     * Cleanup the adapter when activity is destroyed
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        /* Set adapter for the listView */
        mFriendAdapter.cleanup();
        mActiveListRef.removeEventListener(mActiveListRefListener);
        mSharedWithRef.removeEventListener(mSharedWithListener);
    }

    /**
     * Link layout elements from XML and setup the toolbar
     */
    public void initializeScreen() {
        mListView = (ListView) findViewById(R.id.list_view_friends_share);
        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);
        /* Add back button to the action bar */
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        }
    }

    /**
     * Launch AddFriendActivity to find and add user to current user's friends list
     * when the button AddFriend is pressed
     */
    public void onAddFriendPressed(View view) {
        Intent intent = new Intent(ShareListActivity.this, AddFriendActivity.class);
        startActivity(intent);
    }
}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java
================================================
package com.udacity.firebase.shoppinglistplusplus.utils;

import com.udacity.firebase.shoppinglistplusplus.BuildConfig;

/**
 * Constants class store most important strings and paths of the app
 */
public final class Constants {

    /**
     * Constants related to locations in Firebase, such as the name of the node
     * where user lists are stored (ie "userLists")
     */
    public static final String FIREBASE_LOCATION_SHOPPING_LIST_ITEMS = "shoppingListItems";
    public static final String FIREBASE_LOCATION_USERS = "users";
    public static final String FIREBASE_LOCATION_USER_LISTS = "userLists";
    public static final String FIREBASE_LOCATION_USER_FRIENDS = "userFriends";
    public static final String FIREBASE_LOCATION_LISTS_SHARED_WITH = "sharedWith";
    public static final String FIREBASE_LOCATION_UID_MAPPINGS = "uidMappings";
    public static final String FIREBASE_LOCATION_OWNER_MAPPINGS = "ownerMappings";



    /**
     * Constants for Firebase object properties
     */
    public static final String FIREBASE_PROPERTY_BOUGHT = "bought";
    public static final String FIREBASE_PROPERTY_BOUGHT_BY = "boughtBy";
    public static final String FIREBASE_PROPERTY_LIST_NAME = "listName";
    public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED = "timestampLastChanged";
    public static final String FIREBASE_PROPERTY_TIMESTAMP = "timestamp";
    public static final String FIREBASE_PROPERTY_ITEM_NAME = "itemName";
    public static final String FIREBASE_PROPERTY_EMAIL = "email";
    public static final String FIREBASE_PROPERTY_USERS_SHOPPING = "usersShopping";
    public static final String FIREBASE_PROPERTY_USER_HAS_LOGGED_IN_WITH_PASSWORD = "hasLoggedInWithPassword";
    public static final String FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED_REVERSE = "timestampLastChangedReverse";


    /**
     * Constants for Firebase URL
     */
    public static final String FIREBASE_URL = BuildConfig.UNIQUE_FIREBASE_ROOT_URL;
    public static final String FIREBASE_URL_SHOPPING_LIST_ITEMS = FIREBASE_URL + "/" + FIREBASE_LOCATION_SHOPPING_LIST_ITEMS;
    public static final String FIREBASE_URL_USERS = FIREBASE_URL + "/" + FIREBASE_LOCATION_USERS;
    public static final String FIREBASE_URL_USER_LISTS = FIREBASE_URL + "/" + FIREBASE_LOCATION_USER_LISTS;
    public static final String FIREBASE_URL_USER_FRIENDS = FIREBASE_URL + "/" + FIREBASE_LOCATION_USER_FRIENDS;
    public static final String FIREBASE_URL_LISTS_SHARED_WITH = FIREBASE_URL + "/" + FIREBASE_LOCATION_LISTS_SHARED_WITH;

    /**
     * Constants for bundles, extras and shared preferences keys
     */
    public static final String KEY_LIST_NAME = "LIST_NAME";
    public static final String KEY_LAYOUT_RESOURCE = "LAYOUT_RESOURCE";
    public static final String KEY_LIST_ID = "LIST_ID";
    public static final String KEY_SIGNUP_EMAIL = "SIGNUP_EMAIL";
    public static final String KEY_LIST_ITEM_NAME = "ITEM_NAME";
    public static final String KEY_LIST_ITEM_ID = "LIST_ITEM_ID";
    public static final String KEY_PROVIDER = "PROVIDER";
    public static final String KEY_ENCODED_EMAIL = "ENCODED_EMAIL";
    public static final String KEY_LIST_OWNER = "LIST_OWNER";
    public static final String KEY_GOOGLE_EMAIL = "GOOGLE_EMAIL";
    public static final String KEY_PREF_SORT_ORDER_LISTS = "PERF_SORT_ORDER_LISTS";
    public static final String KEY_SHARED_WITH_USERS = "SHARED_WITH_USERS";


    /**
     * Constants for Firebase login
     */
    public static final String PASSWORD_PROVIDER = "password";
    public static final String GOOGLE_PROVIDER = "google";
    public static final String PROVIDER_DATA_DISPLAY_NAME = "displayName";


    /**
     * Constant for sorting
     */
    public static final String ORDER_BY_KEY = "orderByPushKey";
    public static final String ORDER_BY_OWNER_EMAIL = "orderByOwnerEmail";



}


================================================
FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java
================================================
package com.udacity.firebase.shoppinglistplusplus.utils;

import android.content.Context;
import android.util.Log;

import com.firebase.client.DataSnapshot;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import com.firebase.client.ServerValue;
import com.firebase.client.ValueEventListener;
import com.udacity.firebase.shoppinglistplusplus.model.ShoppingList;
import com.udacity.firebase.shoppinglistplusplus.model.User;

import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * Utility class
 */
public class Utils {
    /**
     * Format the timestamp with SimpleDateFormat
     */
    public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    private Context mContext = null;


    /**
     * Public constructor that takes mContext for later use
     */
    public Utils(Context con) {
        mContext = con;
    }

    /**
     * Return true if currentUserEmail equals to shoppingList.owner()
     * Return false otherwise
     */
    public static boolean checkIfOwner(ShoppingList shoppingList, String currentUserEmail) {
        return (shoppingList.getOwner() != null &&
                shoppingList.getOwner().equals(currentUserEmail));
    }

    /**
     * Encode user email to use it as a Firebase key (Firebase does not allow "." in the key name)
     * Encoded email is also used as "userEmail", list and item "owner" value
     */
    public static String encodeEmail(String userEmail) {
        return userEmail.replace(".", ",");
    }
    
    /**
     * Email is being decoded just once to display real email in AutocompleteFriendAdapter
     *
     * @see com.udacity.firebase.shoppinglistplusplus.ui.sharing.AutocompleteFriendAdapter
     */
    public static String decodeEmail(String userEmail) {
        return userEmail.replace(",", ".");
    }

    /**
     * Adds values to a pre-existing HashMap for updating a property for all of the ShoppingList copies.
     * The HashMap can then be used with {@link Firebase#updateChildren(Map)} to update the property
     * for all ShoppingList copies.
     *
     * @param sharedWith            The list of users the shopping list that has been updated is shared with.
     * @param listId           The id of the shopping list.
     * @param owner            The owner of the shopping list.
     * @param mapToUpdate      The map containing the key, value pairs which will be used
     *                         to update the Firebase database. This MUST be a Hashmap of key
     *                         value pairs who's urls are absolute (i.e. from the root node)
     * @param propertyToUpdate The property to update
     * @param valueToUpdate    The value to update
     * @return The updated HashMap with the new value inserted in all lists
     */
    public static HashMap<String, Object> updateMapForAllWithValue
    (final HashMap<String, User> sharedWith, final String listId,
     final String owner, HashMap<String, Object> mapToUpdate,
     String propertyToUpdate, Object valueToUpdate) {

        mapToUpdate.put("/" + Constants.FIREBASE_LOCATION_USER_LISTS + "/" + owner + "/"
                + listId + "/" + propertyToUpdate, valueToUpdate);
        if (sharedWith != null) {
            for (User user : sharedWith.values()) {
                mapToUpdate.put("/" + Constants.FIREBASE_LOCATION_USER_LISTS + "/" + user.getEmail() + "/"
                        + listId + "/" + propertyToUpdate, valueToUpdate);
            }
        }

        return mapToUpdate;
    }

    /**
     * Adds values to a pre-existing HashMap for updating all Last Changed Timestamps for all of
     * the ShoppingList copies. This method uses {@link #updateMapForAllWithValue} to update the
     * last changed timestamp for all ShoppingList copies.
     *
     * @param sharedWith           The list of users the shopping list that has been updated is shared with.
     * @param listId               The id of the shopping list.
     * @param owner                The owner of the shopping list.
     * @param mapToAddDateToUpdate The map containing the key, value pairs which will be used
     *                             to update the Firebase database. This MUST be a Hashmap of key
     *                             value pairs who's urls are absolute (i.e. from the root node)
     * @return
     */
    public static HashMap<String, Object> updateMapWithTimestampLastChanged
    (final HashMap<String, User> sharedWith, final String listId,
     final String owner, HashMap<String, Object> mapToAddDateToUpdate) {
        /**
         * Set raw version of date to the ServerValue.TIMESTAMP value and save into dateCreatedMap
         */
        HashMap<String, Object> timestampNowHash = new HashMap<>();
        timestampNowHash.put(Constants.FIREBASE_PROPERTY_TIMESTAMP, ServerValue.TIMESTAMP);

        updateMapForAllWithValue(sharedWith, listId, owner, mapToAddDateToUpdate,
                Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED, timestampNowHash);

        return mapToAddDateToUpdate;
    }
    /**
     * Once an update is made to a ShoppingList, this method is responsible for updating the
     * reversed timestamp to be equal to the negation of the current timestamp. This comes after
     * the updateMapWithTimestampChanged because ServerValue.TIMESTAMP must be resolved to a long
     * value.
     *
     * @param firebaseError      The Firebase error, if there was one, from the original update. This
     *                           method should only run if the shopping list's timestamp last changed
     *                           was successfully updated.
     * @param logTagFromActivity The log tag from the activity calling this method
     * @param listId             The updated shopping list push ID
     * @param sharedWith         The list of users that this updated shopping list is shared with
     * @param owner              The owner of the updated shopping list
     */
    public static void updateTimestampReversed(FirebaseError firebaseError, final String logTagFromActivity,
                                               final String listId, final HashMap<String, User> sharedWith,
                                               final String owner) {
        if (firebaseError != null) {
            Log.d(logTagFromActivity, "Error updating timestamp: " + firebaseError.getMessage());
        } else {
            final Firebase firebaseRef = new Firebase(Constants.FIREBASE_URL);
            firebaseRef.child(Constants.FIREBASE_LOCATION_USER_LISTS).child(owner)
                    .child(listId).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {

                    ShoppingList list = dataSnapshot.getValue(ShoppingList.class);
                    if (list != null) {
                        long timeReverse = -(list.getTimestampLastChangedLong());
                        String timeReverseLocation = Constants.FIREBASE_PROPERTY_TIMESTAMP_LAST_CHANGED_REVERSE
                                + "/" + Constants.FIREBASE_PROPERTY_TIMESTAMP;

                        /**
                         * Create map and fill it in with deep path multi write operations list
                         */
                        HashMap<String, Object> updatedShoppingListData = new HashMap<String, Object>();

                        updateMapForAllWithValue(sharedWith, listId, owner, updatedShoppingListData,
                                timeReverseLocation, timeReverse);
                        firebaseRef.updateChildren(updatedShoppingListData);
                    }
                }

                @Override
                public void onCancelled(FirebaseError firebaseError) {
                    Log.d(logTagFromActivity, "Error updating data: " + firebaseError.getMessage());
                }
            });
        }
    }
}


================================================
FILE: app/src/main/res/layout/activity_active_list_details.xml
================================================
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/grey"
    tools:context=".ui.activeListDetails.ActiveListDetailsActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:theme="@style/Toolbar" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white"
            android:orientation="horizontal"
            android:padding="8dp"
            android:visibility="visible">

            <TextView
                android:id="@+id/text_view_people_shopping"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="start"
                android:layout_weight="1"
                android:textSize="@dimen/list_item_text_size" />

            <Button
                android:id="@+id/button_shopping"
                style="@style/ShoppingButton"
                android:layout_gravity="end"
                android:onClick="toggleShopping"
                />
        </LinearLayout>

        <ListView
            android:id="@+id/list_view_shopping_list_items"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none" />
    </LinearLayout>

    <android.support.design.widget.FloatingActionButton xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/fab_detail_add_meal"
        style="@style/FAB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_toLeftOf="@+id/fab_detail_add_item"
        android:layout_toStartOf="@+id/fab_detail_add_item"
        android:onClick="addMeal"
        android:src="@drawable/abc_ic_menu_copy_mtrl_am_alpha"
        app:borderWidth="0dp"
        app:elevation="6dp"
        app:pressedTranslationZ="12dp"
        android:visibility="gone"/>

    <android.support.design.widget.FloatingActionButton xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/fab_detail_add_item"
        style="@style/FAB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:onClick="showAddListItemDialog"
        android:src="@drawable/icon_add"
        app:borderWidth="0dp"
        app:elevation="6dp"
        app:pressedTranslationZ="12dp" />
</RelativeLayout>




================================================
FILE: app/src/main/res/layout/activity_add_friend.xml
================================================
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.sharing.AddFriendActivity">

    <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:theme="@style/Toolbar" />

    <ListView
        android:id="@+id/list_view_friends_autocomplete"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/til_add_friend_email"
        android:layout_below="@+id/app_bar"
        android:layout_centerHorizontal="true" />

    <android.support.design.widget.TextInputLayout
        android:id="@+id/til_add_friend_email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_margin="@dimen/margin_small">

        <EditText
            android:id="@+id/edit_text_add_friend_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/hint_enter_friends_email"
            android:imeOptions="flagNoExtractUi"
            android:inputType="textEmailAddress"
            android:textColor="@color/black"
            android:textColorHint="@color/dark_grey"
            android:visibility="visible"/>
    </android.support.design.widget.TextInputLayout>

</RelativeLayout>


================================================
FILE: app/src/main/res/layout/activity_create_account.xml
================================================
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    tools:context=".ui.login.CreateAccountActivity">

    <LinearLayout
        android:id="@+id/linear_layout_create_account_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <LinearLayout
            android:layout_width="0px"
            android:layout_height="0px"
            android:focusable="true"
            android:focusableInTouchMode="true" />

        <android.support.design.widget.TextInputLayout
            android:id="@+id/til_username_create"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/lists_title_bottom_margin"
            app:hintTextAppearance="@style/HintText">

            <EditText
                android:id="@+id/edit_text_username_create"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:hint="@string/hint_enter_username"
                android:inputType="textCapWords"
                android:padding="@dimen/edit_text_padding"
                android:textColor="@color/black"
                android:textColorHint="@color/dark_grey"
                android:textSize="@dimen/login_buttons_text_size" />
        </android.support.design.widget.TextInputLayout>

        <android.support.design.widget.TextInputLayout
            android:id="@+id/til_email_create"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:hintTextAppearance="@style/HintText"
            android:layout_marginBottom="@dimen/margin_normal">

            <EditText
                android:id="@+id/edit_text_email_create"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:hint="@string/hint_enter_email"
                android:inputType="textEmailAddress"
                android:padding="@dimen/edit_text_padding"
                android:textColor="@color/black"
                android:textColorHint="@color/dark_grey"
                android:textSize="@dimen/login_buttons_text_size" />
        </android.support.design.widget.TextInputLayout>

        <Button
            android:id="@+id/btn_create_account_final"
            android:layout_width="match_parent"
            android:layout_height="@dimen/input_field_height"
            android:background="@color/primary"
            android:onClick="onCreateAccountPressed"
            android:text="@string/title_activity_create_account"
            android:textColor="@android:color/white"
            android:textSize="@dimen/login_buttons_text_size" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/margin_small"
            android:gravity="center"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/tv_already_have_account"
                style="@style/LoginCreateText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/text_already_have_account" />

            <TextView
                android:id="@+id/tv_sign_in"
                style="@style/LoginCreateTextButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onSignInPressed"
                android:text="@string/button_sign_in" />
        </LinearLayout>
    </LinearLayout>
</ScrollView>

================================================
FILE: app/src/main/res/layout/activity_login.xml
================================================
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    tools:context=".ui.login.LoginActivity">

    <LinearLayout
        android:id="@+id/linear_layout_login_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <LinearLayout
            android:layout_width="0px"
            android:layout_height="0px"
            android:focusable="true"
            android:focusableInTouchMode="true" />

        <ImageView
            android:id="@+id/iv_logo_image"
            android:layout_width="@dimen/logo_image_size"
            android:layout_height="@dimen/logo_image_size"
            android:layout_marginBottom="@dimen/lists_title_bottom_margin"
            android:contentDescription="@string/image_view_logo_image"
            android:src="@drawable/ic_shopping_list" />

        <android.support.design.widget.TextInputLayout
            android:id="@+id/til_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="@dimen/lists_title_bottom_margin"
            app:hintTextAppearance="@style/Hin
Download .txt
gitextract_41bacwm3/

├── .gitignore
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── keystores/
│   │   ├── debug.jks
│   │   └── myDebugKey.jks
│   ├── proguard-rules.pro
│   └── src/
│       ├── androidTest/
│       │   └── java/
│       │       └── com/
│       │           └── udacity/
│       │               └── firebase/
│       │                   └── shoppinglistplusplus/
│       │                       └── ApplicationTest.java
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── udacity/
│           │           └── firebase/
│           │               └── shoppinglistplusplus/
│           │                   ├── ShoppingListApplication.java
│           │                   ├── model/
│           │                   │   ├── ShoppingList.java
│           │                   │   ├── ShoppingListItem.java
│           │                   │   └── User.java
│           │                   ├── ui/
│           │                   │   ├── BaseActivity.java
│           │                   │   ├── MainActivity.java
│           │                   │   ├── SettingsActivity.java
│           │                   │   ├── activeListDetails/
│           │                   │   │   ├── ActiveListDetailsActivity.java
│           │                   │   │   ├── ActiveListItemAdapter.java
│           │                   │   │   ├── AddListItemDialogFragment.java
│           │                   │   │   ├── EditListDialogFragment.java
│           │                   │   │   ├── EditListItemNameDialogFragment.java
│           │                   │   │   ├── EditListNameDialogFragment.java
│           │                   │   │   └── RemoveListDialogFragment.java
│           │                   │   ├── activeLists/
│           │                   │   │   ├── ActiveListAdapter.java
│           │                   │   │   ├── AddListDialogFragment.java
│           │                   │   │   └── ShoppingListsFragment.java
│           │                   │   ├── login/
│           │                   │   │   ├── CreateAccountActivity.java
│           │                   │   │   └── LoginActivity.java
│           │                   │   ├── meals/
│           │                   │   │   ├── AddMealDialogFragment.java
│           │                   │   │   └── MealsFragment.java
│           │                   │   └── sharing/
│           │                   │       ├── AddFriendActivity.java
│           │                   │       ├── AutocompleteFriendAdapter.java
│           │                   │       ├── FriendAdapter.java
│           │                   │       └── ShareListActivity.java
│           │                   └── utils/
│           │                       ├── Constants.java
│           │                       └── Utils.java
│           └── res/
│               ├── layout/
│               │   ├── activity_active_list_details.xml
│               │   ├── activity_add_friend.xml
│               │   ├── activity_create_account.xml
│               │   ├── activity_login.xml
│               │   ├── activity_main.xml
│               │   ├── activity_share_list.xml
│               │   ├── dialog_add_item.xml
│               │   ├── dialog_add_list.xml
│               │   ├── dialog_add_meal.xml
│               │   ├── dialog_edit_item.xml
│               │   ├── dialog_edit_list.xml
│               │   ├── footer_empty.xml
│               │   ├── fragment_meals.xml
│               │   ├── fragment_shopping_lists.xml
│               │   ├── single_active_list.xml
│               │   ├── single_active_list_item.xml
│               │   ├── single_autocomplete_item.xml
│               │   └── single_user_item.xml
│               ├── layout-land/
│               │   ├── activity_create_account.xml
│               │   └── activity_login.xml
│               ├── menu/
│               │   ├── menu_base.xml
│               │   ├── menu_list_details.xml
│               │   └── menu_main.xml
│               ├── values/
│               │   ├── arrays.xml
│               │   ├── colors.xml
│               │   ├── dimens.xml
│               │   ├── strings.xml
│               │   ├── styles.xml
│               │   └── themes.xml
│               ├── values-sw480dp/
│               │   └── dimens.xml
│               ├── values-sw640dp/
│               │   └── dimens.xml
│               ├── values-v21/
│               │   ├── styles.xml
│               │   └── themes.xml
│               ├── values-w480dp/
│               │   └── dimens.xml
│               ├── values-w820dp/
│               │   └── dimens.xml
│               └── xml/
│                   └── preference_screen.xml
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── rules/
│   ├── git.log
│   ├── name_branches_commits.py
│   ├── rules.bolt
│   └── rules.json
└── settings.gradle
Download .txt
SYMBOL INDEX (206 symbols across 29 files)

FILE: app/src/androidTest/java/com/udacity/firebase/shoppinglistplusplus/ApplicationTest.java
  class ApplicationTest (line 9) | public class ApplicationTest extends ApplicationTestCase<Application> {
    method ApplicationTest (line 10) | public ApplicationTest() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ShoppingListApplication.java
  class ShoppingListApplication (line 8) | public class ShoppingListApplication extends android.app.Application {
    method onCreate (line 10) | @Override

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java
  class ShoppingList (line 13) | public class ShoppingList {
    method ShoppingList (line 24) | public ShoppingList() {
    method ShoppingList (line 35) | public ShoppingList(String listName, String owner, HashMap<String, Obj...
    method getListName (line 46) | public String getListName() {
    method getOwner (line 50) | public String getOwner() {
    method getTimestampLastChanged (line 54) | public HashMap<String, Object> getTimestampLastChanged() {
    method getTimestampCreated (line 58) | public HashMap<String, Object> getTimestampCreated() {
    method getTimestampLastChangedReverse (line 62) | public HashMap<String, Object> getTimestampLastChangedReverse() {
    method getTimestampLastChangedLong (line 66) | @JsonIgnore
    method getTimestampCreatedLong (line 72) | @JsonIgnore
    method getTimestampLastChangedReverseLong (line 77) | @JsonIgnore
    method getUsersShopping (line 83) | public HashMap getUsersShopping() {
    method setTimestampLastChangedToNow (line 87) | public void setTimestampLastChangedToNow() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java
  class ShoppingListItem (line 6) | public class ShoppingListItem {
    method ShoppingListItem (line 15) | public ShoppingListItem() {
    method ShoppingListItem (line 25) | public ShoppingListItem(String itemName, String owner) {
    method getItemName (line 33) | public String getItemName() { return itemName; }
    method getOwner (line 35) | public String getOwner() {
    method getBoughtBy (line 39) | public String getBoughtBy() {
    method isBought (line 43) | public boolean isBought() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/User.java
  class User (line 8) | public class User {
    method User (line 18) | public User() {
    method User (line 29) | public User(String name, String email, HashMap<String, Object> timesta...
    method getName (line 36) | public String getName() {
    method getEmail (line 40) | public String getEmail() {
    method getTimestampJoined (line 44) | public HashMap<String, Object> getTimestampJoined() {
    method isHasLoggedInWithPassword (line 48) | public boolean isHasLoggedInWithPassword() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/BaseActivity.java
  class BaseActivity (line 31) | public abstract class BaseActivity extends AppCompatActivity implements
    method onCreate (line 39) | @Override
    method onDestroy (line 88) | @Override
    method onSaveInstanceState (line 98) | @Override
    method onCreateOptionsMenu (line 103) | @Override
    method onOptionsItemSelected (line 110) | @Override
    method initializeBackground (line 127) | protected void initializeBackground(LinearLayout linearLayout) {
    method logout (line 143) | protected void logout() {
    method takeUserToLoginScreenOnUnAuth (line 163) | private void takeUserToLoginScreenOnUnAuth() {
    method onConnectionFailed (line 171) | @Override

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/MainActivity.java
  class MainActivity (line 33) | public class MainActivity extends BaseActivity {
    method onCreate (line 38) | @Override
    method onCreateOptionsMenu (line 88) | @Override
    method onOptionsItemSelected (line 100) | @Override
    method onDestroy (line 114) | @Override
    method initializeScreen (line 123) | public void initializeScreen() {
    method showAddListDialog (line 143) | public void showAddListDialog(View view) {
    method showAddMealDialog (line 152) | public void showAddMealDialog(View view) {
    class SectionPagerAdapter (line 161) | public class SectionPagerAdapter extends FragmentStatePagerAdapter {
      method SectionPagerAdapter (line 163) | public SectionPagerAdapter(FragmentManager fm) {
      method getItem (line 172) | @Override
      method getCount (line 196) | @Override
      method getPageTitle (line 206) | @Override

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/SettingsActivity.java
  class SettingsActivity (line 17) | public class SettingsActivity extends PreferenceActivity {
    method onCreate (line 19) | @Override
    class SortPreferenceFragment (line 32) | public static class SortPreferenceFragment extends PreferenceFragment ...
      method onCreate (line 33) | @Override
      method onPreferenceChange (line 51) | @Override
      method bindPreferenceSummaryToValue (line 60) | private void bindPreferenceSummaryToValue(Preference preference) {
      method setPreferenceSummary (line 76) | private void setPreferenceSummary(Preference preference, Object valu...

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java
  class ActiveListDetailsActivity (line 39) | public class ActiveListDetailsActivity extends BaseActivity {
    method onCreate (line 56) | @Override
    method onCreateOptionsMenu (line 282) | @Override
    method onOptionsItemSelected (line 304) | @Override
    method onDestroy (line 350) | @Override
    method initializeScreen (line 362) | private void initializeScreen() {
    method setWhosShoppingText (line 382) | private void setWhosShoppingText(HashMap<String, User> usersShopping) {
    method archiveList (line 456) | public void archiveList() {
    method addMeal (line 464) | public void addMeal(View view) {
    method removeList (line 470) | public void removeList() {
    method showAddListItemDialog (line 480) | public void showAddListItemDialog(View view) {
    method showEditListNameDialog (line 490) | public void showEditListNameDialog() {
    method showEditListItemNameDialog (line 503) | public void showEditListItemNameDialog(String itemName, String itemId) {
    method toggleShopping (line 514) | public void toggleShopping(View view) {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java
  class ActiveListItemAdapter (line 31) | public class ActiveListItemAdapter extends FirebaseListAdapter<ShoppingL...
    method ActiveListItemAdapter (line 40) | public ActiveListItemAdapter(Activity activity, Class<ShoppingListItem...
    method setShoppingList (line 51) | public void setShoppingList(ShoppingList shoppingList) {
    method setSharedWithUsers (line 56) | public void setSharedWithUsers(HashMap<String, User> sharedWithUsers) {
    method populateView (line 66) | @Override
    method removeItem (line 118) | private void removeItem(String itemId) {
    method setItemAppearanceBaseOnBoughtStatus (line 142) | private void setItemAppearanceBaseOnBoughtStatus(String owner, final T...

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java
  class AddListItemDialogFragment (line 22) | public class AddListItemDialogFragment extends EditListDialogFragment {
    method newInstance (line 27) | public static AddListItemDialogFragment newInstance(ShoppingList shopp...
    method onCreate (line 41) | @Override
    method onCreateDialog (line 46) | @Override
    method doListEdit (line 57) | @Override

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java
  class EditListDialogFragment (line 26) | public abstract class EditListDialogFragment extends DialogFragment {
    method newInstanceHelper (line 44) | protected static Bundle newInstanceHelper(ShoppingList shoppingList, i...
    method onCreate (line 58) | @Override
    method onActivityCreated (line 71) | @Override
    method createDialogHelper (line 77) | protected Dialog createDialogHelper(int stringResourceForPositiveButto...
    method helpSetDefaultValueEditText (line 138) | protected void helpSetDefaultValueEditText(String defaultText) {
    method doListEdit (line 146) | protected abstract void doListEdit();

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java
  class EditListItemNameDialogFragment (line 19) | public class EditListItemNameDialogFragment extends EditListDialogFragme...
    method newInstance (line 25) | public static EditListItemNameDialogFragment newInstance(ShoppingList ...
    method onCreate (line 42) | @Override
    method onCreateDialog (line 50) | @Override
    method doListEdit (line 69) | protected void doListEdit() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java
  class EditListNameDialogFragment (line 19) | public class EditListNameDialogFragment extends EditListDialogFragment {
    method newInstance (line 26) | public static EditListNameDialogFragment newInstance(ShoppingList shop...
    method onCreate (line 40) | @Override
    method onCreateDialog (line 47) | @Override
    method doListEdit (line 65) | protected void doListEdit() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java
  class RemoveListDialogFragment (line 23) | public class RemoveListDialogFragment extends DialogFragment {
    method newInstance (line 33) | public static RemoveListDialogFragment newInstance(ShoppingList shoppi...
    method onCreate (line 47) | @Override
    method onCreateDialog (line 55) | @Override
    method removeList (line 78) | private void removeList() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java
  class ActiveListAdapter (line 22) | public class ActiveListAdapter extends FirebaseListAdapter<ShoppingList> {
    method ActiveListAdapter (line 28) | public ActiveListAdapter(Activity activity, Class<ShoppingList> modelC...
    method populateView (line 40) | @Override

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java
  class AddListDialogFragment (line 31) | public class AddListDialogFragment extends DialogFragment {
    method newInstance (line 39) | public static AddListDialogFragment newInstance(String encodedEmail) {
    method onCreate (line 50) | @Override
    method onActivityCreated (line 59) | @Override
    method onCreateDialog (line 65) | @Override
    method addShoppingList (line 104) | public void addShoppingList() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java
  class ShoppingListsFragment (line 28) | public class ShoppingListsFragment extends Fragment {
    method ShoppingListsFragment (line 33) | public ShoppingListsFragment() {
    method newInstance (line 41) | public static ShoppingListsFragment newInstance(String encodedEmail) {
    method onCreate (line 52) | @Override
    method onCreateView (line 60) | @Override
    method onResume (line 96) | @Override
    method onPause (line 137) | @Override
    method initializeScreen (line 146) | private void initializeScreen(View rootView) {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java
  class CreateAccountActivity (line 34) | public class CreateAccountActivity extends BaseActivity {
    method onCreate (line 43) | @Override
    method onCreateOptionsMenu (line 64) | @Override
    method initializeScreen (line 73) | public void initializeScreen() {
    method onSignInPressed (line 89) | public void onSignInPressed(View view) {
    method onCreateAccountPressed (line 99) | public void onCreateAccountPressed(View view) {
    method createUserInFirebaseHelper (line 208) | private void createUserInFirebaseHelper(final String authUserId) {
    method isEmailValid (line 249) | private boolean isEmailValid(String email) {
    method isUserNameValid (line 260) | private boolean isUserNameValid(String userName) {
    method showErrorToast (line 272) | private void showErrorToast(String message) {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java
  class LoginActivity (line 51) | public class LoginActivity extends BaseActivity {
    method onCreate (line 75) | @Override
    method onResume (line 107) | @Override
    method onPause (line 157) | @Override
    method onCreateOptionsMenu (line 168) | @Override
    method onSignInPressed (line 177) | public void onSignInPressed(View view) {
    method onSignUpPressed (line 184) | public void onSignUpPressed(View view) {
    method initializeScreen (line 192) | public void initializeScreen() {
    method signInPassword (line 209) | public void signInPassword() {
    class MyAuthResultHandler (line 233) | private class MyAuthResultHandler implements Firebase.AuthResultHandler {
      method MyAuthResultHandler (line 237) | public MyAuthResultHandler(String provider) {
      method onAuthenticated (line 245) | @Override
      method onAuthenticationError (line 278) | @Override
    method setAuthenticatedUserPasswordProvider (line 310) | private void setAuthenticatedUserPasswordProvider(AuthData authData) {
    method setAuthenticatedUserGoogle (line 374) | private void setAuthenticatedUserGoogle(final AuthData authData) {
    method showErrorToast (line 436) | private void showErrorToast(String message) {
    method loginWithGoogle (line 446) | private void loginWithGoogle(String token) {
    method setupGoogleSignIn (line 472) | private void setupGoogleSignIn() {
    method onSignInGooglePressed (line 486) | public void onSignInGooglePressed(View view) {
    method onConnectionFailed (line 493) | @Override
    method onActivityResult (line 508) | @Override
    method handleSignInResult (line 519) | private void handleSignInResult(GoogleSignInResult result) {
    method getGoogleOAuthTokenAndLogin (line 540) | private void getGoogleOAuthTokenAndLogin() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/AddMealDialogFragment.java
  class AddMealDialogFragment (line 21) | public class AddMealDialogFragment extends DialogFragment {
    method newInstance (line 28) | public static AddMealDialogFragment newInstance() {
    method onCreate (line 38) | @Override
    method onActivityCreated (line 46) | @Override
    method onCreateDialog (line 52) | @Override
    method addMeal (line 91) | public void addMeal() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/MealsFragment.java
  class MealsFragment (line 20) | public class MealsFragment extends Fragment {
    method newInstance (line 26) | public static MealsFragment newInstance() {
    method MealsFragment (line 33) | public MealsFragment() {
    method onSaveInstanceState (line 37) | @Override
    method onActivityCreated (line 42) | @Override
    method onCreate (line 47) | @Override
    method onCreateView (line 52) | @Override
    method onResume (line 76) | @Override
    method onPause (line 81) | @Override
    method initializeScreen (line 86) | private void initializeScreen(View rootView) {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AddFriendActivity.java
  class AddFriendActivity (line 19) | public class AddFriendActivity extends BaseActivity {
    method onCreate (line 29) | @Override
    method onDestroy (line 81) | @Override
    method initializeScreen (line 92) | public void initializeScreen() {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AutocompleteFriendAdapter.java
  class AutocompleteFriendAdapter (line 23) | public class AutocompleteFriendAdapter extends FirebaseListAdapter<User> {
    method AutocompleteFriendAdapter (line 29) | public AutocompleteFriendAdapter(Activity activity, Class<User> modelC...
    method populateView (line 41) | @Override
    method isNotCurrentUser (line 94) | private boolean isNotCurrentUser(User user) {
    method isNotAlreadyAdded (line 105) | private boolean isNotAlreadyAdded(DataSnapshot dataSnapshot, User user) {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/FriendAdapter.java
  class FriendAdapter (line 28) | public class FriendAdapter extends FirebaseListAdapter<User> {
    method FriendAdapter (line 40) | public FriendAdapter(Activity activity, Class<User> modelClass, int mo...
    method populateView (line 54) | @Override
    method setShoppingList (line 147) | public void setShoppingList(ShoppingList shoppingList) {
    method setSharedWithUsers (line 155) | public void setSharedWithUsers(HashMap<String, User> sharedUsersList) {
    method updateFriendInSharedWith (line 166) | private HashMap<String, Object> updateFriendInSharedWith(Boolean addFr...
    method cleanup (line 213) | @Override

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/ShareListActivity.java
  class ShareListActivity (line 25) | public class ShareListActivity extends BaseActivity {
    method onCreate (line 36) | @Override
    method onDestroy (line 126) | @Override
    method initializeScreen (line 138) | public void initializeScreen() {
    method onAddFriendPressed (line 152) | public void onAddFriendPressed(View view) {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java
  class Constants (line 8) | public final class Constants {

FILE: app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java
  class Utils (line 21) | public class Utils {
    method Utils (line 32) | public Utils(Context con) {
    method checkIfOwner (line 40) | public static boolean checkIfOwner(ShoppingList shoppingList, String c...
    method encodeEmail (line 49) | public static String encodeEmail(String userEmail) {
    method decodeEmail (line 58) | public static String decodeEmail(String userEmail) {
    method updateMapForAllWithValue (line 77) | public static HashMap<String, Object> updateMapForAllWithValue
    method updateMapWithTimestampLastChanged (line 107) | public static HashMap<String, Object> updateMapWithTimestampLastChanged
    method updateTimestampReversed (line 135) | public static void updateTimestampReversed(FirebaseError firebaseError...

FILE: rules/name_branches_commits.py
  function sanitize (line 11) | def sanitize(message):
Condensed preview — 82 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (312K chars).
[
  {
    "path": ".gitignore",
    "chars": 496,
    "preview": "#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/"
  },
  {
    "path": "README.md",
    "chars": 2548,
    "preview": "ShoppingList++\n========\n\nShoppingList++ is the companion Android app for the Udacity course [Firebase Essentials : Build"
  },
  {
    "path": "app/.gitignore",
    "chars": 417,
    "preview": "#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/"
  },
  {
    "path": "app/build.gradle",
    "chars": 1587,
    "preview": "apply plugin: 'com.android.application'\napply plugin: 'com.google.gms.google-services'\n\nandroid {\n    compileSdkVersion "
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 663,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /U"
  },
  {
    "path": "app/src/androidTest/java/com/udacity/firebase/shoppinglistplusplus/ApplicationTest.java",
    "chars": 372,
    "preview": "package com.udacity.firebase.shoppinglistplusplus;\n\nimport android.app.Application;\nimport android.test.ApplicationTestC"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 3167,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package="
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ShoppingListApplication.java",
    "chars": 488,
    "preview": "package com.udacity.firebase.shoppinglistplusplus;\n\nimport com.firebase.client.Firebase;\n\n/**\n * Includes one-time initi"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingList.java",
    "chars": 2835,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.model;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport com"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/ShoppingListItem.java",
    "chars": 994,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.model;\n\n/**\n * Defines the data structure for ShoppingListItem objects"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/model/User.java",
    "chars": 1143,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.model;\n\nimport java.util.HashMap;\n\n/**\n * Defines the data structure f"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/BaseActivity.java",
    "chars": 6522,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui;\n\nimport android.content.Intent;\nimport android.content.SharedPrefe"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/MainActivity.java",
    "chars": 7377,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui;\n\nimport android.app.DialogFragment;\nimport android.content.Intent;"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/SettingsActivity.java",
    "chars": 3425,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui;\n\nimport android.content.SharedPreferences;\nimport android.os.Bundl"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListDetailsActivity.java",
    "chars": 24159,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Activity;\nimport android.app"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/ActiveListItemAdapter.java",
    "chars": 9179,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Activity;\nimport android.con"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/AddListItemDialogFragment.java",
    "chars": 4226,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.os.Bu"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListDialogFragment.java",
    "chars": 6107,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.app.D"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListItemNameDialogFragment.java",
    "chars": 4235,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.os.Bu"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/EditListNameDialogFragment.java",
    "chars": 4221,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.os.Bu"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeListDetails/RemoveListDialogFragment.java",
    "chars": 4467,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeListDetails;\n\nimport android.app.Dialog;\nimport android.app.D"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ActiveListAdapter.java",
    "chars": 4385,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;\n\nimport android.app.Activity;\nimport android.util.Log;"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/AddListDialogFragment.java",
    "chars": 6213,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;\n\nimport android.app.Dialog;\nimport android.app.DialogF"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/activeLists/ShoppingListsFragment.java",
    "chars": 5265,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.activeLists;\n\n\nimport android.content.Intent;\nimport android.conten"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/CreateAccountActivity.java",
    "chars": 11235,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.login;\n\nimport android.app.ProgressDialog;\nimport android.content.I"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/login/LoginActivity.java",
    "chars": 24070,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.login;\n\nimport android.app.ProgressDialog;\nimport android.content.I"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/AddMealDialogFragment.java",
    "chars": 3227,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.meals;\n\nimport android.app.Dialog;\nimport android.app.DialogFragmen"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/meals/MealsFragment.java",
    "chars": 2553,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.meals;\n\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fr"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AddFriendActivity.java",
    "chars": 3567,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.os.Bundle;\nimport android.support.v7.widge"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/AutocompleteFriendAdapter.java",
    "chars": 4873,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.app.Activity;\nimport android.util.Log;\nimp"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/FriendAdapter.java",
    "chars": 10255,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.app.Activity;\nimport android.util.Log;\nimp"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/ui/sharing/ShareListActivity.java",
    "chars": 5808,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.ui.sharing;\n\nimport android.content.Intent;\nimport android.os.Bundle;\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Constants.java",
    "chars": 3858,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.utils;\n\nimport com.udacity.firebase.shoppinglistplusplus.BuildConfig;\n"
  },
  {
    "path": "app/src/main/java/com/udacity/firebase/shoppinglistplusplus/utils/Utils.java",
    "chars": 7995,
    "preview": "package com.udacity.firebase.shoppinglistplusplus.utils;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimpo"
  },
  {
    "path": "app/src/main/res/layout/activity_active_list_details.xml",
    "chars": 3156,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk"
  },
  {
    "path": "app/src/main/res/layout/activity_add_friend.xml",
    "chars": 1813,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/t"
  },
  {
    "path": "app/src/main/res/layout/activity_create_account.xml",
    "chars": 4389,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res"
  },
  {
    "path": "app/src/main/res/layout/activity_login.xml",
    "chars": 5090,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "chars": 891,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    and"
  },
  {
    "path": "app/src/main/res/layout/activity_share_list.xml",
    "chars": 1875,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/too"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_item.xml",
    "chars": 1012,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/t"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_list.xml",
    "chars": 984,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/t"
  },
  {
    "path": "app/src/main/res/layout/dialog_add_meal.xml",
    "chars": 975,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/t"
  },
  {
    "path": "app/src/main/res/layout/dialog_edit_item.xml",
    "chars": 914,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/t"
  },
  {
    "path": "app/src/main/res/layout/dialog_edit_list.xml",
    "chars": 922,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/t"
  },
  {
    "path": "app/src/main/res/layout/footer_empty.xml",
    "chars": 629,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/fragment_meals.xml",
    "chars": 1161,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk"
  },
  {
    "path": "app/src/main/res/layout/fragment_shopping_lists.xml",
    "chars": 1444,
    "preview": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk"
  },
  {
    "path": "app/src/main/res/layout/single_active_list.xml",
    "chars": 2242,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/single_active_list_item.xml",
    "chars": 1996,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "app/src/main/res/layout/single_autocomplete_item.xml",
    "chars": 802,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/single_user_item.xml",
    "chars": 1315,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "app/src/main/res/layout-land/activity_create_account.xml",
    "chars": 4530,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res"
  },
  {
    "path": "app/src/main/res/layout-land/activity_login.xml",
    "chars": 4801,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res"
  },
  {
    "path": "app/src/main/res/menu/menu_base.xml",
    "chars": 419,
    "preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\""
  },
  {
    "path": "app/src/main/res/menu/menu_list_details.xml",
    "chars": 1196,
    "preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\""
  },
  {
    "path": "app/src/main/res/menu/menu_main.xml",
    "chars": 665,
    "preview": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\""
  },
  {
    "path": "app/src/main/res/values/arrays.xml",
    "chars": 712,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string-array name=\"pref_sortType_entries_lists\">\n        <item>@"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 502,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"grey\">#EDEEEF</color>\n    <color name=\"dark_green\">#"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "chars": 1073,
    "preview": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 9180,
    "preview": "<resources>\n    <!--\n\n    == Base Strings For the App ==\n\n     -->\n    <string name=\"app_name\">ShoppingList++</string>\n "
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 1489,
    "preview": "<resources>\n\n    <style name=\"ListItemText\">\n        <item name=\"android:textColor\">@color/light_black</item>\n        <i"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "chars": 2305,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppTheme\" parent=\"AppTheme.Base\">\n    </style>\n\n   "
  },
  {
    "path": "app/src/main/res/values-sw480dp/dimens.xml",
    "chars": 886,
    "preview": "<resources>\n\n    <!-- Dimens for text -->\n    <dimen name=\"list_created_by_text_size\">18sp</dimen>\n    <dimen name=\"lv_h"
  },
  {
    "path": "app/src/main/res/values-sw640dp/dimens.xml",
    "chars": 888,
    "preview": "<resources>\n    <!-- Dimens for text -->\n    <dimen name=\"list_created_by_text_size\">24sp</dimen>\n    <dimen name=\"lv_he"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "chars": 277,
    "preview": "<resources>\n    <style name=\"FAB\">\n        <item name=\"android:layout_margin\">16dp</item>\n        <item name=\"fabSize\">n"
  },
  {
    "path": "app/src/main/res/values-v21/themes.xml",
    "chars": 838,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"AppTheme\" parent=\"AppTheme.Base\">\n    </style>\n\n   "
  },
  {
    "path": "app/src/main/res/values-w480dp/dimens.xml",
    "chars": 354,
    "preview": " <resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n      (such as scr"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "chars": 358,
    "preview": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as s"
  },
  {
    "path": "app/src/main/res/xml/preference_screen.xml",
    "chars": 598,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n   "
  },
  {
    "path": "build.gradle",
    "chars": 499,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    r"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 232,
    "preview": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
  },
  {
    "path": "gradlew",
    "chars": 5080,
    "preview": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start "
  },
  {
    "path": "gradlew.bat",
    "chars": 2404,
    "preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
  },
  {
    "path": "rules/git.log",
    "chars": 24232,
    "preview": "commit f73d6708fe2217b1dd19cd0a088e45e0f2c15147 (HEAD, origin/master, master)\nAuthor: Lyla <lyla@udacity.com>\nDate:   Th"
  },
  {
    "path": "rules/name_branches_commits.py",
    "chars": 1925,
    "preview": "import re\nimport subprocess\n\ngit_message_to_branch = {}\n\n#BRANCH_REGEX = re.compile('origin/(\\d[^,]*)[\\),]')\n\nBRANCH_NAM"
  },
  {
    "path": "rules/rules.bolt",
    "chars": 5203,
    "preview": "/** NOTE: This is just an example file for what Bolt code looks like. The rules.json file does not\n mirror the output of"
  },
  {
    "path": "rules/rules.json",
    "chars": 10270,
    "preview": "{\n  \"rules\": {\n    \"sharedWith\": {\n      \"$listId\": {\n        \".read\": \"auth != null && root.child('uidMappings').child("
  },
  {
    "path": "settings.gradle",
    "chars": 15,
    "preview": "include ':app'\n"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the udacity/ShoppingListPlusPlus GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 82 files (287.6 KB), approximately 68.3k tokens, and a symbol index with 206 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!