Merge tag android-5.1.0_r1 into AOSP_5.1_MERGE

Change-Id: Id5a5110ed14672cd06b283267f8d35c0e085149f
diff --git a/.gitignore b/.gitignore
index a762655..dd5dda7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,18 +9,22 @@
 *.class
 
 # generated files
+.google/
 bin/
 out/
 src/common
 src/template
 _index.jd
-README.txt
+CONTRIBUTING.md
+LICENSE
+NOTICE
+README.md
+README-wear.txt
 
 # Libraries used by the app
 # Can explicitly add if we want, but shouldn't do so blindly.  Licenses, bloat, etc.
 /libs
 
-
 # Build stuff (auto-generated by android update project ...)
 ant.properties
 local.properties
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java
index 6db54f6..8b0620f 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/AppRestrictionEnforcerFragment.java
@@ -22,34 +22,34 @@
 import android.content.RestrictionEntry;
 import android.content.RestrictionsManager;
 import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Button;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
 import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
 import android.widget.Switch;
-import android.widget.TextView;
 import android.widget.Toast;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
  * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
  * sample.
  */
-public class AppRestrictionEnforcerFragment extends Fragment implements View.OnClickListener,
-        CompoundButton.OnCheckedChangeListener {
-
-    /**
-     * Package name of the AppRestrictionSchema sample.
-     */
-    private static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
-            = "com.example.android.apprestrictionschema";
+public class AppRestrictionEnforcerFragment extends Fragment implements
+        CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
 
     /**
      * Key for {@link SharedPreferences}
@@ -62,15 +62,38 @@
     private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello";
 
     /**
-     * Default boolean value for "can_say_hello" restriction. The actual value is loaded in
-     * {@link #loadRestrictions(android.app.Activity)}.
+     * Key for the string restriction in AppRestrictionSchema.
      */
-    private boolean mDefaultValueRestrictionSayHello;
+    private static final String RESTRICTION_KEY_MESSAGE = "message";
+
+    /**
+     * Key for the integer restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_NUMBER = "number";
+
+    /**
+     * Key for the choice restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_RANK = "rank";
+
+    /**
+     * Key for the multi-select restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_APPROVALS = "approvals";
+
+    private static final String DELIMETER = ",";
+
+    /**
+     * Current status of the restrictions.
+     */
+    private Bundle mCurrentRestrictions = new Bundle();
 
     // UI Components
-    private TextView mTextStatus;
-    private Button mButtonUnhide;
     private Switch mSwitchSayHello;
+    private EditText mEditMessage;
+    private EditText mEditNumber;
+    private Spinner mSpinnerRank;
+    private LinearLayout mLayoutApprovals;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -80,99 +103,72 @@
 
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        mTextStatus = (TextView) view.findViewById(R.id.status);
-        mButtonUnhide = (Button) view.findViewById(R.id.unhide);
+        // Retain references for the UI elements
         mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello);
-        mButtonUnhide.setOnClickListener(this);
-        mSwitchSayHello.setOnCheckedChangeListener(this);
+        mEditMessage = (EditText) view.findViewById(R.id.message);
+        mEditNumber = (EditText) view.findViewById(R.id.number);
+        mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
+        mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        updateUi(getActivity());
-    }
-
-    @Override
-    public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.unhide: {
-                unhideApp(getActivity());
-                break;
-            }
-        }
+        loadRestrictions(getActivity());
     }
 
     @Override
     public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
         switch (compoundButton.getId()) {
             case R.id.say_hello: {
-                allowSayHello(getActivity(), checked);
+                saveCanSayHello(getActivity(), checked);
+                break;
+            }
+            case R.id.approval: {
+                if (checked) {
+                    addApproval(getActivity(), (String) compoundButton.getTag());
+                } else {
+                    removeApproval(getActivity(), (String) compoundButton.getTag());
+                }
                 break;
             }
         }
     }
 
-    /**
-     * Updates the UI components according to the current status of AppRestrictionSchema and its
-     * restriction.
-     *
-     * @param activity The activity
-     */
-    private void updateUi(Activity activity) {
-        PackageManager packageManager = activity.getPackageManager();
-        try {
-            ApplicationInfo info = packageManager.getApplicationInfo(
-                    PACKAGE_NAME_APP_RESTRICTION_SCHEMA, PackageManager.GET_UNINSTALLED_PACKAGES);
-            DevicePolicyManager devicePolicyManager =
-                    (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
-            if (0 < (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
-                if (!devicePolicyManager.isApplicationHidden(
-                        EnforcerDeviceAdminReceiver.getComponentName(activity),
-                        PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
-                    // The app is ready
-                    loadRestrictions(activity);
-                    mTextStatus.setVisibility(View.GONE);
-                    mButtonUnhide.setVisibility(View.GONE);
-                    mSwitchSayHello.setVisibility(View.VISIBLE);
-                    mSwitchSayHello.setOnCheckedChangeListener(null);
-                    mSwitchSayHello.setChecked(canSayHello(activity));
-                    mSwitchSayHello.setOnCheckedChangeListener(this);
-                } else {
-                    // The app is installed but hidden in this profile
-                    mTextStatus.setText(R.string.status_not_activated);
-                    mTextStatus.setVisibility(View.VISIBLE);
-                    mButtonUnhide.setVisibility(View.VISIBLE);
-                    mSwitchSayHello.setVisibility(View.GONE);
+    private TextWatcher mWatcherMessage = new EasyTextWatcher() {
+        @Override
+        public void afterTextChanged(Editable s) {
+            saveMessage(getActivity(), s.toString());
+        }
+    };
+
+    private TextWatcher mWatcherNumber = new EasyTextWatcher() {
+        @Override
+        public void afterTextChanged(Editable s) {
+            try {
+                String string = s.toString();
+                if (!TextUtils.isEmpty(string)) {
+                    saveNumber(getActivity(), Integer.parseInt(string));
                 }
-            } else {
-                // Need to reinstall the sample app
-                mTextStatus.setText(R.string.status_need_reinstall);
-                mTextStatus.setVisibility(View.VISIBLE);
-                mButtonUnhide.setVisibility(View.GONE);
-                mSwitchSayHello.setVisibility(View.GONE);
+            } catch (NumberFormatException e) {
+                Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            mTextStatus.setText(R.string.status_not_installed);
-            mTextStatus.setVisibility(View.VISIBLE);
-            mButtonUnhide.setVisibility(View.GONE);
-            mSwitchSayHello.setVisibility(View.GONE);
+        }
+    };
+
+    @Override
+    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+        switch (parent.getId()) {
+            case R.id.rank: {
+                saveRank(getActivity(), (String) parent.getAdapter().getItem(position));
+                break;
+            }
         }
     }
 
-    /**
-     * Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
-     *
-     * @param activity The activity
-     */
-    private void unhideApp(Activity activity) {
-        DevicePolicyManager devicePolicyManager =
-                (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
-        devicePolicyManager.setApplicationHidden(
-                EnforcerDeviceAdminReceiver.getComponentName(activity),
-                PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
-        Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
-        updateUi(activity);
+    @Override
+    public void onNothingSelected(AdapterView<?> parent) {
+        // Nothing to do
     }
 
     /**
@@ -182,50 +178,187 @@
      * @param activity The activity
      */
     private void loadRestrictions(Activity activity) {
-        RestrictionsManager restrictionsManager =
+        RestrictionsManager manager =
                 (RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE);
         List<RestrictionEntry> restrictions =
-                restrictionsManager.getManifestRestrictions(PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
+                manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
+        SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
         for (RestrictionEntry restriction : restrictions) {
-            if (RESTRICTION_KEY_SAY_HELLO.equals(restriction.getKey())) {
-                mDefaultValueRestrictionSayHello = restriction.getSelectedState();
+            String key = restriction.getKey();
+            if (RESTRICTION_KEY_SAY_HELLO.equals(key)) {
+                updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO,
+                        restriction.getSelectedState()));
+            } else if (RESTRICTION_KEY_MESSAGE.equals(key)) {
+                updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE,
+                        restriction.getSelectedString()));
+            } else if (RESTRICTION_KEY_NUMBER.equals(key)) {
+                updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER,
+                        restriction.getIntValue()));
+            } else if (RESTRICTION_KEY_RANK.equals(key)) {
+                updateRank(activity, restriction.getChoiceValues(),
+                        prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString()));
+            } else if (RESTRICTION_KEY_APPROVALS.equals(key)) {
+                updateApprovals(activity, restriction.getChoiceValues(),
+                        TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS,
+                                        TextUtils.join(DELIMETER,
+                                                restriction.getAllSelectedStrings())),
+                                DELIMETER));
             }
         }
     }
 
-    /**
-     * Returns whether the AppRestrictionSchema is currently allowed to say hello to its user. Note
-     * that a profile/device owner needs to remember each restriction value on its own.
-     *
-     * @param activity The activity
-     * @return True if the AppRestrictionSchema is allowed to say hello
-     */
-    private boolean canSayHello(Activity activity) {
-        SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
-        return prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO, mDefaultValueRestrictionSayHello);
+    private void updateCanSayHello(boolean canSayHello) {
+        mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello);
+        mSwitchSayHello.setOnCheckedChangeListener(null);
+        mSwitchSayHello.setChecked(canSayHello);
+        mSwitchSayHello.setOnCheckedChangeListener(this);
+    }
+
+    private void updateMessage(String message) {
+        mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
+        mEditMessage.removeTextChangedListener(mWatcherMessage);
+        mEditMessage.setText(message);
+        mEditMessage.addTextChangedListener(mWatcherMessage);
+    }
+
+    private void updateNumber(int number) {
+        mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
+        mEditNumber.removeTextChangedListener(mWatcherNumber);
+        mEditNumber.setText(String.valueOf(number));
+        mEditNumber.addTextChangedListener(mWatcherNumber);
+    }
+
+    private void updateRank(Context context, String[] ranks, String selectedRank) {
+        mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank);
+        mSpinnerRank.setAdapter(new ArrayAdapter<>(context,
+                android.R.layout.simple_spinner_dropdown_item, ranks));
+        mSpinnerRank.setSelection(search(ranks, selectedRank));
+        mSpinnerRank.setOnItemSelectedListener(this);
+    }
+
+    private void updateApprovals(Context context, String[] approvals,
+                                 String[] selectedApprovals) {
+        mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals);
+        mLayoutApprovals.removeAllViews();
+        for (String approval : approvals) {
+            Switch sw = new Switch(context);
+            sw.setText(approval);
+            sw.setTag(approval);
+            sw.setChecked(Arrays.asList(selectedApprovals).contains(approval));
+            sw.setOnCheckedChangeListener(this);
+            sw.setId(R.id.approval);
+            mLayoutApprovals.addView(sw);
+        }
     }
 
     /**
-     * Sets the value for the "cay_say_hello" restriction of AppRestrictionSchema.
+     * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
      *
      * @param activity The activity
      * @param allow    The value to be set for the restriction.
      */
-    private void allowSayHello(Activity activity, boolean allow) {
+    private void saveCanSayHello(Activity activity, boolean allow) {
+        mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
+        saveRestrictions(activity);
+        // Note that the owner app needs to remember the restrictions on its own.
+        editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply();
+    }
+
+    /**
+     * Saves the value for the "message" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity
+     * @param message  The value to be set for the restriction.
+     */
+    private void saveMessage(Activity activity, String message) {
+        mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
+        saveRestrictions(activity);
+        editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply();
+    }
+
+    /**
+     * Saves the value for the "number" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity
+     * @param number   The value to be set for the restriction.
+     */
+    private void saveNumber(Activity activity, int number) {
+        mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
+        saveRestrictions(activity);
+        editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply();
+    }
+
+    /**
+     * Saves the value for the "rank" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity
+     * @param rank     The value to be set for the restriction.
+     */
+    private void saveRank(Activity activity, String rank) {
+        mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank);
+        saveRestrictions(activity);
+        editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply();
+    }
+
+    private void addApproval(Activity activity, String approval) {
+        List<String> approvals = new ArrayList<>(Arrays.asList(
+                mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
+        if (approvals.contains(approval)) {
+            return;
+        }
+        approvals.add(approval);
+        saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
+    }
+
+    private void removeApproval(Activity activity, String approval) {
+        List<String> approvals = new ArrayList<>(Arrays.asList(
+                mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
+        if (!approval.contains(approval)) {
+            return;
+        }
+        approvals.remove(approval);
+        saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
+    }
+
+    /**
+     * Saves the value for the "approvals" restriction of AppRestrictionSchema.
+     *
+     * @param activity  The activity
+     * @param approvals The value to be set for the restriction.
+     */
+    private void saveApprovals(Activity activity, String[] approvals) {
+        mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals);
+        saveRestrictions(activity);
+        editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS,
+                TextUtils.join(DELIMETER, approvals)).apply();
+    }
+
+    private void saveRestrictions(Activity activity) {
         DevicePolicyManager devicePolicyManager
                 = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        Bundle restrictions = new Bundle();
-        restrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
         devicePolicyManager.setApplicationRestrictions(
                 EnforcerDeviceAdminReceiver.getComponentName(activity),
-                PACKAGE_NAME_APP_RESTRICTION_SCHEMA, restrictions);
-        // The profile/device owner needs to remember the current state of restrictions on its own
-        activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE)
-                .edit()
-                .putBoolean(RESTRICTION_KEY_SAY_HELLO, allow)
-                .apply();
-        Toast.makeText(activity, allow ? R.string.allowed : R.string.disallowed,
-                Toast.LENGTH_SHORT).show();
+                Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions);
+    }
+
+    private SharedPreferences.Editor editPreferences(Activity activity) {
+        return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit();
+    }
+
+    /**
+     * Sequential search
+     *
+     * @param array The string array
+     * @param s     The string to search for
+     * @return Index if found. -1 if not found.
+     */
+    private int search(String[] array, String s) {
+        for (int i = 0; i < array.length; ++i) {
+            if (s.equals(array[i])) {
+                return i;
+            }
+        }
+        return -1;
     }
 
 }
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/Constants.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/Constants.java
new file mode 100644
index 0000000..bb4e958
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/Constants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apprestrictionenforcer;
+
+public interface Constants {
+
+    /**
+     * Package name of the AppRestrictionSchema sample.
+     */
+    public static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
+            = "com.example.android.apprestrictionschema";
+
+}
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/EasyTextWatcher.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/EasyTextWatcher.java
new file mode 100644
index 0000000..8e0abb4
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/EasyTextWatcher.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apprestrictionenforcer;
+
+import android.text.TextWatcher;
+
+/**
+ * This is a wrapper around {@link TextWatcher} that overrides
+ * {@link TextWatcher#beforeTextChanged(CharSequence, int, int, int)} and
+ * {@link TextWatcher#onTextChanged(CharSequence, int, int, int)} with empty bodies.
+ */
+public abstract class EasyTextWatcher implements TextWatcher {
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        // Do nothing
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        // Do nothing
+    }
+
+}
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/MainActivity.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/MainActivity.java
index 72224e1..c6b012b 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/MainActivity.java
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/MainActivity.java
@@ -18,24 +18,44 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.support.v4.app.FragmentActivity;
 
-public class MainActivity extends FragmentActivity {
+public class MainActivity extends FragmentActivity implements StatusFragment.StatusUpdatedListener {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main_real);
         if (null == savedInstanceState) {
-            DevicePolicyManager manager = (DevicePolicyManager)
-                    getSystemService(Context.DEVICE_POLICY_SERVICE);
-            if (manager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
-                // If the managed profile is already set up, we show the main screen.
-                showMainFragment();
-            } else {
-                // If not, we show the set up screen.
+            DevicePolicyManager devicePolicyManager =
+                    (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+            PackageManager packageManager = getPackageManager();
+            if (!devicePolicyManager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
+                // If the managed profile is not yet set up, we show the setup screen.
                 showSetupProfile();
+            } else {
+                try {
+                    ApplicationInfo info = packageManager.getApplicationInfo(
+                            Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
+                            PackageManager.GET_UNINSTALLED_PACKAGES);
+                    if (0 == (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
+                        // Need to reinstall the sample app
+                        showStatusProfile();
+                    } else if (devicePolicyManager.isApplicationHidden(
+                            EnforcerDeviceAdminReceiver.getComponentName(this),
+                            Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
+                        // The app is installed but hidden in this profile
+                        showStatusProfile();
+                    } else {
+                        // Everything is clear; show the main screen
+                        showMainFragment();
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    showStatusProfile();
+                }
             }
         }
     }
@@ -46,10 +66,21 @@
                 .commit();
     }
 
+    private void showStatusProfile() {
+        getSupportFragmentManager().beginTransaction()
+                .replace(R.id.container, new StatusFragment())
+                .commit();
+    }
+
     private void showMainFragment() {
         getSupportFragmentManager().beginTransaction()
                 .replace(R.id.container, new AppRestrictionEnforcerFragment())
                 .commit();
     }
 
+    @Override
+    public void onStatusUpdated() {
+        showMainFragment();
+    }
+
 }
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/StatusFragment.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/StatusFragment.java
new file mode 100644
index 0000000..f4a4eb7
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/StatusFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apprestrictionenforcer;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Provides UI for enabling the target app in this profile. The status of the app can be
+ * uninstalled, hidden, or enabled depending on the situations. This fragment shows suitable
+ * controls for each status.
+ */
+public class StatusFragment extends Fragment implements View.OnClickListener {
+
+    private TextView mTextStatus;
+    private Button mButtonUnhide;
+    private StatusUpdatedListener mListener;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_status, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        mTextStatus = (TextView) view.findViewById(R.id.status);
+        mButtonUnhide = (Button) view.findViewById(R.id.unhide);
+        mButtonUnhide.setOnClickListener(this);
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mListener = (StatusUpdatedListener) activity;
+    }
+
+    @Override
+    public void onDetach() {
+        mListener = null;
+        super.onDetach();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updateUi(getActivity());
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.unhide: {
+                unhideApp(getActivity());
+                break;
+            }
+        }
+    }
+
+    private void updateUi(Activity activity) {
+        PackageManager packageManager = activity.getPackageManager();
+        try {
+            ApplicationInfo info = packageManager.getApplicationInfo(
+                    Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
+                    PackageManager.GET_UNINSTALLED_PACKAGES);
+            DevicePolicyManager devicePolicyManager =
+                    (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
+            if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+                if (!devicePolicyManager.isApplicationHidden(
+                        EnforcerDeviceAdminReceiver.getComponentName(activity),
+                        Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
+                    // The app is ready to enforce restrictions
+                    // This is unlikely to happen in this sample as unhideApp() handles it.
+                    mListener.onStatusUpdated();
+                } else {
+                    // The app is installed but hidden in this profile
+                    mTextStatus.setText(R.string.status_not_activated);
+                    mButtonUnhide.setVisibility(View.VISIBLE);
+                }
+            } else {
+                // Need to reinstall the sample app
+                mTextStatus.setText(R.string.status_need_reinstall);
+                mButtonUnhide.setVisibility(View.GONE);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // Need to reinstall the sample app
+            mTextStatus.setText(R.string.status_need_reinstall);
+            mButtonUnhide.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
+     *
+     * @param activity The activity
+     */
+    private void unhideApp(Activity activity) {
+        DevicePolicyManager devicePolicyManager =
+                (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
+        devicePolicyManager.setApplicationHidden(
+                EnforcerDeviceAdminReceiver.getComponentName(activity),
+                Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
+        Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
+        mListener.onStatusUpdated();
+    }
+
+    public interface StatusUpdatedListener {
+        public void onStatusUpdated();
+    }
+
+}
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml
index e6c50a2..0118191 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_app_restriction_enforcer.xml
@@ -14,31 +14,111 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:paddingBottom="@dimen/vertical_page_margin"
-    android:paddingLeft="@dimen/horizontal_page_margin"
-    android:paddingRight="@dimen/horizontal_page_margin"
-    android:paddingTop="@dimen/vertical_page_margin">
 
-    <TextView
-        android:id="@+id/status"
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/status_not_installed" />
+        android:orientation="vertical"
+        android:paddingBottom="@dimen/vertical_page_margin"
+        android:paddingLeft="@dimen/horizontal_page_margin"
+        android:paddingRight="@dimen/horizontal_page_margin"
+        android:paddingTop="@dimen/vertical_page_margin">
 
-    <Button
-        android:id="@+id/unhide"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/unhide" />
 
-    <Switch
-        android:id="@+id/say_hello"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/allow_saying_hello" />
+        <Switch
+            android:id="@+id/say_hello"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/allow_saying_hello"/>
 
-</LinearLayout>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/vertical_page_margin"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:labelFor="@id/message"
+                android:text="@string/message"/>
+
+            <EditText
+                android:id="@id/message"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:inputType="text"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/vertical_page_margin"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:labelFor="@id/number"
+                android:text="@string/number"/>
+
+            <EditText
+                android:id="@id/number"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:inputType="number"
+                android:maxLines="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/vertical_page_margin"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/rank"/>
+
+            <Spinner
+                android:id="@+id/rank"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/margin_small"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/vertical_page_margin"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/approvals"/>
+
+            <LinearLayout
+                android:id="@+id/approvals"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/margin_small"
+                android:orientation="vertical"/>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_status.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_status.xml
new file mode 100644
index 0000000..a2d60eb
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_status.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/vertical_page_margin"
+    android:paddingLeft="@dimen/horizontal_page_margin"
+    android:paddingRight="@dimen/horizontal_page_margin"
+    android:paddingTop="@dimen/vertical_page_margin">
+
+    <TextView
+        android:id="@+id/status"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/status_not_installed"/>
+
+    <Button
+        android:id="@+id/unhide"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/unhide"/>
+
+</LinearLayout>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/separator.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/separator.xml
new file mode 100644
index 0000000..6927d80
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/separator.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="1dp"
+      android:layout_marginBottom="@dimen/margin_medium"
+      android:layout_marginTop="@dimen/margin_medium"
+      android:background="#9000"/>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/values/ids.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/values/ids.xml
new file mode 100644
index 0000000..04ba4ec
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/values/ids.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <item name="message" type="id"/>
+    <item name="number" type="id"/>
+    <item name="approval" type="id"/>
+</resources>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
index 3029e04..e35daee 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
@@ -25,4 +25,8 @@
     <string name="allowed">Allowed</string>
     <string name="disallowed">Disallowed</string>
     <string name="profile_name">AppRestrictionEnforcer </string>
+    <string name="message">Message: </string>
+    <string name="number">Number: </string>
+    <string name="rank">Rank: </string>
+    <string name="approvals">Approvals: </string>
 </resources>
diff --git a/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties b/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..f2e517b 100644
--- a/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Dec 01 16:00:44 JST 2014
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java b/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java
index 76f024f..7b8dba8 100644
--- a/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java
+++ b/admin/AppRestrictionSchema/Application/src/main/java/com/example/android/apprestrictionschema/AppRestrictionSchemaFragment.java
@@ -17,10 +17,12 @@
 package com.example.android.apprestrictionschema;
 
 import android.content.Context;
+import android.content.RestrictionEntry;
 import android.content.RestrictionsManager;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,6 +32,8 @@
 
 import com.example.android.common.logger.Log;
 
+import java.util.List;
+
 /**
  * Pressing the button on this fragment pops up a simple Toast message. The button is enabled or
  * disabled according to the restrictions set by device/profile owner. You can use the
@@ -40,9 +44,21 @@
     // Tag for the logger
     private static final String TAG = "AppRestrictionSchemaFragment";
 
+    private static final String KEY_CAN_SAY_HELLO = "can_say_hello";
+    private static final String KEY_MESSAGE = "message";
+    private static final String KEY_NUMBER = "number";
+    private static final String KEY_RANK = "rank";
+    private static final String KEY_APPROVALS = "approvals";
+
+    // Message to show when the button is clicked (String restriction)
+    private String mMessage;
+
     // UI Components
     private TextView mTextSayHello;
     private Button mButtonSayHello;
+    private TextView mTextNumber;
+    private TextView mTextRank;
+    private TextView mTextApprovals;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -54,48 +70,103 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         mTextSayHello = (TextView) view.findViewById(R.id.say_hello_explanation);
         mButtonSayHello = (Button) view.findViewById(R.id.say_hello);
+        mTextNumber = (TextView) view.findViewById(R.id.your_number);
+        mTextRank = (TextView) view.findViewById(R.id.your_rank);
+        mTextApprovals = (TextView) view.findViewById(R.id.approvals_you_have);
         mButtonSayHello.setOnClickListener(this);
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        // Update the UI according to the configured restrictions
-        RestrictionsManager restrictionsManager =
-                (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
-        Bundle restrictions = restrictionsManager.getApplicationRestrictions();
-        updateUI(restrictions);
+        resolveRestrictions();
     }
 
-    private void updateUI(Bundle restrictions) {
-        if (canSayHello(restrictions)) {
-            mTextSayHello.setText(R.string.explanation_can_say_hello_true);
-            mButtonSayHello.setEnabled(true);
-        } else {
-            mTextSayHello.setText(R.string.explanation_can_say_hello_false);
-            mButtonSayHello.setEnabled(false);
+    private void resolveRestrictions() {
+        RestrictionsManager manager =
+                (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
+        Bundle restrictions = manager.getApplicationRestrictions();
+        List<RestrictionEntry> entries = manager.getManifestRestrictions(getActivity().getApplicationContext().getPackageName());
+        for (RestrictionEntry entry : entries) {
+            String key = entry.getKey();
+            Log.d(TAG, "key: " + key);
+            if (key.equals(KEY_CAN_SAY_HELLO)) {
+                updateCanSayHello(entry, restrictions);
+            } else if (key.equals(KEY_MESSAGE)) {
+                updateMessage(entry, restrictions);
+            } else if (key.equals(KEY_NUMBER)) {
+                updateNumber(entry, restrictions);
+            } else if (key.equals(KEY_RANK)) {
+                updateRank(entry, restrictions);
+            } else if (key.equals(KEY_APPROVALS)) {
+                updateApprovals(entry, restrictions);
+            }
         }
     }
 
-    /**
-     * Returns the current status of the restriction.
-     *
-     * @param restrictions The application restrictions
-     * @return True if the app is allowed to say hello
-     */
-    private boolean canSayHello(Bundle restrictions) {
-        final boolean defaultValue = false;
-        boolean canSayHello = restrictions == null ? defaultValue :
-                restrictions.getBoolean("can_say_hello", defaultValue);
-        Log.d(TAG, "canSayHello: " + canSayHello);
-        return canSayHello;
+    private void updateCanSayHello(RestrictionEntry entry, Bundle restrictions) {
+        boolean canSayHello;
+        if (restrictions == null || !restrictions.containsKey(KEY_CAN_SAY_HELLO)) {
+            canSayHello = entry.getSelectedState();
+        } else {
+            canSayHello = restrictions.getBoolean(KEY_CAN_SAY_HELLO);
+        }
+        mTextSayHello.setText(canSayHello ?
+                R.string.explanation_can_say_hello_true :
+                R.string.explanation_can_say_hello_false);
+        mButtonSayHello.setEnabled(canSayHello);
+    }
+
+    private void updateMessage(RestrictionEntry entry, Bundle restrictions) {
+        if (restrictions == null || !restrictions.containsKey(KEY_MESSAGE)) {
+            mMessage = entry.getSelectedString();
+        } else {
+            mMessage = restrictions.getString(KEY_MESSAGE);
+        }
+    }
+
+    private void updateNumber(RestrictionEntry entry, Bundle restrictions) {
+        int number;
+        if (restrictions == null || !restrictions.containsKey(KEY_NUMBER)) {
+            number = entry.getIntValue();
+        } else {
+            number = restrictions.getInt(KEY_NUMBER);
+        }
+        mTextNumber.setText(getString(R.string.your_number, number));
+    }
+
+    private void updateRank(RestrictionEntry entry, Bundle restrictions) {
+        String rank;
+        if (restrictions == null || !restrictions.containsKey(KEY_RANK)) {
+            rank = entry.getSelectedString();
+        } else {
+            rank = restrictions.getString(KEY_RANK);
+        }
+        mTextRank.setText(getString(R.string.your_rank, rank));
+    }
+
+    private void updateApprovals(RestrictionEntry entry, Bundle restrictions) {
+        String[] approvals;
+        if (restrictions == null || !restrictions.containsKey(KEY_APPROVALS)) {
+            approvals = entry.getAllSelectedStrings();
+        } else {
+            approvals = restrictions.getStringArray(KEY_APPROVALS);
+        }
+        String text;
+        if (approvals == null || approvals.length == 0) {
+            text = getString(R.string.none);
+        } else {
+            text = TextUtils.join(", ", approvals);
+        }
+        mTextApprovals.setText(getString(R.string.approvals_you_have, text));
     }
 
     @Override
     public void onClick(View view) {
         switch (view.getId()) {
             case R.id.say_hello: {
-                Toast.makeText(getActivity(), R.string.message_hello, Toast.LENGTH_SHORT).show();
+                Toast.makeText(getActivity(), getString(R.string.message, mMessage),
+                        Toast.LENGTH_SHORT).show();
                 break;
             }
         }
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml b/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml
index fc5e23d..18ca0a4 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/layout/fragment_app_restriction_schema.xml
@@ -14,24 +14,60 @@
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:padding="@dimen/margin_medium">
 
-    <TextView
-        android:id="@+id/say_hello_explanation"
+
+<ScrollView 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">
+
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/explanation_can_say_hello_true"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
+        android:orientation="vertical"
+        android:padding="@dimen/margin_medium">
 
-    <Button
-        android:id="@+id/say_hello"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/margin_medium"
-        android:text="@string/action_can_say_hello" />
+        <TextView
+            android:id="@+id/say_hello_explanation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/explanation_can_say_hello_true"/>
 
-</LinearLayout>
+        <Button
+            android:id="@+id/say_hello"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/margin_medium"
+            android:text="@string/action_can_say_hello"/>
+
+        <include layout="@layout/separator"/>
+
+        <TextView
+            android:id="@+id/your_number"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/your_number"/>
+
+        <include layout="@layout/separator"/>
+
+        <TextView
+            android:id="@+id/your_rank"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/your_rank"/>
+
+        <include layout="@layout/separator"/>
+
+        <TextView
+            android:id="@+id/approvals_you_have"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/approvals_you_have"/>
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/layout/separator.xml b/admin/AppRestrictionSchema/Application/src/main/res/layout/separator.xml
new file mode 100644
index 0000000..6927d80
--- /dev/null
+++ b/admin/AppRestrictionSchema/Application/src/main/res/layout/separator.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="1dp"
+      android:layout_marginBottom="@dimen/margin_medium"
+      android:layout_marginTop="@dimen/margin_medium"
+      android:background="#9000"/>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml b/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
new file mode 100644
index 0000000..558d097
--- /dev/null
+++ b/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+
+    <!-- Bool restriction -->
+    <string name="title_can_say_hello">Can say hello</string>
+    <string name="description_can_say_hello">Whether the app can say hello to the user</string>
+    <bool name="default_can_say_hello">false</bool>
+
+    <!-- String restriction -->
+    <string name="title_message">Message</string>
+    <string name="description_message">A message string to show</string>
+    <string name="default_message">Hello!</string>
+
+    <!-- Integer restriction -->
+    <string name="title_number">Number</string>
+    <string name="description_number">Sample integer value</string>
+    <integer name="default_number">32582657</integer>
+
+    <!-- Choice restriction -->
+    <string name="title_rank">Rank</string>
+    <string name="description_rank">Rank of the user</string>
+    <string-array name="entries_rank">
+        <item>Apprentice</item>
+        <item>Intermediate</item>
+        <item>Master</item>
+    </string-array>
+    <string name="entry_value_rank_apprentice">apprentice</string>
+    <string name="entry_value_rank_intermediate">intermediate</string>
+    <string name="entry_value_rank_master">master</string>
+    <string-array name="entry_values_rank">
+        <item>@string/entry_value_rank_apprentice</item>
+        <item>@string/entry_value_rank_intermediate</item>
+        <item>@string/entry_value_rank_master</item>
+    </string-array>
+    <string name="default_rank">@string/entry_value_rank_apprentice</string>
+
+    <!-- Multi-select restriction -->
+    <string name="title_approvals">Approvals</string>
+    <string name="description_approvals">Approvals</string>
+    <string-array name="entries_approvals">
+        <item>Read</item>
+        <item>Write</item>
+        <item>Execute</item>
+    </string-array>
+    <string name="entry_value_approvals_read">read</string>
+    <string name="entry_value_approvals_write">write</string>
+    <string name="entry_value_approvals_execute">execute</string>
+    <string-array name="entry_values_approvals">
+        <item>@string/entry_value_approvals_read</item>
+        <item>@string/entry_value_approvals_write</item>
+        <item>@string/entry_value_approvals_execute</item>
+    </string-array>
+    <string-array name="default_approvals">
+        <!-- Empty -->
+    </string-array>
+
+    <!-- Hidden restriction -->
+    <string name="title_secret_code">Secret code</string>
+    <string name="description_secret_code">This restriction is hidden and will not be shown to the administrator.</string>
+    <string name="default_secret_code">(Hidden restriction must have some default value)</string>
+
+</resources>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml b/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
index b8ef110..6dce123 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
@@ -16,11 +16,14 @@
 -->
 <resources>
 
-    <string name="title_can_say_hello">Can say hello</string>
-    <string name="description_can_say_hello">Whether the app can say hello to the user</string>
     <string name="explanation_can_say_hello_true">I can say hello to you.</string>
     <string name="explanation_can_say_hello_false">I am restricted from saying hello to you.</string>
     <string name="action_can_say_hello">Say hello</string>
-    <string name="message_hello">Hello!</string>
+    <string name="message">All I can say is \"%s\".</string>
 
-</resources>
\ No newline at end of file
+    <string name="your_number">Your number: %d</string>
+    <string name="your_rank">Your rank: %s</string>
+    <string name="approvals_you_have">Approvals you have: %s</string>
+    <string name="none">none</string>
+
+</resources>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml b/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
index 409527f..9e47f45 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
@@ -16,11 +16,55 @@
 -->
 <restrictions xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <!--
+    Refer to the javadoc of RestrictionsManager for detail of this file.
+    https://developer.android.com/reference/android/content/RestrictionsManager.html
+    -->
+
     <restriction
-        android:defaultValue="false"
+        android:defaultValue="@bool/default_can_say_hello"
         android:description="@string/description_can_say_hello"
         android:key="can_say_hello"
         android:restrictionType="bool"
-        android:title="@string/title_can_say_hello" />
+        android:title="@string/title_can_say_hello"/>
+
+    <restriction
+        android:defaultValue="@string/default_message"
+        android:description="@string/description_message"
+        android:key="message"
+        android:restrictionType="string"
+        android:title="@string/title_message"/>
+
+    <restriction
+        android:defaultValue="@integer/default_number"
+        android:description="@string/description_number"
+        android:key="number"
+        android:restrictionType="integer"
+        android:title="@string/title_number"/>
+
+    <restriction
+        android:defaultValue="@string/default_rank"
+        android:description="@string/description_rank"
+        android:entries="@array/entries_rank"
+        android:entryValues="@array/entry_values_rank"
+        android:key="rank"
+        android:restrictionType="choice"
+        android:title="@string/title_rank"/>
+
+    <restriction
+        android:defaultValue="@array/default_approvals"
+        android:description="@string/description_approvals"
+        android:entries="@array/entries_approvals"
+        android:entryValues="@array/entry_values_approvals"
+        android:key="approvals"
+        android:restrictionType="multi-select"
+        android:title="@string/title_approvals"/>
+
+    <restriction
+        android:defaultValue="@string/default_secret_code"
+        android:description="@string/description_secret_code"
+        android:key="secret_code"
+        android:restrictionType="hidden"
+        android:title="@string/title_secret_code"/>
 
 </restrictions>
diff --git a/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties b/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..fb79885 100644
--- a/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Mon Dec 01 11:44:58 JST 2014
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/admin/AppRestrictionSchema/screenshots/main.png b/admin/AppRestrictionSchema/screenshots/main.png
new file mode 100644
index 0000000..0d0d9c4
--- /dev/null
+++ b/admin/AppRestrictionSchema/screenshots/main.png
Binary files differ
diff --git a/admin/AppRestrictionSchema/template-params.xml b/admin/AppRestrictionSchema/template-params.xml
index c6ec4a1..6603034 100644
--- a/admin/AppRestrictionSchema/template-params.xml
+++ b/admin/AppRestrictionSchema/template-params.xml
@@ -39,4 +39,72 @@
     <common src="logger" />
     <common src="activities" />
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Device Admin</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>Application/main/big_icon.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.content.RestrictionsManager</android>
+        </api_refs>
+
+        <description>
+A basic app showing how to allow a device administrator to restrict user
+activities using the Android Device Administration API. The app exports
+a custom policy that enables or disables a UI control. Device Administration
+applications are able to enforce a specific value for this policy, as
+directed by enterprise administrators.
+        </description>
+
+        <intro>
+<![CDATA[
+The [Android Device Administration API][1] allows enterprise administrators to
+enforce specific policies on a managed device. The system provides policies
+that control settings such as password complexity, screen lock, or camera
+availability. Developers can also augment this list with custom policies
+that control specific features within their applications. For example,
+a web browser could provide access to a whitelist of allowed domains.
+
+The list of policies exposed by an app must be specified using a file
+inside of the `res/xml` directory, using the `<restriction>` tag:
+
+```xml
+<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <restriction
+            android:defaultValue="false"
+            android:description="@string/description_can_say_hello"
+            android:key="can_say_hello"
+            android:restrictionType="bool"
+            android:title="@string/title_can_say_hello" />
+
+</restrictions>
+```
+
+In this sample, that file can be found at
+`Application/src/main/res/xml/app_restrictions.xml`. This file must be
+also be declared inside of `ApplicationManifest.xml` using a `<meta-data>`
+element:
+
+```xml
+<meta-data
+        android:name="android.content.APP_RESTRICTIONS"
+        android:resource="@xml/app_restrictions" />
+```
+
+At runtime, the current list of restrictions enforced by policy can be
+checked by calling [RestrictionsManager.getApplicationRestrictions()][2].
+
+[1]: http://developer.android.com/guide/topics/admin/device-admin.html
+[2]: https://developer.android.com/reference/android/content/RestrictionsManager.html#getApplicationRestrictions()
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/admin/BasicManagedProfile/gradle/wrapper/gradle-wrapper.properties b/admin/BasicManagedProfile/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/admin/BasicManagedProfile/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/BasicManagedProfile/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/admin/BasicManagedProfile/screenshots/icon-web.png b/admin/BasicManagedProfile/screenshots/icon-web.png
new file mode 100755
index 0000000..9ffff0d
--- /dev/null
+++ b/admin/BasicManagedProfile/screenshots/icon-web.png
Binary files differ
diff --git a/admin/BasicManagedProfile/screenshots/main.png b/admin/BasicManagedProfile/screenshots/main.png
new file mode 100644
index 0000000..ecb066e
--- /dev/null
+++ b/admin/BasicManagedProfile/screenshots/main.png
Binary files differ
diff --git a/admin/BasicManagedProfile/screenshots/not_set_up.png b/admin/BasicManagedProfile/screenshots/not_set_up.png
new file mode 100644
index 0000000..4db7e9f
--- /dev/null
+++ b/admin/BasicManagedProfile/screenshots/not_set_up.png
Binary files differ
diff --git a/admin/BasicManagedProfile/screenshots/set_up.png b/admin/BasicManagedProfile/screenshots/set_up.png
new file mode 100644
index 0000000..18d3998
--- /dev/null
+++ b/admin/BasicManagedProfile/screenshots/set_up.png
Binary files differ
diff --git a/admin/BasicManagedProfile/template-params.xml b/admin/BasicManagedProfile/template-params.xml
index c1ba1cb..ff926ab 100644
--- a/admin/BasicManagedProfile/template-params.xml
+++ b/admin/BasicManagedProfile/template-params.xml
@@ -42,4 +42,61 @@
     </strings>
     <template src="base"/>
 
+       <metadata>
+        <status>PUBLISHED</status>
+        <categories>Device Admin</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshotsicon-web.png</icon>
+        <screenshots>
+            <img>screenshots/not_set_up.png</img>
+            <img>screenshots/set_up.png</img>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.admin.DevicePolicyManager</android>
+            <android>android.app.admin.DeviceAdminReceiver</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates basic functionalities of Managed Profile API
+introduced in Android 5.0 Lollipop. You can set up this app as a
+profile owner, and use this app to enable/disable apps in the newly
+created managed profile. You can also set restrictions to some apps,
+enable/disable Intent forwarding between profiles, and wipe out all
+the data associated with the profile.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+As of Android 5.0, DevicePolicyManager introduces new features to
+support managed profile.
+
+To set up this app as a profile owner, you need to encrypt your device
+(you are prompted to do if you haven't). This doesn't wipe out the
+device, but be aware that you can set up only one managed profile at a
+time.
+
+[isProfileOwnerApp][1] can be used to determine if a particular
+package is registered as the profile owner for the current user. You
+can initiate the provisioning flow of a managed profile with Intent of
+[ACTION_PROVISION_MANAGED_PROFILE][2].
+
+You have to implement a class extending [DeviceAdminReceiver][3] to
+receive the result of the provisioning flow. Use
+[setProfileEnabled][4] to enable the newly created profile, and your
+app is now set up as a profile owner.
+
+[1]: http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#isProfileOwnerApp(java.lang.String)
+[2]: http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#ACTION_PROVISION_MANAGED_PROFILE
+[3]: http://developer.android.com/reference/android/app/admin/DeviceAdminReceiver.html
+[4]: http://developer.android.com/reference/android/app/admin/DevicePolicyManager.html#setProfileEnabled(android.content.ComponentName)
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/background/JobScheduler/Application/src/main/java/com/example/android/jobscheduler/MainActivity.java b/background/JobScheduler/Application/src/main/java/com/example/android/jobscheduler/MainActivity.java
index f495bf1..624e22d 100644
--- a/background/JobScheduler/Application/src/main/java/com/example/android/jobscheduler/MainActivity.java
+++ b/background/JobScheduler/Application/src/main/java/com/example/android/jobscheduler/MainActivity.java
@@ -140,9 +140,9 @@
         boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
         boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
         if (requiresUnmetered) {
-            builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED);
+            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
         } else if (requiresAnyConnectivity) {
-            builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY);
+            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
         }
         builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
         builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
diff --git a/background/JobScheduler/gradle/wrapper/gradle-wrapper.properties b/background/JobScheduler/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/background/JobScheduler/gradle/wrapper/gradle-wrapper.properties
+++ b/background/JobScheduler/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/background/alarms/RepeatingAlarm/gradle/wrapper/gradle-wrapper.properties b/background/alarms/RepeatingAlarm/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/background/alarms/RepeatingAlarm/gradle/wrapper/gradle-wrapper.properties
+++ b/background/alarms/RepeatingAlarm/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/build.gradle b/build.gradle
index 37cbf22..082f67d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,8 +24,8 @@
 "ui/actionbarcompat/ActionBarCompat-Styled",
 "ui/actionbarcompat/ActionBarCompat-ListPopupMenu",
 "ui/actionbarcompat/ActionBarCompat-ShareActionProvider",
-"ui/notifications/BasicNotifications",
-"ui/notifications/CustomNotifications",
+"notification/BasicNotifications",
+"notification/CustomNotifications",
 "ui/actionbar/DoneBar",
 "ui/graphics/PdfRendererBasic",
 "ui/window/BasicImmersiveMode",
@@ -47,6 +47,7 @@
 "ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic",
 "ui/views/SwipeRefreshLayout/SwipeRefreshListFragment",
 "ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews",
+"media/MediaBrowserService",
 "media/MediaRouter",
 "media/MediaEffects",
 "admin/BasicManagedProfile",
@@ -60,10 +61,10 @@
 "ui/views/FloatingActionButton/FloatingActionButtonBasic",
 "ui/views/RevealEffect/RevealEffectBasic",
 "background/JobScheduler",
-"wearable/wear/SynchronizedNotifications",
 "ui/views/RecyclerView",
 "ui/views/CardView",
-"ui/notifications/LNotifications",
+"notification/LNotifications",
+"notification/MessagingService",
 "ui/DrawableTinting",
 "ui/Interpolator",
 "media/HdrViewfinder",
@@ -71,7 +72,7 @@
 "ui/activitytasks/DocumentCentricRelinquishIdentity",
 "admin/AppRestrictionEnforcer",
 "admin/AppRestrictionSchema",
-"bluetooth/BluetoothChat",
+"connectivity/bluetooth/BluetoothChat",
 "wearable/wear/AgendaData",
 "wearable/wear/DataLayer",
 "wearable/wear/DelayedConfirmation",
@@ -87,8 +88,14 @@
 "wearable/wear/RecipeAssistant",
 "wearable/wear/SkeletonWearableApp",
 "wearable/wear/SpeedTracker",
+"wearable/wear/SynchronizedNotifications",
 "wearable/wear/Timer",
-"wearable/wear/WatchViewStub"
+"wearable/wear/WatchViewStub",
+"wearable/wear/WatchFace",
+"content/webview/PermissionRequest",
+"content/documentsUi/DirectorySelection",
+"system/AppUsageStatistics",
+"media/ScreenCapture",
 ]
 
 List<String> taskNames = [
diff --git a/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/bluetooth/BluetoothChat/screenshots/1-launch.png b/connectivity/bluetooth/BluetoothChat/screenshots/1-launch.png
new file mode 100644
index 0000000..6b6741e
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothChat/screenshots/1-launch.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothChat/screenshots/2-devices.png b/connectivity/bluetooth/BluetoothChat/screenshots/2-devices.png
new file mode 100644
index 0000000..db31dc0
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothChat/screenshots/2-devices.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothChat/screenshots/3-chat.png b/connectivity/bluetooth/BluetoothChat/screenshots/3-chat.png
new file mode 100644
index 0000000..65830e8
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothChat/screenshots/3-chat.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothChat/screenshots/icon-web.png b/connectivity/bluetooth/BluetoothChat/screenshots/icon-web.png
new file mode 100644
index 0000000..737f347
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothChat/screenshots/icon-web.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothChat/template-params.xml b/connectivity/bluetooth/BluetoothChat/template-params.xml
index a4ca497..2bf81c3 100644
--- a/connectivity/bluetooth/BluetoothChat/template-params.xml
+++ b/connectivity/bluetooth/BluetoothChat/template-params.xml
@@ -47,4 +47,59 @@
     <!-- Include common code modules by uncommenting them below. -->
     <common src="logger" />
     <common src="activities"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Connectivity</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-launch.png</img>
+            <img>screenshots/2-devices.png</img>
+            <img>screenshots/3-chat.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.bluetooth.BluetoothAdapter</android>
+            <android>android.bluetooth.BluetoothDevice</android>
+            <android>android.bluetooth.BluetoothServerSocket</android>
+            <android>android.bluetooth.BluetoothSocket</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample shows how to implement two-way text chat over Bluetooth between two Android devices, using
+all the fundamental Bluetooth API capabilities.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample should be run on two Android devices at the same time, to establish a two-way chat over
+Bluetooth between the devices. Select "Made discoverable" in overflow menu on one device and click
+on the Bluetooth icon on the other one, to find the device and establish the connection.
+
+The sample demonstrates the following, using the [Bluetooth API][1]:
+
+1. [Setting up][2] Bluetooth
+2. [Scanning][3] for other Bluetooth devices
+3. [Querying][4] the local Bluetooth adapter for paired Bluetooth devices
+4. [Establishing RFCOMM][5] channels/sockets
+5. [Connecting][6] to a remote device
+6. [Transfering][7] data over Bluetooth
+
+[1]: http://developer.android.com/guide/topics/connectivity/bluetooth.html
+[2]: http://developer.android.com/guide/topics/connectivity/bluetooth.html#Permissions
+[3]: http://developer.android.com/guide/topics/connectivity/bluetooth.html#FindingDevices
+[4]: http://developer.android.com/guide/topics/connectivity/bluetooth.html#QueryingPairedDevices
+[5]: http://developer.android.com/guide/topics/connectivity/bluetooth.html#ConnectingDevices
+[6]: http://developer.android.com/guide/topics/connectivity/bluetooth.html#ConnectingAsAClient
+[7]: http://developer.android.com/guide/topics/connectivity/bluetooth.html#ManagingAConnection
+]]>
+        </intro>
+    </metadata>
+
+
 </sample>
diff --git a/connectivity/bluetooth/BluetoothLeGatt/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothLeGatt/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/connectivity/bluetooth/BluetoothLeGatt/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/bluetooth/BluetoothLeGatt/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/bluetooth/BluetoothLeGatt/screenshots/1-main.png b/connectivity/bluetooth/BluetoothLeGatt/screenshots/1-main.png
new file mode 100644
index 0000000..19ceb47
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothLeGatt/screenshots/1-main.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothLeGatt/screenshots/2-detail.png b/connectivity/bluetooth/BluetoothLeGatt/screenshots/2-detail.png
new file mode 100644
index 0000000..0bb0598
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothLeGatt/screenshots/2-detail.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothLeGatt/screenshots/icon-web.png b/connectivity/bluetooth/BluetoothLeGatt/screenshots/icon-web.png
new file mode 100644
index 0000000..9420668
--- /dev/null
+++ b/connectivity/bluetooth/BluetoothLeGatt/screenshots/icon-web.png
Binary files differ
diff --git a/connectivity/bluetooth/BluetoothLeGatt/template-params.xml b/connectivity/bluetooth/BluetoothLeGatt/template-params.xml
index 323e1d7..b699f67 100644
--- a/connectivity/bluetooth/BluetoothLeGatt/template-params.xml
+++ b/connectivity/bluetooth/BluetoothLeGatt/template-params.xml
@@ -33,4 +33,43 @@
 
     <template src="base"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Connectivity</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-detail.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Service</android>
+            <android>android.bluetooth.BluetoothGattService</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates how to use the Bluetooth LE Generic Attribute Profile (GATT)
+to transmit arbitrary data between devices.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample shows a list of available Bluetooth LE devices and provides
+an interface to connect, display data and display GATT services and
+characteristics supported by the devices.
+
+It creates a [Service][1] for managing connection and data communication with a GATT server
+hosted on a given Bluetooth LE device.
+
+The Activities communicate with the Service, which in turn interacts with the [Bluetooth LE API][2].
+
+[1]:http://developer.android.com/reference/android/app/Service.html
+[2]:https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-hdpi/ic_launcher.png b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-hdpi/ic_launcher.png
index 22ce606..fad6812 100755
--- a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-mdpi/ic_launcher.png b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-mdpi/ic_launcher.png
index f21e17b..04cf115 100755
--- a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 64b8059..53736df 100755
--- a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index 6b4434a..6afbfca 100755
--- a/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/connectivity/network/BasicNetworking/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/gradle/wrapper/gradle-wrapper.properties b/connectivity/network/BasicNetworking/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/connectivity/network/BasicNetworking/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/network/BasicNetworking/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/network/BasicNetworking/screenshots/big_icon.png b/connectivity/network/BasicNetworking/screenshots/big_icon.png
new file mode 100644
index 0000000..a9c8849
--- /dev/null
+++ b/connectivity/network/BasicNetworking/screenshots/big_icon.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/screenshots/start.png b/connectivity/network/BasicNetworking/screenshots/start.png
new file mode 100644
index 0000000..e244f18
--- /dev/null
+++ b/connectivity/network/BasicNetworking/screenshots/start.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/screenshots/tested.png b/connectivity/network/BasicNetworking/screenshots/tested.png
new file mode 100644
index 0000000..96359c6
--- /dev/null
+++ b/connectivity/network/BasicNetworking/screenshots/tested.png
Binary files differ
diff --git a/connectivity/network/BasicNetworking/template-params.xml b/connectivity/network/BasicNetworking/template-params.xml
index 3413740..efdabbd 100644
--- a/connectivity/network/BasicNetworking/template-params.xml
+++ b/connectivity/network/BasicNetworking/template-params.xml
@@ -25,15 +25,51 @@
     <strings>
         <intro>
             <![CDATA[
-                This sample demonstrates how to use the ConnectivityManager to determine if you have
-                a network connection, and if so, what type of connection it is.
-                \n\nA "NetworkInfo" object is retrieved from the ConnectivityManager, which contains information
-                on the active connection, and then the connection type is printed to an on-screen console.
+            This sample demonstrates how to use the ConnectivityManager to determine if you have
+            a network connection, and if so, what type of connection it is.
+            \n\nA "NetworkInfo" object is retrieved from the ConnectivityManager, which contains information
+            on the active connection, and then the connection type is printed to an on-screen console.
             ]]>
         </intro>
     </strings>
 
     <template src="base"/>
     <common src="logger"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Connectivity</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/big_icon.png</icon>
+        <screenshots>
+            <img>screenshots/start.png</img>
+            <img>screenshots/tested.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.net.ConnectivityManager</android>
+            <android>android.net.NetworkInfo</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates how to check network connectivity with Android APIs.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+It utilizes the [`ConnectivityManager`][1] to determine if you have
+a network connection, and if so, what type of connection it is.
 
+A [`NetworkInfo`][2] object is retrieved from the ConnectivityManager, which contains information
+on the active connection, and then the connection type is printed to an on-screen console.
+
+Multiple types of connectivity can be displayed and could be used to take different measures
+in actual production code.
+
+[1]: https://developer.android.com/reference/android/net/ConnectivityManager.html
+[2]: https://developer.android.com/reference/android/net/NetworkInfo.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/connectivity/network/NetworkConnect/gradle/wrapper/gradle-wrapper.properties b/connectivity/network/NetworkConnect/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/connectivity/network/NetworkConnect/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/network/NetworkConnect/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/nfc/BeamLargeFiles/Application/src/main/AndroidManifest.xml b/connectivity/nfc/BeamLargeFiles/Application/src/main/AndroidManifest.xml
index 6ef958d..b084253 100644
--- a/connectivity/nfc/BeamLargeFiles/Application/src/main/AndroidManifest.xml
+++ b/connectivity/nfc/BeamLargeFiles/Application/src/main/AndroidManifest.xml
@@ -45,7 +45,7 @@
 
         <!-- Content provider, used to provide images for transmission. -->
         <provider
-            android:name=".AssetProvider"
+            android:name="com.example.android.common.assetprovider.AssetProvider"
             android:authorities="com.example.android.beamlargefiles.files"
             android:exported="true"/>
 
diff --git a/connectivity/nfc/BeamLargeFiles/Application/src/main/res/menu/main.xml b/connectivity/nfc/BeamLargeFiles/Application/src/main/res/menu/main.xml
new file mode 100644
index 0000000..8de3baa
--- /dev/null
+++ b/connectivity/nfc/BeamLargeFiles/Application/src/main/res/menu/main.xml
@@ -0,0 +1,18 @@
+<!--
+  Copyright 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android" />
+
diff --git a/connectivity/nfc/BeamLargeFiles/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/BeamLargeFiles/gradle/wrapper/gradle-wrapper.properties
index 5c22dec..0c71e76 100644
--- a/connectivity/nfc/BeamLargeFiles/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/nfc/BeamLargeFiles/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/nfc/BeamLargeFiles/template-params.xml b/connectivity/nfc/BeamLargeFiles/template-params.xml
index 947cf70..79142a4 100644
--- a/connectivity/nfc/BeamLargeFiles/template-params.xml
+++ b/connectivity/nfc/BeamLargeFiles/template-params.xml
@@ -16,6 +16,7 @@
 -->
 <sample>
     <name>BeamLargeFiles</name>
+    <group>Connectivity</group>
     <package>com.example.android.beamlargefiles</package>
 
 
diff --git a/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties
index a7bd4a7..4819af8 100644
--- a/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/sync/BasicSyncAdapter/Application/src/main/big_icon.png b/connectivity/sync/BasicSyncAdapter/Application/src/main/big_icon.png
new file mode 100644
index 0000000..7904a13
--- /dev/null
+++ b/connectivity/sync/BasicSyncAdapter/Application/src/main/big_icon.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-hdpi/ic_launcher.png b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-hdpi/ic_launcher.png
index a0f7005..7920c98 100644
--- a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-mdpi/ic_launcher.png b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-mdpi/ic_launcher.png
index a085462..1de60fe 100644
--- a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 4f78eb8..fc4bfeb 100644
--- a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index b198ee3..646ce94 100644
--- a/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/connectivity/sync/BasicSyncAdapter/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/gradle/wrapper/gradle-wrapper.properties b/connectivity/sync/BasicSyncAdapter/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/connectivity/sync/BasicSyncAdapter/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/sync/BasicSyncAdapter/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/connectivity/sync/BasicSyncAdapter/screenshots/icon-web.png b/connectivity/sync/BasicSyncAdapter/screenshots/icon-web.png
new file mode 100644
index 0000000..7904a13
--- /dev/null
+++ b/connectivity/sync/BasicSyncAdapter/screenshots/icon-web.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/screenshots/main.png b/connectivity/sync/BasicSyncAdapter/screenshots/main.png
new file mode 100644
index 0000000..c0a9d48
--- /dev/null
+++ b/connectivity/sync/BasicSyncAdapter/screenshots/main.png
Binary files differ
diff --git a/connectivity/sync/BasicSyncAdapter/template-params.xml b/connectivity/sync/BasicSyncAdapter/template-params.xml
index 7fba138..304a682 100644
--- a/connectivity/sync/BasicSyncAdapter/template-params.xml
+++ b/connectivity/sync/BasicSyncAdapter/template-params.xml
@@ -35,6 +35,52 @@
             ]]>
         </intro>
     </strings>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Background, Connectivity</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Service</android>
+            <android>android.content.AbstractThreadedSyncAdapter</android>
+            <android>android.content.ContentProvider</android>
+            <android>android.content.ContentResolver</android>
+            <android>android.content.SyncResult</android>
+            <android>android.database.sqlite.SQLiteDatabase</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates using SyncAdapter to fetch background
+data for an app. It covers the creation of the required Service
+that the OS uses to initiate the background data sync as well as
+scheduling syncs with background data.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample demonstrates using SyncAdapter to fetch background data
+for an app. SyncAdapters can be used to execute your data transfer
+code at configurable intervals, while efficiently using battery and
+other system resources.
+
+This sample implements all the required elements of a sync adapter.
+- Creates a sync adapter class.
+- Creates a bound Service which the OS uses to initiate a sync.
+- Defines the sync adapter properties in an XML resource file.
+- Declares the bound Service in the app manifest.
+
+For more on SyncAdapters refer to [Transferring Data Using Sync Adapters][1]
+
+[1]: http://developer.android.com/training/sync-adapters/index.html
+]]>
+        </intro>
+    </metadata>
 
     <template src="base"/>
     <common src="accounts"/>
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/content/WidgetData/Application/.gitignore
similarity index 100%
copy from ui/notifications/LNotifications/Application/.gitignore
copy to content/WidgetData/Application/.gitignore
diff --git a/content/WidgetData/Application/README-fragmentview.txt b/content/WidgetData/Application/README-fragmentview.txt
new file mode 100644
index 0000000..38d903f
--- /dev/null
+++ b/content/WidgetData/Application/README-fragmentview.txt
@@ -0,0 +1,37 @@
+<!--
+        Copyright 2013 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+-->
+
+Steps to implement FragmentView template:
+-in template-params.xml.ftl:
+    -add the following line to common imports
+        <common src="activities"/>
+
+-Add a Fragment to show behavior.  In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+   For instance, if your package name is com.example.foo, create the file
+   src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/fragmentViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/content/WidgetData/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to content/WidgetData/Application/proguard-project.txt
diff --git a/content/WidgetData/Application/src/main/AndroidManifest.xml b/content/WidgetData/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1c5fb74
--- /dev/null
+++ b/content/WidgetData/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.widgetdata"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk android:minSdkVersion="4"
+        android:targetSdkVersion="20" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <!-- The widget provider -->
+        <receiver android:name="WeatherWidgetProvider">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <!-- This specifies the widget provider info -->
+            <meta-data android:name="android.appwidget.provider"
+                android:resource="@xml/widgetinfo" />
+        </receiver>
+
+        <!-- The service serving the RemoteViews to the collection widget -->
+        <service android:name="WeatherWidgetService"
+            android:permission="android.permission.BIND_REMOTEVIEWS"
+            android:exported="false" />
+
+        <!-- The content provider serving the (fake) weather data -->
+        <provider android:name="WeatherDataProvider"
+            android:authorities="com.example.android.widgetdata.provider" android:exported="true" />
+
+        <activity android:name=".MainActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+
+</manifest>
diff --git a/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherDataProvider.java b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherDataProvider.java
new file mode 100644
index 0000000..2508f01
--- /dev/null
+++ b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherDataProvider.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.widgetdata;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import java.util.ArrayList;
+
+/**
+ * A dummy class that we are going to use internally to store weather data.  Generally, this data
+ * will be stored in an external and persistent location (ie. File, Database, SharedPreferences) so
+ * that the data can persist if the process is ever killed.  For simplicity, in this sample the
+ * data will only be stored in memory.
+ */
+class WeatherDataPoint {
+    String day;
+    int degrees;
+
+    WeatherDataPoint(String d, int deg) {
+        day = d;
+        degrees = deg;
+    }
+}
+
+/**
+ * The AppWidgetProvider for our sample weather widget.
+ */
+public class WeatherDataProvider extends ContentProvider {
+    public static final Uri CONTENT_URI =
+        Uri.parse("content://com.example.android.widgetdata.provider");
+    public static class Columns {
+        public static final String ID = "_id";
+        public static final String DAY = "day";
+        public static final String TEMPERATURE = "temperature";
+    }
+
+    /**
+     * Generally, this data will be stored in an external and persistent location (ie. File,
+     * Database, SharedPreferences) so that the data can persist if the process is ever killed.
+     * For simplicity, in this sample the data will only be stored in memory.
+     */
+    private static final ArrayList<WeatherDataPoint> sData = new ArrayList<WeatherDataPoint>();
+
+    @Override
+    public boolean onCreate() {
+        // We are going to initialize the data provider with some default values
+        sData.add(new WeatherDataPoint("Monday", 13));
+        sData.add(new WeatherDataPoint("Tuesday", 1));
+        sData.add(new WeatherDataPoint("Wednesday", 7));
+        sData.add(new WeatherDataPoint("Thursday", 4));
+        sData.add(new WeatherDataPoint("Friday", 22));
+        sData.add(new WeatherDataPoint("Saturday", -10));
+        sData.add(new WeatherDataPoint("Sunday", -13));
+        sData.add(new WeatherDataPoint("Monday", 8));
+        sData.add(new WeatherDataPoint("Tuesday", 11));
+        sData.add(new WeatherDataPoint("Wednesday", -1));
+        sData.add(new WeatherDataPoint("Thursday", 27));
+        sData.add(new WeatherDataPoint("Friday", 27));
+        sData.add(new WeatherDataPoint("Saturday", 27));
+        sData.add(new WeatherDataPoint("Sunday", 27));
+        return true;
+    }
+
+    @Override
+    public synchronized Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        assert(uri.getPathSegments().isEmpty());
+
+        // In this sample, we only query without any parameters, so we can just return a cursor to
+        // all the weather data.
+        final MatrixCursor c = new MatrixCursor(
+                new String[]{ Columns.ID, Columns.DAY, Columns.TEMPERATURE });
+        for (int i = 0; i < sData.size(); ++i) {
+            final WeatherDataPoint data = sData.get(i);
+            c.addRow(new Object[]{ new Integer(i), data.day, new Integer(data.degrees) });
+        }
+        return c;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "vnd.android.cursor.dir/vnd.widgetdata.temperature";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        // This example code does not support inserting
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        // This example code does not support deleting
+        return 0;
+    }
+
+    @Override
+    public synchronized int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        assert(uri.getPathSegments().size() == 1);
+
+        // In this sample, we only update the content provider individually for each row with new
+        // temperature values.
+        final int index = Integer.parseInt(uri.getPathSegments().get(0));
+        final MatrixCursor c = new MatrixCursor(
+                new String[]{ Columns.ID, Columns.DAY, Columns.TEMPERATURE });
+        assert(0 <= index && index < sData.size());
+        final WeatherDataPoint data = sData.get(index);
+        data.degrees = values.getAsInteger(Columns.TEMPERATURE);
+
+        // Notify any listeners that the data backing the content provider has changed, and return
+        // the number of rows affected.
+        getContext().getContentResolver().notifyChange(uri, null);
+        return 1;
+    }
+
+}
diff --git a/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherWidgetProvider.java b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherWidgetProvider.java
new file mode 100644
index 0000000..cd80148
--- /dev/null
+++ b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherWidgetProvider.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.widgetdata;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.database.Cursor;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+import java.util.Random;
+
+/**
+ * Our data observer just notifies an update for all weather widgets when it detects a change.
+ */
+class WeatherDataProviderObserver extends ContentObserver {
+    private AppWidgetManager mAppWidgetManager;
+    private ComponentName mComponentName;
+
+    WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h) {
+        super(h);
+        mAppWidgetManager = mgr;
+        mComponentName = cn;
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        // The data has changed, so notify the widget that the collection view needs to be updated.
+        // In response, the factory's onDataSetChanged() will be called which will requery the
+        // cursor for the new data.
+        mAppWidgetManager.notifyAppWidgetViewDataChanged(
+                mAppWidgetManager.getAppWidgetIds(mComponentName), R.id.weather_list);
+    }
+}
+
+/**
+ * The weather widget's AppWidgetProvider.
+ */
+public class WeatherWidgetProvider extends AppWidgetProvider {
+    public static String CLICK_ACTION = "com.example.android.widgetdata.CLICK";
+    public static String REFRESH_ACTION = "com.example.android.widgetdata.REFRESH";
+    public static String EXTRA_DAY_ID = "com.example.android.widgetdata.day";
+
+    private static HandlerThread sWorkerThread;
+    private static Handler sWorkerQueue;
+    private static WeatherDataProviderObserver sDataObserver;
+    private static final int sMaxDegrees = 96;
+
+    private boolean mIsLargeLayout = true;
+    private int mHeaderWeatherState = 0;
+
+    public WeatherWidgetProvider() {
+        // Start the worker thread
+        sWorkerThread = new HandlerThread("WeatherWidgetProvider-worker");
+        sWorkerThread.start();
+        sWorkerQueue = new Handler(sWorkerThread.getLooper());
+    }
+
+    // XXX: clear the worker queue if we are destroyed?
+
+    @Override
+    public void onEnabled(Context context) {
+        // Register for external updates to the data to trigger an update of the widget.  When using
+        // content providers, the data is often updated via a background service, or in response to
+        // user interaction in the main app.  To ensure that the widget always reflects the current
+        // state of the data, we must listen for changes and update ourselves accordingly.
+        final ContentResolver r = context.getContentResolver();
+        if (sDataObserver == null) {
+            final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
+            final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);
+            sDataObserver = new WeatherDataProviderObserver(mgr, cn, sWorkerQueue);
+            r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver);
+        }
+    }
+
+    @Override
+    public void onReceive(Context ctx, Intent intent) {
+        final String action = intent.getAction();
+        if (action.equals(REFRESH_ACTION)) {
+            // BroadcastReceivers have a limited amount of time to do work, so for this sample, we
+            // are triggering an update of the data on another thread.  In practice, this update
+            // can be triggered from a background service, or perhaps as a result of user actions
+            // inside the main application.
+            final Context context = ctx;
+            sWorkerQueue.removeMessages(0);
+            sWorkerQueue.post(new Runnable() {
+                @Override
+                public void run() {
+                    final ContentResolver r = context.getContentResolver();
+                    final Cursor c = r.query(WeatherDataProvider.CONTENT_URI, null, null, null, 
+                            null);
+                    final int count = c.getCount();
+
+                    // We disable the data changed observer temporarily since each of the updates
+                    // will trigger an onChange() in our data observer.
+                    r.unregisterContentObserver(sDataObserver);
+                    for (int i = 0; i < count; ++i) {
+                        final Uri uri = ContentUris.withAppendedId(WeatherDataProvider.CONTENT_URI, i);
+                        final ContentValues values = new ContentValues();
+                        values.put(WeatherDataProvider.Columns.TEMPERATURE,
+                                new Random().nextInt(sMaxDegrees));
+                        r.update(uri, values, null, null);
+                    }
+                    r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver);
+
+                    final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
+                    final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);
+                    mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.weather_list);
+                }
+            });
+
+            final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                    AppWidgetManager.INVALID_APPWIDGET_ID);
+        } else if (action.equals(CLICK_ACTION)) {
+            // Show a toast
+            final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                    AppWidgetManager.INVALID_APPWIDGET_ID);
+            final String day = intent.getStringExtra(EXTRA_DAY_ID);
+            final String formatStr = ctx.getResources().getString(R.string.toast_format_string);
+            Toast.makeText(ctx, String.format(formatStr, day), Toast.LENGTH_SHORT).show();
+        }
+
+        super.onReceive(ctx, intent);
+    }
+
+    private RemoteViews buildLayout(Context context, int appWidgetId, boolean largeLayout) {
+        RemoteViews rv;
+        if (largeLayout) {
+            // Specify the service to provide data for the collection widget.  Note that we need to
+            // embed the appWidgetId via the data otherwise it will be ignored.
+            final Intent intent = new Intent(context, WeatherWidgetService.class);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+            rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
+            rv.setRemoteAdapter(appWidgetId, R.id.weather_list, intent);
+
+            // Set the empty view to be displayed if the collection is empty.  It must be a sibling
+            // view of the collection view.
+            rv.setEmptyView(R.id.weather_list, R.id.empty_view);
+
+            // Bind a click listener template for the contents of the weather list.  Note that we
+            // need to update the intent's data if we set an extra, since the extras will be
+            // ignored otherwise.
+            final Intent onClickIntent = new Intent(context, WeatherWidgetProvider.class);
+            onClickIntent.setAction(WeatherWidgetProvider.CLICK_ACTION);
+            onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+            onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
+            final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0,
+                    onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            rv.setPendingIntentTemplate(R.id.weather_list, onClickPendingIntent);
+
+            // Bind the click intent for the refresh button on the widget
+            final Intent refreshIntent = new Intent(context, WeatherWidgetProvider.class);
+            refreshIntent.setAction(WeatherWidgetProvider.REFRESH_ACTION);
+            final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,
+                    refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);
+
+            // Restore the minimal header
+            rv.setTextViewText(R.id.city_name, context.getString(R.string.city_name));
+        } else {
+            rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout_small);
+
+            // Update the header to reflect the weather for "today"
+            Cursor c = context.getContentResolver().query(WeatherDataProvider.CONTENT_URI, null,
+                    null, null, null);
+            if (c.moveToPosition(0)) {
+                int tempColIndex = c.getColumnIndex(WeatherDataProvider.Columns.TEMPERATURE);
+                int temp = c.getInt(tempColIndex);
+                String formatStr = context.getResources().getString(R.string.header_format_string);
+                String header = String.format(formatStr, temp,
+                        context.getString(R.string.city_name));
+                rv.setTextViewText(R.id.city_name, header);
+            }
+            c.close();
+        }
+        return rv;
+    }
+
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        // Update each of the widgets with the remote adapter
+        for (int i = 0; i < appWidgetIds.length; ++i) {
+            RemoteViews layout = buildLayout(context, appWidgetIds[i], mIsLargeLayout);
+            appWidgetManager.updateAppWidget(appWidgetIds[i], layout);
+        }
+        super.onUpdate(context, appWidgetManager, appWidgetIds);
+    }
+
+    @Override
+    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
+            int appWidgetId, Bundle newOptions) {
+
+        int minWidth = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
+        int maxWidth = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
+        int minHeight = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
+        int maxHeight = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
+
+        RemoteViews layout;
+        if (minHeight < 100) {
+            mIsLargeLayout = false;
+        } else {
+            mIsLargeLayout = true;
+        }
+        layout = buildLayout(context, appWidgetId, mIsLargeLayout);
+        appWidgetManager.updateAppWidget(appWidgetId, layout);
+    }
+}
diff --git a/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherWidgetService.java b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherWidgetService.java
new file mode 100644
index 0000000..b88eb5f
--- /dev/null
+++ b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WeatherWidgetService.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.widgetdata;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService;
+
+/**
+ * This is the service that provides the factory to be bound to the collection service.
+ */
+public class WeatherWidgetService extends RemoteViewsService {
+    @Override
+    public RemoteViewsFactory onGetViewFactory(Intent intent) {
+        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
+    }
+}
+
+/**
+ * This is the factory that will provide data to the collection widget.
+ */
+class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+    private Context mContext;
+    private Cursor mCursor;
+    private int mAppWidgetId;
+
+    public StackRemoteViewsFactory(Context context, Intent intent) {
+        mContext = context;
+        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                AppWidgetManager.INVALID_APPWIDGET_ID);
+    }
+
+    public void onCreate() {
+        // Since we reload the cursor in onDataSetChanged() which gets called immediately after
+        // onCreate(), we do nothing here.
+    }
+
+    public void onDestroy() {
+        if (mCursor != null) {
+            mCursor.close();
+        }
+    }
+
+    public int getCount() {
+        return mCursor.getCount();
+    }
+
+    public RemoteViews getViewAt(int position) {
+        // Get the data for this position from the content provider
+        String day = "Unknown Day";
+        int temp = 0;
+        if (mCursor.moveToPosition(position)) {
+            final int dayColIndex = mCursor.getColumnIndex(WeatherDataProvider.Columns.DAY);
+            final int tempColIndex = mCursor.getColumnIndex(
+                    WeatherDataProvider.Columns.TEMPERATURE);
+            day = mCursor.getString(dayColIndex);
+            temp = mCursor.getInt(tempColIndex);
+        }
+
+        // Return a proper item with the proper day and temperature
+        final String formatStr = mContext.getResources().getString(R.string.item_format_string);
+        final int itemId = R.layout.widget_item;
+        RemoteViews rv = new RemoteViews(mContext.getPackageName(), itemId);
+        rv.setTextViewText(R.id.widget_item, String.format(formatStr, temp, day));
+
+        // Set the click intent so that we can handle it and show a toast message
+        final Intent fillInIntent = new Intent();
+        final Bundle extras = new Bundle();
+        extras.putString(WeatherWidgetProvider.EXTRA_DAY_ID, day);
+        fillInIntent.putExtras(extras);
+        rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
+
+        return rv;
+    }
+    public RemoteViews getLoadingView() {
+        // We aren't going to return a default loading view in this sample
+        return null;
+    }
+
+    public int getViewTypeCount() {
+        // Technically, we have two types of views (the dark and light background views)
+        return 2;
+    }
+
+    public long getItemId(int position) {
+        return position;
+    }
+
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    public void onDataSetChanged() {
+        // Refresh the cursor
+        if (mCursor != null) {
+            mCursor.close();
+        }
+        mCursor = mContext.getContentResolver().query(WeatherDataProvider.CONTENT_URI, null, null,
+                null, null);
+    }
+}
diff --git a/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WidgetDataFragment.java b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WidgetDataFragment.java
new file mode 100644
index 0000000..b7500a9
--- /dev/null
+++ b/content/WidgetData/Application/src/main/java/com/example/android/widgetdata/WidgetDataFragment.java
@@ -0,0 +1,19 @@
+package com.example.android.widgetdata;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A Dummy Fragment that shows the intro text from a layout.
+ */
+public class WidgetDataFragment extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment, null);
+    }
+}
diff --git a/content/WidgetData/Application/src/main/res/drawable-hdpi/ic_launcher.png b/content/WidgetData/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a602e84
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/drawable-hdpi/sunny.png b/content/WidgetData/Application/src/main/res/drawable-hdpi/sunny.png
new file mode 100644
index 0000000..42785b9
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-hdpi/sunny.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/drawable-mdpi/ic_launcher.png b/content/WidgetData/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c828af2
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/drawable-mdpi/sunny.png b/content/WidgetData/Application/src/main/res/drawable-mdpi/sunny.png
new file mode 100644
index 0000000..9453447
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-mdpi/sunny.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/drawable-nodpi/preview.png b/content/WidgetData/Application/src/main/res/drawable-nodpi/preview.png
new file mode 100644
index 0000000..b9c8780
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-nodpi/preview.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/content/WidgetData/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..b8911f9
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/content/WidgetData/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..042438f
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/content/WidgetData/Application/src/main/res/layout/fragment.xml b/content/WidgetData/Application/src/main/res/layout/fragment.xml
new file mode 100644
index 0000000..9281e3a
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/layout/fragment.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/vertical_page_margin"
+        android:layout_marginLeft="@dimen/horizontal_page_margin"
+        android:layout_marginRight="@dimen/horizontal_page_margin"
+        android:layout_marginTop="@dimen/vertical_page_margin"
+        android:text="@string/usage" />
+
+</ScrollView>
\ No newline at end of file
diff --git a/content/WidgetData/Application/src/main/res/layout/widget_item.xml b/content/WidgetData/Application/src/main/res/layout/widget_item.xml
new file mode 100644
index 0000000..bd3b3f5
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/layout/widget_item.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget_item"
+    android:layout_width="match_parent"
+    android:layout_height="46dp"
+    android:background="#F0F0F0"
+    android:gravity="center_vertical"
+    android:paddingLeft="25dp"
+    android:textColor="#232323"
+    android:textSize="20sp" />
diff --git a/content/WidgetData/Application/src/main/res/layout/widget_layout.xml b/content/WidgetData/Application/src/main/res/layout/widget_layout.xml
new file mode 100644
index 0000000..8aca749
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/layout/widget_layout.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginBottom="@dimen/widget_margin_bottom"
+    android:layout_marginLeft="@dimen/widget_margin_left"
+    android:layout_marginRight="@dimen/widget_margin_right"
+    android:layout_marginTop="@dimen/widget_margin_top"
+    android:orientation="vertical">
+    <!-- We define separate margins to allow for flexibility in twiddling the margins
+         depending on device form factor and target SDK version. -->
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="80dp"
+        android:background="#F8F8F8"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/city_weather"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:adjustViewBounds="true"
+            android:padding="12dp"
+            android:scaleType="fitStart"
+            android:src="@drawable/sunny" />
+
+        <TextView
+            android:id="@+id/city_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:text="@string/city_name"
+            android:textAllCaps="true"
+            android:textColor="#232323"
+            android:textSize="24sp" />
+    </LinearLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:layout_weight="1"
+        android:background="#F8F8F8">
+
+        <ListView
+            android:id="@+id/weather_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+        <TextView
+            android:id="@+id/empty_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="@string/empty_view_text"
+            android:textColor="#ffffff"
+            android:textSize="20sp"
+            android:visibility="gone" />
+    </FrameLayout>
+
+    <Button
+        android:id="@+id/refresh"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#F8F8F8"
+        android:gravity="center"
+        android:padding="12dp"
+        android:text="@string/refresh"
+        android:textAllCaps="true"
+        android:textColor="#232323"
+        android:textSize="14sp" />
+</LinearLayout>
diff --git a/content/WidgetData/Application/src/main/res/layout/widget_layout_small.xml b/content/WidgetData/Application/src/main/res/layout/widget_layout_small.xml
new file mode 100644
index 0000000..40fff69
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/layout/widget_layout_small.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginBottom="@dimen/widget_margin_bottom"
+    android:layout_marginLeft="@dimen/widget_margin_left"
+    android:layout_marginRight="@dimen/widget_margin_right"
+    android:layout_marginTop="@dimen/widget_margin_top"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="80dp"
+        android:background="#F8F8F8"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:id="@+id/city_weather"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:adjustViewBounds="true"
+            android:padding="12dp"
+            android:scaleType="fitStart"
+            android:src="@drawable/sunny" />
+
+        <TextView
+            android:id="@+id/city_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:text="@string/city_name"
+            android:textAllCaps="true"
+            android:textColor="#232323"
+            android:textSize="24sp" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/content/WidgetData/Application/src/main/res/values-v14/dimens.xml b/content/WidgetData/Application/src/main/res/values-v14/dimens.xml
new file mode 100644
index 0000000..8b5494e
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/values-v14/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <dimen name="widget_margin_top">0dp</dimen>
+    <dimen name="widget_margin_bottom">0dp</dimen>
+    <dimen name="widget_margin_left">0dp</dimen>
+    <dimen name="widget_margin_right">0dp</dimen>
+</resources>
diff --git a/content/WidgetData/Application/src/main/res/values/dimens.xml b/content/WidgetData/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..00257a9
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <dimen name="widget_margin_top">8dp</dimen>
+    <dimen name="widget_margin_bottom">8dp</dimen>
+    <dimen name="widget_margin_left">8dp</dimen>
+    <dimen name="widget_margin_right">8dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/content/WidgetData/Application/src/main/res/values/strings.xml b/content/WidgetData/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..00fbb9a
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string name="empty_view_text">No cities found...</string>
+    <string name="toast_format_string">%1$s!</string>
+    <string name="item_format_string">%1$d\u00B0 on %2$s</string>
+    <string name="header_format_string">%1$d\u00B0 in %2$s</string>
+
+    <string name="refresh">Refresh</string>
+    <string name="city_name">San Francisco</string>
+    <string name="usage">Add the widget named <b>Widget Data</b> to your home screen to use this sample.</string>
+</resources>
diff --git a/content/WidgetData/Application/src/main/res/xml/widgetinfo.xml b/content/WidgetData/Application/src/main/res/xml/widgetinfo.xml
new file mode 100644
index 0000000..2e41943
--- /dev/null
+++ b/content/WidgetData/Application/src/main/res/xml/widgetinfo.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<appwidget-provider
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  android:minWidth="280dp"
+  android:minHeight="180dp"
+  android:updatePeriodMillis="1800000"
+  android:initialLayout="@layout/widget_layout"
+  android:resizeMode="vertical"
+  android:minResizeWidth="280dp"
+  android:minResizeHeight="70dp"
+  android:previewImage="@drawable/preview">
+</appwidget-provider>
diff --git a/content/WidgetData/Application/tests/AndroidManifest.xml b/content/WidgetData/Application/tests/AndroidManifest.xml
new file mode 100644
index 0000000..2ba50ad
--- /dev/null
+++ b/content/WidgetData/Application/tests/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2013 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.android.listwidget.tests"
+          android:versionCode="1"
+          android:versionName="1.0">
+
+    <uses-sdk
+            android:minSdkVersion="18"
+            android:targetSdkVersion="19" />
+
+    <!-- We add an application tag here just so that we can indicate that
+         this package needs to link against the android.test library,
+         which is needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <!--
+    Specifies the instrumentation test runner used to run the tests.
+    -->
+    <instrumentation
+            android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="com.example.android.listwidget"
+            android:label="Tests for com.example.android.listwidget" />
+
+</manifest>
\ No newline at end of file
diff --git a/content/WidgetData/Application/tests/src/com/example/android/listwidget/tests/SampleTests.java b/content/WidgetData/Application/tests/src/com/example/android/listwidget/tests/SampleTests.java
new file mode 100644
index 0000000..ee8f93a
--- /dev/null
+++ b/content/WidgetData/Application/tests/src/com/example/android/listwidget/tests/SampleTests.java
@@ -0,0 +1,76 @@
+/*
+* Copyright 2013 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/*
+* Copyright (C) 2013 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.listwidget.tests;
+
+import com.example.android.listwidget.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for listwidget sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private ListwidgetFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (ListwidgetFragment)
+            mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+    * Test if the test fixture has been set up correctly.
+    */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    /**
+    * Add more tests below.
+    */
+
+}
\ No newline at end of file
diff --git a/ui/notifications/BasicNotifications/LICENSE b/content/WidgetData/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to content/WidgetData/LICENSE
diff --git a/content/WidgetData/README.md b/content/WidgetData/README.md
new file mode 100644
index 0000000..cdba710
--- /dev/null
+++ b/content/WidgetData/README.md
@@ -0,0 +1,67 @@
+Android WidgetData Sample
+===================================
+
+Sample demonstrating how to instantiate an ActionBar on Android, define
+action items, and set an "up" navigation link. Uses the Support Library
+for compatibility with pre-3.0 devices.
+
+Introduction
+------------
+
+Long intro here.
+
+Multi-paragraph introduction to sample, from an educational point-of-view.
+*Makrdown* formatting allowed. See [Markdown Documentation][1]
+for details.
+
+[1]: http://daringfireball.net/projects/markdown/syntax
+
+Pre-requisites
+--------------
+
+- Android SDK v21
+- Android Build Tools v21.1.1
+- Android Support Repository
+
+Screenshots
+-------------
+
+<img src="screenshots/1-main.png" height="400" alt="Screenshot"/> <img src="screenshots/2-settings.png" height="400" alt="Screenshot"/> 
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-WidgetData
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License.  You may obtain a copy of
+the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/content/WidgetData/build.gradle b/content/WidgetData/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/content/WidgetData/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/content/WidgetData/buildSrc/build.gradle b/content/WidgetData/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/content/WidgetData/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/content/WidgetData/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to content/WidgetData/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/content/WidgetData/gradle/wrapper/gradle-wrapper.properties b/content/WidgetData/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..fe55261
--- /dev/null
+++ b/content/WidgetData/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Jan 07 17:32:06 EST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/content/WidgetData/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to content/WidgetData/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/content/WidgetData/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to content/WidgetData/gradlew.bat
diff --git a/content/WidgetData/screenshots/1-widget.png b/content/WidgetData/screenshots/1-widget.png
new file mode 100644
index 0000000..3d2b8d3
--- /dev/null
+++ b/content/WidgetData/screenshots/1-widget.png
Binary files differ
diff --git a/content/WidgetData/screenshots/icon-web.png b/content/WidgetData/screenshots/icon-web.png
new file mode 100644
index 0000000..8a9d0bd
--- /dev/null
+++ b/content/WidgetData/screenshots/icon-web.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/content/WidgetData/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to content/WidgetData/settings.gradle
diff --git a/content/WidgetData/template-params.xml b/content/WidgetData/template-params.xml
new file mode 100644
index 0000000..552f3a2
--- /dev/null
+++ b/content/WidgetData/template-params.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?><!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>WidgetData</name>
+    <group>Content</group>
+    <package>com.example.android.widgetdata</package>
+
+    <!-- change minSdk if needed-->
+    <minSdk>11</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+            This sample demonstrates how to create a list-based widget specifically backed by a
+            content provider.\n\n
+This sample uses a collection view (a ListView) to present some mock weather data in a widget.
+In particular, we will be using a content provider to demonstrate how the widget can retrieve data
+and update itself when you are using more complex data sources.
+When working with external data, or data which must be fetched over high latency, it is important
+to keep the data cached in a persistent location so that the widget feels responsive.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base" />
+    <template src="FragmentView" />
+    <common src="activities"/>
+    <common src="logger" />
+
+    <metadata>
+    <status>PUBLISHED</status>
+    <categories>Content</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>INTERMEDIATE</level>
+    <icon>screenshots/icon-web.png</icon>
+    <screenshots>
+        <img>screenshots/1-widget.png</img>
+    </screenshots>
+    <api_refs>
+        <android>android.widget.RemoteViews</android>
+        <android>android.widget.RemoteViewsService</android>
+        <android>android.database.ContentObserver</android>
+        <android>android.content.ContentResolver</android>
+    </api_refs>
+    <description>
+<![CDATA[
+This sample demonstrates how to create a list-based widget specifically backed 
+by a content provider.
+]]>
+    </description>
+    <intro>
+<![CDATA[
+This sample uses a collection view (a ListView) to present some mock weather data in a widget.
+In particular, we will be using a content provider to demonstrate how the widget can retrieve data
+and update itself when you are using more complex data sources.
+When working with external data, or data which must be fetched over high latency, it is important
+to keep the data cached in a persistent location so that the widget feels responsive.
+
+We define a [ContentProvider][1] that handles queries from a [RemoteView][2] [ListView][3] widget.
+A [ContentObserver][4] notifies the widget when data changes in the ContentProvider.
+Data for the widget is provided by a [RemoteViewsService][5] that populates a RemoteView collection (the ListView).
+
+[1]: http://developer.android.com/reference/android/content/ContentProvider.html
+[2]: http://developer.android.com/reference/android/widget/RemoteViews.RemoteView.html
+[3]: http://developer.android.com/reference/android/widget/ListView.html
+[4]: http://developer.android.com/reference/android/database/ContentObserver.html
+[5]: http://developer.android.com/reference/android/widget/RemoteViewsService.html
+]]>
+    </intro>
+</metadata>
+</sample>
diff --git a/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties b/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties
index 96bdeaf..542898d 100644
--- a/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties
+++ b/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties
@@ -4,4 +4,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/content/contacts/BasicContactables/screenshots/1-main.png b/content/contacts/BasicContactables/screenshots/1-main.png
new file mode 100644
index 0000000..2c11a7d
--- /dev/null
+++ b/content/contacts/BasicContactables/screenshots/1-main.png
Binary files differ
diff --git a/content/contacts/BasicContactables/screenshots/2-search.png b/content/contacts/BasicContactables/screenshots/2-search.png
new file mode 100644
index 0000000..b8b012f
--- /dev/null
+++ b/content/contacts/BasicContactables/screenshots/2-search.png
Binary files differ
diff --git a/content/contacts/BasicContactables/screenshots/3-results.png b/content/contacts/BasicContactables/screenshots/3-results.png
new file mode 100644
index 0000000..48856ce
--- /dev/null
+++ b/content/contacts/BasicContactables/screenshots/3-results.png
Binary files differ
diff --git a/content/contacts/BasicContactables/screenshots/icon-web.png b/content/contacts/BasicContactables/screenshots/icon-web.png
new file mode 100644
index 0000000..8ad9e14
--- /dev/null
+++ b/content/contacts/BasicContactables/screenshots/icon-web.png
Binary files differ
diff --git a/content/contacts/BasicContactables/template-params.xml b/content/contacts/BasicContactables/template-params.xml
index ee2ba11..01274ae 100644
--- a/content/contacts/BasicContactables/template-params.xml
+++ b/content/contacts/BasicContactables/template-params.xml
@@ -38,4 +38,51 @@
     <template src="base"/>
     <common src="logger"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Content</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-search.png</img>
+            <img>screenshots/3-results.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.SearchManager</android>
+            <android>android.widget.SearchView</android>
+            <android>android.app.LoaderManager</android>
+            <android>android.content.CursorLoader</android>
+            <android>android.provider.ContactsContract.CommonDataKinds</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample shows how to search for contacts, displaying a SearchView in the Action Bar for user input and implementing a query Cursor with CommonDataKinds.Contactables.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample displays a [SearchView][1] in the Action Bar when the search icon is clicked. It then implements the [LoaderManager.LoaderCallbacks][2] interface to query the contacts table, using a [CursorLoader][3].
+
+For details on how to implement the [SearchView][1], refer to the training guide [Setting up the search interface][4].
+
+For details on how to implement the [LoaderManager.LoaderCallbacks][2] interface, refer to the [Using the LoaderManager Callbacks][5] guide.
+
+For details on how to query the contacts provider, refer to the [Contacts Provider Access][6] guide.
+
+[1]: http://developer.android.com/reference/android/widget/SearchView.html
+[2]: http://developer.android.com/reference/android/app/LoaderManager.LoaderCallbacks.html
+[3]: http://developer.android.com/reference/android/content/CursorLoader.html
+[4]: http://developer.android.com/training/search/setup.html
+[5]: http://developer.android.com/guide/components/loaders.html#callback
+[6]: http://developer.android.com/guide/topics/providers/contacts-provider.html#Access
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/notifications/BasicNotifications/Application/.gitignore b/content/documentsUi/DirectorySelection/Application/.gitignore
similarity index 100%
copy from ui/notifications/BasicNotifications/Application/.gitignore
copy to content/documentsUi/DirectorySelection/Application/.gitignore
diff --git a/ui/notifications/BasicNotifications/Application/proguard-project.txt b/content/documentsUi/DirectorySelection/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/BasicNotifications/Application/proguard-project.txt
copy to content/documentsUi/DirectorySelection/Application/proguard-project.txt
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/AndroidManifest.xml b/content/documentsUi/DirectorySelection/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..46cddcf
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.directoryselection"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".DirectorySelectionActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+
+</manifest>
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectoryEntry.java b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectoryEntry.java
new file mode 100644
index 0000000..04c1c89
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectoryEntry.java
@@ -0,0 +1,25 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.directoryselection;
+
+/**
+ * Entity class that represents an directory entry.
+ */
+public class DirectoryEntry {
+    public String fileName;
+    public String mimeType;
+}
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectoryEntryAdapter.java b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectoryEntryAdapter.java
new file mode 100644
index 0000000..e92c71e
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectoryEntryAdapter.java
@@ -0,0 +1,100 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.directoryselection;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Provide views to RecyclerView with the directory entries.
+ */
+public class DirectoryEntryAdapter extends RecyclerView.Adapter<DirectoryEntryAdapter.ViewHolder> {
+
+    static final String DIRECTORY_MIME_TYPE = "vnd.android.document/directory";
+    private List<DirectoryEntry> mDirectoryEntries;
+
+    /**
+     * Provide a reference to the type of views that you are using (custom ViewHolder)
+     */
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        private final TextView mFileName;
+        private final TextView mMimeType;
+        private final ImageView mImageView;
+
+        public ViewHolder(View v) {
+            super(v);
+            mFileName = (TextView) v.findViewById(R.id.textview_filename);
+            mMimeType = (TextView) v.findViewById(R.id.textview_mimetype);
+            mImageView = (ImageView) v.findViewById(R.id.entry_image);
+        }
+
+        public TextView getFileName() {
+            return mFileName;
+        }
+
+        public TextView getMimeType() {
+            return mMimeType;
+        }
+
+        public ImageView getImageView() {
+            return mImageView;
+        }
+    }
+
+    /**
+     * Initialize the directory entries of the Adapter.
+     *
+     * @param directoryEntries an array of {@link DirectoryEntry}.
+     */
+    public DirectoryEntryAdapter(List<DirectoryEntry> directoryEntries) {
+        mDirectoryEntries = directoryEntries;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+        View v = LayoutInflater.from(viewGroup.getContext())
+                .inflate(R.layout.directory_item, viewGroup, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
+        viewHolder.getFileName().setText(mDirectoryEntries.get(position).fileName);
+        viewHolder.getMimeType().setText(mDirectoryEntries.get(position).mimeType);
+
+        if (DIRECTORY_MIME_TYPE.equals(mDirectoryEntries.get(position).mimeType)) {
+            viewHolder.getImageView().setImageResource(R.drawable.ic_folder_grey600_36dp);
+        } else {
+            viewHolder.getImageView().setImageResource(R.drawable.ic_description_grey600_36dp);
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return mDirectoryEntries.size();
+    }
+
+    public void setDirectoryEntries(List<DirectoryEntry> directoryEntries) {
+        mDirectoryEntries = directoryEntries;
+    }
+}
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionActivity.java b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionActivity.java
new file mode 100644
index 0000000..d27ba72
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionActivity.java
@@ -0,0 +1,37 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.directoryselection;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Launcher Activity for the Directory Selection sample app.
+ */
+public class DirectorySelectionActivity extends FragmentActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_directory_selection);
+        if (savedInstanceState == null) {
+            getSupportFragmentManager().beginTransaction()
+                    .add(R.id.container, DirectorySelectionFragment.newInstance())
+                    .commit();
+        }
+    }
+}
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java
new file mode 100644
index 0000000..4af55db
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/java/com/example/android/directoryselection/DirectorySelectionFragment.java
@@ -0,0 +1,231 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.directoryselection;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment that demonstrates how to use Directory Selection API.
+ */
+public class DirectorySelectionFragment extends Fragment {
+
+    private static final String TAG = DirectorySelectionFragment.class.getSimpleName();
+
+    public static final int REQUEST_CODE_OPEN_DIRECTORY = 1;
+
+    Uri mCurrentDirectoryUri;
+    TextView mCurrentDirectoryTextView;
+    Button mCreateDirectoryButton;
+    RecyclerView mRecyclerView;
+    DirectoryEntryAdapter mAdapter;
+    RecyclerView.LayoutManager mLayoutManager;
+
+    /**
+     * Use this factory method to create a new instance of
+     * this fragment using the provided parameters.
+     *
+     * @return A new instance of fragment {@link DirectorySelectionFragment}.
+     */
+    public static DirectorySelectionFragment newInstance() {
+        DirectorySelectionFragment fragment = new DirectorySelectionFragment();
+        return fragment;
+    }
+
+    public DirectorySelectionFragment() {
+        // Required empty public constructor
+    }
+
+    @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
+        return inflater.inflate(R.layout.fragment_directory_selection, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View rootView, Bundle savedInstanceState) {
+        super.onViewCreated(rootView, savedInstanceState);
+
+        rootView.findViewById(R.id.button_open_directory)
+                .setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+                        startActivityForResult(intent, REQUEST_CODE_OPEN_DIRECTORY);
+                    }
+                });
+
+        mCurrentDirectoryTextView = (TextView) rootView
+                .findViewById(R.id.textview_current_directory);
+        mCreateDirectoryButton = (Button) rootView.findViewById(R.id.button_create_directory);
+        mCreateDirectoryButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final EditText editView = new EditText(getActivity());
+                new AlertDialog.Builder(getActivity())
+                        .setTitle(R.string.create_directory)
+                        .setView(editView)
+                        .setPositiveButton(android.R.string.ok,
+                                new DialogInterface.OnClickListener() {
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                        createDirectory(mCurrentDirectoryUri,
+                                                editView.getText().toString());
+                                        updateDirectoryEntries(mCurrentDirectoryUri);
+                                    }
+                                })
+                        .setNegativeButton(android.R.string.cancel,
+                                new DialogInterface.OnClickListener() {
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                    }
+                                })
+                        .show();
+            }
+        });
+        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_directory_entries);
+        mLayoutManager = new LinearLayoutManager(getActivity());
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        mRecyclerView.scrollToPosition(0);
+        mAdapter = new DirectoryEntryAdapter(new ArrayList<DirectoryEntry>());
+        mRecyclerView.setAdapter(mAdapter);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == REQUEST_CODE_OPEN_DIRECTORY && resultCode == Activity.RESULT_OK) {
+            Log.d(TAG, String.format("Open Directory result Uri : %s", data.getData()));
+            updateDirectoryEntries(data.getData());
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+
+    /**
+     * Updates the current directory of the uri passed as an argument and its children directories.
+     * And updates the {@link #mRecyclerView} depending on the contents of the children.
+     *
+     * @param uri The uri of the current directory.
+     */
+    //VisibileForTesting
+    void updateDirectoryEntries(Uri uri) {
+        ContentResolver contentResolver = getActivity().getContentResolver();
+        Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
+                DocumentsContract.getTreeDocumentId(uri));
+        Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
+                DocumentsContract.getTreeDocumentId(uri));
+
+        Cursor docCursor = contentResolver.query(docUri, new String[]{
+                Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
+        try {
+            while (docCursor.moveToNext()) {
+                Log.d(TAG, "found doc =" + docCursor.getString(0) + ", mime=" + docCursor
+                        .getString(1));
+                mCurrentDirectoryUri = uri;
+                mCurrentDirectoryTextView.setText(docCursor.getString(0));
+                mCreateDirectoryButton.setEnabled(true);
+            }
+        } finally {
+            closeQuietly(docCursor);
+        }
+
+        Cursor childCursor = contentResolver.query(childrenUri, new String[]{
+                Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
+        try {
+            List<DirectoryEntry> directoryEntries = new ArrayList<>();
+            while (childCursor.moveToNext()) {
+                Log.d(TAG, "found child=" + childCursor.getString(0) + ", mime=" + childCursor
+                        .getString(1));
+                DirectoryEntry entry = new DirectoryEntry();
+                entry.fileName = childCursor.getString(0);
+                entry.mimeType = childCursor.getString(1);
+                directoryEntries.add(entry);
+            }
+            mAdapter.setDirectoryEntries(directoryEntries);
+            mAdapter.notifyDataSetChanged();
+        } finally {
+            closeQuietly(childCursor);
+        }
+    }
+
+    /**
+     * Creates a directory under the directory represented as the uri in the argument.
+     *
+     * @param uri The uri of the directory under which a new directory is created.
+     * @param directoryName The directory name of a new directory.
+     */
+    //VisibileForTesting
+    void createDirectory(Uri uri, String directoryName) {
+        ContentResolver contentResolver = getActivity().getContentResolver();
+        Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
+                DocumentsContract.getTreeDocumentId(uri));
+        Uri directoryUri = DocumentsContract
+                .createDocument(contentResolver, docUri, Document.MIME_TYPE_DIR, directoryName);
+        if (directoryUri != null) {
+            Log.i(TAG, String.format(
+                    "Created directory : %s, Document Uri : %s, Created directory Uri : %s",
+                    directoryName, docUri, directoryUri));
+            Toast.makeText(getActivity(), String.format("Created a directory [%s]",
+                    directoryName), Toast.LENGTH_SHORT).show();
+        } else {
+            Log.w(TAG, String.format("Failed to create a directory : %s, Uri %s", directoryName,
+                    docUri));
+            Toast.makeText(getActivity(), String.format("Failed to created a directory [%s] : ",
+                    directoryName), Toast.LENGTH_SHORT).show();
+        }
+
+    }
+
+    public void closeQuietly(AutoCloseable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) {
+            }
+        }
+    }
+}
+
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_description_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..dd7d073
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_folder_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..6c022d4
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_launcher.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..49ee854
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_description_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..ac18b57
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_folder_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..e3dccd2
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_launcher.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..282a00c
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_description_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..50f854e
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_folder_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..6fbc404
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..7293ad6
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_description_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..33df5d9
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_folder_grey600_36dp.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..ed2f08e
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..7b618d4
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/layout/activity_directory_selection.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/layout/activity_directory_selection.xml
new file mode 100644
index 0000000..db65583
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/layout/activity_directory_selection.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/container"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             tools:context="com.example.android.directoryselection.DirectorySelectionActivity"
+             tools:ignore="MergeRootFrame" />
\ No newline at end of file
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/layout/directory_item.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/layout/directory_item.xml
new file mode 100644
index 0000000..0763cff
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/layout/directory_item.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/directory_item_height"
+        >
+
+    <LinearLayout android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:orientation="horizontal"
+                  android:layout_centerVertical="true"
+                  android:gravity="center_vertical"
+            >
+        <ImageView android:id="@+id/entry_image"
+                   android:layout_width="wrap_content"
+                   android:layout_height="wrap_content"
+                   android:src="@drawable/ic_folder_grey600_36dp"
+                />
+        <LinearLayout android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:layout_marginLeft="@dimen/margin_medium"
+                      android:orientation="vertical"
+                >
+            <View android:id="@+id/divisor"
+                  android:layout_width="match_parent"
+                  android:layout_height="1dp"
+                  android:background="#aaaaaa"/>
+
+            <TextView
+                    android:id="@+id/textview_filename"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    style="@style/DirectoryEntryNameFont"
+                    />
+            <TextView
+                    android:id="@+id/textview_mimetype"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"/>
+
+        </LinearLayout>
+    </LinearLayout>
+</RelativeLayout>
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml
new file mode 100644
index 0000000..d63219c
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/layout/fragment_directory_selection.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:gravity="center_vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:padding="@dimen/margin_medium">
+
+    <LinearLayout android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:orientation="horizontal"
+            >
+
+        <Button android:id="@+id/button_open_directory"
+                android:text="@string/open_directory"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+
+        <Button android:id="@+id/button_create_directory"
+                android:text="@string/create_directory"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                />
+    </LinearLayout>
+
+    <LinearLayout android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:orientation="horizontal"
+                  android:layout_marginLeft="@dimen/margin_small"
+                  android:layout_marginRight="@dimen/margin_small"
+            >
+
+        <TextView android:id="@+id/label_current_directory"
+                  android:text="@string/selected_directory"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"/>
+
+        <TextView android:id="@+id/textview_current_directory"
+                  android:enabled="false"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  style="@style/DirectoryEntryNameFont"
+                />
+
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+            android:id="@+id/recyclerview_directory_entries"
+            android:layout_marginLeft="@dimen/margin_small"
+            android:layout_marginRight="@dimen/margin_small"
+            android:scrollbars="vertical"
+            android:drawSelectorOnTop="true"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+</LinearLayout>
+
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/menu/main.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/menu/main.xml
new file mode 100644
index 0000000..e9b5e0b
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/menu/main.xml
@@ -0,0 +1,16 @@
+<!--
+  Copyright 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/values/dimens.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..53d0182
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+    <dimen name="directory_item_height">72dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/values/strings.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..24d59dd
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <string name="open_directory">Open directory</string>
+    <string name="create_directory">Create Directory</string>
+    <string name="selected_directory">"Selected Directory : "</string>
+</resources>
diff --git a/content/documentsUi/DirectorySelection/Application/src/main/res/values/styles.xml b/content/documentsUi/DirectorySelection/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..38441f3
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/src/main/res/values/styles.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <style name="DirectoryEntryNameFont" parent="@android:style/TextAppearance.Medium">
+        <item name="android:textColor">#000000</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectoryEntryAdapterTest.java b/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectoryEntryAdapterTest.java
new file mode 100644
index 0000000..97b2629
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectoryEntryAdapterTest.java
@@ -0,0 +1,82 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.directoryselection;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link DirectorySelectionFragment}.
+ */
+public class DirectoryEntryAdapterTest
+        extends ActivityInstrumentationTestCase2<DirectorySelectionActivity> {
+
+    private static final String FILE1 = "file1";
+    private static final String MIME_TYPE1 = "text/appliaction";
+    private static final String DIRECTORY1 = "directory1";
+
+    private DirectorySelectionActivity mTestActivity;
+    private DirectorySelectionFragment mTestFragment;
+    private DirectoryEntryAdapter mAdapter;
+    private List<DirectoryEntry> mDirectoryEntries;
+
+    public DirectoryEntryAdapterTest() {
+        super(DirectorySelectionActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mTestActivity = getActivity();
+        mTestFragment = (DirectorySelectionFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(0);
+        mDirectoryEntries = new ArrayList<>();
+
+        DirectoryEntry file = new DirectoryEntry();
+        file.fileName = FILE1;
+        file.mimeType = MIME_TYPE1;
+        mDirectoryEntries.add(file);
+
+        DirectoryEntry directory = new DirectoryEntry();
+        directory.fileName = DIRECTORY1;
+        directory.mimeType = DirectoryEntryAdapter.DIRECTORY_MIME_TYPE;
+        mDirectoryEntries.add(directory);
+    }
+
+    public void testGetItemCount() {
+        mTestFragment.mAdapter.setDirectoryEntries(mDirectoryEntries);
+
+        assertEquals(2, mTestFragment.mAdapter.getItemCount());
+    }
+}
diff --git a/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectorySelectionActivityTest.java b/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectorySelectionActivityTest.java
new file mode 100644
index 0000000..8f767ae
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectorySelectionActivityTest.java
@@ -0,0 +1,56 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.directoryselection;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Tests for {@link DirectorySelectionActivity}.
+ */
+public class DirectorySelectionActivityTest
+        extends ActivityInstrumentationTestCase2<DirectorySelectionActivity> {
+
+    private DirectorySelectionActivity mTestActivity;
+    private DirectorySelectionFragment mTestFragment;
+
+    public DirectorySelectionActivityTest() {
+        super(DirectorySelectionActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (DirectorySelectionFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(0);
+    }
+
+    /**
+     * Test if the test fixture has been set up correctly.
+     */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+}
diff --git a/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectorySelectionFragmentTest.java b/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectorySelectionFragmentTest.java
new file mode 100644
index 0000000..90cd30f
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/Application/tests/src/com/example/android/directoryselection/DirectorySelectionFragmentTest.java
@@ -0,0 +1,68 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.directoryselection;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Tests for {@link com.example.android.directoryselection.DirectorySelectionFragment}.
+ */
+public class DirectorySelectionFragmentTest
+        extends ActivityInstrumentationTestCase2<DirectorySelectionActivity> {
+
+    private DirectorySelectionActivity mTestActivity;
+    private DirectorySelectionFragment mTestFragment;
+
+    public DirectorySelectionFragmentTest() {
+        super(DirectorySelectionActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (DirectorySelectionFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(0);
+    }
+
+    public void testPreconditions() {
+        assertNotNull(mTestFragment.mCurrentDirectoryTextView);
+        assertNotNull(mTestFragment.mCreateDirectoryButton);
+        assertNotNull(mTestFragment.mRecyclerView);
+        assertNotNull(mTestFragment.mAdapter);
+        assertNotNull(mTestFragment.mLayoutManager);
+    }
+}
diff --git a/content/documentsUi/DirectorySelection/build.gradle b/content/documentsUi/DirectorySelection/build.gradle
new file mode 100644
index 0000000..18f393f
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../../build"
+  pathToSamplesCommon "../../../common"
+}
+apply from: "../../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/content/documentsUi/DirectorySelection/buildSrc/build.gradle b/content/documentsUi/DirectorySelection/buildSrc/build.gradle
new file mode 100644
index 0000000..7cebf71
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.jar b/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
copy to content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.properties b/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..3e37868
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Dec 03 14:12:05 JST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/content/documentsUi/DirectorySelection/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to content/documentsUi/DirectorySelection/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/content/documentsUi/DirectorySelection/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to content/documentsUi/DirectorySelection/gradlew.bat
diff --git a/content/documentsUi/DirectorySelection/screenshots/screenshot-1.png b/content/documentsUi/DirectorySelection/screenshots/screenshot-1.png
new file mode 100644
index 0000000..a4310dc
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/screenshots/screenshot-1.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/screenshots/screenshot-2.png b/content/documentsUi/DirectorySelection/screenshots/screenshot-2.png
new file mode 100644
index 0000000..cd27507
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/screenshots/screenshot-2.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/screenshots/screenshot-3.png b/content/documentsUi/DirectorySelection/screenshots/screenshot-3.png
new file mode 100644
index 0000000..0795475
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/screenshots/screenshot-3.png
Binary files differ
diff --git a/content/documentsUi/DirectorySelection/screenshots/web-icon.png b/content/documentsUi/DirectorySelection/screenshots/web-icon.png
new file mode 100755
index 0000000..f0a573f
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/screenshots/web-icon.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/content/documentsUi/DirectorySelection/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to content/documentsUi/DirectorySelection/settings.gradle
diff --git a/content/documentsUi/DirectorySelection/template-params.xml b/content/documentsUi/DirectorySelection/template-params.xml
new file mode 100644
index 0000000..c0172f6
--- /dev/null
+++ b/content/documentsUi/DirectorySelection/template-params.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>DirectorySelection</name>
+    <group>Content</group>
+    <package>com.example.android.directoryselection</package>
+
+    <dependency>com.android.support:recyclerview-v7:+</dependency>
+
+    <!-- change minSdk if needed-->
+    <minSdk>21</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+            This sample explains how to use Directory selection API, which was introduced
+            in Android 5.0.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base" />
+
+    <metadata>
+        <status>DRAFTED</status>
+        <categories>Content</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/web-icon.png</icon>
+        <screenshots>
+            <img>screenshots/screenshot-1.png</img>
+            <img>screenshots/screenshot-2.png</img>
+            <img>screenshots/screenshot-3.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.content.ContentResolver</android>
+            <android>android.provider.DocumentsContract</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic app showing how to use Directory Selection API to let users
+select an entire directory subtree, which extends the Storage Access Framework
+introduced in Android 4.4 (API level 19).
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+The [Directory Selection][1] API, which was introduced in Android 5.0 (API level 21)
+extends the [Storage Access Framework][2] to let users select an entire directory subtree,
+giving apps read/write access to all contained documents without requiring user
+confirmation for each item.
+
+To select a directory subtree, build and send an [OPEN_DOCUMENT_TREE intent][3] like in the
+following code:
+
+```java
+Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+startActivityForResult(intent, REQUEST_CODE_OPEN_DIRECTORY);
+```
+
+The system displays all [DocumentsProvider][4] instances that support subtree selection,
+ letting the user browse and select a directory.
+
+The returned URI represents access to the selected subtree. You can then use
+[buildChildDocumentsUriUsingTree()][5] to access to the child documents and
+[buildDocumentUriUsingTree()][6] to access to the selected directory itself along with [query()][7]
+to explore the subtree.
+
+This example explores the child documents and the selected document by following code:
+
+```java
+@Override
+public void onActivityResult(int requestCode, int resultCode, Intent data) {
+    super.onActivityResult(requestCode, resultCode, data);
+    if (requestCode == REQUEST_CODE_OPEN_DIRECTORY && resultCode == Activity.RESULT_OK) {
+        updateDirectoryEntries(data.getData());
+    }
+}
+
+void updateDirectoryEntries(Uri uri) {
+    ContentResolver contentResolver = getActivity().getContentResolver();
+    Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
+            DocumentsContract.getTreeDocumentId(uri));
+    Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
+            DocumentsContract.getTreeDocumentId(uri));
+
+    Cursor docCursor = contentResolver.query(docUri, new String[]{
+            Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
+    try {
+        while (docCursor.moveToNext()) {
+            Log.d(TAG, "found doc =" + docCursor.getString(0) + ", mime=" + docCursor
+                    .getString(1));
+            mCurrentDirectoryUri = uri;
+            mCurrentDirectoryTextView.setText(docCursor.getString(0));
+            mCreateDirectoryButton.setEnabled(true);
+        }
+    } finally {
+        closeQuietly(docCursor);
+    }
+
+    Cursor childCursor = contentResolver.query(childrenUri, new String[]{
+            Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
+    try {
+        List<DirectoryEntry> directoryEntries = new ArrayList<>();
+        while (childCursor.moveToNext()) {
+            Log.d(TAG, "found child=" + childCursor.getString(0) + ", mime=" + childCursor
+                    .getString(1));
+            DirectoryEntry entry = new DirectoryEntry();
+            entry.fileName = childCursor.getString(0);
+            entry.mimeType = childCursor.getString(1);
+            directoryEntries.add(entry);
+        }
+        mAdapter.setDirectoryEntries(directoryEntries);
+        mAdapter.notifyDataSetChanged();
+    } finally {
+        closeQuietly(childCursor);
+    }
+}
+```
+
+Also, the new [createDocument()][8] method lets you create new documents or directories
+anywhere under the subtree.
+
+This example creates a new directory by following code:
+
+```java
+ContentResolver contentResolver = getActivity().getContentResolver();
+Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
+        DocumentsContract.getTreeDocumentId(uri));
+Uri directoryUri = DocumentsContract
+        .createDocument(contentResolver, docUri, Document.MIME_TYPE_DIR, directoryName);
+```
+
+[1]: https://developer.android.com/about/versions/android-5.0.html#Storage
+[2]: https://developer.android.com/guide/topics/providers/document-provider.html
+[3]: https://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT_TREE
+[4]: https://developer.android.com/reference/android/provider/DocumentsProvider.html
+[5]: https://developer.android.com/reference/android/provider/DocumentsContract.html#buildChildDocumentsUriUsingTree(android.net.Uri%2C%20java.lang.String)
+[6]: https://developer.android.com/reference/android/provider/DocumentsContract.html#buildDocumentUriUsingTree(android.net.Uri%2C%20java.lang.String)
+[7]: https://developer.android.com/reference/android/content/ContentResolver.html#query(android.net.Uri%2C%20java.lang.String%5B%5D%2C%20java.lang.String%2C%20java.lang.String%5B%5D%2C%20java.lang.String)
+[8]: https://developer.android.com/reference/android/provider/DocumentsContract.html#createDocument(android.content.ContentResolver%2C%20android.net.Uri%2C%20java.lang.String%2C%20java.lang.String)
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/content/documentsUi/StorageClient/gradle/wrapper/gradle-wrapper.properties b/content/documentsUi/StorageClient/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/content/documentsUi/StorageClient/gradle/wrapper/gradle-wrapper.properties
+++ b/content/documentsUi/StorageClient/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/content/documentsUi/StorageClient/template-params.xml b/content/documentsUi/StorageClient/template-params.xml
index f6843e7..0b2ddf2 100644
--- a/content/documentsUi/StorageClient/template-params.xml
+++ b/content/documentsUi/StorageClient/template-params.xml
@@ -20,7 +20,6 @@
     <package>com.example.android.storageclient</package>
     <!-- change minSdk if needed-->
     <minSdk>4</minSdk>
-    <compileSdkVersion>19</compileSdkVersion>
 
     <strings>
         <intro>
diff --git a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-hdpi/ic_launcher.png b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-hdpi/ic_launcher.png
old mode 100755
new mode 100644
index c5d6972..8ab9c3c
--- a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-mdpi/ic_launcher.png b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-mdpi/ic_launcher.png
old mode 100755
new mode 100644
index 59e6bd9..8a2df94
--- a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xhdpi/ic_launcher.png
old mode 100755
new mode 100644
index 5b8b7be..9c196e1
--- a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
old mode 100755
new mode 100644
index 474bbd2..558f901
--- a/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/content/documentsUi/StorageProvider/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/Application/src/main/res/ic_launcher.png b/content/documentsUi/StorageProvider/Application/src/main/res/ic_launcher.png
deleted file mode 100755
index c5d6972..0000000
--- a/content/documentsUi/StorageProvider/Application/src/main/res/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/content/documentsUi/StorageProvider/gradle/wrapper/gradle-wrapper.properties b/content/documentsUi/StorageProvider/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/content/documentsUi/StorageProvider/gradle/wrapper/gradle-wrapper.properties
+++ b/content/documentsUi/StorageProvider/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/content/documentsUi/StorageProvider/screenshots/1-logged-out.png b/content/documentsUi/StorageProvider/screenshots/1-logged-out.png
new file mode 100644
index 0000000..9faee62
--- /dev/null
+++ b/content/documentsUi/StorageProvider/screenshots/1-logged-out.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/screenshots/2-logged-in.png b/content/documentsUi/StorageProvider/screenshots/2-logged-in.png
new file mode 100644
index 0000000..7bfa36d
--- /dev/null
+++ b/content/documentsUi/StorageProvider/screenshots/2-logged-in.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/screenshots/icon-web.png b/content/documentsUi/StorageProvider/screenshots/icon-web.png
new file mode 100644
index 0000000..b12b181
--- /dev/null
+++ b/content/documentsUi/StorageProvider/screenshots/icon-web.png
Binary files differ
diff --git a/content/documentsUi/StorageProvider/template-params.xml b/content/documentsUi/StorageProvider/template-params.xml
index bda57ad..058aace 100644
--- a/content/documentsUi/StorageProvider/template-params.xml
+++ b/content/documentsUi/StorageProvider/template-params.xml
@@ -22,12 +22,9 @@
     <group>Content</group>
     <package>com.example.android.storageprovider</package>
 
-
-
     <!-- change minSdk if needed-->
     <minSdk>19</minSdk>
 
-
     <strings>
         <intro>
             <![CDATA[
@@ -43,6 +40,36 @@
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Content</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-logged-out.png</img>
+            <img>screenshots/2-logged-in.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.provider.DocumentsProvider</android>
+        </api_refs>
+        <description>
+This sample shows how to implement a simple documents provider using the storage access
+framework available in Android 4.4.
+        </description>
 
+        <intro>
+This sample uses the [StorageAccessFramework][1] introduced in Android 4.4 to implement a [DocumentsProvider][2].
+
+See [Writing A Custom Document Provider guide][3] for all the details on how to do this.
+
+
+[1]: https://developer.android.com/guide/topics/providers/document-provider.html
+[2]: https://developer.android.com/reference/android/provider/DocumentsProvider.html
+[3]: https://developer.android.com/guide/topics/providers/document-provider.html#custom
+        </intro>
+    </metadata>
 
 </sample>
diff --git a/content/multiuser/AppRestrictions/gradle/wrapper/gradle-wrapper.properties b/content/multiuser/AppRestrictions/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/content/multiuser/AppRestrictions/gradle/wrapper/gradle-wrapper.properties
+++ b/content/multiuser/AppRestrictions/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/content/webview/PermissionRequest/Application/.gitignore
similarity index 100%
copy from ui/notifications/LNotifications/Application/.gitignore
copy to content/webview/PermissionRequest/Application/.gitignore
diff --git a/content/webview/PermissionRequest/Application/README-fragmentview.txt b/content/webview/PermissionRequest/Application/README-fragmentview.txt
new file mode 100644
index 0000000..38d903f
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/README-fragmentview.txt
@@ -0,0 +1,37 @@
+<!--
+        Copyright 2013 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+-->
+
+Steps to implement FragmentView template:
+-in template-params.xml.ftl:
+    -add the following line to common imports
+        <common src="activities"/>
+
+-Add a Fragment to show behavior.  In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+   For instance, if your package name is com.example.foo, create the file
+   src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/fragmentViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/content/webview/PermissionRequest/Application/README-singleview.txt b/content/webview/PermissionRequest/Application/README-singleview.txt
new file mode 100644
index 0000000..0cacd46
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/README-singleview.txt
@@ -0,0 +1,47 @@
+<#--
+        Copyright 2013 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+-->
+
+Steps to implement SingleView template:
+-in template-params.xml.ftl:
+    -add the following line to common imports
+        <common src="activities"/>
+
+    -add a string for the action button's text using the element name "sample_action".
+    This element should be a child of <strings>:
+        <strings>
+        ...
+        <sample_action>ButtonText</sample_action>
+        ...
+        </strings>
+
+
+
+-Add a Fragment to handle behavior.  In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+   For instance, if your package name is com.example.foo, create the file
+   src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/singleViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/content/webview/PermissionRequest/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to content/webview/PermissionRequest/Application/proguard-project.txt
diff --git a/content/webview/PermissionRequest/Application/src/main/AndroidManifest.xml b/content/webview/PermissionRequest/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f8fd050
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.permissionrequest"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/content/webview/PermissionRequest/Application/src/main/assets/sample.css b/content/webview/PermissionRequest/Application/src/main/assets/sample.css
new file mode 100644
index 0000000..9d1364c
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/assets/sample.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+body {
+    text-align: center;
+}
+
+div {
+    margin-bottom: 8px;
+}
+
+button {
+    height: 48px;
+    width: 80px;
+    font-weight: bold;
+    font-size: 14pt;
+}
+
+video {
+    width: 240px;
+    height: 180px;
+    transform: scaleX(-1); /* Mirror */
+}
diff --git a/content/webview/PermissionRequest/Application/src/main/assets/sample.html b/content/webview/PermissionRequest/Application/src/main/assets/sample.html
new file mode 100644
index 0000000..a9729a3
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/assets/sample.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!--
+  Copyright 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<html>
+<head>
+    <title>sample</title>
+    <link rel="stylesheet" href="sample.css">
+</head>
+<body>
+
+<div>
+    <button id="toggle">Start</button>
+</div>
+<video id="video" autoplay></video>
+
+<script src="sample.js"></script>
+</body>
+</html>
diff --git a/content/webview/PermissionRequest/Application/src/main/assets/sample.js b/content/webview/PermissionRequest/Application/src/main/assets/sample.js
new file mode 100644
index 0000000..e2806a0
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/assets/sample.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function () {
+    "use strict";
+
+    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
+    window.URL = window.URL || window.webkitURL;
+
+    window.onload = function () {
+
+        var video = document.querySelector('#video'),
+            toggle = document.querySelector('#toggle'),
+            stream = null;
+
+        if (!navigator.getUserMedia) {
+            console.error('getUserMedia not supported');
+        }
+
+        toggle.addEventListener('click', function () {
+            if (null === stream) {
+                // This call to "getUserMedia" initiates a PermissionRequest in the WebView.
+                navigator.getUserMedia({ video: true }, function (s) {
+                    stream = s;
+                    video.src = window.URL.createObjectURL(stream);
+                    toggle.innerText = 'Stop';
+                    console.log('Started');
+                }, function (error) {
+                    console.error('Error starting camera. Denied.');
+                });
+            } else {
+                stream.stop();
+                stream = null;
+                toggle.innerText = 'Start';
+                console.log('Stopped');
+            }
+        });
+
+        console.log('Page loaded');
+
+    };
+
+})();
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java
new file mode 100644
index 0000000..7dae56e
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/ConfirmationDialogFragment.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.permissionrequest;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.text.TextUtils;
+
+/**
+ * Prompts the user to confirm permission request.
+ */
+public class ConfirmationDialogFragment extends DialogFragment {
+
+    private static final String ARG_RESOURCES = "resources";
+
+    /**
+     * Creates a new instance of ConfirmationDialogFragment.
+     *
+     * @param resources The list of resources requested by PermissionRequeste.
+     * @return A new instance.
+     */
+    public static ConfirmationDialogFragment newInstance(String[] resources) {
+        ConfirmationDialogFragment fragment = new ConfirmationDialogFragment();
+        Bundle args = new Bundle();
+        args.putStringArray(ARG_RESOURCES, resources);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        String[] resources = getArguments().getStringArray(ARG_RESOURCES);
+        return new AlertDialog.Builder(getActivity())
+                .setMessage(getString(R.string.confirmation, TextUtils.join("\n", resources)))
+                .setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        ((Listener) getParentFragment()).onConfirmation(false);
+                    }
+                })
+                .setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        ((Listener) getParentFragment()).onConfirmation(true);
+                    }
+                })
+                .create();
+    }
+
+    /**
+     * Callback for the user's response.
+     */
+    public interface Listener {
+
+        /**
+         * Called when the PermissoinRequest is allowed or denied by the user.
+         *
+         * @param allowed True if the user allowed the request.
+         */
+        public void onConfirmation(boolean allowed);
+    }
+
+}
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java
new file mode 100644
index 0000000..44f1d6e
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/PermissionRequestFragment.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.permissionrequest;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.ConsoleMessage;
+import android.webkit.PermissionRequest;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import com.example.android.common.logger.Log;
+
+/**
+ * This fragment shows a {@link WebView} and loads a web app from the {@link SimpleWebServer}.
+ */
+public class PermissionRequestFragment extends Fragment
+        implements ConfirmationDialogFragment.Listener {
+
+    private static final String TAG = PermissionRequestFragment.class.getSimpleName();
+
+    private static final String FRAGMENT_DIALOG = "dialog";
+
+    /**
+     * We use this web server to serve HTML files in the assets folder. This is because we cannot
+     * use the JavaScript method "getUserMedia" from "file:///android_assets/..." URLs.
+     */
+    private SimpleWebServer mWebServer;
+
+    /**
+     * A reference to the {@link WebView}.
+     */
+    private WebView mWebView;
+
+    /**
+     * This field stores the {@link PermissionRequest} from the web application until it is allowed
+     * or denied by user.
+     */
+    private PermissionRequest mPermissionRequest;
+
+    /**
+     * For testing.
+     */
+    private ConsoleMonitor mConsoleMonitor;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_permission_request, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        mWebView = (WebView) view.findViewById(R.id.web_view);
+        // Here, we use #mWebChromeClient with implementation for handling PermissionRequests.
+        mWebView.setWebChromeClient(mWebChromeClient);
+        configureWebSettings(mWebView.getSettings());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        final int port = 8080;
+        mWebServer = new SimpleWebServer(port, getResources().getAssets());
+        mWebServer.start();
+        mWebView.loadUrl("http://localhost:" + port + "/sample.html");
+    }
+
+    @Override
+    public void onPause() {
+        mWebServer.stop();
+        super.onPause();
+    }
+
+    @SuppressLint("SetJavaScriptEnabled")
+    private static void configureWebSettings(WebSettings settings) {
+        settings.setJavaScriptEnabled(true);
+    }
+
+    /**
+     * This {@link WebChromeClient} has implementation for handling {@link PermissionRequest}.
+     */
+    private WebChromeClient mWebChromeClient = new WebChromeClient() {
+
+        // This method is called when the web content is requesting permission to access some
+        // resources.
+        @Override
+        public void onPermissionRequest(PermissionRequest request) {
+            Log.i(TAG, "onPermissionRequest");
+            mPermissionRequest = request;
+            ConfirmationDialogFragment.newInstance(request.getResources())
+                    .show(getChildFragmentManager(), FRAGMENT_DIALOG);
+        }
+
+        // This method is called when the permission request is canceled by the web content.
+        @Override
+        public void onPermissionRequestCanceled(PermissionRequest request) {
+            Log.i(TAG, "onPermissionRequestCanceled");
+            // We dismiss the prompt UI here as the request is no longer valid.
+            mPermissionRequest = null;
+            DialogFragment fragment = (DialogFragment) getChildFragmentManager()
+                    .findFragmentByTag(FRAGMENT_DIALOG);
+            if (null != fragment) {
+                fragment.dismiss();
+            }
+        }
+
+        @Override
+        public boolean onConsoleMessage(@NonNull ConsoleMessage message) {
+            switch (message.messageLevel()) {
+                case TIP:
+                    Log.v(TAG, message.message());
+                    break;
+                case LOG:
+                    Log.i(TAG, message.message());
+                    break;
+                case WARNING:
+                    Log.w(TAG, message.message());
+                    break;
+                case ERROR:
+                    Log.e(TAG, message.message());
+                    break;
+                case DEBUG:
+                    Log.d(TAG, message.message());
+                    break;
+            }
+            if (null != mConsoleMonitor) {
+                mConsoleMonitor.onConsoleMessage(message);
+            }
+            return true;
+        }
+
+    };
+
+    @Override
+    public void onConfirmation(boolean allowed) {
+        if (allowed) {
+            mPermissionRequest.grant(mPermissionRequest.getResources());
+            Log.d(TAG, "Permission granted.");
+        } else {
+            mPermissionRequest.deny();
+            Log.d(TAG, "Permission request denied.");
+        }
+        mPermissionRequest = null;
+    }
+
+    public void setConsoleMonitor(ConsoleMonitor monitor) {
+        mConsoleMonitor = monitor;
+    }
+
+    /**
+     * For testing.
+     */
+    public interface ConsoleMonitor {
+        public void onConsoleMessage(ConsoleMessage message);
+    }
+
+}
diff --git a/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java
new file mode 100644
index 0000000..36b7c46
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/java/com/example/android/permissionrequest/SimpleWebServer.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.permissionrequest;
+
+import android.content.res.AssetManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Implementation of a very basic HTTP server. The contents are loaded from the assets folder. This
+ * server handles one request at a time. It only supports GET method.
+ */
+public class SimpleWebServer implements Runnable {
+
+    private static final String TAG = "SimpleWebServer";
+
+    /**
+     * The port number we listen to
+     */
+    private final int mPort;
+
+    /**
+     * {@link android.content.res.AssetManager} for loading files to serve.
+     */
+    private final AssetManager mAssets;
+
+    /**
+     * True if the server is running.
+     */
+    private boolean mIsRunning;
+
+    /**
+     * The {@link java.net.ServerSocket} that we listen to.
+     */
+    private ServerSocket mServerSocket;
+
+    /**
+     * WebServer constructor.
+     */
+    public SimpleWebServer(int port, AssetManager assets) {
+        mPort = port;
+        mAssets = assets;
+    }
+
+    /**
+     * This method starts the web server listening to the specified port.
+     */
+    public void start() {
+        mIsRunning = true;
+        new Thread(this).start();
+    }
+
+    /**
+     * This method stops the web server
+     */
+    public void stop() {
+        try {
+            mIsRunning = false;
+            if (null != mServerSocket) {
+                mServerSocket.close();
+                mServerSocket = null;
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Error closing the server socket.", e);
+        }
+    }
+
+    @Override
+    public void run() {
+        try {
+            mServerSocket = new ServerSocket(mPort);
+            while (mIsRunning) {
+                Socket socket = mServerSocket.accept();
+                handle(socket);
+                socket.close();
+            }
+        } catch (SocketException e) {
+            // The server was stopped; ignore.
+        } catch (IOException e) {
+            Log.e(TAG, "Web server error.", e);
+        }
+    }
+
+    /**
+     * Respond to a request from a client.
+     *
+     * @param socket The client socket.
+     * @throws IOException
+     */
+    private void handle(Socket socket) throws IOException {
+        BufferedReader reader = null;
+        PrintStream output = null;
+        try {
+            String route = null;
+
+            // Read HTTP headers and parse out the route.
+            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            String line;
+            while (!TextUtils.isEmpty(line = reader.readLine())) {
+                if (line.startsWith("GET /")) {
+                    int start = line.indexOf('/') + 1;
+                    int end = line.indexOf(' ', start);
+                    route = line.substring(start, end);
+                    break;
+                }
+            }
+
+            // Output stream that we send the response to
+            output = new PrintStream(socket.getOutputStream());
+
+            // Prepare the content to send.
+            if (null == route) {
+                writeServerError(output);
+                return;
+            }
+            byte[] bytes = loadContent(route);
+            if (null == bytes) {
+                writeServerError(output);
+                return;
+            }
+
+            // Send out the content.
+            output.println("HTTP/1.0 200 OK");
+            output.println("Content-Type: " + detectMimeType(route));
+            output.println("Content-Length: " + bytes.length);
+            output.println();
+            output.write(bytes);
+            output.flush();
+        } finally {
+            if (null != output) {
+                output.close();
+            }
+            if (null != reader) {
+                reader.close();
+            }
+        }
+    }
+
+    /**
+     * Writes a server error response (HTTP/1.0 500) to the given output stream.
+     *
+     * @param output The output stream.
+     */
+    private void writeServerError(PrintStream output) {
+        output.println("HTTP/1.0 500 Internal Server Error");
+        output.flush();
+    }
+
+    /**
+     * Loads all the content of {@code fileName}.
+     *
+     * @param fileName The name of the file.
+     * @return The content of the file.
+     * @throws IOException
+     */
+    private byte[] loadContent(String fileName) throws IOException {
+        InputStream input = null;
+        try {
+            ByteArrayOutputStream output = new ByteArrayOutputStream();
+            input = mAssets.open(fileName);
+            byte[] buffer = new byte[1024];
+            int size;
+            while (-1 != (size = input.read(buffer))) {
+                output.write(buffer, 0, size);
+            }
+            output.flush();
+            return output.toByteArray();
+        } catch (FileNotFoundException e) {
+            return null;
+        } finally {
+            if (null != input) {
+                input.close();
+            }
+        }
+    }
+
+    /**
+     * Detects the MIME type from the {@code fileName}.
+     *
+     * @param fileName The name of the file.
+     * @return A MIME type.
+     */
+    private String detectMimeType(String fileName) {
+        if (TextUtils.isEmpty(fileName)) {
+            return null;
+        } else if (fileName.endsWith(".html")) {
+            return "text/html";
+        } else if (fileName.endsWith(".js")) {
+            return "application/javascript";
+        } else if (fileName.endsWith(".css")) {
+            return "text/css";
+        } else {
+            return "application/octet-stream";
+        }
+    }
+
+}
diff --git a/content/webview/PermissionRequest/Application/src/main/res/drawable-hdpi/ic_launcher.png b/content/webview/PermissionRequest/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..4dae25b
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/content/webview/PermissionRequest/Application/src/main/res/drawable-mdpi/ic_launcher.png b/content/webview/PermissionRequest/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..e896d49
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/content/webview/PermissionRequest/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/content/webview/PermissionRequest/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5309f55
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/content/webview/PermissionRequest/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/content/webview/PermissionRequest/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..61b137b
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/content/webview/PermissionRequest/Application/src/main/res/layout/fragment_permission_request.xml b/content/webview/PermissionRequest/Application/src/main/res/layout/fragment_permission_request.xml
new file mode 100644
index 0000000..2e97a0a
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/res/layout/fragment_permission_request.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <WebView
+        android:id="@+id/web_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml b/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c3e5574
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <string name="confirmation">This web page wants to use following resources:\n\n%s</string>
+    <string name="allow">Allow</string>
+    <string name="deny">Deny</string>
+</resources>
diff --git a/content/webview/PermissionRequest/Application/tests/src/com/example/android/permissionrequest/test/SampleTests.java b/content/webview/PermissionRequest/Application/tests/src/com/example/android/permissionrequest/test/SampleTests.java
new file mode 100644
index 0000000..a88589b
--- /dev/null
+++ b/content/webview/PermissionRequest/Application/tests/src/com/example/android/permissionrequest/test/SampleTests.java
@@ -0,0 +1,196 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.permissionrequest.test;
+
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.MotionEvent;
+import android.view.View;
+import android.webkit.ConsoleMessage;
+import android.webkit.WebView;
+
+import com.example.android.permissionrequest.ConfirmationDialogFragment;
+import com.example.android.permissionrequest.MainActivity;
+import com.example.android.permissionrequest.PermissionRequestFragment;
+import com.example.android.permissionrequest.R;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * Tests for PermissionRequest sample.
+ */
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private PermissionRequestFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTestActivity = getActivity();
+        mTestFragment = (PermissionRequestFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+     * Test if the test fixture has been set up correctly.
+     */
+    public void testPreconditions() {
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    public void testWebView_grantPermissionRequest() throws Throwable {
+        View view = mTestFragment.getView();
+        assertNotNull(view);
+        final WebView webView = (WebView) view.findViewById(R.id.web_view);
+        assertNotNull(webView);
+
+        final ConsoleMonitor monitor = new ConsoleMonitor();
+        mTestFragment.setConsoleMonitor(monitor);
+
+        // Click the "Start" button
+        assertTrue(monitor.waitForKeyword("Page loaded", 2000));
+        clickToggle(webView);
+
+        // Wait for the dialog
+        ConfirmationDialogFragment dialogFragment = waitForDialog();
+        assertNotNull(dialogFragment);
+
+        // Click "Allow"
+        monitor.reset();
+        clickDialogButton(dialogFragment, android.R.id.button1);
+
+        assertTrue(monitor.waitForKeyword("Started", 2000));
+
+        // Click the "Stop" button
+        monitor.reset();
+        clickToggle(webView);
+        assertTrue(monitor.waitForKeyword("Stopped", 2000));
+
+        // Click the "Start" button
+        monitor.reset();
+        clickToggle(webView);
+
+        // Wait for the dialog
+        dialogFragment = waitForDialog();
+        assertNotNull(dialogFragment);
+
+        // Click "Deny"
+        monitor.reset();
+        clickDialogButton(dialogFragment, android.R.id.button2);
+        assertTrue(monitor.waitForKeyword("Denied", 2000));
+    }
+
+    /**
+     * Click the Start/Stop button.
+     *
+     * @param webView The {@link WebView}.
+     * @throws Throwable
+     */
+    private void clickToggle(final WebView webView) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Rect rect = new Rect();
+                webView.getHitRect(rect);
+                int x = rect.width() / 2;
+                int y = 100;
+                MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+                        SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN, x, y, 0);
+                webView.dispatchTouchEvent(event);
+                event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+                        SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, x, y, 0);
+                webView.dispatchTouchEvent(event);
+            }
+        });
+    }
+
+    /**
+     * Wait for the dialog for 2 seconds (100 ms * 20 trials).
+     *
+     * @return The dialog.
+     * @throws InterruptedException
+     */
+    private ConfirmationDialogFragment waitForDialog() throws InterruptedException {
+        int count = 20;
+        ConfirmationDialogFragment dialog = null;
+        while (0 < count) {
+            dialog = (ConfirmationDialogFragment) mTestFragment.getChildFragmentManager()
+                    .findFragmentByTag("dialog");
+            if (null != dialog) {
+                break;
+            }
+            Thread.sleep(100);
+            --count;
+        }
+        return dialog;
+    }
+
+    /**
+     * Press the specified button on the dialog.
+     *
+     * @param dialogFragment The dialog.
+     * @param buttonId       The resource ID of the button to press.
+     * @throws Throwable
+     */
+    private void clickDialogButton(final ConfirmationDialogFragment dialogFragment,
+                                   final int buttonId) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                dialogFragment.getDialog().findViewById(buttonId).performClick();
+                assertFalse(dialogFragment.isVisible());
+            }
+        });
+    }
+
+    private class ConsoleMonitor implements PermissionRequestFragment.ConsoleMonitor {
+
+        private final ConcurrentLinkedQueue<String> mMessages = new ConcurrentLinkedQueue<String>();
+
+        @Override
+        public void onConsoleMessage(ConsoleMessage message) {
+            mMessages.offer(message.message());
+        }
+
+        public boolean waitForKeyword(String keyword, long timeoutMs) throws InterruptedException {
+            long time = 0;
+            while (time < timeoutMs) {
+                String message;
+                while (null != (message = mMessages.poll())) {
+                    if (message.contains(keyword)) {
+                        return true;
+                    }
+                }
+                Thread.sleep(100);
+                time += 100;
+            }
+            return false;
+        }
+
+        public void reset() {
+            mMessages.clear();
+        }
+
+    }
+
+}
diff --git a/content/webview/PermissionRequest/build.gradle b/content/webview/PermissionRequest/build.gradle
new file mode 100644
index 0000000..18f393f
--- /dev/null
+++ b/content/webview/PermissionRequest/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../../build"
+  pathToSamplesCommon "../../../common"
+}
+apply from: "../../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/content/webview/PermissionRequest/buildSrc/build.gradle b/content/webview/PermissionRequest/buildSrc/build.gradle
new file mode 100644
index 0000000..7cebf71
--- /dev/null
+++ b/content/webview/PermissionRequest/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.properties b/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4eda7c5
--- /dev/null
+++ b/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Dec 09 18:05:08 JST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/content/webview/PermissionRequest/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to content/webview/PermissionRequest/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/content/webview/PermissionRequest/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to content/webview/PermissionRequest/gradlew.bat
diff --git a/content/webview/PermissionRequest/screenshots/icon_web.png b/content/webview/PermissionRequest/screenshots/icon_web.png
new file mode 100644
index 0000000..4a8c2f1
--- /dev/null
+++ b/content/webview/PermissionRequest/screenshots/icon_web.png
Binary files differ
diff --git a/content/webview/PermissionRequest/screenshots/image1.png b/content/webview/PermissionRequest/screenshots/image1.png
new file mode 100644
index 0000000..9ce9fc8
--- /dev/null
+++ b/content/webview/PermissionRequest/screenshots/image1.png
Binary files differ
diff --git a/content/webview/PermissionRequest/screenshots/image2.png b/content/webview/PermissionRequest/screenshots/image2.png
new file mode 100644
index 0000000..e919bd1
--- /dev/null
+++ b/content/webview/PermissionRequest/screenshots/image2.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/content/webview/PermissionRequest/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to content/webview/PermissionRequest/settings.gradle
diff --git a/content/webview/PermissionRequest/template-params.xml b/content/webview/PermissionRequest/template-params.xml
new file mode 100644
index 0000000..6a11636
--- /dev/null
+++ b/content/webview/PermissionRequest/template-params.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>PermissionRequest</name>
+    <group>Content</group>
+    <package>com.example.android.permissionrequest</package>
+
+    <minSdk>21</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+            This sample shows how to handle PermissionRequest coming from web apps inside of a
+            WebView.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base"/>
+    <template src="FragmentView"/>
+    <common src="logger"/>
+    <common src="activities"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Content</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon_web.png</icon>
+        <screenshots>
+            <img>screenshots/image1.png</img>
+            <img>screenshots/image2.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.webkit.PermissionRequest</android>
+            <android>android.webkit.WebView</android>
+            <android>android.webkit.WebChromeClient</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates how to use the PermissionRequest API to
+securely provide access to restricted system features (such as a
+camera or microphone) from within a WebView. In this example, a dialog
+is created to allow users to explicitly approve or reject each
+request.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+PermissionRequest can be used by setting up a custom WebChromeClient.
+
+```java
+mWebView.setWebChromeClient(mWebChromeClient);
+```
+
+In you WebChromeClient implementation, you need to override
+[onPermissionRequest][1]. This method is called when the web content
+is requesting permission to access some resources, providing an
+opportunity to approve or reject the request. In this implementation,
+we display a dialog to allow the user to approve or reject any
+request. In other applications, you may want to implement a whitelist
+of allowed APIs.  Also, override [onPermissionRequestCanceled][2] for
+handling cancellation of the PermissionRequest by the web content.
+
+When the user confirms or denies the request, you can respond back to
+the web content by [grant][3] or [deny][4] respectively.
+
+```java
+mPermissionRequest.grant(mPermissionRequest.getResources());
+```
+
+This sample provides the web content from the assets folder in the
+app. Since WebView is not allowed to use getUserMedia from a "file://"
+URL, the app uses the SimpleWebServer class to provide the content via
+"http://localhost".
+
+[1]: http://developer.android.com/reference/android/webkit/WebChromeClient.html#onPermissionRequest(android.webkit.PermissionRequest)
+[2]: http://developer.android.com/reference/android/webkit/WebChromeClient.html#onPermissionRequestCanceled(android.webkit.PermissionRequest)
+[3]: http://developer.android.com/reference/android/webkit/PermissionRequest.html#grant(java.lang.String[])
+[4]: http://developer.android.com/reference/android/webkit/PermissionRequest.html#deny()
+]]>
+        </intro>
+    </metadata>
+
+</sample>
diff --git a/experimental/ndkSampleGen/gradle/wrapper/gradle-wrapper.properties b/experimental/ndkSampleGen/gradle/wrapper/gradle-wrapper.properties
index ad443be..b0ae9b5 100644
--- a/experimental/ndkSampleGen/gradle/wrapper/gradle-wrapper.properties
+++ b/experimental/ndkSampleGen/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties b/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
+++ b/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/input/gestures/BasicGestureDetect/screenshots/1-main.png b/input/gestures/BasicGestureDetect/screenshots/1-main.png
new file mode 100644
index 0000000..e7c3419
--- /dev/null
+++ b/input/gestures/BasicGestureDetect/screenshots/1-main.png
Binary files differ
diff --git a/input/gestures/BasicGestureDetect/screenshots/icon-web.png b/input/gestures/BasicGestureDetect/screenshots/icon-web.png
new file mode 100644
index 0000000..04d0248
--- /dev/null
+++ b/input/gestures/BasicGestureDetect/screenshots/icon-web.png
Binary files differ
diff --git a/input/gestures/BasicGestureDetect/template-params.xml b/input/gestures/BasicGestureDetect/template-params.xml
index fc8b48d..cacdd0d 100644
--- a/input/gestures/BasicGestureDetect/template-params.xml
+++ b/input/gestures/BasicGestureDetect/template-params.xml
@@ -39,4 +39,42 @@
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Input</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.GestureDetector</android>
+            <android>android.view.MotionEvent</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample detects gestures on a view and logs them. In order to try this
+sample out, try dragging or tapping the text.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+In this sample, the gestures are detected using a custom gesture listener that extends
+[SimpleOnGestureListener][1] and writes the detected [MotionEvent][2] into the log.
+
+In this example, the steps followed to set up the gesture detector are:
+1. Create the GestureListener that includes all your callbacks.
+2. Create the GestureDetector ([SimpleOnGestureListener][1]) that will take the listener as an argument.
+3. For the view where the gestures will occur, create an [onTouchListener][3]
+that sends all motion events to the gesture detector.
+
+[1]: http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html
+[2]: http://developer.android.com/reference/android/view/MotionEvent.html
+[3]: http://developer.android.com/reference/android/view/View.OnTouchListener.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties b/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
+++ b/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/input/multitouch/BasicMultitouch/screenshots/icon-web.png b/input/multitouch/BasicMultitouch/screenshots/icon-web.png
new file mode 100644
index 0000000..2f5f709
--- /dev/null
+++ b/input/multitouch/BasicMultitouch/screenshots/icon-web.png
Binary files differ
diff --git a/input/multitouch/BasicMultitouch/screenshots/intro.png b/input/multitouch/BasicMultitouch/screenshots/intro.png
new file mode 100644
index 0000000..4d1f535
--- /dev/null
+++ b/input/multitouch/BasicMultitouch/screenshots/intro.png
Binary files differ
diff --git a/input/multitouch/BasicMultitouch/screenshots/touches.png b/input/multitouch/BasicMultitouch/screenshots/touches.png
new file mode 100644
index 0000000..a771887
--- /dev/null
+++ b/input/multitouch/BasicMultitouch/screenshots/touches.png
Binary files differ
diff --git a/input/multitouch/BasicMultitouch/template-params.xml b/input/multitouch/BasicMultitouch/template-params.xml
index 7677818..40d2a18 100644
--- a/input/multitouch/BasicMultitouch/template-params.xml
+++ b/input/multitouch/BasicMultitouch/template-params.xml
@@ -28,15 +28,58 @@
     <strings>
         <intro>
             <![CDATA[
-This samples demonstrates the use of MotionEvent properties to keep track of individual touches
+This sample demonstrates the use of MotionEvent properties to keep track of individual touches
 across multiple touch events.
 \n\nTouch the screen with multiple fingers to show that the pointer id
-(also represented by a colour) does not change as new touch events are received.</string>
+(also represented by a color) does not change as new touch events are received.
             ]]>
         </intro>
     </strings>
 
     <template src="base"/>
     <common src="logger"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Views, Input</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/intro.png</img>
+            <img>screenshots/touches.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.MotionEvent</android>
+        </api_refs>
+        <description>
+<![CDATA[
+Sample demonstrates the use of [MotionEvent][1] properties to keep track of
+individual touches across multiple touch events.
 
+[1]: http://developer.android.com/reference/android/view/MotionEvent.html
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This is an example of keeping track of individual touches across multiple
+[MotionEvent][1]s.
+
+This sample uses a custom View (`TouchDisplayView`) that responds to
+touch events and draws a colored circle for each touch point. The view holds
+data related to a touch pointer, including its current position, pressure,
+and its past touch history.
+
+The View draws graphics based on data associated with each touch event to a
+canvas. A large circle indicates the current position of a touch, while smaller
+trailing circles represent previous positions for that touch.
+The size of the large circle is scaled depending on the pressure of the user's
+touch.
+
+[1]: http://developer.android.com/reference/android/view/MotionEvent.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties b/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
+++ b/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/BasicMediaDecoder/screenshots/1-launch.png b/media/BasicMediaDecoder/screenshots/1-launch.png
new file mode 100644
index 0000000..87c246f
--- /dev/null
+++ b/media/BasicMediaDecoder/screenshots/1-launch.png
Binary files differ
diff --git a/media/BasicMediaDecoder/screenshots/2-play-video.png b/media/BasicMediaDecoder/screenshots/2-play-video.png
new file mode 100644
index 0000000..17f61fa
--- /dev/null
+++ b/media/BasicMediaDecoder/screenshots/2-play-video.png
Binary files differ
diff --git a/media/BasicMediaDecoder/screenshots/icon-web.png b/media/BasicMediaDecoder/screenshots/icon-web.png
new file mode 100644
index 0000000..c67722e
--- /dev/null
+++ b/media/BasicMediaDecoder/screenshots/icon-web.png
Binary files differ
diff --git a/media/BasicMediaDecoder/template-params.xml b/media/BasicMediaDecoder/template-params.xml
index bcb01af..ff8861c 100644
--- a/media/BasicMediaDecoder/template-params.xml
+++ b/media/BasicMediaDecoder/template-params.xml
@@ -33,4 +33,75 @@
 
     <template src="base"/>
     <common src="media"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Media</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-launch.png</img>
+            <img>screenshots/2-play-video.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.media.MediaCodec</android>
+            <android>android.media.MediaExtractor</android>
+            <android>android.animation.TimeAnimator</android>
+        </api_refs>
+
+        <description>
+This sample shows how to use the MediaCoder to decode a video,
+use a TimeAnimator to sync the rendering commands with the system
+display frame rendering and finally render it to a TextureView.
+        </description>
+
+        <intro>
+<![CDATA[
+[MediaCodec][1] was introduced in API 16, and can be used for low level (decoding/encoding) operations.
+In the same API was also introduced [TimeAnimator][2], which can be used to synchronise animation frames.
+Finally, [MediaExtractor][3] provides a simple way to extract demuxed media data from a data source.
+
+The main steps are described below:
+
+1. Create a layout with a [TextureView][4] for your activity.
+2. Initialise a MediaExtractor instance with `new MediaExtractor()` and a TimeAnimator instance with
+`new TimeAnimator()`.
+3. To start video playback, call `setDataSource(this, videoUri, null)` on your MediaExtractor instance,
+where `videoUri` is the URI of your video source.
+4. On your MediaExtractor instance, call `getTrackCount()` to know how many tracks you have in your streams.
+They may not all be video tracks. Deselect all tracks by calling `unselectTrack(i)` where `i` is
+the index of the track.
+5. Get the mime type of a track by calling `getTrackFormat(i).getString(MediaFormat.KEY_MIME)`
+on your MediaExtractor instance, where `i` is the index of your selected track.
+If the mime type contains "video/", then this is a video track so you can select it, using `selectTrack(i)`
+on your MediaExtractor instance.
+6. Create a MediaCodec instance by calling `MediaCodec.createDecoderByType(mimeType)`.
+7. Configure your MediaCodec instance with `configure(trackFormat, textureView, null,  0)`,
+where `trackFormat` is obtained by calling `getTrackFormat(i)` on your MediaExtractor instance.
+8. Set a TimeListener on your TimeAnimation instance, and override its `onTimeUpdate(final TimeAnimator animation,
+final long totalTime, final long deltaTime)` method.
+9. In `onTimeUpdate`, check if the media track has reached the end of stream, using `getSampleFlags()`
+on  your MediaExtractor instance and looking for `MediaCodec.BUFFER_FLAG_END_OF_STREAM` flag.
+10. Still in `onTimeUpdate`, assuming this isn't the end of the sample, write the media sample to your
+MediaDecoder instance, using `queueInputBuffer(index, 0, size, presentationTimeUs, flags)` method.
+You will need to set up your buffers, refer to [MediaCodec][1] documentation for details.
+11. After writing the media sample, you need to advance the sample, calling `advance()` on your
+TimeExtractor instance (this is a blocking operation and should be done outside the main thread).
+12. Finally, you can release and render the media sample by calling
+`dequeueOutputBuffer(info, timeout)` and `releaseOutputBuffer(i, true)`, refer to [MediaCodec][1]
+documentation for details.
+13. In `onPause()` or if you have reached the end of the stream, call `end()` on your TimeAnimation instance,
+then call `stop()` and `release()` on your MediaCodec instance, and finally, call `release()` on your
+MediaExtractor instance.
+
+[1]: http://developer.android.com/reference/android/media/MediaCodec.html
+[2]: http://developer.android.com/reference/android/animation/TimeAnimator.html
+[3]: http://developer.android.com/reference/android/media/MediaExtractor.html
+[4]: http://developer.android.com/reference/android/view/TextureView.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties b/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
+++ b/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java b/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java
index f4bf220..2414ed6 100644
--- a/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java
+++ b/media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java
@@ -43,6 +43,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Message;
 import android.util.Log;
 import android.util.Size;
 import android.util.SparseIntArray;
@@ -266,8 +267,10 @@
                     int afState = result.get(CaptureResult.CONTROL_AF_STATE);
                     if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                             CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
-                        int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
-                        if (aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
+                        // CONTROL_AE_STATE can be null on some devices
+                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+                        if (aeState == null ||
+                                aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                             mState = STATE_WAITING_NON_PRECAPTURE;
                             captureStillPicture();
                         } else {
@@ -277,17 +280,19 @@
                     break;
                 }
                 case STATE_WAITING_PRECAPTURE: {
-                    int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
-                    if (CaptureResult.CONTROL_AE_STATE_PRECAPTURE == aeState) {
-                        mState = STATE_WAITING_NON_PRECAPTURE;
-                    } else if (CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED == aeState) {
+                    // CONTROL_AE_STATE can be null on some devices
+                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+                    if (aeState == null ||
+                            aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
+                            aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
                         mState = STATE_WAITING_NON_PRECAPTURE;
                     }
                     break;
                 }
                 case STATE_WAITING_NON_PRECAPTURE: {
-                    int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
-                    if (CaptureResult.CONTROL_AE_STATE_PRECAPTURE != aeState) {
+                    // CONTROL_AE_STATE can be null on some devices
+                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+                    if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                         mState = STATE_PICTURE_TAKEN;
                         captureStillPicture();
                     }
@@ -311,6 +316,32 @@
     };
 
     /**
+     * A {@link Handler} for showing {@link Toast}s.
+     */
+    private Handler mMessageHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            Activity activity = getActivity();
+            if (activity != null) {
+                Toast.makeText(activity, (String) msg.obj, Toast.LENGTH_SHORT).show();
+            }
+        }
+    };
+
+    /**
+     * Shows a {@link Toast} on the UI thread.
+     *
+     * @param text The message to show
+     */
+    private void showToast(String text) {
+        // We show a Toast by sending request message to mMessageHandler. This makes sure that the
+        // Toast is shown on the UI thread.
+        Message message = Message.obtain();
+        message.obj = text;
+        mMessageHandler.sendMessage(message);
+    }
+
+    /**
      * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
      * width and height are at least as large as the respective requested values, and whose aspect
      * ratio matches with the specified value.
@@ -569,10 +600,7 @@
 
                         @Override
                         public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
-                            Activity activity = getActivity();
-                            if (null != activity) {
-                                Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
-                            }
+                            showToast("Failed");
                         }
                     }, null
             );
@@ -685,7 +713,7 @@
                 @Override
                 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                                TotalCaptureResult result) {
-                    Toast.makeText(getActivity(), "Saved: " + mFile, Toast.LENGTH_SHORT).show();
+                    showToast("Saved: " + mFile);
                     unlockFocus();
                 }
             };
diff --git a/media/Camera2Basic/README.md b/media/Camera2Basic/README.md
index 73b9eac..b848e02 100644
--- a/media/Camera2Basic/README.md
+++ b/media/Camera2Basic/README.md
@@ -1,16 +1,55 @@
 Android Camera2Basic Sample
-==============================
+===================================
 
-This sample demonstrates the basic use of Camera2 API. Check the source code to see how you can
-display camera preview and take pictures.
+This sample demonstrates how to use basic functionalities of Camera2
+API. You can learn how to iterate through characteristics of all the
+cameras attached to the device, display a camera preview, and take
+pictures.
+
+Introduction
+------------
+
+The [Camera2 API][1] provides an interface to individual camera
+devices connected to an Android device. It replaces the deprecated
+Camera class.
+
+Use [getCameraIdList][2] to get a list of all the available
+cameras. You can then use [getCameraCharacteristics][3] and find the
+best camera that suits your need (front/rear facing, resolution etc).
+
+Create an instance of [CameraDevice.StateCallback][4] and open a
+camera. It is ready to start camera preview when the camera is opened.
+
+This sample uses TextureView to show the camera preview. Create a
+[CameraCaptureSession][5] and set a repeating [CaptureRequest][6] to it.
+
+Still image capture takes several steps. First, you need to lock the
+focus of the camera by updating the CaptureRequest for the camera
+preview. Then, in a similar way, you need to run a precapture
+sequence. After that, it is ready to capture a picture. Create a new
+CaptureRequest and call [capture][7]. Don't forget to unlock the focus
+when you are done.
+
+[1]: https://developer.android.com/reference/android/hardware/camera2/package-summary.html
+[2]: https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList()
+[3]: https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraCharacteristics(java.lang.String)
+[4]: https://developer.android.com/reference/android/hardware/camera2/CameraDevice.StateCallback.html
+[5]: https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html
+[6]: https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html
+[7]: https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html#capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler)
 
 Pre-requisites
 --------------
 
-- Android SDK (L Developer Preview)
-- Android Build Tools v20
+- Android SDK v21
+- Android Build Tools v21.1.1
 - Android Support Repository
 
+Screenshots
+-------------
+
+<img src="screenshots/main.png" height="400" alt="Screenshot"/> 
+
 Getting Started
 ---------------
 
@@ -32,7 +71,7 @@
 License
 -------
 
-Copyright 2014 The Android Open Source Project
+Copyright 2014 The Android Open Source Project, Inc.
 
 Licensed to the Apache Software Foundation (ASF) under one or more contributor
 license agreements.  See the NOTICE file distributed with this work for
@@ -41,7 +80,7 @@
 use this file except in compliance with the License.  You may obtain a copy of
 the License at
 
-  http://www.apache.org/licenses/LICENSE-2.0
+http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties b/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
+++ b/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/Camera2Basic/screenshots/icon-web.png b/media/Camera2Basic/screenshots/icon-web.png
new file mode 100644
index 0000000..d9bd4c4
--- /dev/null
+++ b/media/Camera2Basic/screenshots/icon-web.png
Binary files differ
diff --git a/media/Camera2Basic/screenshots/main.png b/media/Camera2Basic/screenshots/main.png
new file mode 100644
index 0000000..47419eb
--- /dev/null
+++ b/media/Camera2Basic/screenshots/main.png
Binary files differ
diff --git a/media/Camera2Basic/template-params.xml b/media/Camera2Basic/template-params.xml
index f89b6a3..b4afe4c 100644
--- a/media/Camera2Basic/template-params.xml
+++ b/media/Camera2Basic/template-params.xml
@@ -34,4 +34,68 @@
 
     <template src="base"/>
 
+        <metadata>
+        <status>PUBLISHED</status>
+        <categories>Media, Camera, Camera2</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.hardware.camera2.CameraManager</android>
+            <android>android.hardware.camera2.CameraDevice</android>
+            <android>android.hardware.camera2.CameraCharacteristics</android>
+            <android>android.hardware.camera2.CameraCaptureSession</android>
+            <android>android.hardware.camera2.CaptureRequest</android>
+            <android>android.hardware.camera2.CaptureResult</android>
+            <android>android.view.TextureView</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates how to use basic functionalities of Camera2
+API. You can learn how to iterate through characteristics of all the
+cameras attached to the device, display a camera preview, and take
+pictures.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+The [Camera2 API][1] provides an interface to individual camera
+devices connected to an Android device. It replaces the deprecated
+Camera class.
+
+Use [getCameraIdList][2] to get a list of all the available
+cameras. You can then use [getCameraCharacteristics][3] and find the
+best camera that suits your need (front/rear facing, resolution etc).
+
+Create an instance of [CameraDevice.StateCallback][4] and open a
+camera. It is ready to start camera preview when the camera is opened.
+
+This sample uses TextureView to show the camera preview. Create a
+[CameraCaptureSession][5] and set a repeating [CaptureRequest][6] to it.
+
+Still image capture takes several steps. First, you need to lock the
+focus of the camera by updating the CaptureRequest for the camera
+preview. Then, in a similar way, you need to run a precapture
+sequence. After that, it is ready to capture a picture. Create a new
+CaptureRequest and call [capture][7]. Don't forget to unlock the focus
+when you are done.
+
+[1]: https://developer.android.com/reference/android/hardware/camera2/package-summary.html
+[2]: https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList()
+[3]: https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraCharacteristics(java.lang.String)
+[4]: https://developer.android.com/reference/android/hardware/camera2/CameraDevice.StateCallback.html
+[5]: https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html
+[6]: https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html
+[7]: https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html#capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler)
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/media/Camera2Video/gradle/wrapper/gradle-wrapper.properties b/media/Camera2Video/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/Camera2Video/gradle/wrapper/gradle-wrapper.properties
+++ b/media/Camera2Video/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/Camera2Video/screenshots/1-launch.png b/media/Camera2Video/screenshots/1-launch.png
new file mode 100644
index 0000000..24105bb
--- /dev/null
+++ b/media/Camera2Video/screenshots/1-launch.png
Binary files differ
diff --git a/media/Camera2Video/screenshots/2-record.png b/media/Camera2Video/screenshots/2-record.png
new file mode 100644
index 0000000..bf79522
--- /dev/null
+++ b/media/Camera2Video/screenshots/2-record.png
Binary files differ
diff --git a/media/Camera2Video/screenshots/3-save.png b/media/Camera2Video/screenshots/3-save.png
new file mode 100644
index 0000000..a070da4
--- /dev/null
+++ b/media/Camera2Video/screenshots/3-save.png
Binary files differ
diff --git a/media/Camera2Video/screenshots/icon-web.png b/media/Camera2Video/screenshots/icon-web.png
new file mode 100644
index 0000000..a0a61c0
--- /dev/null
+++ b/media/Camera2Video/screenshots/icon-web.png
Binary files differ
diff --git a/media/Camera2Video/template-params.xml b/media/Camera2Video/template-params.xml
index d85ffcf..320f3ac 100644
--- a/media/Camera2Video/template-params.xml
+++ b/media/Camera2Video/template-params.xml
@@ -33,4 +33,66 @@
 
     <template src="base"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Media</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-launch.png</img>
+            <img>screenshots/2-record.png</img>
+            <img>screenshots/3-save.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.hardware.camera2.CameraCaptureSession</android>
+            <android>android.hardware.camera2.CameraDevice</android>
+            <android>android.view.TextureView</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample shows how to record video using the new Camera2 API in Android Lollipop.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+Android Lollipop introduced a new camera API, called camera2. This sample uses [CameraDevice][1]
+and [CameraCaptureSession][2] to record video. It also uses a custom [TextureView][3] to render the output.
+
+The main steps are:
+
+1. Create a custom TextureView class and add it to the layout. The purpose of the custom TextureView is
+to be able to draw itself according to an aspect ratio, which is set via a public method. Additionally,
+the `onMeasure(int widthMeasureSpec, int heightMeasureSpec)` method is overridden, using the aspect ratio.
+2. Implement a `TextureView.SurfaceTextureListener` on your TextureView, and override its
+`onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height)` method to calculate
+the matrix to apply to the TextureView so the camera output fits. Use the method `setTransform(matrix)` on
+the TextureView.
+3. Implement a [`CameraDevice.StateCallback`][4] to receive events about changes of the state of the
+camera device. Override its methods to set your CameraDevice instance, start the preview, and stop
+and release the camera.
+4. When starting the preview, set up the MediaRecorder to accept video format.
+5. Then, set up a [`CaptureRequest.Builder`][5] using `createCaptureRequest(CameraDevice.TEMPLATE_RECORD)`
+on your CameraDevice instance.
+6. Then, implement a [`CameraCaptureSession.StateCallback`][6], using the method
+`createCaptureSession(surfaces, new CameraCaptureSession.StateCallback(){})` on your CameraDevice instance,
+where `surfaces` is a list consisting of the surface view of your TextureView and the surface of
+your MediaRecorder instance.
+7. Use `start()` and `stop()` methods on your MediaRecorder instance to actually start and stop the recording.
+8. Lastly, set up and clean up your camera device in `onResume()` and `onPause()`.
+
+
+[1]: https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html
+[2]: http://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html
+[3]: http://developer.android.com/reference/android/view/TextureView.html
+[4]: https://developer.android.com/reference/android/hardware/camera2/CameraDevice.StateCallback.html
+[5]: http://developer.android.com/reference/android/hardware/camera2/CaptureRequest.Builder.html
+[6]: http://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.StateCallback.html
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/media/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties b/media/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties
+++ b/media/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/MediaEffects/MediaEffectsSample/.gitignore b/media/MediaBrowserService/Application/.gitignore
similarity index 100%
copy from media/MediaEffects/MediaEffectsSample/.gitignore
copy to media/MediaBrowserService/Application/.gitignore
diff --git a/media/MediaEffects/MediaEffectsSample/proguard-project.txt b/media/MediaBrowserService/Application/proguard-project.txt
similarity index 100%
copy from media/MediaEffects/MediaEffectsSample/proguard-project.txt
copy to media/MediaBrowserService/Application/proguard-project.txt
diff --git a/media/MediaBrowserService/Application/src/androidTest/java/com/example/android/mediabrowserservice/test/SampleTests.java b/media/MediaBrowserService/Application/src/androidTest/java/com/example/android/mediabrowserservice/test/SampleTests.java
new file mode 100644
index 0000000..7bf34eb
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/androidTest/java/com/example/android/mediabrowserservice/test/SampleTests.java
@@ -0,0 +1,76 @@
+/*
+* Copyright 2013 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.mediabrowserservice.test;
+
+import com.example.android.mediabrowserservice.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for MediaBrowserService sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private MediaBrowserServiceFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (MediaBrowserServiceFragment)
+            mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+    * Test if the test fixture has been set up correctly.
+    */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    /**
+    * Add more tests below.
+    */
+
+}
diff --git a/media/MediaBrowserService/Application/src/main/AndroidManifest.xml b/media/MediaBrowserService/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6d05c27
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.mediabrowserservice"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <uses-sdk
+        android:minSdkVersion="21"
+        android:targetSdkVersion="21" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme">
+
+        <meta-data android:name="com.google.android.gms.car.application"
+            android:resource="@xml/automotive_app_desc"/>
+
+
+        <activity android:name="com.example.android.mediabrowserservice.MusicPlayerActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <!-- (OPTIONAL) use this meta data to indicate which icon should be used in media
+            notifications (for example, when the music changes and the user is
+            looking at another app) -->
+        <meta-data
+            android:name="com.google.android.gms.car.notification.SmallIcon"
+            android:resource="@drawable/ic_notification" />
+
+        <service
+            android:name="com.example.android.mediabrowserservice.MusicService"
+            android:exported="true"
+            >
+            <intent-filter>
+                <action android:name="android.media.browse.MediaBrowserService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java
new file mode 100644
index 0000000..e4ccf76
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Fragment that lists all the various browsable queues available
+ * from a {@link android.service.media.MediaBrowserService}.
+ * <p/>
+ * It uses a {@link MediaBrowser} to connect to the {@link MusicService}. Once connected,
+ * the fragment subscribes to get all the children. All {@link MediaBrowser.MediaItem}'s
+ * that can be browsed are shown in a ListView.
+ */
+public class BrowseFragment extends Fragment {
+
+    private static final String TAG = LogHelper.makeLogTag(BrowseFragment.class.getSimpleName());
+
+    public static final String ARG_MEDIA_ID = "media_id";
+
+    public static interface FragmentDataHelper {
+        void onMediaItemSelected(MediaBrowser.MediaItem item);
+    }
+
+    // The mediaId to be used for subscribing for children using the MediaBrowser.
+    private String mMediaId;
+
+    private MediaBrowser mMediaBrowser;
+    private BrowseAdapter mBrowserAdapter;
+
+    private MediaBrowser.SubscriptionCallback mSubscriptionCallback = new MediaBrowser.SubscriptionCallback() {
+
+        @Override
+        public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+            mBrowserAdapter.clear();
+            mBrowserAdapter.notifyDataSetInvalidated();
+            for (MediaBrowser.MediaItem item : children) {
+                mBrowserAdapter.add(item);
+            }
+            mBrowserAdapter.notifyDataSetChanged();
+        }
+
+        @Override
+        public void onError(String id) {
+            Toast.makeText(getActivity(), R.string.error_loading_media,
+                    Toast.LENGTH_LONG).show();
+        }
+    };
+
+    private MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+        @Override
+        public void onConnected() {
+            LogHelper.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken());
+
+            if (mMediaId == null) {
+                mMediaId = mMediaBrowser.getRoot();
+            }
+            mMediaBrowser.subscribe(mMediaId, mSubscriptionCallback);
+            if (mMediaBrowser.getSessionToken() == null) {
+                throw new IllegalArgumentException("No Session token");
+            }
+            MediaController mediaController = new MediaController(getActivity(),
+                    mMediaBrowser.getSessionToken());
+            getActivity().setMediaController(mediaController);
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            LogHelper.d(TAG, "onConnectionFailed");
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            LogHelper.d(TAG, "onConnectionSuspended");
+            getActivity().setMediaController(null);
+        }
+    };
+
+    public static BrowseFragment newInstance(String mediaId) {
+        Bundle args = new Bundle();
+        args.putString(ARG_MEDIA_ID, mediaId);
+        BrowseFragment fragment = new BrowseFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_list, container, false);
+
+        mBrowserAdapter = new BrowseAdapter(getActivity());
+
+        View controls = rootView.findViewById(R.id.controls);
+        controls.setVisibility(View.GONE);
+
+        ListView listView = (ListView) rootView.findViewById(R.id.list_view);
+        listView.setAdapter(mBrowserAdapter);
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                MediaBrowser.MediaItem item = mBrowserAdapter.getItem(position);
+                try {
+                    FragmentDataHelper listener = (FragmentDataHelper) getActivity();
+                    listener.onMediaItemSelected(item);
+                } catch (ClassCastException ex) {
+                    Log.e(TAG, "Exception trying to cast to FragmentDataHelper", ex);
+                }
+            }
+        });
+
+        Bundle args = getArguments();
+        mMediaId = args.getString(ARG_MEDIA_ID, null);
+
+        mMediaBrowser = new MediaBrowser(getActivity(),
+                new ComponentName(getActivity(), MusicService.class),
+                mConnectionCallback, null);
+
+        return rootView;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mMediaBrowser.connect();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mMediaBrowser.disconnect();
+    }
+
+    // An adapter for showing the list of browsed MediaItem's
+    private static class BrowseAdapter extends ArrayAdapter<MediaBrowser.MediaItem> {
+
+        public BrowseAdapter(Context context) {
+            super(context, R.layout.media_list_item, new ArrayList<MediaBrowser.MediaItem>());
+        }
+
+        static class ViewHolder {
+            ImageView mImageView;
+            TextView mTitleView;
+            TextView mDescriptionView;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+
+            ViewHolder holder;
+
+            if (convertView == null) {
+                convertView = LayoutInflater.from(getContext())
+                        .inflate(R.layout.media_list_item, parent, false);
+                holder = new ViewHolder();
+                holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
+                holder.mImageView.setVisibility(View.GONE);
+                holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
+                holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            MediaBrowser.MediaItem item = getItem(position);
+            holder.mTitleView.setText(item.getDescription().getTitle());
+            holder.mDescriptionView.setText(item.getDescription().getDescription());
+            if (item.isPlayable()) {
+                holder.mImageView.setImageDrawable(
+                        getContext().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+                holder.mImageView.setVisibility(View.VISIBLE);
+            }
+            return convertView;
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MediaNotificationManager.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MediaNotificationManager.java
new file mode 100644
index 0000000..aec4691
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MediaNotificationManager.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.mediabrowserservice;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.AsyncTask;
+import android.util.LruCache;
+
+import com.example.android.mediabrowserservice.utils.BitmapHelper;
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.io.IOException;
+
+/**
+ * Keeps track of a notification and updates it automatically for a given
+ * MediaSession. Maintaining a visible notification (usually) guarantees that the music service
+ * won't be killed during playback.
+ */
+public class MediaNotificationManager extends BroadcastReceiver {
+    private static final String TAG = LogHelper.makeLogTag(MediaNotificationManager.class.getSimpleName());
+
+    private static final int NOTIFICATION_ID = 412;
+
+    public static final String ACTION_PAUSE = "com.example.android.mediabrowserservice.pause";
+    public static final String ACTION_PLAY = "com.example.android.mediabrowserservice.play";
+    public static final String ACTION_PREV = "com.example.android.mediabrowserservice.prev";
+    public static final String ACTION_NEXT = "com.example.android.mediabrowserservice.next";
+
+    private static final int MAX_ALBUM_ART_CACHE_SIZE = 1024*1024;
+
+    private final MusicService mService;
+    private MediaSession.Token mSessionToken;
+    private MediaController mController;
+    private MediaController.TransportControls mTransportControls;
+    private final LruCache<String, Bitmap> mAlbumArtCache;
+
+    private PlaybackState mPlaybackState;
+    private MediaMetadata mMetadata;
+
+    private Notification.Builder mNotificationBuilder;
+    private NotificationManager mNotificationManager;
+    private Notification.Action mPlayPauseAction;
+
+    private PendingIntent mPauseIntent, mPlayIntent, mPreviousIntent, mNextIntent;
+
+    private int mNotificationColor;
+
+    private boolean mStarted = false;
+
+    public MediaNotificationManager(MusicService service) {
+        mService = service;
+        updateSessionToken();
+
+        // simple album art cache that holds no more than
+        // MAX_ALBUM_ART_CACHE_SIZE bytes:
+        mAlbumArtCache = new LruCache<String, Bitmap>(MAX_ALBUM_ART_CACHE_SIZE) {
+            @Override
+            protected int sizeOf(String key, Bitmap value) {
+                return value.getByteCount();
+            }
+        };
+
+        mNotificationColor = getNotificationColor();
+
+        mNotificationManager = (NotificationManager) mService
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        String pkg = mService.getPackageName();
+        mPauseIntent = PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+        mPlayIntent = PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+        mPreviousIntent = PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+        mNextIntent = PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    protected int getNotificationColor() {
+        int notificationColor = 0;
+        String packageName = mService.getPackageName();
+        try {
+            Context packageContext = mService.createPackageContext(packageName, 0);
+            ApplicationInfo applicationInfo =
+                    mService.getPackageManager().getApplicationInfo(packageName, 0);
+            packageContext.setTheme(applicationInfo.theme);
+            Resources.Theme theme = packageContext.getTheme();
+            TypedArray ta = theme.obtainStyledAttributes(
+                    new int[] {android.R.attr.colorPrimary});
+            notificationColor = ta.getColor(0, Color.DKGRAY);
+            ta.recycle();
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return notificationColor;
+    }
+
+    /**
+     * Posts the notification and starts tracking the session to keep it
+     * updated. The notification will automatically be removed if the session is
+     * destroyed before {@link #stopNotification} is called.
+     */
+    public void startNotification() {
+        if (!mStarted) {
+            mController.registerCallback(mCb);
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ACTION_NEXT);
+            filter.addAction(ACTION_PAUSE);
+            filter.addAction(ACTION_PLAY);
+            filter.addAction(ACTION_PREV);
+            mService.registerReceiver(this, filter);
+
+            mMetadata = mController.getMetadata();
+            mPlaybackState = mController.getPlaybackState();
+
+            mStarted = true;
+            // The notification must be updated after setting started to true
+            updateNotificationMetadata();
+        }
+    }
+
+    /**
+     * Removes the notification and stops tracking the session. If the session
+     * was destroyed this has no effect.
+     */
+    public void stopNotification() {
+        mStarted = false;
+        mController.unregisterCallback(mCb);
+        try {
+            mNotificationManager.cancel(NOTIFICATION_ID);
+            mService.unregisterReceiver(this);
+        } catch (IllegalArgumentException ex) {
+            // ignore if the receiver is not registered.
+        }
+        mService.stopForeground(true);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        LogHelper.d(TAG, "Received intent with action " + action);
+        if (ACTION_PAUSE.equals(action)) {
+            mTransportControls.pause();
+        } else if (ACTION_PLAY.equals(action)) {
+            mTransportControls.play();
+        } else if (ACTION_NEXT.equals(action)) {
+            mTransportControls.skipToNext();
+        } else if (ACTION_PREV.equals(action)) {
+            mTransportControls.skipToPrevious();
+        }
+    }
+
+    /**
+     * Update the state based on a change on the session token. Called either when
+     * we are running for the first time or when the media session owner has destroyed the session
+     * (see {@link android.media.session.MediaController.Callback#onSessionDestroyed()})
+     */
+    private void updateSessionToken() {
+        MediaSession.Token freshToken = mService.getSessionToken();
+        if (mSessionToken == null || !mSessionToken.equals(freshToken)) {
+            if (mController != null) {
+                mController.unregisterCallback(mCb);
+            }
+            mSessionToken = freshToken;
+            mController = new MediaController(mService, mSessionToken);
+            mTransportControls = mController.getTransportControls();
+            if (mStarted) {
+                mController.registerCallback(mCb);
+            }
+        }
+    }
+
+    private final MediaController.Callback mCb = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            mPlaybackState = state;
+            LogHelper.d(TAG, "Received new playback state", state);
+            updateNotificationPlaybackState();
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            mMetadata = metadata;
+            LogHelper.d(TAG, "Received new metadata ", metadata);
+            updateNotificationMetadata();
+        }
+
+        @Override
+        public void onSessionDestroyed() {
+            super.onSessionDestroyed();
+            LogHelper.d(TAG, "Session was destroyed, resetting to the new session token");
+            updateSessionToken();
+        }
+    };
+
+    private void updateNotificationMetadata() {
+        LogHelper.d(TAG, "updateNotificationMetadata. mMetadata=" + mMetadata);
+        if (mMetadata == null || mPlaybackState == null) {
+            return;
+        }
+
+        updatePlayPauseAction();
+
+        mNotificationBuilder = new Notification.Builder(mService);
+        int playPauseActionIndex = 0;
+
+        // If skip to previous action is enabled
+        if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
+            mNotificationBuilder
+                    .addAction(R.drawable.ic_skip_previous_white_24dp,
+                            mService.getString(R.string.label_previous), mPreviousIntent);
+            playPauseActionIndex = 1;
+        }
+
+        mNotificationBuilder.addAction(mPlayPauseAction);
+
+        // If skip to next action is enabled
+        if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+            mNotificationBuilder.addAction(R.drawable.ic_skip_next_white_24dp,
+                    mService.getString(R.string.label_next), mNextIntent);
+        }
+
+        MediaDescription description = mMetadata.getDescription();
+
+        String fetchArtUrl = null;
+        Bitmap art = description.getIconBitmap();
+        if (art == null && description.getIconUri() != null) {
+            // This sample assumes the iconUri will be a valid URL formatted String, but
+            // it can actually be any valid Android Uri formatted String.
+            // async fetch the album art icon
+            String artUrl = description.getIconUri().toString();
+            art = mAlbumArtCache.get(artUrl);
+            if (art == null) {
+                fetchArtUrl = artUrl;
+                // use a placeholder art while the remote art is being downloaded
+                art = BitmapFactory.decodeResource(mService.getResources(), R.drawable.ic_default_art);
+            }
+        }
+
+        mNotificationBuilder
+                .setStyle(new Notification.MediaStyle()
+                        .setShowActionsInCompactView(playPauseActionIndex)  // only show play/pause in compact view
+                        .setMediaSession(mSessionToken))
+                .setColor(mNotificationColor)
+                .setSmallIcon(R.drawable.ic_notification)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setUsesChronometer(true)
+                .setContentTitle(description.getTitle())
+                .setContentText(description.getSubtitle())
+                .setLargeIcon(art);
+
+        updateNotificationPlaybackState();
+
+        mService.startForeground(NOTIFICATION_ID, mNotificationBuilder.build());
+        if (fetchArtUrl != null) {
+            fetchBitmapFromURLAsync(fetchArtUrl);
+        }
+    }
+
+    private void updatePlayPauseAction() {
+        LogHelper.d(TAG, "updatePlayPauseAction");
+        String label;
+        int icon;
+        PendingIntent intent;
+        if (mPlaybackState.getState() == PlaybackState.STATE_PLAYING) {
+            label = mService.getString(R.string.label_pause);
+            icon = R.drawable.ic_pause_white_24dp;
+            intent = mPauseIntent;
+        } else {
+            label = mService.getString(R.string.label_play);
+            icon = R.drawable.ic_play_arrow_white_24dp;
+            intent = mPlayIntent;
+        }
+        if (mPlayPauseAction == null) {
+            mPlayPauseAction = new Notification.Action(icon, label, intent);
+        } else {
+            mPlayPauseAction.icon = icon;
+            mPlayPauseAction.title = label;
+            mPlayPauseAction.actionIntent = intent;
+        }
+    }
+
+    private void updateNotificationPlaybackState() {
+        LogHelper.d(TAG, "updateNotificationPlaybackState. mPlaybackState=" + mPlaybackState);
+        if (mPlaybackState == null || !mStarted) {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. cancelling notification!");
+            mService.stopForeground(true);
+            return;
+        }
+        if (mNotificationBuilder == null) {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. there is no notificationBuilder. Ignoring request to update state!");
+            return;
+        }
+        if (mPlaybackState.getPosition() >= 0) {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. updating playback position to ",
+                    (System.currentTimeMillis() - mPlaybackState.getPosition()) / 1000, " seconds");
+            mNotificationBuilder
+                    .setWhen(System.currentTimeMillis() - mPlaybackState.getPosition())
+                    .setShowWhen(true)
+                    .setUsesChronometer(true);
+            mNotificationBuilder.setShowWhen(true);
+        } else {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. hiding playback position");
+            mNotificationBuilder
+                    .setWhen(0)
+                    .setShowWhen(false)
+                    .setUsesChronometer(false);
+        }
+
+        updatePlayPauseAction();
+
+        // Make sure that the notification can be dismissed by the user when we are not playing:
+        mNotificationBuilder.setOngoing(mPlaybackState.getState() == PlaybackState.STATE_PLAYING);
+
+        mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
+    }
+
+    public void fetchBitmapFromURLAsync(final String source) {
+        LogHelper.d(TAG, "getBitmapFromURLAsync: starting asynctask to fetch ", source);
+        new AsyncTask<Void, Void, Bitmap>() {
+            @Override
+            protected Bitmap doInBackground(Void[] objects) {
+                Bitmap bitmap = null;
+                try {
+                    bitmap = BitmapHelper.fetchAndRescaleBitmap(source,
+                            BitmapHelper.MEDIA_ART_BIG_WIDTH, BitmapHelper.MEDIA_ART_BIG_HEIGHT);
+                    mAlbumArtCache.put(source, bitmap);
+                } catch (IOException e) {
+                    LogHelper.e(TAG, e, "getBitmapFromURLAsync: " + source);
+                }
+                return bitmap;
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap bitmap) {
+                if (bitmap != null && mMetadata != null &&
+                        mNotificationBuilder != null && mMetadata.getDescription() != null &&
+                        !source.equals(mMetadata.getDescription().getIconUri())) {
+                    // If the media is still the same, update the notification:
+                    LogHelper.d(TAG, "getBitmapFromURLAsync: set bitmap to ", source);
+                    mNotificationBuilder.setLargeIcon(bitmap);
+                    mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
+                }
+            }
+        }.execute();
+    }
+
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicPlayerActivity.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicPlayerActivity.java
new file mode 100644
index 0000000..ac231c7
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicPlayerActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice;
+
+import android.app.Activity;
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+
+/**
+ * Main activity for the music player.
+ */
+public class MusicPlayerActivity extends Activity
+        implements BrowseFragment.FragmentDataHelper {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_player);
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction()
+                    .add(R.id.container, BrowseFragment.newInstance(null))
+                    .commit();
+        }
+    }
+
+    @Override
+    public void onMediaItemSelected(MediaBrowser.MediaItem item) {
+        if (item.isPlayable()) {
+            getMediaController().getTransportControls().playFromMediaId(item.getMediaId(), null);
+            QueueFragment queueFragment = QueueFragment.newInstance();
+            getFragmentManager().beginTransaction()
+                    .replace(R.id.container, queueFragment)
+                    .addToBackStack(null)
+                    .commit();
+        } else if (item.isBrowsable()) {
+            getFragmentManager().beginTransaction()
+                    .replace(R.id.container, BrowseFragment.newInstance(item.getMediaId()))
+                    .addToBackStack(null)
+                    .commit();
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicService.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicService.java
new file mode 100644
index 0000000..b055d69
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicService.java
@@ -0,0 +1,947 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.mediabrowserservice;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.service.media.MediaBrowserService;
+
+import com.example.android.mediabrowserservice.model.MusicProvider;
+import com.example.android.mediabrowserservice.utils.LogHelper;
+import com.example.android.mediabrowserservice.utils.MediaIDHelper;
+import com.example.android.mediabrowserservice.utils.QueueHelper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_ROOT;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.createBrowseCategoryMediaID;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.extractBrowseCategoryFromMediaID;
+
+/**
+ * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
+ * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
+ * exposes it through its MediaSession.Token, which allows the client to create a MediaController
+ * that connects to and send control commands to the MediaSession remotely. This is useful for
+ * user interfaces that need to interact with your media session, like Android Auto. You can
+ * (should) also use the same service from your app's UI, which gives a seamless playback
+ * experience to the user.
+ *
+ * To implement a MediaBrowserService, you need to:
+ *
+ * <ul>
+ *
+ * <li> Extend {@link android.service.media.MediaBrowserService}, implementing the media browsing
+ *      related methods {@link android.service.media.MediaBrowserService#onGetRoot} and
+ *      {@link android.service.media.MediaBrowserService#onLoadChildren};
+ * <li> In onCreate, start a new {@link android.media.session.MediaSession} and notify its parent
+ *      with the session's token {@link android.service.media.MediaBrowserService#setSessionToken};
+ *
+ * <li> Set a callback on the
+ *      {@link android.media.session.MediaSession#setCallback(android.media.session.MediaSession.Callback)}.
+ *      The callback will receive all the user's actions, like play, pause, etc;
+ *
+ * <li> Handle all the actual music playing using any method your app prefers (for example,
+ *      {@link android.media.MediaPlayer})
+ *
+ * <li> Update playbackState, "now playing" metadata and queue, using MediaSession proper methods
+ *      {@link android.media.session.MediaSession#setPlaybackState(android.media.session.PlaybackState)}
+ *      {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata)} and
+ *      {@link android.media.session.MediaSession#setQueue(java.util.List)})
+ *
+ * <li> Declare and export the service in AndroidManifest with an intent receiver for the action
+ *      android.media.browse.MediaBrowserService
+ *
+ * </ul>
+ *
+ * To make your app compatible with Android Auto, you also need to:
+ *
+ * <ul>
+ *
+ * <li> Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+ *      with a &lt;automotiveApp&gt; root element. For a media app, this must include
+ *      an &lt;uses name="media"/&gt; element as a child.
+ *      For example, in AndroidManifest.xml:
+ *          &lt;meta-data android:name="com.google.android.gms.car.application"
+ *              android:resource="@xml/automotive_app_desc"/&gt;
+ *      And in res/values/automotive_app_desc.xml:
+ *          &lt;automotiveApp&gt;
+ *              &lt;uses name="media"/&gt;
+ *          &lt;/automotiveApp&gt;
+ *
+ * </ul>
+
+ * @see <a href="README.md">README.md</a> for more details.
+ *
+ */
+
+public class MusicService extends MediaBrowserService implements OnPreparedListener,
+        OnCompletionListener, OnErrorListener, AudioManager.OnAudioFocusChangeListener {
+
+    private static final String TAG = LogHelper.makeLogTag(MusicService.class.getSimpleName());
+
+    // Action to thumbs up a media item
+    private static final String CUSTOM_ACTION_THUMBS_UP = "thumbs_up";
+    // Delay stopSelf by using a handler.
+    private static final int STOP_DELAY = 30000;
+
+    // The volume we set the media player to when we lose audio focus, but are
+    // allowed to reduce the volume instead of stopping playback.
+    public static final float VOLUME_DUCK = 0.2f;
+
+    // The volume we set the media player when we have audio focus.
+    public static final float VOLUME_NORMAL = 1.0f;
+    public static final String ANDROID_AUTO_PACKAGE_NAME = "com.google.android.projection.gearhead";
+    public static final String ANDROID_AUTO_SIMULATOR_PACKAGE_NAME = "com.google.android.mediasimulator";
+
+    // Music catalog manager
+    private MusicProvider mMusicProvider;
+
+    private MediaSession mSession;
+    private MediaPlayer mMediaPlayer;
+
+    // "Now playing" queue:
+    private List<MediaSession.QueueItem> mPlayingQueue;
+    private int mCurrentIndexOnQueue;
+
+    // Current local media player state
+    private int mState = PlaybackState.STATE_NONE;
+
+    // Wifi lock that we hold when streaming files from the internet, in order
+    // to prevent the device from shutting off the Wifi radio
+    private WifiLock mWifiLock;
+
+    private MediaNotificationManager mMediaNotificationManager;
+
+    // Indicates whether the service was started.
+    private boolean mServiceStarted;
+
+    enum AudioFocus {
+        NoFocusNoDuck, // we don't have audio focus, and can't duck
+        NoFocusCanDuck, // we don't have focus, but can play at a low volume
+                        // ("ducking")
+        Focused // we have full audio focus
+    }
+
+    // Type of audio focus we have:
+    private AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck;
+    private AudioManager mAudioManager;
+
+    // Indicates if we should start playing immediately after we gain focus.
+    private boolean mPlayOnFocusGain;
+
+    private Handler mDelayedStopHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if ((mMediaPlayer != null && mMediaPlayer.isPlaying()) ||
+                    mPlayOnFocusGain) {
+                LogHelper.d(TAG, "Ignoring delayed stop since the media player is in use.");
+                return;
+            }
+            LogHelper.d(TAG, "Stopping service with delay handler.");
+            stopSelf();
+            mServiceStarted = false;
+        }
+    };
+
+    /*
+     * (non-Javadoc)
+     * @see android.app.Service#onCreate()
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        LogHelper.d(TAG, "onCreate");
+
+        mPlayingQueue = new ArrayList<>();
+
+        // Create the Wifi lock (this does not acquire the lock, this just creates it)
+        mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
+                .createWifiLock(WifiManager.WIFI_MODE_FULL, "MusicDemo_lock");
+
+
+        // Create the music catalog metadata provider
+        mMusicProvider = new MusicProvider();
+        mMusicProvider.retrieveMedia(new MusicProvider.Callback() {
+            @Override
+            public void onMusicCatalogReady(boolean success) {
+                mState = success ? PlaybackState.STATE_NONE : PlaybackState.STATE_ERROR;
+            }
+        });
+
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+
+        // Start a new MediaSession
+        mSession = new MediaSession(this, "MusicService");
+        setSessionToken(mSession.getSessionToken());
+        mSession.setCallback(new MediaSessionCallback());
+        mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
+                MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+
+        // Use these extras to reserve space for the corresponding actions, even when they are disabled
+        // in the playbackstate, so the custom actions don't reflow.
+        Bundle extras = new Bundle();
+        extras.putBoolean(
+            "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT",
+            true);
+        extras.putBoolean(
+            "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS",
+            true);
+        // If you want to reserve the Queue slot when there is no queue
+        // (mSession.setQueue(emptylist)), uncomment the lines below:
+        // extras.putBoolean(
+        //   "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE",
+        //   true);
+        mSession.setExtras(extras);
+
+        updatePlaybackState(null);
+
+        mMediaNotificationManager = new MediaNotificationManager(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.app.Service#onDestroy()
+     */
+    @Override
+    public void onDestroy() {
+        LogHelper.d(TAG, "onDestroy");
+
+        // Service is being killed, so make sure we release our resources
+        handleStopRequest(null);
+
+        mDelayedStopHandler.removeCallbacksAndMessages(null);
+        // In particular, always release the MediaSession to clean up resources
+        // and notify associated MediaController(s).
+        mSession.release();
+    }
+
+
+    @Override
+    public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+        LogHelper.d(TAG, "OnGetRoot: clientPackageName=" + clientPackageName,
+                "; clientUid=" + clientUid + " ; rootHints=", rootHints);
+        // To ensure you are not allowing any arbitrary app to browse your app's contents, you
+        // need to check the origin:
+        if (!PackageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
+            // If the request comes from an untrusted package, return null. No further calls will
+            // be made to other media browsing methods.
+            LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package "
+                    + clientPackageName);
+            return null;
+        }
+        if (ANDROID_AUTO_PACKAGE_NAME.equals(clientPackageName)) {
+            // Optional: if your app needs to adapt ads, music library or anything else that
+            // needs to run differently when connected to the car, this is where you should handle
+            // it.
+        }
+        return new BrowserRoot(MEDIA_ID_ROOT, null);
+    }
+
+    @Override
+    public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+        if (!mMusicProvider.isInitialized()) {
+            // Use result.detach to allow calling result.sendResult from another thread:
+            result.detach();
+
+            mMusicProvider.retrieveMedia(new MusicProvider.Callback() {
+                @Override
+                public void onMusicCatalogReady(boolean success) {
+                    if (success) {
+                        loadChildrenImpl(parentMediaId, result);
+                    } else {
+                        updatePlaybackState(getString(R.string.error_no_metadata));
+                        result.sendResult(new ArrayList<MediaItem>());
+                    }
+                }
+            });
+
+        } else {
+            // If our music catalog is already loaded/cached, load them into result immediately
+            loadChildrenImpl(parentMediaId, result);
+        }
+    }
+
+    /**
+     * Actual implementation of onLoadChildren that assumes that MusicProvider is already
+     * initialized.
+     */
+    private void loadChildrenImpl(final String parentMediaId,
+                                  final Result<List<MediaBrowser.MediaItem>> result) {
+        LogHelper.d(TAG, "OnLoadChildren: parentMediaId=", parentMediaId);
+
+        List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>();
+
+        if (MEDIA_ID_ROOT.equals(parentMediaId)) {
+            LogHelper.d(TAG, "OnLoadChildren.ROOT");
+            mediaItems.add(new MediaBrowser.MediaItem(
+                    new MediaDescription.Builder()
+                        .setMediaId(MEDIA_ID_MUSICS_BY_GENRE)
+                        .setTitle(getString(R.string.browse_genres))
+                        .setIconUri(Uri.parse("android.resource://" +
+                                "com.example.android.mediabrowserservice/drawable/ic_by_genre"))
+                        .setSubtitle(getString(R.string.browse_genre_subtitle))
+                        .build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
+            ));
+
+        } else if (MEDIA_ID_MUSICS_BY_GENRE.equals(parentMediaId)) {
+            LogHelper.d(TAG, "OnLoadChildren.GENRES");
+            for (String genre: mMusicProvider.getGenres()) {
+                MediaBrowser.MediaItem item = new MediaBrowser.MediaItem(
+                    new MediaDescription.Builder()
+                        .setMediaId(createBrowseCategoryMediaID(MEDIA_ID_MUSICS_BY_GENRE, genre))
+                        .setTitle(genre)
+                        .setSubtitle(getString(R.string.browse_musics_by_genre_subtitle, genre))
+                        .build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
+                );
+                mediaItems.add(item);
+            }
+
+        } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_GENRE)) {
+            String genre = extractBrowseCategoryFromMediaID(parentMediaId)[1];
+            LogHelper.d(TAG, "OnLoadChildren.SONGS_BY_GENRE  genre=", genre);
+            for (MediaMetadata track: mMusicProvider.getMusicsByGenre(genre)) {
+                // Since mediaMetadata fields are immutable, we need to create a copy, so we
+                // can set a hierarchy-aware mediaID. We will need to know the media hierarchy
+                // when we get a onPlayFromMusicID call, so we can create the proper queue based
+                // on where the music was selected from (by artist, by genre, random, etc)
+                String hierarchyAwareMediaID = MediaIDHelper.createTrackMediaID(
+                        MEDIA_ID_MUSICS_BY_GENRE, genre, track);
+                MediaMetadata trackCopy = new MediaMetadata.Builder(track)
+                        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
+                        .build();
+                MediaBrowser.MediaItem bItem = new MediaBrowser.MediaItem(
+                        trackCopy.getDescription(), MediaItem.FLAG_PLAYABLE);
+                mediaItems.add(bItem);
+            }
+        } else {
+            LogHelper.w(TAG, "Skipping unmatched parentMediaId: ", parentMediaId);
+        }
+        result.sendResult(mediaItems);
+    }
+
+
+
+    private final class MediaSessionCallback extends MediaSession.Callback {
+        @Override
+        public void onPlay() {
+            LogHelper.d(TAG, "play");
+
+            if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
+                mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider);
+                mSession.setQueue(mPlayingQueue);
+                mSession.setQueueTitle(getString(R.string.random_queue_title));
+                // start playing from the beginning of the queue
+                mCurrentIndexOnQueue = 0;
+            }
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+                handlePlayRequest();
+            }
+        }
+
+        @Override
+        public void onSkipToQueueItem(long queueId) {
+            LogHelper.d(TAG, "OnSkipToQueueItem:" + queueId);
+
+            if (mState == PlaybackState.STATE_PAUSED) {
+                mState = PlaybackState.STATE_STOPPED;
+            }
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+
+                // set the current index on queue from the music Id:
+                mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, queueId);
+
+                // play the music
+                handlePlayRequest();
+            }
+        }
+
+        @Override
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+            LogHelper.d(TAG, "playFromMediaId mediaId:", mediaId, "  extras=", extras);
+
+            if (mState == PlaybackState.STATE_PAUSED) {
+                mState = PlaybackState.STATE_STOPPED;
+            }
+
+            // The mediaId used here is not the unique musicId. This one comes from the
+            // MediaBrowser, and is actually a "hierarchy-aware mediaID": a concatenation of
+            // the hierarchy in MediaBrowser and the actual unique musicID. This is necessary
+            // so we can build the correct playing queue, based on where the track was
+            // selected from.
+            mPlayingQueue = QueueHelper.getPlayingQueue(mediaId, mMusicProvider);
+            mSession.setQueue(mPlayingQueue);
+            String queueTitle = getString(R.string.browse_musics_by_genre_subtitle,
+                    MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaId));
+            mSession.setQueueTitle(queueTitle);
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+                String uniqueMusicID = MediaIDHelper.extractMusicIDFromMediaID(mediaId);
+
+                // set the current index on queue from the music Id:
+                mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(
+                        mPlayingQueue, uniqueMusicID);
+
+                // play the music
+                handlePlayRequest();
+            }
+        }
+
+        @Override
+        public void onPause() {
+            LogHelper.d(TAG, "pause. current state=" + mState);
+            handlePauseRequest();
+        }
+
+        @Override
+        public void onStop() {
+            LogHelper.d(TAG, "stop. current state=" + mState);
+            handleStopRequest(null);
+        }
+
+        @Override
+        public void onSkipToNext() {
+            LogHelper.d(TAG, "skipToNext");
+            mCurrentIndexOnQueue++;
+            if (mPlayingQueue != null && mCurrentIndexOnQueue >= mPlayingQueue.size()) {
+                mCurrentIndexOnQueue = 0;
+            }
+            if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+                mState = PlaybackState.STATE_STOPPED;
+                handlePlayRequest();
+            } else {
+                LogHelper.e(TAG, "skipToNext: cannot skip to next. next Index=" +
+                        mCurrentIndexOnQueue + " queue length=" +
+                        (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
+                handleStopRequest("Cannot skip");
+            }
+        }
+
+        @Override
+        public void onSkipToPrevious() {
+            LogHelper.d(TAG, "skipToPrevious");
+            mCurrentIndexOnQueue--;
+            if (mPlayingQueue != null && mCurrentIndexOnQueue < 0) {
+                // This sample's behavior: skipping to previous when in first song restarts the
+                // first song.
+                mCurrentIndexOnQueue = 0;
+            }
+            if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+                mState = PlaybackState.STATE_STOPPED;
+                handlePlayRequest();
+            } else {
+                LogHelper.e(TAG, "skipToPrevious: cannot skip to previous. previous Index=" +
+                        mCurrentIndexOnQueue + " queue length=" +
+                        (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
+                handleStopRequest("Cannot skip");
+            }
+        }
+
+        @Override
+        public void onCustomAction(String action, Bundle extras) {
+            if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
+                LogHelper.i(TAG, "onCustomAction: favorite for current track");
+                MediaMetadata track = getCurrentPlayingMusic();
+                if (track != null) {
+                    String mediaId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+                    mMusicProvider.setFavorite(mediaId, !mMusicProvider.isFavorite(mediaId));
+                }
+                updatePlaybackState(null);
+            } else {
+                LogHelper.e(TAG, "Unsupported action: ", action);
+            }
+
+        }
+
+        @Override
+        public void onPlayFromSearch(String query, Bundle extras) {
+            LogHelper.d(TAG, "playFromSearch  query=", query);
+
+            if (mState == PlaybackState.STATE_PAUSED) {
+                mState = PlaybackState.STATE_STOPPED;
+            }
+
+            mPlayingQueue = QueueHelper.getPlayingQueueFromSearch(query, mMusicProvider);
+            LogHelper.d(TAG, "playFromSearch  playqueue.length=" + mPlayingQueue.size());
+            mSession.setQueue(mPlayingQueue);
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+                // start playing from the beginning of the queue
+                mCurrentIndexOnQueue = 0;
+
+                handlePlayRequest();
+            }
+        }
+    }
+
+
+
+    /*
+     * Called when media player is done playing current song.
+     * @see android.media.MediaPlayer.OnCompletionListener
+     */
+    @Override
+    public void onCompletion(MediaPlayer player) {
+        LogHelper.d(TAG, "onCompletion from MediaPlayer");
+        // The media player finished playing the current song, so we go ahead
+        // and start the next.
+        if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+            // In this sample, we restart the playing queue when it gets to the end:
+            mCurrentIndexOnQueue++;
+            if (mCurrentIndexOnQueue >= mPlayingQueue.size()) {
+                mCurrentIndexOnQueue = 0;
+            }
+            handlePlayRequest();
+        } else {
+            // If there is nothing to play, we stop and release the resources:
+            handleStopRequest(null);
+        }
+    }
+
+    /*
+     * Called when media player is done preparing.
+     * @see android.media.MediaPlayer.OnPreparedListener
+     */
+    @Override
+    public void onPrepared(MediaPlayer player) {
+        LogHelper.d(TAG, "onPrepared from MediaPlayer");
+        // The media player is done preparing. That means we can start playing if we
+        // have audio focus.
+        configMediaPlayerState();
+    }
+
+    /**
+     * Called when there's an error playing media. When this happens, the media
+     * player goes to the Error state. We warn the user about the error and
+     * reset the media player.
+     *
+     * @see android.media.MediaPlayer.OnErrorListener
+     */
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        LogHelper.e(TAG, "Media player error: what=" + what + ", extra=" + extra);
+        handleStopRequest("MediaPlayer error " + what + " (" + extra + ")");
+        return true; // true indicates we handled the error
+    }
+
+
+
+
+    /**
+     * Called by AudioManager on audio focus changes.
+     */
+    @Override
+    public void onAudioFocusChange(int focusChange) {
+        LogHelper.d(TAG, "onAudioFocusChange. focusChange=" + focusChange);
+        if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+            // We have gained focus:
+            mAudioFocus = AudioFocus.Focused;
+
+        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
+                focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
+                focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+            // We have lost focus. If we can duck (low playback volume), we can keep playing.
+            // Otherwise, we need to pause the playback.
+            boolean canDuck = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
+            mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck : AudioFocus.NoFocusNoDuck;
+
+            // If we are playing, we need to reset media player by calling configMediaPlayerState
+            // with mAudioFocus properly set.
+            if (mState == PlaybackState.STATE_PLAYING && !canDuck) {
+                // If we don't have audio focus and can't duck, we save the information that
+                // we were playing, so that we can resume playback once we get the focus back.
+                mPlayOnFocusGain = true;
+            }
+        } else {
+            LogHelper.e(TAG, "onAudioFocusChange: Ignoring unsupported focusChange: " + focusChange);
+        }
+
+        configMediaPlayerState();
+    }
+
+
+
+    /**
+     * Handle a request to play music
+     */
+    private void handlePlayRequest() {
+        LogHelper.d(TAG, "handlePlayRequest: mState=" + mState);
+
+        mDelayedStopHandler.removeCallbacksAndMessages(null);
+        if (!mServiceStarted) {
+            LogHelper.v(TAG, "Starting service");
+            // The MusicService needs to keep running even after the calling MediaBrowser
+            // is disconnected. Call startService(Intent) and then stopSelf(..) when we no longer
+            // need to play media.
+            startService(new Intent(getApplicationContext(), MusicService.class));
+            mServiceStarted = true;
+        }
+
+        mPlayOnFocusGain = true;
+        tryToGetAudioFocus();
+
+        if (!mSession.isActive()) {
+            mSession.setActive(true);
+        }
+
+        // actually play the song
+        if (mState == PlaybackState.STATE_PAUSED) {
+            // If we're paused, just continue playback and restore the
+            // 'foreground service' state.
+            configMediaPlayerState();
+        } else {
+            // If we're stopped or playing a song,
+            // just go ahead to the new song and (re)start playing
+            playCurrentSong();
+        }
+    }
+
+
+    /**
+     * Handle a request to pause music
+     */
+    private void handlePauseRequest() {
+        LogHelper.d(TAG, "handlePauseRequest: mState=" + mState);
+
+        if (mState == PlaybackState.STATE_PLAYING) {
+            // Pause media player and cancel the 'foreground service' state.
+            mState = PlaybackState.STATE_PAUSED;
+            if (mMediaPlayer.isPlaying()) {
+                mMediaPlayer.pause();
+            }
+            // while paused, retain the MediaPlayer but give up audio focus
+            relaxResources(false);
+            giveUpAudioFocus();
+        }
+        updatePlaybackState(null);
+    }
+
+    /**
+     * Handle a request to stop music
+     */
+    private void handleStopRequest(String withError) {
+        LogHelper.d(TAG, "handleStopRequest: mState=" + mState + " error=", withError);
+        mState = PlaybackState.STATE_STOPPED;
+
+        // let go of all resources...
+        relaxResources(true);
+        giveUpAudioFocus();
+        updatePlaybackState(withError);
+
+        mMediaNotificationManager.stopNotification();
+
+        // service is no longer necessary. Will be started again if needed.
+        stopSelf();
+        mServiceStarted = false;
+    }
+
+    /**
+     * Releases resources used by the service for playback. This includes the
+     * "foreground service" status, the wake locks and possibly the MediaPlayer.
+     *
+     * @param releaseMediaPlayer Indicates whether the Media Player should also
+     *            be released or not
+     */
+    private void relaxResources(boolean releaseMediaPlayer) {
+        LogHelper.d(TAG, "relaxResources. releaseMediaPlayer=" + releaseMediaPlayer);
+        // stop being a foreground service
+        stopForeground(true);
+
+        // reset the delayed stop handler.
+        mDelayedStopHandler.removeCallbacksAndMessages(null);
+        mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
+
+        // stop and release the Media Player, if it's available
+        if (releaseMediaPlayer && mMediaPlayer != null) {
+            mMediaPlayer.reset();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+
+        // we can also release the Wifi lock, if we're holding it
+        if (mWifiLock.isHeld()) {
+            mWifiLock.release();
+        }
+    }
+
+    /**
+     * Reconfigures MediaPlayer according to audio focus settings and
+     * starts/restarts it. This method starts/restarts the MediaPlayer
+     * respecting the current audio focus state. So if we have focus, it will
+     * play normally; if we don't have focus, it will either leave the
+     * MediaPlayer paused or set it to a low volume, depending on what is
+     * allowed by the current focus settings. This method assumes mPlayer !=
+     * null, so if you are calling it, you have to do so from a context where
+     * you are sure this is the case.
+     */
+    private void configMediaPlayerState() {
+        LogHelper.d(TAG, "configAndStartMediaPlayer. mAudioFocus=" + mAudioFocus);
+        if (mAudioFocus == AudioFocus.NoFocusNoDuck) {
+            // If we don't have audio focus and can't duck, we have to pause,
+            if (mState == PlaybackState.STATE_PLAYING) {
+                handlePauseRequest();
+            }
+        } else {  // we have audio focus:
+            if (mAudioFocus == AudioFocus.NoFocusCanDuck) {
+                mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK); // we'll be relatively quiet
+            } else {
+                mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL); // we can be loud again
+            }
+            // If we were playing when we lost focus, we need to resume playing.
+            if (mPlayOnFocusGain) {
+                if (!mMediaPlayer.isPlaying()) {
+                    LogHelper.d(TAG, "configAndStartMediaPlayer startMediaPlayer.");
+                    mMediaPlayer.start();
+                }
+                mPlayOnFocusGain = false;
+                mState = PlaybackState.STATE_PLAYING;
+            }
+        }
+        updatePlaybackState(null);
+    }
+
+    /**
+     * Makes sure the media player exists and has been reset. This will create
+     * the media player if needed, or reset the existing media player if one
+     * already exists.
+     */
+    private void createMediaPlayerIfNeeded() {
+        LogHelper.d(TAG, "createMediaPlayerIfNeeded. needed? " + (mMediaPlayer==null));
+        if (mMediaPlayer == null) {
+            mMediaPlayer = new MediaPlayer();
+
+            // Make sure the media player will acquire a wake-lock while
+            // playing. If we don't do that, the CPU might go to sleep while the
+            // song is playing, causing playback to stop.
+            mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+
+            // we want the media player to notify us when it's ready preparing,
+            // and when it's done playing:
+            mMediaPlayer.setOnPreparedListener(this);
+            mMediaPlayer.setOnCompletionListener(this);
+            mMediaPlayer.setOnErrorListener(this);
+        } else {
+            mMediaPlayer.reset();
+        }
+    }
+
+    /**
+     * Starts playing the current song in the playing queue.
+     */
+    void playCurrentSong() {
+        MediaMetadata track = getCurrentPlayingMusic();
+        if (track == null) {
+            LogHelper.e(TAG, "playSong:  ignoring request to play next song, because cannot" +
+                    " find it." +
+                    " currentIndex=" + mCurrentIndexOnQueue +
+                    " playQueue.size=" + (mPlayingQueue==null?"null": mPlayingQueue.size()));
+            return;
+        }
+        String source = track.getString(MusicProvider.CUSTOM_METADATA_TRACK_SOURCE);
+        LogHelper.d(TAG, "playSong:  current (" + mCurrentIndexOnQueue + ") in playingQueue. " +
+                " musicId=" + track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) +
+                " source=" + source);
+
+        mState = PlaybackState.STATE_STOPPED;
+        relaxResources(false); // release everything except MediaPlayer
+
+        try {
+            createMediaPlayerIfNeeded();
+
+            mState = PlaybackState.STATE_BUFFERING;
+
+            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mMediaPlayer.setDataSource(source);
+
+            // Starts preparing the media player in the background. When
+            // it's done, it will call our OnPreparedListener (that is,
+            // the onPrepared() method on this class, since we set the
+            // listener to 'this'). Until the media player is prepared,
+            // we *cannot* call start() on it!
+            mMediaPlayer.prepareAsync();
+
+            // If we are streaming from the internet, we want to hold a
+            // Wifi lock, which prevents the Wifi radio from going to
+            // sleep while the song is playing.
+            mWifiLock.acquire();
+
+            updatePlaybackState(null);
+            updateMetadata();
+
+        } catch (IOException ex) {
+            LogHelper.e(TAG, ex, "IOException playing song");
+            updatePlaybackState(ex.getMessage());
+        }
+    }
+
+
+
+    private void updateMetadata() {
+        if (!QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+            LogHelper.e(TAG, "Can't retrieve current metadata.");
+            mState = PlaybackState.STATE_ERROR;
+            updatePlaybackState(getResources().getString(R.string.error_no_metadata));
+            return;
+        }
+        MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
+        String mediaId = queueItem.getDescription().getMediaId();
+        MediaMetadata track = mMusicProvider.getMusic(mediaId);
+        String trackId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+        if (!mediaId.equals(trackId)) {
+            throw new IllegalStateException("track ID (" + trackId + ") " +
+                    "should match mediaId (" + mediaId + ")");
+        }
+        LogHelper.d(TAG, "Updating metadata for MusicID= " + mediaId);
+        mSession.setMetadata(track);
+    }
+
+
+    /**
+     * Update the current media player state, optionally showing an error message.
+     *
+     * @param error if not null, error message to present to the user.
+     *
+     */
+    private void updatePlaybackState(String error) {
+
+        LogHelper.d(TAG, "updatePlaybackState, setting session playback state to " + mState);
+        long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
+        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+            position = mMediaPlayer.getCurrentPosition();
+        }
+        PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
+                .setActions(getAvailableActions());
+
+        setCustomAction(stateBuilder);
+
+        // If there is an error message, send it to the playback state:
+        if (error != null) {
+            // Error states are really only supposed to be used for errors that cause playback to
+            // stop unexpectedly and persist until the user takes action to fix it.
+            stateBuilder.setErrorMessage(error);
+            mState = PlaybackState.STATE_ERROR;
+        }
+        stateBuilder.setState(mState, position, 1.0f, SystemClock.elapsedRealtime());
+
+        // Set the activeQueueItemId if the current index is valid.
+        if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+            MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+            stateBuilder.setActiveQueueItemId(item.getQueueId());
+        }
+
+        mSession.setPlaybackState(stateBuilder.build());
+
+        if (mState == PlaybackState.STATE_PLAYING || mState == PlaybackState.STATE_PAUSED) {
+            mMediaNotificationManager.startNotification();
+        }
+    }
+
+    private void setCustomAction(PlaybackState.Builder stateBuilder) {
+        MediaMetadata currentMusic = getCurrentPlayingMusic();
+        if (currentMusic != null) {
+            // Set appropriate "Favorite" icon on Custom action:
+            String mediaId = currentMusic.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+            int favoriteIcon = R.drawable.ic_star_off;
+            if (mMusicProvider.isFavorite(mediaId)) {
+                favoriteIcon = R.drawable.ic_star_on;
+            }
+            LogHelper.d(TAG, "updatePlaybackState, setting Favorite custom action of music ",
+                    mediaId, " current favorite=", mMusicProvider.isFavorite(mediaId));
+            stateBuilder.addCustomAction(CUSTOM_ACTION_THUMBS_UP, getString(R.string.favorite),
+                    favoriteIcon);
+        }
+    }
+
+    private long getAvailableActions() {
+        long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
+                PlaybackState.ACTION_PLAY_FROM_SEARCH;
+        if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
+            return actions;
+        }
+        if (mState == PlaybackState.STATE_PLAYING) {
+            actions |= PlaybackState.ACTION_PAUSE;
+        }
+        if (mCurrentIndexOnQueue > 0) {
+            actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+        }
+        if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) {
+            actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
+        }
+        return actions;
+    }
+
+    private MediaMetadata getCurrentPlayingMusic() {
+        if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+            MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+            if (item != null) {
+                LogHelper.d(TAG, "getCurrentPlayingMusic for musicId=",
+                        item.getDescription().getMediaId());
+                return mMusicProvider.getMusic(item.getDescription().getMediaId());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Try to get the system audio focus.
+     */
+    void tryToGetAudioFocus() {
+        LogHelper.d(TAG, "tryToGetAudioFocus");
+        if (mAudioFocus != AudioFocus.Focused) {
+            int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
+                    AudioManager.AUDIOFOCUS_GAIN);
+            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+                mAudioFocus = AudioFocus.Focused;
+            }
+        }
+    }
+
+    /**
+     * Give up the audio focus.
+     */
+    void giveUpAudioFocus() {
+        LogHelper.d(TAG, "giveUpAudioFocus");
+        if (mAudioFocus == AudioFocus.Focused) {
+            if (mAudioManager.abandonAudioFocus(this) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+                mAudioFocus = AudioFocus.NoFocusNoDuck;
+            }
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/PackageValidator.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/PackageValidator.java
new file mode 100644
index 0000000..9e40894
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/PackageValidator.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Process;
+import android.util.Base64;
+import android.util.Log;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+/**
+ * Validates that the calling package is authorized to use this
+ * {@link android.service.media.MediaBrowserService}.
+ */
+public class PackageValidator {
+
+    private static final String TAG = LogHelper.makeLogTag(PackageValidator.class.getSimpleName());
+
+    // Replace with your package whitelist
+    static final byte[][] VALID_PUBLIC_SIGNATURES = new byte[][]{
+        // Android Auto release public key
+        extractKey(
+        "\060\202\003\275\060\202\002\245\240\003\002\001\002\002\011\000\307\217\236\113" +
+        "\223\101\060\006\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060" +
+        "\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061\023\060\021\006\003" +
+        "\125\004\010\014\012\103\141\154\151\146\157\162\156\151\141\061\026\060\024\006" +
+        "\003\125\004\007\014\015\115\157\165\156\164\141\151\156\040\126\151\145\167\061" +
+        "\024\060\022\006\003\125\004\012\014\013\107\157\157\147\154\145\040\111\156\143" +
+        "\056\061\020\060\016\006\003\125\004\013\014\007\101\156\144\162\157\151\144\061" +
+        "\021\060\017\006\003\125\004\003\014\010\147\145\141\162\150\145\141\144\060\036" +
+        "\027\015\061\064\060\065\062\067\062\063\060\065\063\064\132\027\015\064\061\061" +
+        "\060\061\062\062\063\060\065\063\064\132\060\165\061\013\060\011\006\003\125\004" +
+        "\006\023\002\125\123\061\023\060\021\006\003\125\004\010\014\012\103\141\154\151" +
+        "\146\157\162\156\151\141\061\026\060\024\006\003\125\004\007\014\015\115\157\165" +
+        "\156\164\141\151\156\040\126\151\145\167\061\024\060\022\006\003\125\004\012\014" +
+        "\013\107\157\157\147\154\145\040\111\156\143\056\061\020\060\016\006\003\125\004" +
+        "\013\014\007\101\156\144\162\157\151\144\061\021\060\017\006\003\125\004\003\014" +
+        "\010\147\145\141\162\150\145\141\144\060\202\001\042\060\015\006\011\052\206\110" +
+        "\206\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002\202\001" +
+        "\001\000\323\235\027\016\103\110\261\124\114\137\154\023\275\132\145\244\053\270" +
+        "\072\331\362\064\255\257\344\036\317\113\340\340\202\141\366\312\346\142\302\224" +
+        "\356\255\322\203\103\324\175\123\074\107\365\116\045\260\057\246\043\025\344\210" +
+        "\026\012\041\143\125\200\313\142\116\014\144\023\056\334\201\153\335\140\170\015" +
+        "\142\221\156\360\214\131\051\200\362\135\353\076\323\152\137\276\233\272\334\302" +
+        "\001\017\363\347\275\121\142\246\215\150\122\266\337\172\330\376\232\272\004\246" +
+        "\071\300\357\130\024\113\103\244\370\176\227\131\153\046\157\314\105\035\005\114" +
+        "\241\225\204\043\073\024\047\151\341\233\301\034\234\371\000\075\363\131\000\157" +
+        "\276\134\263\321\072\204\120\011\253\060\311\213\035\343\142\156\140\003\367\013" +
+        "\006\156\204\067\024\154\305\246\223\272\301\213\320\125\103\310\046\222\266\360" +
+        "\252\217\170\003\272\222\264\265\051\334\334\202\232\122\222\130\166\231\323\224" +
+        "\254\244\103\360\261\367\055\221\255\050\134\156\133\206\004\372\353\261\014\013" +
+        "\064\076\142\301\115\326\202\121\057\264\052\372\143\020\214\122\154\337\002\003" +
+        "\001\000\001\243\120\060\116\060\035\006\003\125\035\016\004\026\004\024\032\360" +
+        "\137\140\327\256\350\224\211\122\162\131\012\046\201\032\311\327\316\333\060\037" +
+        "\006\003\125\035\043\004\030\060\026\200\024\032\360\137\140\327\256\350\224\211" +
+        "\122\162\131\012\046\201\032\311\327\316\333\060\014\006\003\125\035\023\004\005" +
+        "\060\003\001\001\377\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000" +
+        "\003\202\001\001\000\224\153\003\143\101\017\273\163\101\110\176\144\352\054\077" +
+        "\300\230\175\173\174\114\301\055\173\022\262\206\226\034\226\242\014\111\063\062" +
+        "\343\000\336\240\321\240\217\037\020\170\320\204\002\373\312\200\227\344\113\355" +
+        "\124\061\352\214\155\265\375\046\337\134\224\031\003\334\065\206\355\330\054\101" +
+        "\114\040\053\363\316\150\054\256\155\331\060\042\346\324\063\205\336\231\021\210" +
+        "\241\131\045\026\121\337\327\360\024\021\242\354\133\242\313\075\101\260\100\376" +
+        "\042\061\320\352\103\153\030\200\162\256\302\157\256\323\205\345\331\017\021\256" +
+        "\103\307\346\035\206\313\307\316\051\022\371\267\015\003\201\374\262\014\222\112" +
+        "\120\111\361\002\325\377\250\077\134\301\336\352\317\123\367\122\274\100\377\054" +
+        "\050\016\166\272\161\147\227\142\355\054\022\312\347\276\126\257\323\145\014\267" +
+        "\342\323\362\200\114\303\331\337\041\026\130\177\311\370\126\220\310\263\071\342" +
+        "\027\161\254\225\001\007\115\237\234\351\006\113\232\313\133\044\030\350\320\103" +
+        "\231\023\154\067\003\316\050\016\331\035\253\252\176\207\011\337\145\345\235\026" +
+        "\041"),
+
+        // Android Auto debug public key
+        extractKey(
+        "\060\202\003\275\060\202\002\245\240\003\002\001\002\002\011\000\347\344\006\360" +
+        "\327\303\226\363\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060" +
+        "\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061\023\060\021\006\003" +
+        "\125\004\010\014\012\103\141\154\151\146\157\162\156\151\141\061\026\060\024\006" +
+        "\003\125\004\007\014\015\115\157\165\156\164\141\151\156\040\126\151\145\167\061" +
+        "\024\060\022\006\003\125\004\012\014\013\107\157\157\147\154\145\040\111\156\143" +
+        "\056\061\020\060\016\006\003\125\004\013\014\007\101\156\144\162\157\151\144\061" +
+        "\021\060\017\006\003\125\004\003\014\010\147\145\141\162\150\145\141\144\060\036" +
+        "\027\015\061\064\060\065\062\067\062\063\060\062\065\061\132\027\015\064\061\061" +
+        "\060\061\062\062\063\060\062\065\061\132\060\165\061\013\060\011\006\003\125\004" +
+        "\006\023\002\125\123\061\023\060\021\006\003\125\004\010\014\012\103\141\154\151" +
+        "\146\157\162\156\151\141\061\026\060\024\006\003\125\004\007\014\015\115\157\165" +
+        "\156\164\141\151\156\040\126\151\145\167\061\024\060\022\006\003\125\004\012\014" +
+        "\013\107\157\157\147\154\145\040\111\156\143\056\061\020\060\016\006\003\125\004" +
+        "\013\014\007\101\156\144\162\157\151\144\061\021\060\017\006\003\125\004\003\014" +
+        "\010\147\145\141\162\150\145\141\144\060\202\001\042\060\015\006\011\052\206\110" +
+        "\206\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002\202\001" +
+        "\001\000\242\356\360\300\022\205\313\071\352\245\032\336\264\235\304\126\236\171" +
+        "\375\212\364\343\320\040\347\011\106\276\260\247\214\203\374\016\263\053\123\353" +
+        "\044\174\247\265\016\154\051\260\263\155\236\030\142\064\177\211\323\115\013\242" +
+        "\115\341\163\310\335\130\247\212\072\212\163\050\140\315\274\277\307\276\164\273" +
+        "\321\234\244\333\250\043\366\073\114\060\174\375\331\246\135\246\154\003\353\261" +
+        "\115\231\071\106\330\121\021\257\344\360\060\076\132\201\243\347\260\124\166\316" +
+        "\126\272\272\005\057\034\154\363\353\226\003\306\220\231\261\017\323\243\014\203" +
+        "\056\174\140\061\250\057\206\364\276\071\354\167\312\035\205\067\272\111\177\004" +
+        "\264\334\247\106\166\105\217\154\272\237\364\127\246\323\333\071\216\067\231\133" +
+        "\363\267\106\011\312\241\023\310\047\204\013\053\275\036\176\060\031\250\234\201" +
+        "\031\300\331\311\003\060\072\317\274\034\211\047\255\247\374\371\304\131\044\074" +
+        "\352\073\036\353\266\331\174\063\162\206\007\141\005\226\064\351\353\361\162\304" +
+        "\222\347\002\216\220\225\171\373\032\266\032\225\062\064\310\265\075\165\002\003" +
+        "\001\000\001\243\120\060\116\060\035\006\003\125\035\016\004\026\004\024\365\003" +
+        "\311\347\022\104\014\017\014\015\003\053\217\110\146\333\360\066\005\031\060\037" +
+        "\006\003\125\035\043\004\030\060\026\200\024\365\003\311\347\022\104\014\017\014" +
+        "\015\003\053\217\110\146\333\360\066\005\031\060\014\006\003\125\035\023\004\005" +
+        "\060\003\001\001\377\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000" +
+        "\003\202\001\001\000\015\312\371\207\121\121\360\212\146\067\210\122\261\100\075" +
+        "\112\160\220\127\045\332\324\144\041\316\224\040\105\261\176\236\231\040\072\175" +
+        "\214\171\272\174\155\335\274\126\227\340\242\200\366\070\023\120\134\045\034\146" +
+        "\111\373\245\150\376\372\353\175\036\023\233\035\126\225\344\123\140\322\227\103" +
+        "\250\271\332\365\006\175\143\212\022\371\232\342\214\256\364\135\237\304\216\126" +
+        "\024\036\370\156\322\222\043\144\006\303\360\051\202\026\132\060\111\036\171\250" +
+        "\044\243\063\230\222\337\262\331\007\175\222\062\275\101\006\046\053\064\013\347" +
+        "\160\250\330\101\122\274\162\324\321\316\032\115\101\003\301\201\160\100\367\305" +
+        "\345\371\335\103\077\055\064\045\144\056\027\113\054\232\022\234\046\353\337\164" +
+        "\111\305\027\261\357\153\034\377\200\044\075\237\066\253\100\215\302\044\037\035" +
+        "\071\165\160\027\311\234\310\064\101\317\202\121\371\200\351\136\216\201\017\347" +
+        "\306\267\136\150\277\354\346\250\057\061\151\077\117\327\362\140\240\065\342\062" +
+        "\034\277\352\274\040\166\057\126\304\367\374\231\276\323\234\020\276\012\113\027" +
+        "\320"),
+    };
+
+    /**
+     * Disallow instantiation of this helper class.
+     */
+    private PackageValidator() {}
+
+    /**
+     * Throws when the caller is not authorized to get data from this MediaBrowserService
+     */
+    public static void checkCallerAllowed(Context context, String callingPackage, int callingUid) {
+        if (!isCallerAllowed(context, callingPackage, callingUid)) {
+            throw new SecurityException("signature check failed.");
+        }
+    }
+
+    /**
+     * @return false if the caller is not authorized to get data from this MediaBrowserService
+     */
+    public static boolean isCallerAllowed(Context context, String callingPackage, int callingUid) {
+        // Always allow calls from the framework or development environment.
+        if (Process.SYSTEM_UID == callingUid || Process.myUid() == callingUid) {
+            return true;
+        }
+        if (BuildConfig.DEBUG) {
+            // When your app is built in debug mode, any app is allowed to connect to it and browse
+            // its media library. If you want to test the behavior of your app when it gets
+            // released, either build a release version or remove this clause.
+            Log.i(TAG, "Allowing caller '"+callingPackage+" because app was built in debug mode.");
+            return true;
+        }
+        PackageInfo packageInfo;
+        final PackageManager packageManager = context.getPackageManager();
+        try {
+            packageInfo = packageManager.getPackageInfo(
+                    callingPackage, PackageManager.GET_SIGNATURES);
+        } catch (PackageManager.NameNotFoundException ignored) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Package manager can't find package " + callingPackage
+                        + ", defaulting to false");
+            }
+            return false;
+        }
+        if (packageInfo == null) {
+            Log.w(TAG, "Package manager can't find package: " + callingPackage);
+            return false;
+        }
+
+        if (packageInfo.signatures.length != 1) {
+            Log.w(TAG, "Package has more than one signature.");
+            return false;
+        }
+        final byte[] signature = packageInfo.signatures[0].toByteArray();
+
+        for (int i = 0; i < VALID_PUBLIC_SIGNATURES.length; i++) {
+            byte[] validSignature = VALID_PUBLIC_SIGNATURES[i];
+            if (Arrays.equals(validSignature, signature)) {
+                return true;
+            }
+        }
+
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "Signature not valid.  Found: \n" +
+                    Base64.encodeToString(signature, 0));
+        }
+        return false;
+    }
+
+    private static byte[] extractKey(String keyString) {
+        try {
+            return keyString.getBytes("ISO-8859-1");
+        } catch (UnsupportedEncodingException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueAdapter.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueAdapter.java
new file mode 100644
index 0000000..4f24e99
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice;
+
+import android.app.Activity;
+import android.media.session.MediaSession;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A list adapter for items in a queue
+ */
+public class QueueAdapter extends ArrayAdapter<MediaSession.QueueItem> {
+
+    // The currently selected/active queue item Id.
+    private long mActiveQueueItemId = MediaSession.QueueItem.UNKNOWN_ID;
+
+    public QueueAdapter(Activity context) {
+        super(context, R.layout.media_list_item, new ArrayList<MediaSession.QueueItem>());
+    }
+
+    public void setActiveQueueItemId(long id) {
+        this.mActiveQueueItemId = id;
+    }
+
+    private static class ViewHolder {
+        ImageView mImageView;
+        TextView mTitleView;
+        TextView mDescriptionView;
+    }
+
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder holder;
+
+        if (convertView == null) {
+            convertView = LayoutInflater.from(getContext())
+                    .inflate(R.layout.media_list_item, parent, false);
+            holder = new ViewHolder();
+            holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
+            holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
+            holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
+            convertView.setTag(holder);
+        } else {
+            holder = (ViewHolder) convertView.getTag();
+        }
+
+        MediaSession.QueueItem item = getItem(position);
+        holder.mTitleView.setText(item.getDescription().getTitle());
+        if (item.getDescription().getDescription() != null) {
+            holder.mDescriptionView.setText(item.getDescription().getDescription());
+        }
+
+        // If the itemId matches the active Id then use a different icon
+        if (mActiveQueueItemId == item.getQueueId()) {
+            holder.mImageView.setImageDrawable(
+                    getContext().getDrawable(R.drawable.ic_equalizer_white_24dp));
+        } else {
+            holder.mImageView.setImageDrawable(
+                    getContext().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+        }
+        return convertView;
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueFragment.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueFragment.java
new file mode 100644
index 0000000..98b40e7
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueFragment.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ImageButton;
+import android.widget.ListView;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.util.List;
+
+/**
+ * A class that shows the Media Queue to the user.
+ */
+public class QueueFragment extends Fragment {
+
+    private static final String TAG = LogHelper.makeLogTag(QueueFragment.class.getSimpleName());
+
+    private ImageButton mSkipNext;
+    private ImageButton mSkipPrevious;
+    private ImageButton mPlayPause;
+
+    private MediaBrowser mMediaBrowser;
+    private MediaController.TransportControls mTransportControls;
+    private MediaController mMediaController;
+    private PlaybackState mPlaybackState;
+
+    private QueueAdapter mQueueAdapter;
+
+    private MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+        @Override
+        public void onConnected() {
+            LogHelper.d(TAG, "onConnected: session token ", mMediaBrowser.getSessionToken());
+
+            if (mMediaBrowser.getSessionToken() == null) {
+                throw new IllegalArgumentException("No Session token");
+            }
+
+            mMediaController = new MediaController(getActivity(),
+                    mMediaBrowser.getSessionToken());
+            mTransportControls = mMediaController.getTransportControls();
+            mMediaController.registerCallback(mSessionCallback);
+
+            getActivity().setMediaController(mMediaController);
+            mPlaybackState = mMediaController.getPlaybackState();
+
+            List<MediaSession.QueueItem> queue = mMediaController.getQueue();
+            if (queue != null) {
+                mQueueAdapter.clear();
+                mQueueAdapter.notifyDataSetInvalidated();
+                mQueueAdapter.addAll(queue);
+                mQueueAdapter.notifyDataSetChanged();
+            }
+            onPlaybackStateChanged(mPlaybackState);
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            LogHelper.d(TAG, "onConnectionFailed");
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            LogHelper.d(TAG, "onConnectionSuspended");
+            mMediaController.unregisterCallback(mSessionCallback);
+            mTransportControls = null;
+            mMediaController = null;
+            getActivity().setMediaController(null);
+        }
+    };
+
+    // Receive callbacks from the MediaController. Here we update our state such as which queue
+    // is being shown, the current title and description and the PlaybackState.
+    private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+
+        @Override
+        public void onSessionDestroyed() {
+            LogHelper.d(TAG, "Session destroyed. Need to fetch a new Media Session");
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            if (state == null) {
+                return;
+            }
+            LogHelper.d(TAG, "Received playback state change to state ", state.getState());
+            mPlaybackState = state;
+            QueueFragment.this.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
+            LogHelper.d(TAG, "onQueueChanged ", queue);
+            if (queue != null) {
+                mQueueAdapter.clear();
+                mQueueAdapter.notifyDataSetInvalidated();
+                mQueueAdapter.addAll(queue);
+                mQueueAdapter.notifyDataSetChanged();
+            }
+        }
+    };
+
+    public static QueueFragment newInstance() {
+        return new QueueFragment();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_list, container, false);
+
+        mSkipPrevious = (ImageButton) rootView.findViewById(R.id.skip_previous);
+        mSkipPrevious.setEnabled(false);
+        mSkipPrevious.setOnClickListener(mButtonListener);
+
+        mSkipNext = (ImageButton) rootView.findViewById(R.id.skip_next);
+        mSkipNext.setEnabled(false);
+        mSkipNext.setOnClickListener(mButtonListener);
+
+        mPlayPause = (ImageButton) rootView.findViewById(R.id.play_pause);
+        mPlayPause.setEnabled(true);
+        mPlayPause.setOnClickListener(mButtonListener);
+
+        mQueueAdapter = new QueueAdapter(getActivity());
+
+        ListView mListView = (ListView) rootView.findViewById(R.id.list_view);
+        mListView.setAdapter(mQueueAdapter);
+        mListView.setFocusable(true);
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                MediaSession.QueueItem item = mQueueAdapter.getItem(position);
+                mTransportControls.skipToQueueItem(item.getQueueId());
+            }
+        });
+
+        mMediaBrowser = new MediaBrowser(getActivity(),
+                new ComponentName(getActivity(), MusicService.class),
+                mConnectionCallback, null);
+
+        return rootView;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mMediaBrowser != null) {
+            mMediaBrowser.connect();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mSessionCallback);
+        }
+        if (mMediaBrowser != null) {
+            mMediaBrowser.disconnect();
+        }
+    }
+
+
+    private void onPlaybackStateChanged(PlaybackState state) {
+        LogHelper.d(TAG, "onPlaybackStateChanged ", state);
+        if (state == null) {
+            return;
+        }
+        mQueueAdapter.setActiveQueueItemId(state.getActiveQueueItemId());
+        mQueueAdapter.notifyDataSetChanged();
+        boolean enablePlay = false;
+        StringBuilder statusBuilder = new StringBuilder();
+        switch (state.getState()) {
+            case PlaybackState.STATE_PLAYING:
+                statusBuilder.append("playing");
+                enablePlay = false;
+                break;
+            case PlaybackState.STATE_PAUSED:
+                statusBuilder.append("paused");
+                enablePlay = true;
+                break;
+            case PlaybackState.STATE_STOPPED:
+                statusBuilder.append("ended");
+                enablePlay = true;
+                break;
+            case PlaybackState.STATE_ERROR:
+                statusBuilder.append("error: ").append(state.getErrorMessage());
+                break;
+            case PlaybackState.STATE_BUFFERING:
+                statusBuilder.append("buffering");
+                break;
+            case PlaybackState.STATE_NONE:
+                statusBuilder.append("none");
+                enablePlay = false;
+                break;
+            case PlaybackState.STATE_CONNECTING:
+                statusBuilder.append("connecting");
+                break;
+            default:
+                statusBuilder.append(mPlaybackState);
+        }
+        statusBuilder.append(" -- At position: ").append(state.getPosition());
+        LogHelper.d(TAG, statusBuilder.toString());
+
+        if (enablePlay) {
+            mPlayPause.setImageDrawable(
+                    getActivity().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+        } else {
+            mPlayPause.setImageDrawable(getActivity().getDrawable(R.drawable.ic_pause_white_24dp));
+        }
+
+        mSkipPrevious.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
+        mSkipNext.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
+
+        LogHelper.d(TAG, "Queue From MediaController *** Title " +
+                mMediaController.getQueueTitle() + "\n: Queue: " + mMediaController.getQueue() +
+                "\n Metadata " + mMediaController.getMetadata());
+    }
+
+    private View.OnClickListener mButtonListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final int state = mPlaybackState == null ?
+                    PlaybackState.STATE_NONE : mPlaybackState.getState();
+            switch (v.getId()) {
+                case R.id.play_pause:
+                    LogHelper.d(TAG, "Play button pressed, in state " + state);
+                    if (state == PlaybackState.STATE_PAUSED ||
+                            state == PlaybackState.STATE_STOPPED ||
+                            state == PlaybackState.STATE_NONE) {
+                        playMedia();
+                    } else if (state == PlaybackState.STATE_PLAYING) {
+                        pauseMedia();
+                    }
+                    break;
+                case R.id.skip_previous:
+                    LogHelper.d(TAG, "Start button pressed, in state " + state);
+                    skipToPrevious();
+                    break;
+                case R.id.skip_next:
+                    skipToNext();
+                    break;
+            }
+        }
+    };
+
+    private void playMedia() {
+        if (mTransportControls != null) {
+            mTransportControls.play();
+        }
+    }
+
+    private void pauseMedia() {
+        if (mTransportControls != null) {
+            mTransportControls.pause();
+        }
+    }
+
+    private void skipToPrevious() {
+        if (mTransportControls != null) {
+            mTransportControls.skipToPrevious();
+        }
+    }
+
+    private void skipToNext() {
+        if (mTransportControls != null) {
+            mTransportControls.skipToNext();
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
new file mode 100644
index 0000000..6b6a64e
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.mediabrowserservice.model;
+
+import android.media.MediaMetadata;
+import android.os.AsyncTask;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility class to get a list of MusicTrack's based on a server-side JSON
+ * configuration.
+ */
+public class MusicProvider {
+
+    private static final String TAG = LogHelper.makeLogTag(MusicProvider.class.getSimpleName());
+
+    private static final String CATALOG_URL = "http://storage.googleapis.com/automotive-media/music.json";
+
+    public static final String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
+
+    private static String JSON_MUSIC = "music";
+    private static String JSON_TITLE = "title";
+    private static String JSON_ALBUM = "album";
+    private static String JSON_ARTIST = "artist";
+    private static String JSON_GENRE = "genre";
+    private static String JSON_SOURCE = "source";
+    private static String JSON_IMAGE = "image";
+    private static String JSON_TRACK_NUMBER = "trackNumber";
+    private static String JSON_TOTAL_TRACK_COUNT = "totalTrackCount";
+    private static String JSON_DURATION = "duration";
+
+    private final ReentrantLock initializationLock = new ReentrantLock();
+
+    // Categorized caches for music track data:
+    private final HashMap<String, List<MediaMetadata>> mMusicListByGenre;
+    private final HashMap<String, MediaMetadata> mMusicListById;
+
+    private final HashSet<String> mFavoriteTracks;
+
+    enum State {
+        NON_INITIALIZED, INITIALIZING, INITIALIZED;
+    }
+
+    private State mCurrentState = State.NON_INITIALIZED;
+
+
+    public interface Callback {
+        void onMusicCatalogReady(boolean success);
+    }
+
+    public MusicProvider() {
+        mMusicListByGenre = new HashMap<>();
+        mMusicListById = new HashMap<>();
+        mFavoriteTracks = new HashSet<>();
+    }
+
+    /**
+     * Get an iterator over the list of genres
+     *
+     * @return
+     */
+    public Iterable<String> getGenres() {
+        if (mCurrentState != State.INITIALIZED) {
+            return new ArrayList<String>(0);
+        }
+        return mMusicListByGenre.keySet();
+    }
+
+    /**
+     * Get music tracks of the given genre
+     *
+     * @return
+     */
+    public Iterable<MediaMetadata> getMusicsByGenre(String genre) {
+        if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
+            return new ArrayList<MediaMetadata>();
+        }
+        return mMusicListByGenre.get(genre);
+    }
+
+    /**
+     * Very basic implementation of a search that filter music tracks which title containing
+     * the given query.
+     *
+     * @return
+     */
+    public Iterable<MediaMetadata> searchMusics(String titleQuery) {
+        ArrayList<MediaMetadata> result = new ArrayList<>();
+        if (mCurrentState != State.INITIALIZED) {
+            return result;
+        }
+        titleQuery = titleQuery.toLowerCase();
+        for (MediaMetadata track: mMusicListById.values()) {
+            if (track.getString(MediaMetadata.METADATA_KEY_TITLE).toLowerCase()
+                    .contains(titleQuery)) {
+                result.add(track);
+            }
+        }
+        return result;
+    }
+
+    public MediaMetadata getMusic(String mediaId) {
+        return mMusicListById.get(mediaId);
+    }
+
+    public void setFavorite(String mediaId, boolean favorite) {
+        if (favorite) {
+            mFavoriteTracks.add(mediaId);
+        } else {
+            mFavoriteTracks.remove(mediaId);
+        }
+    }
+
+    public boolean isFavorite(String musicId) {
+        return mFavoriteTracks.contains(musicId);
+    }
+
+    public boolean isInitialized() {
+        return mCurrentState == State.INITIALIZED;
+    }
+
+    /**
+     * Get the list of music tracks from a server and caches the track information
+     * for future reference, keying tracks by mediaId and grouping by genre.
+     *
+     * @return
+     */
+    public void retrieveMedia(final Callback callback) {
+
+        if (mCurrentState == State.INITIALIZED) {
+            // Nothing to do, execute callback immediately
+            callback.onMusicCatalogReady(true);
+            return;
+        }
+
+        // Asynchronously load the music catalog in a separate thread
+        new AsyncTask() {
+            @Override
+            protected Object doInBackground(Object[] objects) {
+                retrieveMediaAsync(callback);
+                return null;
+            }
+        }.execute();
+    }
+
+    private void retrieveMediaAsync(Callback callback) {
+        initializationLock.lock();
+
+        try {
+            if (mCurrentState == State.NON_INITIALIZED) {
+                mCurrentState = State.INITIALIZING;
+
+                int slashPos = CATALOG_URL.lastIndexOf('/');
+                String path = CATALOG_URL.substring(0, slashPos + 1);
+                JSONObject jsonObj = parseUrl(CATALOG_URL);
+
+                JSONArray tracks = jsonObj.getJSONArray(JSON_MUSIC);
+                if (tracks != null) {
+                    for (int j = 0; j < tracks.length(); j++) {
+                        MediaMetadata item = buildFromJSON(tracks.getJSONObject(j), path);
+                        String genre = item.getString(MediaMetadata.METADATA_KEY_GENRE);
+                        List<MediaMetadata> list = mMusicListByGenre.get(genre);
+                        if (list == null) {
+                            list = new ArrayList<>();
+                        }
+                        list.add(item);
+                        mMusicListByGenre.put(genre, list);
+                        mMusicListById.put(item.getString(MediaMetadata.METADATA_KEY_MEDIA_ID),
+                                item);
+                    }
+                }
+                mCurrentState = State.INITIALIZED;
+            }
+        } catch (RuntimeException | JSONException e) {
+            LogHelper.e(TAG, e, "Could not retrieve music list");
+        } finally {
+            if (mCurrentState != State.INITIALIZED) {
+                // Something bad happened, so we reset state to NON_INITIALIZED to allow
+                // retries (eg if the network connection is temporary unavailable)
+                mCurrentState = State.NON_INITIALIZED;
+            }
+            initializationLock.unlock();
+            if (callback != null) {
+                callback.onMusicCatalogReady(mCurrentState == State.INITIALIZED);
+            }
+        }
+    }
+
+    private MediaMetadata buildFromJSON(JSONObject json, String basePath) throws JSONException {
+        String title = json.getString(JSON_TITLE);
+        String album = json.getString(JSON_ALBUM);
+        String artist = json.getString(JSON_ARTIST);
+        String genre = json.getString(JSON_GENRE);
+        String source = json.getString(JSON_SOURCE);
+        String iconUrl = json.getString(JSON_IMAGE);
+        int trackNumber = json.getInt(JSON_TRACK_NUMBER);
+        int totalTrackCount = json.getInt(JSON_TOTAL_TRACK_COUNT);
+        int duration = json.getInt(JSON_DURATION) * 1000; // ms
+
+        LogHelper.d(TAG, "Found music track: ", json);
+
+        // Media is stored relative to JSON file
+        if (!source.startsWith("http")) {
+            source = basePath + source;
+        }
+        if (!iconUrl.startsWith("http")) {
+            iconUrl = basePath + iconUrl;
+        }
+        // Since we don't have a unique ID in the server, we fake one using the hashcode of
+        // the music source. In a real world app, this could come from the server.
+        String id = String.valueOf(source.hashCode());
+
+        // Adding the music source to the MediaMetadata (and consequently using it in the
+        // mediaSession.setMetadata) is not a good idea for a real world music app, because
+        // the session metadata can be accessed by notification listeners. This is done in this
+        // sample for convenience only.
+        return new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, id)
+                .putString(CUSTOM_METADATA_TRACK_SOURCE, source)
+                .putString(MediaMetadata.METADATA_KEY_ALBUM, album)
+                .putString(MediaMetadata.METADATA_KEY_ARTIST, artist)
+                .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+                .putString(MediaMetadata.METADATA_KEY_GENRE, genre)
+                .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, iconUrl)
+                .putString(MediaMetadata.METADATA_KEY_TITLE, title)
+                .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, trackNumber)
+                .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, totalTrackCount)
+                .build();
+    }
+
+    /**
+     * Download a JSON file from a server, parse the content and return the JSON
+     * object.
+     *
+     * @param urlString
+     * @return
+     */
+    private JSONObject parseUrl(String urlString) {
+        InputStream is = null;
+        try {
+            java.net.URL url = new java.net.URL(urlString);
+            URLConnection urlConnection = url.openConnection();
+            is = new BufferedInputStream(urlConnection.getInputStream());
+            BufferedReader reader = new BufferedReader(new InputStreamReader(
+                    urlConnection.getInputStream(), "iso-8859-1"));
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+            }
+            return new JSONObject(sb.toString());
+        } catch (Exception e) {
+            LogHelper.e(TAG, "Failed to parse the json for media list", e);
+            return null;
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/BitmapHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/BitmapHelper.java
new file mode 100644
index 0000000..5f0e767
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/BitmapHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class BitmapHelper {
+
+    // Bitmap size for album art in media notifications when there are more than 3 playback actions
+    public static final int MEDIA_ART_SMALL_WIDTH=64;
+    public static final int MEDIA_ART_SMALL_HEIGHT=64;
+
+    // Bitmap size for album art in media notifications when there are no more than 3 playback actions
+    public static final int MEDIA_ART_BIG_WIDTH=128;
+    public static final int MEDIA_ART_BIG_HEIGHT=128;
+
+    public static final Bitmap scaleBitmap(int scaleFactor, InputStream is) {
+        // Get the dimensions of the bitmap
+        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
+
+        // Decode the image file into a Bitmap sized to fill the View
+        bmOptions.inJustDecodeBounds = false;
+        bmOptions.inSampleSize = scaleFactor;
+
+        Bitmap bitmap = BitmapFactory.decodeStream(is, null, bmOptions);
+        return bitmap;
+    }
+
+    public static final int findScaleFactor(int targetW, int targetH, InputStream is) {
+        // Get the dimensions of the bitmap
+        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
+        bmOptions.inJustDecodeBounds = true;
+        BitmapFactory.decodeStream(is, null, bmOptions);
+        int actualW = bmOptions.outWidth;
+        int actualH = bmOptions.outHeight;
+
+        // Determine how much to scale down the image
+        return Math.min(actualW/targetW, actualH/targetH);
+    }
+
+    public static final Bitmap fetchAndRescaleBitmap(String uri, int width, int height)
+            throws IOException {
+        URL url = new URL(uri);
+        HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
+        httpConnection.setDoInput(true);
+        httpConnection.connect();
+        InputStream inputStream = httpConnection.getInputStream();
+        int scaleFactor = findScaleFactor(width, height, inputStream);
+
+        httpConnection = (HttpURLConnection) url.openConnection();
+        httpConnection.setDoInput(true);
+        httpConnection.connect();
+        inputStream = httpConnection.getInputStream();
+        Bitmap bitmap = scaleBitmap(scaleFactor, inputStream);
+        return bitmap;
+    }
+
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/LogHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/LogHelper.java
new file mode 100644
index 0000000..bf5e3a7
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/LogHelper.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.mediabrowserservice.utils;
+
+import android.util.Log;
+
+import com.example.android.mediabrowserservice.BuildConfig;
+
+public class LogHelper {
+    private static final String LOG_PREFIX = "sample_";
+    private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length();
+    private static final int MAX_LOG_TAG_LENGTH = 23;
+
+    public static String makeLogTag(String str) {
+        if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) {
+            return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1);
+        }
+
+        return LOG_PREFIX + str;
+    }
+
+    public static void v(String tag, Object... messages) {
+        // Only log VERBOSE if build type is DEBUG
+        if (BuildConfig.DEBUG) {
+            log(tag, Log.VERBOSE, null, messages);
+        }
+    }
+
+    public static void d(String tag, Object... messages) {
+        // Only log DEBUG if build type is DEBUG
+        if (BuildConfig.DEBUG) {
+            log(tag, Log.DEBUG, null, messages);
+        }
+    }
+
+    public static void i(String tag, Object... messages) {
+        log(tag, Log.INFO, null, messages);
+    }
+
+    public static void w(String tag, Object... messages) {
+        log(tag, Log.WARN, null, messages);
+    }
+
+    public static void w(String tag, Throwable t, Object... messages) {
+        log(tag, Log.WARN, t, messages);
+    }
+
+    public static void e(String tag, Object... messages) {
+        log(tag, Log.ERROR, null, messages);
+    }
+
+    public static void e(String tag, Throwable t, Object... messages) {
+        log(tag, Log.ERROR, t, messages);
+    }
+
+    public static void log(String tag, int level, Throwable t, Object... messages) {
+        if (messages != null && Log.isLoggable(tag, level)) {
+            String message;
+            if (messages.length == 1) {
+                message = messages[0] == null ? null : messages[0].toString();
+            } else {
+                StringBuilder sb = new StringBuilder();
+                for (Object m: messages) {
+                    sb.append(m);
+                }
+                if (t != null) {
+                    sb.append("\n").append(Log.getStackTraceString(t));
+                }
+                message = sb.toString();
+            }
+            Log.println(level, tag, message);
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/MediaIDHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/MediaIDHelper.java
new file mode 100644
index 0000000..ca9dfba
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/MediaIDHelper.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.mediabrowserservice.utils;
+
+import android.media.MediaMetadata;
+
+/**
+ * Utility class to help on queue related tasks.
+ */
+public class MediaIDHelper {
+
+    private static final String TAG = LogHelper.makeLogTag(MediaIDHelper.class.getSimpleName());
+
+    // Media IDs used on browseable items of MediaBrowser
+    public static final String MEDIA_ID_ROOT = "__ROOT__";
+    public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
+
+    public static final String createTrackMediaID(String categoryType, String categoryValue,
+              MediaMetadata track) {
+        // MediaIDs are of the form <categoryType>/<categoryValue>|<musicUniqueId>, to make it easy to
+        // find the category (like genre) that a music was selected from, so we
+        // can correctly build the playing queue. This is specially useful when
+        // one music can appear in more than one list, like "by genre -> genre_1"
+        // and "by artist -> artist_1".
+        return categoryType + "/" + categoryValue + "|" +
+                track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+    }
+
+    public static final String createBrowseCategoryMediaID(String categoryType, String categoryValue) {
+        return categoryType + "/" + categoryValue;
+    }
+
+    /**
+     * Extracts unique musicID from the mediaID. mediaID is, by this sample's convention, a
+     * concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and unique
+     * musicID. This is necessary so we know where the user selected the music from, when the music
+     * exists in more than one music list, and thus we are able to correctly build the playing queue.
+     *
+     * @param musicID
+     * @return
+     */
+    public static final String extractMusicIDFromMediaID(String musicID) {
+        String[] segments = musicID.split("\\|", 2);
+        return segments.length == 2 ? segments[1] : null;
+    }
+
+    /**
+     * Extracts category and categoryValue from the mediaID. mediaID is, by this sample's
+     * convention, a concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and
+     * mediaID. This is necessary so we know where the user selected the music from, when the music
+     * exists in more than one music list, and thus we are able to correctly build the playing queue.
+     *
+     * @param mediaID
+     * @return
+     */
+    public static final String[] extractBrowseCategoryFromMediaID(String mediaID) {
+        if (mediaID.indexOf('|') >= 0) {
+            mediaID = mediaID.split("\\|")[0];
+        }
+        if (mediaID.indexOf('/') == 0) {
+            return new String[]{mediaID, null};
+        } else {
+            return mediaID.split("/", 2);
+        }
+    }
+
+    public static final String extractBrowseCategoryValueFromMediaID(String mediaID) {
+        String[] categoryAndValue = extractBrowseCategoryFromMediaID(mediaID);
+        if (categoryAndValue != null && categoryAndValue.length == 2) {
+            return categoryAndValue[1];
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/QueueHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/QueueHelper.java
new file mode 100644
index 0000000..ba273af
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/QueueHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.mediabrowserservice.utils;
+
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+
+import com.example.android.mediabrowserservice.model.MusicProvider;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+
+/**
+ * Utility class to help on queue related tasks.
+ */
+public class QueueHelper {
+
+    private static final String TAG = LogHelper.makeLogTag(QueueHelper.class.getSimpleName());
+
+    public static final List<MediaSession.QueueItem> getPlayingQueue(String mediaId,
+            MusicProvider musicProvider) {
+
+        // extract the category and unique music ID from the media ID:
+        String[] category = MediaIDHelper.extractBrowseCategoryFromMediaID(mediaId);
+
+        // This sample only supports genre category.
+        if (!category[0].equals(MEDIA_ID_MUSICS_BY_GENRE) || category.length != 2) {
+            LogHelper.e(TAG, "Could not build a playing queue for this mediaId: ", mediaId);
+            return null;
+        }
+
+        String categoryValue = category[1];
+        LogHelper.e(TAG, "Creating playing queue for musics of genre ", categoryValue);
+
+        List<MediaSession.QueueItem> queue = convertToQueue(
+                musicProvider.getMusicsByGenre(categoryValue));
+
+        return queue;
+    }
+
+    public static final List<MediaSession.QueueItem> getPlayingQueueFromSearch(String query,
+            MusicProvider musicProvider) {
+
+        LogHelper.e(TAG, "Creating playing queue for musics from search ", query);
+
+        return convertToQueue(musicProvider.searchMusics(query));
+    }
+
+
+    public static final int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
+             String mediaId) {
+        int index = 0;
+        for (MediaSession.QueueItem item: queue) {
+            if (mediaId.equals(item.getDescription().getMediaId())) {
+                return index;
+            }
+            index++;
+        }
+        return -1;
+    }
+
+    public static final int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
+             long queueId) {
+        int index = 0;
+        for (MediaSession.QueueItem item: queue) {
+            if (queueId == item.getQueueId()) {
+                return index;
+            }
+            index++;
+        }
+        return -1;
+    }
+
+    private static final List<MediaSession.QueueItem> convertToQueue(
+            Iterable<MediaMetadata> tracks) {
+        List<MediaSession.QueueItem> queue = new ArrayList<>();
+        int count = 0;
+        for (MediaMetadata track : tracks) {
+            // We don't expect queues to change after created, so we use the item index as the
+            // queueId. Any other number unique in the queue would work.
+            MediaSession.QueueItem item = new MediaSession.QueueItem(
+                    track.getDescription(), count++);
+            queue.add(item);
+        }
+        return queue;
+
+    }
+
+    /**
+     * Create a random queue. For simplicity sake, instead of a random queue, we create a
+     * queue using the first genre,
+     *
+     * @param musicProvider
+     * @return
+     */
+    public static final List<MediaSession.QueueItem> getRandomQueue(MusicProvider musicProvider) {
+        Iterator<String> genres = musicProvider.getGenres().iterator();
+        if (!genres.hasNext()) {
+            return new ArrayList<>();
+        }
+        String genre = genres.next();
+        Iterable<MediaMetadata> tracks = musicProvider.getMusicsByGenre(genre);
+
+        return convertToQueue(tracks);
+    }
+
+
+
+    public static final boolean isIndexPlayable(int index, List<MediaSession.QueueItem> queue) {
+        return (queue != null && index >= 0 && index < queue.size());
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..05ef6f6
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_notification.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_notification.png
new file mode 100644
index 0000000..d8ea5a9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_notification.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_pause_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
new file mode 100644
index 0000000..b4bdbb5
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..164385d
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_shuffle_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_shuffle_white_24dp.png
new file mode 100644
index 0000000..3eeb0ef
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_shuffle_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png
new file mode 100644
index 0000000..4eaf7ca
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png
new file mode 100644
index 0000000..e59dedb
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-mdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f894fb8
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png
new file mode 100644
index 0000000..dbba844
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..43ade5e
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png
new file mode 100644
index 0000000..14b6d17
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..a55d199
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_shuffle_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_shuffle_white_24dp.png
new file mode 100644
index 0000000..8ce3a60
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_shuffle_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png
new file mode 100644
index 0000000..f282b92
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png
new file mode 100644
index 0000000..2522877
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_by_genre.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_by_genre.png
new file mode 100644
index 0000000..da3b4a7
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_by_genre.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_default_art.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_default_art.png
new file mode 100644
index 0000000..dfb9e67
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_default_art.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png
new file mode 100644
index 0000000..b82a8d9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..3058c27
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png
new file mode 100644
index 0000000..72dfa9f
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..043acd8
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_shuffle_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_shuffle_white_24dp.png
new file mode 100644
index 0000000..718b6b5
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_shuffle_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png
new file mode 100644
index 0000000..4fe6088
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png
new file mode 100644
index 0000000..2c9310a
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_off.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_off.png
new file mode 100644
index 0000000..fb7afb0
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_off.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_on.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_on.png
new file mode 100644
index 0000000..6f7fc75
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_on.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6b4e4a2
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/layout/activity_player.xml b/media/MediaBrowserService/Application/src/main/res/layout/activity_player.xml
new file mode 100644
index 0000000..21cdbbd
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/layout/activity_player.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/container"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             tools:context=".MusicPlayerActivity"
+             tools:ignore="MergeRootFrame" />
diff --git a/media/MediaBrowserService/Application/src/main/res/layout/fragment_list.xml b/media/MediaBrowserService/Application/src/main/res/layout/fragment_list.xml
new file mode 100644
index 0000000..c169fec
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/layout/fragment_list.xml
@@ -0,0 +1,60 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:padding="@dimen/fragment_list_padding">
+
+    <LinearLayout
+        android:id="@+id/controls"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <ImageButton
+            android:id="@+id/skip_previous"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:src="@drawable/ic_skip_previous_white_24dp"
+            android:contentDescription="@string/skip_previous"/>
+
+        <ImageButton
+            android:id="@+id/play_pause"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:src="@drawable/ic_play_arrow_white_24dp"
+            android:contentDescription="@string/play_pause"/>
+
+        <ImageButton
+            android:id="@+id/skip_next"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:src="@drawable/ic_skip_next_white_24dp"
+            android:contentDescription="@string/skip_next"/>
+
+    </LinearLayout>
+
+    <ListView
+        android:id="@+id/list_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    </ListView>
+
+</LinearLayout>
diff --git a/media/MediaBrowserService/Application/src/main/res/layout/media_list_item.xml b/media/MediaBrowserService/Application/src/main/res/layout/media_list_item.xml
new file mode 100644
index 0000000..72c0ccf
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/layout/media_list_item.xml
@@ -0,0 +1,55 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:minHeight="?android:listPreferredItemHeight"
+              android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/play_eq"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:contentDescription="@string/play_item"
+        android:src="@drawable/ic_play_arrow_white_24dp"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:mode="twoLine"
+        android:padding="@dimen/list_item_padding"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/margin_text_view"
+            android:layout_marginTop="@dimen/margin_text_view"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <TextView
+            android:id="@+id/description"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/margin_text_view"
+            android:layout_marginTop="@dimen/margin_text_view"
+            android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/media/MediaBrowserService/Application/src/main/res/values-v21/styles.xml b/media/MediaBrowserService/Application/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..21bb211
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values-v21/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources>
+
+    <style name="AppBaseTheme" parent="android:Theme.Material">
+        <!-- colorPrimary is used for Notification icon and bottom facet bar icons
+        and overflow actions -->
+        <item name="android:colorPrimary">#ffff5722</item>
+
+        <!-- colorPrimaryDark is used for background -->
+        <item name="android:colorPrimaryDark">#ffbf360c</item>
+
+        <!-- colorAccent is sparingly used for accents, like floating action button highlight,
+        progress on playbar-->
+        <item name="android:colorAccent">#ffff5722</item>
+
+    </style>
+
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/dimens.xml b/media/MediaBrowserService/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..e57a8c9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <dimen name="fragment_list_padding">16dp</dimen>
+    <dimen name="list_item_padding">4dp</dimen>
+    <dimen name="margin_text_view">6dp</dimen>
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/strings.xml b/media/MediaBrowserService/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8c88fe3
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+
+    <string name="app_name">MediaBrowserService Sample</string>
+    <string name="favorite">Favorite</string>
+    <string name="error_no_metadata">Unable to retrieve metadata.</string>
+    <string name="browse_genres">Genres</string>
+    <string name="browse_genre_subtitle">Songs by genre</string>
+    <string name="browse_musics_by_genre_subtitle">%1$s songs</string>
+    <string name="random_queue_title">Random music</string>
+    <string name="error_cannot_skip">Cannot skip</string>
+    <string name="error_loading_media">Error Loading Media</string>
+    <string name="play_item">Play item</string>
+    <string name="skip_previous">Skip to previous</string>
+    <string name="play_pause">play or pause</string>
+    <string name="skip_next">Skip to next</string>
+
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/strings_notifications.xml b/media/MediaBrowserService/Application/src/main/res/values/strings_notifications.xml
new file mode 100644
index 0000000..f406ba6
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/strings_notifications.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+
+    <string name="label_pause">Pause</string>
+    <string name="label_play">Play</string>
+    <string name="label_previous">Previous</string>
+    <string name="label_next">Next</string>
+    <string name="error_empty_metadata">Empty metadata!</string>
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/styles.xml b/media/MediaBrowserService/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3be59c1
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<resources>
+
+
+    <style name="AppTheme" parent="AppBaseTheme">
+    </style>
+
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/res/xml/automotive_app_desc.xml b/media/MediaBrowserService/Application/src/main/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..a84750b
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/xml/automotive_app_desc.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<automotiveApp>
+    <uses name="media"/>
+</automotiveApp>
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/media/MediaBrowserService/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to media/MediaBrowserService/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/media/MediaBrowserService/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to media/MediaBrowserService/LICENSE
diff --git a/media/MediaBrowserService/README.md b/media/MediaBrowserService/README.md
new file mode 100644
index 0000000..0072d93
--- /dev/null
+++ b/media/MediaBrowserService/README.md
@@ -0,0 +1,58 @@
+Android MediaBrowserService Sample
+===================================
+
+This sample shows how to implement an audio media app that provides
+media library metadata and playback controls through a standard
+service. It exposes a simple music library through the new
+MediaBrowserService and provides MediaSession callbacks. This allows
+it to be used in Android Auto, for example.
+When not connected to a car, the app has a very simple UI that browses
+the media library and provides simple playback controls. When
+connected to Android Auto, the same service provides data and callback
+to the Android Auto UI in the same manner as it provides them to the
+local UI.
+
+Pre-requisites
+--------------
+
+- Android SDK v21
+- Android Build Tools v21.1.1
+- Android Support Repository
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MediaBrowserService
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License.  You may obtain a copy of
+the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/media/MediaBrowserService/build.gradle b/media/MediaBrowserService/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/media/MediaBrowserService/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/media/MediaBrowserService/buildSrc/build.gradle b/media/MediaBrowserService/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/media/MediaBrowserService/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to media/MediaBrowserService/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties b/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/media/MediaBrowserService/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to media/MediaBrowserService/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/media/MediaBrowserService/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to media/MediaBrowserService/gradlew.bat
diff --git a/media/MediaBrowserService/packaging.yaml b/media/MediaBrowserService/packaging.yaml
new file mode 100644
index 0000000..bcd9ac5
--- /dev/null
+++ b/media/MediaBrowserService/packaging.yaml
@@ -0,0 +1,15 @@
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+
+status:       PUBLISHED
+technologies: [Android]
+categories:   [None]
+languages:    [Java]
+solutions:    [Mobile]
+github:       googlesamples/android-MediaBrowserService
+level:        BEGINNER
+icon:         MediaBrowserServiceSample/src/main/res/drawable-xxhdpi/ic_launcher.png
+license:      apache2-android
diff --git a/media/MediaBrowserService/screenshots/1-main.png b/media/MediaBrowserService/screenshots/1-main.png
new file mode 100644
index 0000000..1b17d0e
--- /dev/null
+++ b/media/MediaBrowserService/screenshots/1-main.png
Binary files differ
diff --git a/media/MediaBrowserService/screenshots/2-music-play.png b/media/MediaBrowserService/screenshots/2-music-play.png
new file mode 100644
index 0000000..1c1439c
--- /dev/null
+++ b/media/MediaBrowserService/screenshots/2-music-play.png
Binary files differ
diff --git a/media/MediaBrowserService/screenshots/3-music-notification.png b/media/MediaBrowserService/screenshots/3-music-notification.png
new file mode 100644
index 0000000..c86c045
--- /dev/null
+++ b/media/MediaBrowserService/screenshots/3-music-notification.png
Binary files differ
diff --git a/media/MediaBrowserService/screenshots/icon-web.png b/media/MediaBrowserService/screenshots/icon-web.png
new file mode 100644
index 0000000..99928a8
--- /dev/null
+++ b/media/MediaBrowserService/screenshots/icon-web.png
Binary files differ
diff --git a/media/MediaBrowserService/settings.gradle b/media/MediaBrowserService/settings.gradle
new file mode 100644
index 0000000..7f11c5f
--- /dev/null
+++ b/media/MediaBrowserService/settings.gradle
@@ -0,0 +1,2 @@
+include 'Application'
+
diff --git a/media/MediaBrowserService/template-params.xml b/media/MediaBrowserService/template-params.xml
new file mode 100644
index 0000000..8f9b2e6
--- /dev/null
+++ b/media/MediaBrowserService/template-params.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>MediaBrowserService</name>
+    <group>Media</group>
+    <package>com.example.android.mediabrowserservice</package>
+
+    <minSdk>21</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+This sample shows how to implement an audio media app that provides
+media library metadata and playback controls through a standard
+service. It exposes a simple music library through the new
+MediaBrowserService and provides MediaSession callbacks. This allows
+it to be used in Android Auto, for example.
+When not connected to a car, the app has a very simple UI that browses
+the media library and provides simple playback controls. When
+connected to Android Auto, the same service provides data and callback
+to the Android Auto UI in the same manner as it provides them to the
+local UI.
+]]></intro>
+    </strings>
+
+    <!-- The basic templates have already been enabled. Uncomment more as desired. -->
+    <template src="basebuild" />
+    <metadata>
+    <status>PUBLISHED</status>
+    <categories>Media</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>ADVANCED</level>
+    <icon>screenshots/icon-web.png</icon>
+    <screenshots>
+        <img>screenshots/1-main.png</img>
+        <img>screenshots/2-music-play.png</img>
+        <img>screenshots/3-music-notification.png</img>
+    </screenshots>
+    <api_refs>
+        <android>android.service.media.MediaBrowserService</android>
+        <android>android.media.browse.MediaBrowser</android>
+        <android>android.media.session.MediaSession</android>
+        <android>android.media.session.MediaController</android>
+        <android>android.app.Notification.MediaStyle</android>
+    </api_refs>
+    <description>
+<![CDATA[
+This sample shows how to implement an audio media app that provides
+media library metadata and playback controls through a standard
+service. It exposes a simple music library through the new
+MediaBrowserService and provides MediaSession callbacks. This allows
+it to be used in Android Auto, for example.
+When not connected to a car, the app has a very simple UI that browses
+the media library and provides simple playback controls. When
+connected to Android Auto, the same service provides data and callback
+to the Android Auto UI in the same manner as it provides them to the
+local UI.
+]]></description>
+    <intro>
+<![CDATA[
+To implement a MediaBrowserService, you need to:
+
+- Extend android.service.media.MediaBrowserService, implementing the media
+  browsing related methods onGetRoot and onLoadChildren;
+
+- In onCreate, start a new MediaSession and call super.setSessionToken() with
+  this MediaSession's token;
+
+- Set a MediaSession.Callback class on the MediaSession. The callback class
+  will receive all the user's actions, like play, pause, etc;
+
+- Handle all the actual music playing using any method your app prefers
+  (for example, the Android MediaPlayer class)
+
+- Whenever it changes, update info about the playing item and the playing
+  queue using MediaSession corresponding methods (setMetadata,
+  setPlaybackState, setQueue, setQueueTitle, etc)
+
+- Handle AudioManager focus change events and react appropriately
+  (e.g. pause when audio focus is lost)
+
+
+To make it compatible with Android Auto, you also need to:
+
+- Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+  with a automotiveApp root element. For a media app, this must include
+  an &lt;uses name="media"/&gt; element as a child.
+
+  For example, in AndroidManifest.xml:
+```
+     <meta-data android:name="com.google.android.gms.car.application"
+       android:resource="@xml/automotive_app_desc"/>
+```
+
+  And in res/xml/automotive\_app\_desc.xml:
+```
+      <?xml version="1.0" encoding="utf-8"?>
+      <automotiveApp>
+          <uses name="media"/>
+      </automotiveApp>
+```
+
+- Declare and export the service in AndroidManifest.xml:
+```
+    <service
+        android:name=".service.MusicService"
+        android:exported="true">
+      <intent-filter>
+         <action android:name="android.media.browse.MediaBrowserService" />
+      </intent-filter>
+    </service>
+```
+]]></intro>
+    </metadata>
+</sample>
diff --git a/media/MediaEffects/MediaEffectsSample/.gitignore b/media/MediaEffects/Application/.gitignore
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/.gitignore
rename to media/MediaEffects/Application/.gitignore
diff --git a/media/MediaEffects/Application/README-fragmentview.txt b/media/MediaEffects/Application/README-fragmentview.txt
new file mode 100644
index 0000000..38d903f
--- /dev/null
+++ b/media/MediaEffects/Application/README-fragmentview.txt
@@ -0,0 +1,37 @@
+<!--
+        Copyright 2013 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+-->
+
+Steps to implement FragmentView template:
+-in template-params.xml.ftl:
+    -add the following line to common imports
+        <common src="activities"/>
+
+-Add a Fragment to show behavior.  In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+   For instance, if your package name is com.example.foo, create the file
+   src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/fragmentViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/media/MediaEffects/MediaEffectsSample/proguard-project.txt b/media/MediaEffects/Application/proguard-project.txt
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/proguard-project.txt
rename to media/MediaEffects/Application/proguard-project.txt
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/AndroidManifest.xml b/media/MediaEffects/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/AndroidManifest.xml
rename to media/MediaEffects/Application/src/main/AndroidManifest.xml
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/java/com/example/android/mediaeffects/GLToolbox.java b/media/MediaEffects/Application/src/main/java/com/example/android/mediaeffects/GLToolbox.java
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/java/com/example/android/mediaeffects/GLToolbox.java
rename to media/MediaEffects/Application/src/main/java/com/example/android/mediaeffects/GLToolbox.java
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/java/com/example/android/mediaeffects/MediaEffectsFragment.java b/media/MediaEffects/Application/src/main/java/com/example/android/mediaeffects/MediaEffectsFragment.java
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/java/com/example/android/mediaeffects/MediaEffectsFragment.java
rename to media/MediaEffects/Application/src/main/java/com/example/android/mediaeffects/MediaEffectsFragment.java
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/java/com/example/android/mediaeffects/TextureRenderer.java b/media/MediaEffects/Application/src/main/java/com/example/android/mediaeffects/TextureRenderer.java
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/java/com/example/android/mediaeffects/TextureRenderer.java
rename to media/MediaEffects/Application/src/main/java/com/example/android/mediaeffects/TextureRenderer.java
diff --git a/media/MediaEffects/Application/src/main/res/drawable-hdpi/ic_launcher.png b/media/MediaEffects/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..950d67b
--- /dev/null
+++ b/media/MediaEffects/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaEffects/Application/src/main/res/drawable-mdpi/ic_launcher.png b/media/MediaEffects/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..cf01e9a
--- /dev/null
+++ b/media/MediaEffects/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-nodpi/puppy.jpg b/media/MediaEffects/Application/src/main/res/drawable-nodpi/puppy.jpg
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/res/drawable-nodpi/puppy.jpg
rename to media/MediaEffects/Application/src/main/res/drawable-nodpi/puppy.jpg
Binary files differ
diff --git a/media/MediaEffects/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/media/MediaEffects/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..b66c0fd
--- /dev/null
+++ b/media/MediaEffects/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaEffects/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/media/MediaEffects/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..1e0344e
--- /dev/null
+++ b/media/MediaEffects/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/layout/fragment_media_effects.xml b/media/MediaEffects/Application/src/main/res/layout/fragment_media_effects.xml
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/res/layout/fragment_media_effects.xml
rename to media/MediaEffects/Application/src/main/res/layout/fragment_media_effects.xml
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/menu/media_effects.xml b/media/MediaEffects/Application/src/main/res/menu/media_effects.xml
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/res/menu/media_effects.xml
rename to media/MediaEffects/Application/src/main/res/menu/media_effects.xml
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/values/strings.xml b/media/MediaEffects/Application/src/main/res/values/strings.xml
similarity index 100%
rename from media/MediaEffects/MediaEffectsSample/src/main/res/values/strings.xml
rename to media/MediaEffects/Application/src/main/res/values/strings.xml
diff --git a/media/MediaEffects/MediaEffectsSample/README-fragmentview.txt b/media/MediaEffects/MediaEffectsSample/README-fragmentview.txt
deleted file mode 100644
index 8853b23..0000000
--- a/media/MediaEffects/MediaEffectsSample/README-fragmentview.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
-        Copyright 2014 The Android Open Source Project
-
-        Licensed under the Apache License, Version 2.0 (the "License");
-        you may not use this file except in compliance with the License.
-        You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-        Unless required by applicable law or agreed to in writing, software
-        distributed under the License is distributed on an "AS IS" BASIS,
-        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-        See the License for the specific language governing permissions and
-        limitations under the License.
--->
-
-Steps to implement FragmentView template:
--in template-params.xml.ftl:
-    -add the following line to common imports
-        <common src="activities"/>
-
--Add a Fragment to show behavior.  In your MainActivity.java class, it will reference a Fragment
- called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
- folder instead of "common" or "templates".
-   For instance, if your package name is com.example.foo, create the file
-   src/main/java/com/example/foo/FooFragment.java
-
-
--Within this fragment, make sure that the onCreate method has the line
- "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
-
--In order to override menu events, override onOptionsItemSelected.
-
--refer to sampleSamples/fragmentViewSample for a reference implementation of a
-project built on this template.
-
-
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-hdpi/ic_launcher.png b/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 960dc8e..0000000
--- a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-mdpi/ic_launcher.png b/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 9356f26..0000000
--- a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-xhdpi/ic_launcher.png b/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 230d558..0000000
--- a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-xxhdpi/ic_launcher.png b/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index c825d4e..0000000
--- a/media/MediaEffects/MediaEffectsSample/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/media/MediaEffects/gradle/wrapper/gradle-wrapper.properties b/media/MediaEffects/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/MediaEffects/gradle/wrapper/gradle-wrapper.properties
+++ b/media/MediaEffects/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/MediaEffects/screenshots/duotone.png b/media/MediaEffects/screenshots/duotone.png
new file mode 100644
index 0000000..331c1a1
--- /dev/null
+++ b/media/MediaEffects/screenshots/duotone.png
Binary files differ
diff --git a/media/MediaEffects/screenshots/icon-web.png b/media/MediaEffects/screenshots/icon-web.png
new file mode 100644
index 0000000..e04452c
--- /dev/null
+++ b/media/MediaEffects/screenshots/icon-web.png
Binary files differ
diff --git a/media/MediaEffects/screenshots/menu.png b/media/MediaEffects/screenshots/menu.png
new file mode 100644
index 0000000..851cda5
--- /dev/null
+++ b/media/MediaEffects/screenshots/menu.png
Binary files differ
diff --git a/media/MediaEffects/settings.gradle b/media/MediaEffects/settings.gradle
index f678548..9464a35 100644
--- a/media/MediaEffects/settings.gradle
+++ b/media/MediaEffects/settings.gradle
@@ -1,4 +1 @@
-
-
-
-include 'MediaEffectsSample'
+include 'Application'
diff --git a/media/MediaEffects/template-params.xml b/media/MediaEffects/template-params.xml
index ba17a4b..8d4db61 100644
--- a/media/MediaEffects/template-params.xml
+++ b/media/MediaEffects/template-params.xml
@@ -42,4 +42,46 @@
     <template src="FragmentView"/>
     <common src="logger"/>
     <common src="activities"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Media, OpenGL</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/menu.png</img>
+            <img>screenshots/duotone.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.media.effect.Effect</android>
+            <android>android.media.effect.EffectContext</android>
+            <android>android.media.effect.EffectFactory</android>
+            <android>android.opengl.GLSurfaceView</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample shows how to use the Media Effects APIs that were
+introduced in Android 4.0.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+The [Media Effects APIs][1] lets you apply effects to image frames
+represented as OpenGL ES 2.0 textures.  Image frames can be images
+loaded from disk, frames from the device's camera, or other video
+streams.
+
+For a list of available effects, refer to [EffectsFactory][2].
+
+[1]: http://developer.android.com/reference/android/media/effect/package-summary.html
+[2]: http://developer.android.com/reference/android/media/effect/EffectFactory.html
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/media/MediaRecorder/Application/src/main/res/drawable-hdpi/ic_launcher.png b/media/MediaRecorder/Application/src/main/res/drawable-hdpi/ic_launcher.png
index 13cd1e8..bea32bc 100644
--- a/media/MediaRecorder/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/media/MediaRecorder/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaRecorder/Application/src/main/res/drawable-mdpi/ic_launcher.png b/media/MediaRecorder/Application/src/main/res/drawable-mdpi/ic_launcher.png
index 00b2bd9..2a3a490 100644
--- a/media/MediaRecorder/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/media/MediaRecorder/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaRecorder/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/media/MediaRecorder/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 953f1cc..9674428 100644
--- a/media/MediaRecorder/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/media/MediaRecorder/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaRecorder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/media/MediaRecorder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index f2ccb10..61e322c 100644
--- a/media/MediaRecorder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/media/MediaRecorder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaRecorder/gradle/wrapper/gradle-wrapper.properties b/media/MediaRecorder/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/media/MediaRecorder/gradle/wrapper/gradle-wrapper.properties
+++ b/media/MediaRecorder/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/MediaRecorder/packaging.yaml b/media/MediaRecorder/packaging.yaml
deleted file mode 100644
index 13cd2e9..0000000
--- a/media/MediaRecorder/packaging.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# GOOGLE SAMPLE PACKAGING DATA
-#
-# This file is used by Google as part of our samples packaging process.
-# End users may safely ignore this file. It has no relevance to other systems.
----
-
-status:       PUBLISHED
-technologies: [Android]
-categories:   [Media]
-languages:    [Java]
-solutions:    [Mobile]
-github:       googlesamples/android-MediaRecorder
-level:        BEGINNER
-icon:         MediaRecorderSample/src/main/res/drawable-xxhdpi/ic_launcher.png
-license:      apache2-android
diff --git a/media/MediaRecorder/screenshots/big_icon.png b/media/MediaRecorder/screenshots/big_icon.png
new file mode 100644
index 0000000..99bd10c
--- /dev/null
+++ b/media/MediaRecorder/screenshots/big_icon.png
Binary files differ
diff --git a/media/MediaRecorder/screenshots/screnshot1.png b/media/MediaRecorder/screenshots/screnshot1.png
new file mode 100644
index 0000000..888c042
--- /dev/null
+++ b/media/MediaRecorder/screenshots/screnshot1.png
Binary files differ
diff --git a/media/MediaRecorder/screenshots/screnshot2.png b/media/MediaRecorder/screenshots/screnshot2.png
new file mode 100644
index 0000000..3ae570e
--- /dev/null
+++ b/media/MediaRecorder/screenshots/screnshot2.png
Binary files differ
diff --git a/media/MediaRecorder/template-params.xml b/media/MediaRecorder/template-params.xml
index 6b6ddc5..fa4a165 100644
--- a/media/MediaRecorder/template-params.xml
+++ b/media/MediaRecorder/template-params.xml
@@ -14,27 +14,62 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
-
-
 <sample>
     <name>MediaRecorder</name>
     <group>Media</group>
     <package>com.example.android.mediarecorder</package>
-
-    <!-- change minSdk if needed-->
     <minSdk>14</minSdk>
-
     <strings>
         <intro>
-            <![CDATA[
-            This sample uses the camera/camcorder as the A/V source for the MediaRecorder API.
-            A TextureView is used as the camera preview which limits the code to API 14+. This
-            can be easily replaced with a SurfaceView to run on older devices.
-            ]]>
+<![CDATA[
+This sample uses the camera/camcorder as the A/V source for the MediaRecorder API.
+A TextureView is used as the camera preview which limits the code to API 14+. This
+can be easily replaced with a SurfaceView to run on older devices.
+]]>
         </intro>
     </strings>
-
     <template src="base"/>
     <common src="media"/>
+        <metadata>
+        <status>PUBLISHED</status>
+        <categories>Media</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/big_icon.png</icon>
+        <screenshots>
+            <img>screenshots/screenshot1.png</img>
+            <img>screenshots/screenshot2.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.hardware.Camera</android>
+            <android>android.media.CamcorderProfile</android>
+            <android>android.media.MediaRecorder</android>
+            <android>android.view.TextureView</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample uses the camera/camcorder as the A/V source for the MediaRecorder API.
+A TextureView is used as the camera preview which limits the code to API 14+. This
+can be easily replaced with a SurfaceView to run on older devices.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample shows how to use the [MediaRecorder][1] API.
+It uses the [Camera][2] as input source and displays a preview on a [TextureView][3]
+The sample features a button to capture the input and stop capturing afterwards.
+
+It demonstrates how to correctly gain control and release the camera.
+The sample also shows how to save the captured audio and video to persistant storage
+and basic error handling.
+
+
+[1]: https://developer.android.com/reference/android/media/MediaRecorder.html
+[2]: https://developer.android.com/reference/android/graphics/Camera.html
+[3]: https://developer.android.com/reference/android/view/TextureView.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/media/MediaRouter/gradle/wrapper/gradle-wrapper.properties b/media/MediaRouter/gradle/wrapper/gradle-wrapper.properties
index 1e61d1f..0c71e76 100644
--- a/media/MediaRouter/gradle/wrapper/gradle-wrapper.properties
+++ b/media/MediaRouter/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/media/MediaRouter/template-params.xml b/media/MediaRouter/template-params.xml
index d31b69f..7792dea 100644
--- a/media/MediaRouter/template-params.xml
+++ b/media/MediaRouter/template-params.xml
@@ -27,8 +27,8 @@
     <!-- change minSdk if needed-->
     <minSdk>13</minSdk>
 
-    <dependency>com.android.support:appcompat-v7:20.+</dependency>
-    <dependency>com.android.support:mediarouter-v7:20.+</dependency>
+    <dependency>com.android.support:appcompat-v7:21.0.2</dependency>
+    <dependency>com.android.support:mediarouter-v7:21.0.2</dependency>
 
 
     <strings>
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/media/ScreenCapture/Application/.gitignore
similarity index 100%
copy from ui/notifications/LNotifications/Application/.gitignore
copy to media/ScreenCapture/Application/.gitignore
diff --git a/media/ScreenCapture/Application/README-fragmentview.txt b/media/ScreenCapture/Application/README-fragmentview.txt
new file mode 100644
index 0000000..38d903f
--- /dev/null
+++ b/media/ScreenCapture/Application/README-fragmentview.txt
@@ -0,0 +1,37 @@
+<!--
+        Copyright 2013 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+-->
+
+Steps to implement FragmentView template:
+-in template-params.xml.ftl:
+    -add the following line to common imports
+        <common src="activities"/>
+
+-Add a Fragment to show behavior.  In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+   For instance, if your package name is com.example.foo, create the file
+   src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/fragmentViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/media/ScreenCapture/Application/README-singleview.txt b/media/ScreenCapture/Application/README-singleview.txt
new file mode 100644
index 0000000..0cacd46
--- /dev/null
+++ b/media/ScreenCapture/Application/README-singleview.txt
@@ -0,0 +1,47 @@
+<#--
+        Copyright 2013 The Android Open Source Project
+
+        Licensed under the Apache License, Version 2.0 (the "License");
+        you may not use this file except in compliance with the License.
+        You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+        Unless required by applicable law or agreed to in writing, software
+        distributed under the License is distributed on an "AS IS" BASIS,
+        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+        See the License for the specific language governing permissions and
+        limitations under the License.
+-->
+
+Steps to implement SingleView template:
+-in template-params.xml.ftl:
+    -add the following line to common imports
+        <common src="activities"/>
+
+    -add a string for the action button's text using the element name "sample_action".
+    This element should be a child of <strings>:
+        <strings>
+        ...
+        <sample_action>ButtonText</sample_action>
+        ...
+        </strings>
+
+
+
+-Add a Fragment to handle behavior.  In your MainActivity.java class, it will reference a Fragment
+ called (yourProjectName)Fragment.java.  Create that file in your project, using the "main" source
+ folder instead of "common" or "templates".
+   For instance, if your package name is com.example.foo, create the file
+   src/main/java/com/example/foo/FooFragment.java
+
+
+-Within this fragment, make sure that the onCreate method has the line
+ "setHasOptionsMenu(true);", to enable the fragment to handle menu events.
+
+-In order to override menu events, override onOptionsItemSelected.
+
+-refer to sampleSamples/singleViewSample for a reference implementation of a
+project built on this template.
+
+
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/media/ScreenCapture/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to media/ScreenCapture/Application/proguard-project.txt
diff --git a/media/ScreenCapture/Application/src/main/AndroidManifest.xml b/media/ScreenCapture/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2dfd4f7
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.screencapture">
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".MainActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/media/ScreenCapture/Application/src/main/java/com/example/android/screencapture/ScreenCaptureFragment.java b/media/ScreenCapture/Application/src/main/java/com/example/android/screencapture/ScreenCaptureFragment.java
new file mode 100644
index 0000000..f134579
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/java/com/example/android/screencapture/ScreenCaptureFragment.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.screencapture;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.example.android.common.logger.Log;
+
+/**
+ * Provides UI for the screen capture.
+ */
+public class ScreenCaptureFragment extends Fragment implements View.OnClickListener {
+
+    private static final String TAG = "ScreenCaptureFragment";
+
+    private static final String STATE_RESULT_CODE = "result_code";
+    private static final String STATE_RESULT_DATA = "result_data";
+
+    private static final int REQUEST_MEDIA_PROJECTION = 1;
+
+    private int mScreenDensity;
+
+    private int mResultCode;
+    private Intent mResultData;
+
+    private Surface mSurface;
+    private MediaProjection mMediaProjection;
+    private VirtualDisplay mVirtualDisplay;
+    private MediaProjectionManager mMediaProjectionManager;
+    private Button mButtonToggle;
+    private SurfaceView mSurfaceView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            mResultCode = savedInstanceState.getInt(STATE_RESULT_CODE);
+            mResultData = savedInstanceState.getParcelable(STATE_RESULT_DATA);
+        }
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_screen_capture, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        mSurfaceView = (SurfaceView) view.findViewById(R.id.surface);
+        mSurface = mSurfaceView.getHolder().getSurface();
+        mButtonToggle = (Button) view.findViewById(R.id.toggle);
+        mButtonToggle.setOnClickListener(this);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        Activity activity = getActivity();
+        DisplayMetrics metrics = new DisplayMetrics();
+        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+        mScreenDensity = metrics.densityDpi;
+        mMediaProjectionManager = (MediaProjectionManager)
+                activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mResultData != null) {
+            outState.putInt(STATE_RESULT_CODE, mResultCode);
+            outState.putParcelable(STATE_RESULT_DATA, mResultData);
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.toggle:
+                if (mVirtualDisplay == null) {
+                    startScreenCapture();
+                } else {
+                    stopScreenCapture();
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_MEDIA_PROJECTION) {
+            if (resultCode != Activity.RESULT_OK) {
+                Log.i(TAG, "User cancelled");
+                Toast.makeText(getActivity(), R.string.user_cancelled, Toast.LENGTH_SHORT).show();
+                return;
+            }
+            Activity activity = getActivity();
+            if (activity == null) {
+                return;
+            }
+            Log.i(TAG, "Starting screen capture");
+            mResultCode = resultCode;
+            mResultData = data;
+            setUpMediaProjection();
+            setUpVirtualDisplay();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        stopScreenCapture();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        tearDownMediaProjection();
+    }
+
+    private void setUpMediaProjection() {
+        mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
+    }
+
+    private void tearDownMediaProjection() {
+        if (mMediaProjection != null) {
+            mMediaProjection.stop();
+            mMediaProjection = null;
+        }
+    }
+
+    private void startScreenCapture() {
+        Activity activity = getActivity();
+        if (mSurface == null || activity == null) {
+            return;
+        }
+        if (mMediaProjection != null) {
+            setUpVirtualDisplay();
+        } else if (mResultCode != 0 && mResultData != null) {
+            setUpMediaProjection();
+            setUpVirtualDisplay();
+        } else {
+            Log.i(TAG, "Requesting confirmation");
+            // This initiates a prompt dialog for the user to confirm screen projection.
+            startActivityForResult(
+                    mMediaProjectionManager.createScreenCaptureIntent(),
+                    REQUEST_MEDIA_PROJECTION);
+        }
+    }
+
+    private void setUpVirtualDisplay() {
+        Log.i(TAG, "Setting up a VirtualDisplay: " +
+                mSurfaceView.getWidth() + "x" + mSurfaceView.getHeight() +
+                " (" + mScreenDensity + ")");
+        mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
+                mSurfaceView.getWidth(), mSurfaceView.getHeight(), mScreenDensity,
+                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+                mSurface, null, null);
+        mButtonToggle.setText(R.string.stop);
+    }
+
+    private void stopScreenCapture() {
+        if (mVirtualDisplay == null) {
+            return;
+        }
+        mVirtualDisplay.release();
+        mVirtualDisplay = null;
+        mButtonToggle.setText(R.string.start);
+    }
+
+}
diff --git a/media/ScreenCapture/Application/src/main/res/drawable-hdpi/ic_launcher.png b/media/ScreenCapture/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..69d518c
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/media/ScreenCapture/Application/src/main/res/drawable-mdpi/ic_launcher.png b/media/ScreenCapture/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c107f97
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/media/ScreenCapture/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/media/ScreenCapture/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..a4d33c2
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/media/ScreenCapture/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/media/ScreenCapture/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fe23a07
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/ScreenCapture/Application/src/main/res/layout/fragment_screen_capture.xml b/media/ScreenCapture/Application/src/main/res/layout/fragment_screen_capture.xml
new file mode 100644
index 0000000..343c920
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/res/layout/fragment_screen_capture.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal">
+
+    <SurfaceView
+        android:id="@+id/surface"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:text="@string/start"/>
+
+</LinearLayout>
diff --git a/media/ScreenCapture/Application/src/main/res/values/strings.xml b/media/ScreenCapture/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..70597e6
--- /dev/null
+++ b/media/ScreenCapture/Application/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <string name="start">Start</string>
+    <string name="stop">Stop</string>
+    <string name="user_cancelled">User denied screen sharing permission</string>
+</resources>
diff --git a/media/ScreenCapture/Application/tests/src/com/example/android/screencapture/test/SampleTests.java b/media/ScreenCapture/Application/tests/src/com/example/android/screencapture/test/SampleTests.java
new file mode 100644
index 0000000..f3f7997
--- /dev/null
+++ b/media/ScreenCapture/Application/tests/src/com/example/android/screencapture/test/SampleTests.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.screencapture.test;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+import android.widget.Button;
+
+import com.example.android.screencapture.MainActivity;
+import com.example.android.screencapture.R;
+import com.example.android.screencapture.ScreenCaptureFragment;
+
+/**
+ * Tests for ScreenCapture sample.
+ */
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private ScreenCaptureFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTestActivity = getActivity();
+        mTestFragment = (ScreenCaptureFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+     * Test if the test fixture has been set up correctly.
+     */
+    public void testPreconditions() {
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    public void testButtonToggle() throws Throwable {
+        final View view = mTestFragment.getView();
+        assertNotNull(view);
+        final Button buttonToggle = (Button) view.findViewById(R.id.toggle);
+        assertNotNull(buttonToggle);
+    }
+
+}
diff --git a/media/ScreenCapture/build.gradle b/media/ScreenCapture/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/media/ScreenCapture/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/media/ScreenCapture/buildSrc/build.gradle b/media/ScreenCapture/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/media/ScreenCapture/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/media/ScreenCapture/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to media/ScreenCapture/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/media/ScreenCapture/gradle/wrapper/gradle-wrapper.properties b/media/ScreenCapture/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/media/ScreenCapture/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/media/ScreenCapture/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to media/ScreenCapture/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/media/ScreenCapture/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to media/ScreenCapture/gradlew.bat
diff --git a/media/ScreenCapture/screenshots/icon-web.png b/media/ScreenCapture/screenshots/icon-web.png
new file mode 100644
index 0000000..6d41a89
--- /dev/null
+++ b/media/ScreenCapture/screenshots/icon-web.png
Binary files differ
diff --git a/media/ScreenCapture/screenshots/main.png b/media/ScreenCapture/screenshots/main.png
new file mode 100644
index 0000000..79fca65
--- /dev/null
+++ b/media/ScreenCapture/screenshots/main.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/media/ScreenCapture/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to media/ScreenCapture/settings.gradle
diff --git a/media/ScreenCapture/template-params.xml b/media/ScreenCapture/template-params.xml
new file mode 100644
index 0000000..956e846
--- /dev/null
+++ b/media/ScreenCapture/template-params.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>ScreenCapture</name>
+    <group>Media</group>
+    <package>com.example.android.screencapture</package>
+
+    <minSdk>21</minSdk>
+
+    <strings>
+        <intro>
+<![CDATA[
+This sample demonstrates how to use Media Projection API introduced in Android 5.0 Lollipop. Press
+"Start" to start capturing the screen.
+]]>
+        </intro>
+    </strings>
+
+    <!-- The basic templates have already been enabled. Uncomment more as desired. -->
+    <template src="base"/>
+    <template src="FragmentView"/>
+    <common src="logger"/>
+    <common src="activities"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Media</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.media.projection.MediaProjection</android>
+            <android>android.media.projection.MediaProjectionManager</android>
+            <android>android.hardware.display.VirtualDisplay</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates how to use Media Projection API to capture device screen in real time and
+show it on a SurfaceView.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+Media Projection API lets you capture the current screen through Surface.
+
+To start media projection, you need to get an instance of [MediaProjection][1]. For this, you have
+to call startActivityForResult with an Intent from
+[MediaProjectionManager.createScreenCaptureIntent()][2]. This shows a confirmation dialog to the
+user. When user confirms it, you will get a result code and data in onActivityResult, so pass those
+to [getMediaProjection][3].
+
+Once you get a MediaProjection, use [createVirtualDisplay][4] and bind it to a Surface.
+
+[1]: https://developer.android.com/reference/android/media/projection/MediaProjection.html
+[2]: https://developer.android.com/reference/android/media/projection/MediaProjectionManager.html#createScreenCaptureIntent()
+[3]: https://developer.android.com/reference/android/media/projection/MediaProjectionManager.html#getMediaProjection(int, android.content.Intent)
+[4]: https://developer.android.com/reference/android/media/projection/MediaProjection.html#createVirtualDisplay(java.lang.String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler)
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/ui/notifications/BasicNotifications/Application/.gitignore b/notification/BasicNotifications/Application/.gitignore
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/.gitignore
rename to notification/BasicNotifications/Application/.gitignore
diff --git a/ui/notifications/BasicNotifications/Application/proguard-project.txt b/notification/BasicNotifications/Application/proguard-project.txt
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/proguard-project.txt
rename to notification/BasicNotifications/Application/proguard-project.txt
diff --git a/ui/notifications/BasicNotifications/Application/src/main/AndroidManifest.xml b/notification/BasicNotifications/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/AndroidManifest.xml
rename to notification/BasicNotifications/Application/src/main/AndroidManifest.xml
diff --git a/notification/BasicNotifications/Application/src/main/big_icon.png b/notification/BasicNotifications/Application/src/main/big_icon.png
new file mode 100755
index 0000000..9eb40ff
--- /dev/null
+++ b/notification/BasicNotifications/Application/src/main/big_icon.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java b/notification/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java
rename to notification/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/layout/sample_layout.xml b/notification/BasicNotifications/Application/src/main/res/layout/sample_layout.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/layout/sample_layout.xml
rename to notification/BasicNotifications/Application/src/main/res/layout/sample_layout.xml
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/values/strings.xml b/notification/BasicNotifications/Application/src/main/res/values/strings.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/values/strings.xml
rename to notification/BasicNotifications/Application/src/main/res/values/strings.xml
diff --git a/ui/notifications/BasicNotifications/Application/tests/AndroidManifest.xml b/notification/BasicNotifications/Application/tests/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/tests/AndroidManifest.xml
rename to notification/BasicNotifications/Application/tests/AndroidManifest.xml
diff --git a/ui/notifications/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java b/notification/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java
rename to notification/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/notification/BasicNotifications/CONTRIB.md
similarity index 100%
rename from ui/notifications/BasicNotifications/CONTRIB.md
rename to notification/BasicNotifications/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/notification/BasicNotifications/LICENSE
similarity index 100%
rename from ui/notifications/BasicNotifications/LICENSE
rename to notification/BasicNotifications/LICENSE
diff --git a/ui/notifications/BasicNotifications/README.md b/notification/BasicNotifications/README.md
similarity index 100%
rename from ui/notifications/BasicNotifications/README.md
rename to notification/BasicNotifications/README.md
diff --git a/notification/BasicNotifications/build.gradle b/notification/BasicNotifications/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/BasicNotifications/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/notification/BasicNotifications/buildSrc/build.gradle b/notification/BasicNotifications/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/notification/BasicNotifications/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
rename to notification/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/notification/BasicNotifications/gradlew
similarity index 100%
rename from ui/notifications/BasicNotifications/gradlew
rename to notification/BasicNotifications/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/notification/BasicNotifications/gradlew.bat
similarity index 100%
rename from ui/notifications/BasicNotifications/gradlew.bat
rename to notification/BasicNotifications/gradlew.bat
diff --git a/ui/notifications/BasicNotifications/packaging.yaml b/notification/BasicNotifications/packaging.yaml
similarity index 100%
rename from ui/notifications/BasicNotifications/packaging.yaml
rename to notification/BasicNotifications/packaging.yaml
diff --git a/notification/BasicNotifications/screenshots/main.png b/notification/BasicNotifications/screenshots/main.png
new file mode 100644
index 0000000..6e65b5e
--- /dev/null
+++ b/notification/BasicNotifications/screenshots/main.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/notification/BasicNotifications/settings.gradle
similarity index 100%
rename from ui/notifications/BasicNotifications/settings.gradle
rename to notification/BasicNotifications/settings.gradle
diff --git a/notification/BasicNotifications/template-params.xml b/notification/BasicNotifications/template-params.xml
new file mode 100644
index 0000000..8d3fb1d
--- /dev/null
+++ b/notification/BasicNotifications/template-params.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<sample>
+    <name>BasicNotifications</name>
+    <group>Notification</group>
+    <package>com.example.android.basicnotifications</package>
+    <!-- change minSdk if needed-->
+    <minSdk>8</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+            This sample demonstrates how to display events in the system\'s notification bar. The
+            NotificationCompat API is used for compatibility with older devices, running Android
+            2.2 (Froyo) or newer.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Notification</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>Application/src/main/big_icon.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.NotificationManager</android>
+            <android>android.support.v4.app.NotificationCompat</android>
+        </api_refs>
+
+        <description>
+A basic app showing how to display events in the system's notification bar using
+the NotificationCompat API.
+NotificationCompat API is used for compatibility with older devices, running Android
+1.6 (Donut) (API level 4) or newer.
+        </description>
+
+        <intro>
+<![CDATA[
+The [Notification API][1] allows the app developers to display a message outside
+of your application's normal UI.
+
+The class [Notification][2] was added in the Android 3.0 (API level 11), but this
+sample refers to the [NotificationCompat][3] class (part of the [support library][4]),
+ which offers the same functionality for Android 1.6 (API level 4) or newer.
+
+A Notificaiton can be created using Notification.Builder object.
+At bare minimum, a Builder object must include the following:
+- A small icon, set by [setSmallIcon()][5]
+- A title, set by [setContentTitle()][6]
+- Detail text, set by [setContentText()][7]
+
+in the code snippet, it looks like following.
+```java
+NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+builder.setSmallIcon(R.drawable.ic_stat_notification);
+builder.setContentTitle("BasicNotifications Sample");
+builder.setContentText("Time to learn about notifications!");
+```
+
+To issue the notification, call notify() method in the [NotificationManager][8].
+The code snippet will immediately display the notification icon in the
+notification bar.
+
+```java
+NotificationManager notificationManager = (NotificationManager) getSystemService(
+        NOTIFICATION_SERVICE);
+notificationManager.notify(NOTIFICATION_ID, builder.build());
+```
+
+[1]: http://developer.android.com/guide/topics/ui/notifiers/notifications.html
+[2]: http://developer.android.com/reference/android/app/Notification.html
+[3]: http://developer.android.com/reference/android/support/v4/app/NotificationCompat.html
+[4]: http://developer.android.com/tools/support-library/index.html
+[5]: http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#setSmallIcon(int)
+[6]: http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#setContentTitle(java.lang.CharSequence) 
+[7]: http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#setContentText(java.lang.CharSequence)
+[8]: http://developer.android.com/reference/android/app/NotificationManager.html
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/ui/notifications/CustomNotifications/Application/.gitignore b/notification/CustomNotifications/Application/.gitignore
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/.gitignore
rename to notification/CustomNotifications/Application/.gitignore
diff --git a/ui/notifications/CustomNotifications/Application/proguard-project.txt b/notification/CustomNotifications/Application/proguard-project.txt
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/proguard-project.txt
rename to notification/CustomNotifications/Application/proguard-project.txt
diff --git a/ui/notifications/CustomNotifications/Application/src/main/AndroidManifest.xml b/notification/CustomNotifications/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/AndroidManifest.xml
rename to notification/CustomNotifications/Application/src/main/AndroidManifest.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java b/notification/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java
rename to notification/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/layout/notification.xml b/notification/CustomNotifications/Application/src/main/res/layout/notification.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/layout/notification.xml
rename to notification/CustomNotifications/Application/src/main/res/layout/notification.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml b/notification/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml
rename to notification/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/layout/sample_main.xml b/notification/CustomNotifications/Application/src/main/res/layout/sample_main.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/layout/sample_main.xml
rename to notification/CustomNotifications/Application/src/main/res/layout/sample_main.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml b/notification/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml
rename to notification/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values-v9/styles.xml b/notification/CustomNotifications/Application/src/main/res/values-v9/styles.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values-v9/styles.xml
rename to notification/CustomNotifications/Application/src/main/res/values-v9/styles.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values/dimens.xml b/notification/CustomNotifications/Application/src/main/res/values/dimens.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values/dimens.xml
rename to notification/CustomNotifications/Application/src/main/res/values/dimens.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values/strings.xml b/notification/CustomNotifications/Application/src/main/res/values/strings.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values/strings.xml
rename to notification/CustomNotifications/Application/src/main/res/values/strings.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values/styles.xml b/notification/CustomNotifications/Application/src/main/res/values/styles.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values/styles.xml
rename to notification/CustomNotifications/Application/src/main/res/values/styles.xml
diff --git a/ui/notifications/CustomNotifications/CONTRIB.md b/notification/CustomNotifications/CONTRIB.md
similarity index 100%
rename from ui/notifications/CustomNotifications/CONTRIB.md
rename to notification/CustomNotifications/CONTRIB.md
diff --git a/ui/notifications/CustomNotifications/LICENSE b/notification/CustomNotifications/LICENSE
similarity index 100%
rename from ui/notifications/CustomNotifications/LICENSE
rename to notification/CustomNotifications/LICENSE
diff --git a/ui/notifications/CustomNotifications/README.md b/notification/CustomNotifications/README.md
similarity index 100%
rename from ui/notifications/CustomNotifications/README.md
rename to notification/CustomNotifications/README.md
diff --git a/notification/CustomNotifications/build.gradle b/notification/CustomNotifications/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/CustomNotifications/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/notification/CustomNotifications/buildSrc/build.gradle b/notification/CustomNotifications/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/notification/CustomNotifications/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.jar
rename to notification/CustomNotifications/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/CustomNotifications/gradlew b/notification/CustomNotifications/gradlew
similarity index 100%
rename from ui/notifications/CustomNotifications/gradlew
rename to notification/CustomNotifications/gradlew
diff --git a/ui/notifications/CustomNotifications/gradlew.bat b/notification/CustomNotifications/gradlew.bat
similarity index 100%
rename from ui/notifications/CustomNotifications/gradlew.bat
rename to notification/CustomNotifications/gradlew.bat
diff --git a/ui/notifications/CustomNotifications/packaging.yaml b/notification/CustomNotifications/packaging.yaml
similarity index 100%
rename from ui/notifications/CustomNotifications/packaging.yaml
rename to notification/CustomNotifications/packaging.yaml
diff --git a/notification/CustomNotifications/screenshots/icon-web.png b/notification/CustomNotifications/screenshots/icon-web.png
new file mode 100644
index 0000000..183e592
--- /dev/null
+++ b/notification/CustomNotifications/screenshots/icon-web.png
Binary files differ
diff --git a/notification/CustomNotifications/screenshots/main-notification.png b/notification/CustomNotifications/screenshots/main-notification.png
new file mode 100644
index 0000000..00d4e5a
--- /dev/null
+++ b/notification/CustomNotifications/screenshots/main-notification.png
Binary files differ
diff --git a/notification/CustomNotifications/screenshots/notification.png b/notification/CustomNotifications/screenshots/notification.png
new file mode 100644
index 0000000..a6abfcb
--- /dev/null
+++ b/notification/CustomNotifications/screenshots/notification.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/settings.gradle b/notification/CustomNotifications/settings.gradle
similarity index 100%
rename from ui/notifications/CustomNotifications/settings.gradle
rename to notification/CustomNotifications/settings.gradle
diff --git a/notification/CustomNotifications/template-params.xml b/notification/CustomNotifications/template-params.xml
new file mode 100644
index 0000000..b18b927
--- /dev/null
+++ b/notification/CustomNotifications/template-params.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>CustomNotifications</name>
+    <group>Notification</group>
+    <package>com.example.android.customnotifications</package>
+
+    <!-- change minSdk if needed-->
+    <minSdk>4</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+            This sample demonstrates notifications with custom content views.
+            ]]>
+        </intro>
+    </strings>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Notification, UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main-notification.png</img>
+            <img>screenshots/notification.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Notification</android>
+            <android>android.app.NotificationManager</android>
+            <android>android.app.PendingIntent</android>
+            <android>android.support.v4.app.NotificationCompat</android>
+            <android>android.widget.RemoteViews</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates notifications with custom content views.
+The use of collapsed and expanded notification views are shown.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample demonstrates notifications with custom content views. It
+also demonstrates the expanded notification introduced in API level 16.
+
+This sample shows all the required steps to create and display custom
+notifications:
+
+- Create Intent to launch Activity when notification is clicked.
+- Define custom layouts for collapsed and expanded views.
+- Use NotificationManager's notify to show notification.
+
+In the sample, click on the "SHOW NOTIFICATION" button to create
+a notification. Use two-fingered gestures up and down to collapse
+and expand the notification.
+]]>
+        </intro>
+    </metadata>
+
+    <template src="base"/>
+
+</sample>
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/notification/LNotifications/Application/.gitignore
similarity index 100%
rename from ui/notifications/LNotifications/Application/.gitignore
rename to notification/LNotifications/Application/.gitignore
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/notification/LNotifications/Application/proguard-project.txt
similarity index 100%
rename from ui/notifications/LNotifications/Application/proguard-project.txt
rename to notification/LNotifications/Application/proguard-project.txt
diff --git a/ui/notifications/LNotifications/Application/src/main/AndroidManifest.xml b/notification/LNotifications/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/AndroidManifest.xml
rename to notification/LNotifications/Application/src/main/AndroidManifest.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/activity_notification.xml b/notification/LNotifications/Application/src/main/res/layout/activity_notification.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/activity_notification.xml
rename to notification/LNotifications/Application/src/main/res/layout/activity_notification.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/contact_entry.xml b/notification/LNotifications/Application/src/main/res/layout/contact_entry.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/contact_entry.xml
rename to notification/LNotifications/Application/src/main/res/layout/contact_entry.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml b/notification/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml
rename to notification/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml b/notification/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml
rename to notification/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml b/notification/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml
rename to notification/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/values/colors.xml b/notification/LNotifications/Application/src/main/res/values/colors.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/values/colors.xml
rename to notification/LNotifications/Application/src/main/res/values/colors.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/values/dimens.xml b/notification/LNotifications/Application/src/main/res/values/dimens.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/values/dimens.xml
rename to notification/LNotifications/Application/src/main/res/values/dimens.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/values/strings.xml b/notification/LNotifications/Application/src/main/res/values/strings.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/values/strings.xml
rename to notification/LNotifications/Application/src/main/res/values/strings.xml
diff --git a/ui/notifications/LNotifications/Application/tests/AndroidManifest.xml b/notification/LNotifications/Application/tests/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/AndroidManifest.xml
rename to notification/LNotifications/Application/tests/AndroidManifest.xml
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/notification/LNotifications/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to notification/LNotifications/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/notification/LNotifications/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to notification/LNotifications/LICENSE
diff --git a/ui/notifications/LNotifications/README.md b/notification/LNotifications/README.md
similarity index 100%
rename from ui/notifications/LNotifications/README.md
rename to notification/LNotifications/README.md
diff --git a/notification/LNotifications/build.gradle b/notification/LNotifications/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/LNotifications/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/notification/LNotifications/buildSrc/build.gradle b/notification/LNotifications/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/notification/LNotifications/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/LNotifications/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
rename to notification/LNotifications/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9f9f676
--- /dev/null
+++ b/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Aug 06 17:33:35 JST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/LNotifications/gradlew b/notification/LNotifications/gradlew
similarity index 100%
rename from ui/notifications/LNotifications/gradlew
rename to notification/LNotifications/gradlew
diff --git a/ui/notifications/LNotifications/gradlew.bat b/notification/LNotifications/gradlew.bat
similarity index 100%
rename from ui/notifications/LNotifications/gradlew.bat
rename to notification/LNotifications/gradlew.bat
diff --git a/ui/notifications/LNotifications/packaging.yaml b/notification/LNotifications/packaging.yaml
similarity index 100%
rename from ui/notifications/LNotifications/packaging.yaml
rename to notification/LNotifications/packaging.yaml
diff --git a/notification/LNotifications/screenshots/1-headsup.png b/notification/LNotifications/screenshots/1-headsup.png
new file mode 100644
index 0000000..e84f7ec
--- /dev/null
+++ b/notification/LNotifications/screenshots/1-headsup.png
Binary files differ
diff --git a/notification/LNotifications/screenshots/2-visibility.png b/notification/LNotifications/screenshots/2-visibility.png
new file mode 100644
index 0000000..5450110
--- /dev/null
+++ b/notification/LNotifications/screenshots/2-visibility.png
Binary files differ
diff --git a/notification/LNotifications/screenshots/3-others.png b/notification/LNotifications/screenshots/3-others.png
new file mode 100644
index 0000000..453c383
--- /dev/null
+++ b/notification/LNotifications/screenshots/3-others.png
Binary files differ
diff --git a/notification/LNotifications/screenshots/icon-web.png b/notification/LNotifications/screenshots/icon-web.png
new file mode 100644
index 0000000..41a1511
--- /dev/null
+++ b/notification/LNotifications/screenshots/icon-web.png
Binary files differ
diff --git a/ui/notifications/LNotifications/settings.gradle b/notification/LNotifications/settings.gradle
similarity index 100%
rename from ui/notifications/LNotifications/settings.gradle
rename to notification/LNotifications/settings.gradle
diff --git a/notification/LNotifications/template-params.xml b/notification/LNotifications/template-params.xml
new file mode 100644
index 0000000..88b2b13
--- /dev/null
+++ b/notification/LNotifications/template-params.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<sample>
+    <name>LNotifications Sample</name>
+    <group>Notification</group>
+    <package>com.example.android.lnotifications</package>
+
+    <!-- change minSdk if needed-->
+    <minSdk>21</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+                This sample demonstrates how new features for notifications introduced in Android L
+                are used such as Heads-Up notifications, visibility, people, category and priority
+                metadata. (Priority metadata has been present since Jelly Bean, but actually unused).
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Notification</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-headsup.png</img>
+            <img>screenshots/2-visibility.png</img>
+            <img>screenshots/3-others.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Notification</android>
+            <android>android.app.Notification.Builder</android>
+            <android>android.app.NotificationManager</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates new features for notifications introduced in Android L.
+These features include heads-up notifications, visibility, people, category and priority
+metadata.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+Android 5.0 (Lollipop, API level 21) introduced additional features to the Notification API.
+Specifically, it allows developers to specify the priority and visibility level for
+notifications. This metadata allows the system to provide enhanced security for notifications
+while the lock screen is active, allows high-priority notifications to appear even when
+the notification bar is closed, and provides users to silence low-priority notifications
+when they're busy.
+
+This metadata is provided via additional arguments to the `Notification.Builder` class.
+
+Specifically, this sample demonstrates how to generate a heads-up notification
+(`HeadsUpNotificationFragment`), how to control visibility on the lock screen
+(`VisibilityMetadataFragment`), and how to set the category, priority, and associated contact
+for a notification (`OtherMetadataFragment`).
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/notification/MessagingService/Application/.gitignore
similarity index 100%
copy from ui/notifications/LNotifications/Application/.gitignore
copy to notification/MessagingService/Application/.gitignore
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/notification/MessagingService/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to notification/MessagingService/Application/proguard-project.txt
diff --git a/notification/MessagingService/Application/src/androidTest/java/com/example/android/messagingservice/test/SampleTests.java b/notification/MessagingService/Application/src/androidTest/java/com/example/android/messagingservice/test/SampleTests.java
new file mode 100644
index 0000000..79d618b
--- /dev/null
+++ b/notification/MessagingService/Application/src/androidTest/java/com/example/android/messagingservice/test/SampleTests.java
@@ -0,0 +1,76 @@
+/*
+* Copyright 2013 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.messagingservice.test;
+
+import com.example.android.messagingservice.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for MessagingService sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private MessagingServiceFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (MessagingServiceFragment)
+            mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+    * Test if the test fixture has been set up correctly.
+    */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    /**
+    * Add more tests below.
+    */
+
+}
diff --git a/notification/MessagingService/Application/src/main/AndroidManifest.xml b/notification/MessagingService/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f8a5850
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.messagingservice">
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <meta-data android:name="com.google.android.gms.car.application"
+                   android:resource="@xml/automotive_app_desc"/>
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".MessagingService">
+        </service>
+
+        <receiver android:name=".MessageReadReceiver">
+            <intent-filter>
+                <action android:name="com.example.android.messagingservice.ACTION_MESSAGE_READ"/>
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name=".MessageReplyReceiver">
+            <intent-filter>
+                <action android:name="com.example.android.messagingservice.ACTION_MESSAGE_REPLY"/>
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/Conversations.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/Conversations.java
new file mode 100644
index 0000000..7425df4
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/Conversations.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * A simple class that denotes unread conversations and messages. In a real world application,
+ * this would be replaced by a content provider that actually gets the unread messages to be
+ * shown to the user.
+ */
+public class Conversations {
+
+    /**
+     * Set of strings used as messages by the sample.
+     */
+    private static final String[] MESSAGES = new String[]{
+            "Are you at home?",
+            "Can you give me a call?",
+            "Hey yt?",
+            "Don't forget to get some milk on your way back home",
+            "Is that project done?",
+            "Did you finish the Messaging app yet?"
+    };
+
+    /**
+     * Senders of the said messages.
+     */
+    private static final String[] PARTICIPANTS = new String[]{
+            "John Smith",
+            "Robert Lawrence",
+            "James Smith",
+            "Jane Doe"
+    };
+
+    static class Conversation {
+
+        private final int conversationId;
+
+        private final String participantName;
+
+        /**
+         * A given conversation can have a single or multiple messages.
+         * Note that the messages are sorted from *newest* to *oldest*
+         */
+        private final List<String> messages;
+
+        private final long timestamp;
+
+        public Conversation(int conversationId, String participantName,
+                            List<String> messages) {
+            this.conversationId = conversationId;
+            this.participantName = participantName;
+            this.messages = messages == null ? Collections.<String>emptyList() : messages;
+            this.timestamp = System.currentTimeMillis();
+        }
+
+        public int getConversationId() {
+            return conversationId;
+        }
+
+        public String getParticipantName() {
+            return participantName;
+        }
+
+        public List<String> getMessages() {
+            return messages;
+        }
+
+        public long getTimestamp() {
+            return timestamp;
+        }
+
+        public String toString() {
+            return "[Conversation: conversationId=" + conversationId +
+                    ", participantName=" + participantName +
+                    ", messages=" + messages +
+                    ", timestamp=" + timestamp + "]";
+        }
+    }
+
+    private Conversations() {
+    }
+
+    public static Conversation[] getUnreadConversations(int howManyConversations,
+                                                        int messagesPerConversation) {
+        Conversation[] conversations = new Conversation[howManyConversations];
+        for (int i = 0; i < howManyConversations; i++) {
+            conversations[i] = new Conversation(
+                    ThreadLocalRandom.current().nextInt(),
+                    name(), makeMessages(messagesPerConversation));
+        }
+        return conversations;
+    }
+
+    private static List<String> makeMessages(int messagesPerConversation) {
+        int maxLen = MESSAGES.length;
+        List<String> messages = new ArrayList<>(messagesPerConversation);
+        for (int i = 0; i < messagesPerConversation; i++) {
+            messages.add(MESSAGES[ThreadLocalRandom.current().nextInt(0, maxLen)]);
+        }
+        return messages;
+    }
+
+    private static String name() {
+        return PARTICIPANTS[ThreadLocalRandom.current().nextInt(0, PARTICIPANTS.length)];
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MainActivity.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MainActivity.java
new file mode 100644
index 0000000..e558a64
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction()
+                    .add(R.id.container, new MessagingFragment())
+                    .commit();
+        }
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageLogger.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageLogger.java
new file mode 100644
index 0000000..d1007b5
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageLogger.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A simple logger that uses shared preferences to log messages, their reads
+ * and replies. Don't use this in a real world application. This logger is only
+ * used for displaying the messages in the text view.
+ */
+public class MessageLogger {
+
+    private static final String PREF_MESSAGE = "MESSAGE_LOGGER";
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    public static final String LOG_KEY = "message_data";
+    public static final String LINE_BREAKS = "\n\n";
+
+    public static void logMessage(Context context, String message) {
+        SharedPreferences prefs = getPrefs(context);
+        message = DATE_FORMAT.format(new Date(System.currentTimeMillis())) + ": " + message;
+        prefs.edit()
+                .putString(LOG_KEY, prefs.getString(LOG_KEY, "") + LINE_BREAKS + message)
+                .apply();
+    }
+
+    public static SharedPreferences getPrefs(Context context) {
+        return context.getSharedPreferences(PREF_MESSAGE, Context.MODE_PRIVATE);
+    }
+
+    public static String getAllMessages(Context context) {
+        return getPrefs(context).getString(LOG_KEY, "");
+    }
+
+    public static void clear(Context context) {
+        getPrefs(context).edit().remove(LOG_KEY).apply();
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReadReceiver.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReadReceiver.java
new file mode 100644
index 0000000..f28a3a7
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReadReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+public class MessageReadReceiver extends BroadcastReceiver {
+    private static final String TAG = MessageReadReceiver.class.getSimpleName();
+
+    private static final String CONVERSATION_ID = "conversation_id";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive");
+        int conversationId = intent.getIntExtra(CONVERSATION_ID, -1);
+        if (conversationId != -1) {
+            Log.d(TAG, "Conversation " + conversationId + " was read");
+            MessageLogger.logMessage(context, "Conversation " + conversationId + " was read.");
+            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+            notificationManager.cancel(conversationId);
+        }
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReplyReceiver.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReplyReceiver.java
new file mode 100644
index 0000000..0a3eba6
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReplyReceiver.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.RemoteInput;
+import android.util.Log;
+
+/**
+ * A receiver that gets called when a reply is sent to a given conversationId
+ */
+public class MessageReplyReceiver extends BroadcastReceiver {
+
+    private static final String TAG = MessageReplyReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (MessagingService.REPLY_ACTION.equals(intent.getAction())) {
+            int conversationId = intent.getIntExtra(MessagingService.CONVERSATION_ID, -1);
+            CharSequence reply = getMessageText(intent);
+            if (conversationId != -1) {
+                Log.d(TAG, "Got reply (" + reply + ") for ConversationId " + conversationId);
+                MessageLogger.logMessage(context, "ConversationId: " + conversationId +
+                        " received a reply: [" + reply + "]");
+            }
+        }
+    }
+
+    /**
+     * Get the message text from the intent.
+     * Note that you should call {@code RemoteInput#getResultsFromIntent(intent)} to process
+     * the RemoteInput.
+     */
+    private CharSequence getMessageText(Intent intent) {
+        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+        if (remoteInput != null) {
+            return remoteInput.getCharSequence(MessagingService.EXTRA_VOICE_REPLY);
+        }
+        return null;
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingFragment.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingFragment.java
new file mode 100644
index 0000000..f8efcc0
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingFragment.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * The main fragment that shows the buttons and the text view containing the log.
+ */
+public class MessagingFragment extends Fragment implements View.OnClickListener {
+
+    private static final String TAG = MessagingFragment.class.getSimpleName();
+
+    private Button mSendSingleConversation;
+    private Button mSendTwoConversations;
+    private Button mSendConversationWithThreeMessages;
+    private TextView mDataPortView;
+    private Button mClearLogButton;
+
+    private Messenger mService;
+    private boolean mBound;
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            mService = new Messenger(service);
+            mBound = true;
+            setButtonsState(true);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mService = null;
+            mBound = false;
+            setButtonsState(false);
+        }
+    };
+
+    private SharedPreferences.OnSharedPreferenceChangeListener listener =
+            new SharedPreferences.OnSharedPreferenceChangeListener() {
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+            if (MessageLogger.LOG_KEY.equals(key)) {
+                mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
+            }
+        }
+    };
+
+    public MessagingFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_message_me, container, false);
+
+        mSendSingleConversation = (Button) rootView.findViewById(R.id.send_1_conversation);
+        mSendSingleConversation.setOnClickListener(this);
+
+        mSendTwoConversations = (Button) rootView.findViewById(R.id.send_2_conversations);
+        mSendTwoConversations.setOnClickListener(this);
+
+        mSendConversationWithThreeMessages =
+                (Button) rootView.findViewById(R.id.send_1_conversation_3_messages);
+        mSendConversationWithThreeMessages.setOnClickListener(this);
+
+        mDataPortView = (TextView) rootView.findViewById(R.id.data_port);
+        mDataPortView.setMovementMethod(new ScrollingMovementMethod());
+
+        mClearLogButton = (Button) rootView.findViewById(R.id.clear);
+        mClearLogButton.setOnClickListener(this);
+
+        setButtonsState(false);
+
+        return rootView;
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (view == mSendSingleConversation) {
+            sendMsg(1, 1);
+        } else if (view == mSendTwoConversations) {
+            sendMsg(2, 1);
+        } else if (view == mSendConversationWithThreeMessages) {
+            sendMsg(1, 3);
+        } else if (view == mClearLogButton) {
+            MessageLogger.clear(getActivity());
+            mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        getActivity().bindService(new Intent(getActivity(), MessagingService.class), mConnection,
+                Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        MessageLogger.getPrefs(getActivity()).unregisterOnSharedPreferenceChangeListener(listener);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
+        MessageLogger.getPrefs(getActivity()).registerOnSharedPreferenceChangeListener(listener);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mBound) {
+            getActivity().unbindService(mConnection);
+            mBound = false;
+        }
+    }
+
+    private void sendMsg(int howManyConversations, int messagesPerConversation) {
+        if (mBound) {
+            Message msg = Message.obtain(null, MessagingService.MSG_SEND_NOTIFICATION,
+                    howManyConversations, messagesPerConversation);
+            try {
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error sending a message", e);
+                MessageLogger.logMessage(getActivity(), "Error occurred while sending a message.");
+            }
+        }
+    }
+
+    private void setButtonsState(boolean enable) {
+        mSendSingleConversation.setEnabled(enable);
+        mSendTwoConversations.setEnabled(enable);
+        mSendConversationWithThreeMessages.setEnabled(enable);
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingService.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingService.java
new file mode 100644
index 0000000..f590061
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingService.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.messagingservice;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.NotificationCompat.CarExtender;
+import android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.RemoteInput;
+import android.util.Log;
+
+import java.util.Iterator;
+
+public class MessagingService extends Service {
+    private static final String TAG = MessagingService.class.getSimpleName();
+
+    public static final String READ_ACTION =
+            "com.example.android.messagingservice.ACTION_MESSAGE_READ";
+    public static final String REPLY_ACTION =
+            "com.example.android.messagingservice.ACTION_MESSAGE_REPLY";
+    public static final String CONVERSATION_ID = "conversation_id";
+    public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
+    public static final int MSG_SEND_NOTIFICATION = 1;
+    public static final String EOL = "\n";
+
+    private NotificationManagerCompat mNotificationManager;
+
+    private final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * Handler of incoming messages from clients.
+     */
+    class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SEND_NOTIFICATION:
+                    int howManyConversations = msg.arg1 <= 0 ? 1 : msg.arg1;
+                    int messagesPerConv = msg.arg2 <= 0 ? 1 : msg.arg2;
+                    sendNotification(howManyConversations, messagesPerConv);
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        Log.d(TAG, "onCreate");
+        mNotificationManager = NotificationManagerCompat.from(getApplicationContext());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "onBind");
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "onStartCommand");
+        return START_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.d(TAG, "onDestroy");
+    }
+
+    // Creates an intent that will be triggered when a message is marked as read.
+    private Intent getMessageReadIntent(int id) {
+        return new Intent()
+                .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+                .setAction(READ_ACTION)
+                .putExtra(CONVERSATION_ID, id);
+    }
+
+    // Creates an Intent that will be triggered when a voice reply is received.
+    private Intent getMessageReplyIntent(int conversationId) {
+        return new Intent()
+                .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+                .setAction(REPLY_ACTION)
+                .putExtra(CONVERSATION_ID, conversationId);
+    }
+
+    private void sendNotification(int howManyConversations, int messagesPerConversation) {
+        Conversations.Conversation[] conversations = Conversations.getUnreadConversations(
+                howManyConversations, messagesPerConversation);
+        for (Conversations.Conversation conv : conversations) {
+            sendNotificationForConversation(conv);
+        }
+    }
+
+    private void sendNotificationForConversation(Conversations.Conversation conversation) {
+        // A pending Intent for reads
+        PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
+                conversation.getConversationId(),
+                getMessageReadIntent(conversation.getConversationId()),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Build a RemoteInput for receiving voice input in a Car Notification
+        RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
+                .setLabel(getApplicationContext().getString(R.string.notification_reply))
+                .build();
+
+        // Building a Pending Intent for the reply action to trigger
+        PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(),
+                conversation.getConversationId(),
+                getMessageReplyIntent(conversation.getConversationId()),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Create the UnreadConversation and populate it with the participant name,
+        // read and reply intents.
+        UnreadConversation.Builder unreadConvBuilder =
+                new UnreadConversation.Builder(conversation.getParticipantName())
+                .setLatestTimestamp(conversation.getTimestamp())
+                .setReadPendingIntent(readPendingIntent)
+                .setReplyAction(replyIntent, remoteInput);
+
+        // Note: Add messages from oldest to newest to the UnreadConversation.Builder
+        StringBuilder messageForNotification = new StringBuilder();
+        for (Iterator<String> messages = conversation.getMessages().iterator();
+             messages.hasNext(); ) {
+            String message = messages.next();
+            unreadConvBuilder.addMessage(message);
+            messageForNotification.append(message);
+            if (messages.hasNext()) {
+                messageForNotification.append(EOL);
+            }
+        }
+
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
+                .setSmallIcon(R.drawable.notification_icon)
+                .setLargeIcon(BitmapFactory.decodeResource(
+                        getApplicationContext().getResources(), R.drawable.android_contact))
+                .setContentText(messageForNotification.toString())
+                .setWhen(conversation.getTimestamp())
+                .setContentTitle(conversation.getParticipantName())
+                .setContentIntent(readPendingIntent)
+                .extend(new CarExtender()
+                        .setUnreadConversation(unreadConvBuilder.build())
+                        .setColor(getApplicationContext()
+                                .getResources().getColor(R.color.default_color_light)));
+
+        MessageLogger.logMessage(getApplicationContext(), "Sending notification "
+                + conversation.getConversationId() + " conversation: " + conversation);
+
+        mNotificationManager.notify(conversation.getConversationId(), builder.build());
+    }
+}
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-hdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-hdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..506cbc5
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-hdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-hdpi/notification_icon.png
new file mode 100644
index 0000000..9cdfca1
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-hdpi/notification_icon.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-mdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-mdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..6296a03
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-mdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-mdpi/notification_icon.png
new file mode 100644
index 0000000..d6069eb
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-mdpi/notification_icon.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-xhdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..52d3c5e
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xhdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/notification_icon.png
new file mode 100644
index 0000000..786ed17
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/notification_icon.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-xxhdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..444fb39
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/notification_icon.png
new file mode 100644
index 0000000..005207c
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/notification_icon.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/layout-land/fragment_message_me.xml b/notification/MessagingService/Application/src/main/res/layout-land/fragment_message_me.xml
new file mode 100644
index 0000000..6f4f88b
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/layout-land/fragment_message_me.xml
@@ -0,0 +1,66 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="center_horizontal"
+              android:orientation="horizontal"
+              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="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Button
+            android:id="@+id/send_1_conversation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/send_1_conversation"/>
+
+        <Button
+            android:id="@+id/send_2_conversations"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/send_2_conversations"/>
+
+        <Button
+            android:id="@+id/send_1_conversation_3_messages"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/send_1_conv_3_messages"/>
+    </LinearLayout>
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="2">
+        <Button
+            android:id="@+id/clear"
+            android:layout_alignParentBottom="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/clear_log"/>
+
+        <TextView
+            android:id="@+id/data_port"
+            android:layout_above="@id/clear"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scrollbars="vertical"/>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/notification/MessagingService/Application/src/main/res/layout/activity_main.xml b/notification/MessagingService/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..59eec80
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity"
+    tools:ignore="MergeRootFrame" />
diff --git a/notification/MessagingService/Application/src/main/res/layout/fragment_message_me.xml b/notification/MessagingService/Application/src/main/res/layout/fragment_message_me.xml
new file mode 100644
index 0000000..29a8c44
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/layout/fragment_message_me.xml
@@ -0,0 +1,57 @@
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout 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"
+    android:orientation="vertical"
+    android:gravity="center_horizontal"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin">
+
+    <Button
+        android:id="@+id/send_1_conversation"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/send_1_conversation"/>
+
+    <Button
+        android:id="@+id/send_2_conversations"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/send_2_conversations"/>
+
+    <Button
+        android:id="@+id/send_1_conversation_3_messages"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/send_1_conv_3_messages"/>
+
+    <TextView
+        android:id="@+id/data_port"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:scrollbars="vertical"/>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/clear"
+        android:text="@string/clear_log"/>
+
+</LinearLayout>
diff --git a/notification/MessagingService/Application/src/main/res/values-v21/styles.xml b/notification/MessagingService/Application/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..f30c97a
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values-v21/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+        <item name="android:colorPrimary">@color/default_color_light</item>
+        <item name="android:colorPrimaryDark">@color/default_color_dark</item>
+    </style>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/colors.xml b/notification/MessagingService/Application/src/main/res/values/colors.xml
new file mode 100644
index 0000000..0e6825b
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <color name="default_color_light">#ff4092c3</color>
+    <color name="default_color_dark">#ff241c99</color>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/dimens.xml b/notification/MessagingService/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..574a35d
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/strings.xml b/notification/MessagingService/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..001b10e
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <string name="app_name">Messaging Sample</string>
+    <string name="action_settings">Settings</string>
+    <string name="title">Messaging Sample</string>
+    <string name="notification_reply">Reply by Voice</string>
+    <string name="send_2_conversations">Send 2 conversations with 1 message</string>
+    <string name="send_1_conversation">Send 1 conversation with 1 message</string>
+    <string name="send_1_conv_3_messages">Send 1 conversation with 3 messages</string>
+    <string name="clear_log">Clear Log</string>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/styles.xml b/notification/MessagingService/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3f1a6af
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources>
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+    </style>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/xml/automotive_app_desc.xml b/notification/MessagingService/Application/src/main/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..9e9f174
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/xml/automotive_app_desc.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<automotiveApp>
+    <uses name="notification"/>
+</automotiveApp>
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/notification/MessagingService/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to notification/MessagingService/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/notification/MessagingService/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to notification/MessagingService/LICENSE
diff --git a/notification/MessagingService/README.md b/notification/MessagingService/README.md
new file mode 100644
index 0000000..69e3aa3
--- /dev/null
+++ b/notification/MessagingService/README.md
@@ -0,0 +1,86 @@
+Android MessagingService Sample
+==============================
+
+This sample shows a simple service that sends notifications using
+NotificationCompat. In addition to sending a notification, it also extends
+the notification with a CarExtender to make it compatible with Android Auto.
+Each unread conversation from a user is sent as a distinct notification.
+
+Checklist while building a messaging app that supports Android Auto:
+-------------------------------------------------------------------
+1. Ensure that Message notifications are extended using
+NotificationCompat.Builder.extend(new CarExtender()...)
+2. Declare a meta-data tag to your AndroidManifest.xml to specify that your app
+is automotive enabled.
+
+example: AndroidManifest.xml
+
+```
+       <meta-data android:name="com.google.android.gms.car.application"
+                   android:resource="@xml/automotive_app_desc"/>
+```
+
+Include the following to indicate that the application wants to show notifications on
+the Android Auto overview screen.
+
+res/xml/automotive\_app\_desc.xml
+```
+<automotiveApp>
+    <uses name="notification"/>
+</automotiveApp>
+```
+
+Flow
+-----
+MessagingFragment is shown to the user. Depending on the button clicked, the MessagingService is
+sent a message. MessagingService in turn creates notifications which can be viewed either on the
+device or in the messaging-simulator.
+When a message is read, the associated PendingIntent is called and MessageReadReceiver is called
+with the appropriate conversationId. Similarly, when a reply is received, the MessageReplyReceiver
+is called with the appropriate conversationId. MessageLogger logs each event and shows them in a
+TextView in MessagingFragment for correlation.
+
+
+Pre-requisites
+--------------
+
+- Android SDK v21
+- Android Support Repository
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MessagingService
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License.  You may obtain a copy of
+the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/notification/MessagingService/build.gradle b/notification/MessagingService/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/MessagingService/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/notification/MessagingService/buildSrc/build.gradle b/notification/MessagingService/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/notification/MessagingService/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/MessagingService/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to notification/MessagingService/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties b/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/notification/MessagingService/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to notification/MessagingService/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/notification/MessagingService/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to notification/MessagingService/gradlew.bat
diff --git a/notification/MessagingService/packaging.yaml b/notification/MessagingService/packaging.yaml
new file mode 100644
index 0000000..38733c7
--- /dev/null
+++ b/notification/MessagingService/packaging.yaml
@@ -0,0 +1,15 @@
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+
+status:       PUBLISHED
+technologies: [Android]
+categories:   [None]
+languages:    [Java]
+solutions:    [Mobile]
+github:       googlesamples/android-MessagingService
+level:        BEGINNER
+icon:         MessagingServiceSample/src/main/res/drawable-xxhdpi/ic_launcher.png
+license:      apache2-android
diff --git a/notification/MessagingService/screenshots/1-main.png b/notification/MessagingService/screenshots/1-main.png
new file mode 100644
index 0000000..a3e527c
--- /dev/null
+++ b/notification/MessagingService/screenshots/1-main.png
Binary files differ
diff --git a/notification/MessagingService/screenshots/2-onemessage.png b/notification/MessagingService/screenshots/2-onemessage.png
new file mode 100644
index 0000000..5970c51
--- /dev/null
+++ b/notification/MessagingService/screenshots/2-onemessage.png
Binary files differ
diff --git a/notification/MessagingService/screenshots/2-threemessages.png b/notification/MessagingService/screenshots/2-threemessages.png
new file mode 100644
index 0000000..9d36923
--- /dev/null
+++ b/notification/MessagingService/screenshots/2-threemessages.png
Binary files differ
diff --git a/notification/MessagingService/screenshots/icon-web.png b/notification/MessagingService/screenshots/icon-web.png
new file mode 100644
index 0000000..3dfd2ec
--- /dev/null
+++ b/notification/MessagingService/screenshots/icon-web.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/notification/MessagingService/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to notification/MessagingService/settings.gradle
diff --git a/notification/MessagingService/template-params.xml b/notification/MessagingService/template-params.xml
new file mode 100644
index 0000000..e83c845
--- /dev/null
+++ b/notification/MessagingService/template-params.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>MessagingService</name>
+    <group>Notification</group>
+    <package>com.example.android.messagingservice</package>
+
+    <minSdk>21</minSdk>
+
+    <strings>
+        <intro>
+            <![CDATA[
+This sample shows a simple service that sends notifications using
+NotificationCompat. In addition to sending a notification, it also extends
+the notification with a CarExtender to make it compatible with Android Auto.
+Each unread conversation from a user is sent as a distinct notification.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="basebuild" />
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Notification</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-onemessage.png</img>
+            <img>screenshots/3-threemessages.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Service</android>
+            <android>android.content.Intent</android>
+            <android>android.support.v4.app.NotificationCompat</android>
+            <android>android.support.v4.app.NotificationCompat.CarExtender</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample shows a simple service that sends notifications using
+NotificationCompat. In addition to sending a notification, it also extends
+the notification with a CarExtender to make it compatible with Android Auto.
+Each unread conversation from a user is sent as a distinct notification.
+]]></description>
+        <intro>
+            <![CDATA[
+#### Checklist while building a messaging app that supports Android Auto:
+1. Ensure that Message notifications are extended using
+NotificationCompat.Builder.extend(new CarExtender()...)
+2. Declare a meta-data tag to your AndroidManifest.xml to specify that your app
+is automotive enabled.
+
+example: AndroidManifest.xml
+
+```
+       <meta-data android:name="com.google.android.gms.car.application"
+                   android:resource="@xml/automotive_app_desc"/>
+```
+
+Include the following to indicate that the application wants to show notifications on
+the Android Auto overview screen.
+
+example: res/xml/automotive\_app\_desc.xml
+```
+        <automotiveApp>
+            <uses name="notification"/>
+        </automotiveApp>
+```
+
+#### Flow
+MessagingFragment is shown to the user. Depending on the button clicked, the MessagingService is
+sent a message. MessagingService in turn creates notifications which can be viewed either on the
+device or in the messaging-simulator.
+
+When a message is read, the associated PendingIntent is triggered and MessageReadReceiver is called
+with the appropriate conversationId. Similarly, when a reply is received, the MessageReplyReceiver
+is called with the appropriate conversationId. MessageLogger logs each event and shows them in a
+TextView in MessagingFragment for correlation.
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/renderScript/BasicRenderScript/gradle/wrapper/gradle-wrapper.properties b/renderScript/BasicRenderScript/gradle/wrapper/gradle-wrapper.properties
index c68abf9..6c90366 100644
--- a/renderScript/BasicRenderScript/gradle/wrapper/gradle-wrapper.properties
+++ b/renderScript/BasicRenderScript/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/renderScript/BasicRenderScript/screenshots/icon-web.png b/renderScript/BasicRenderScript/screenshots/icon-web.png
new file mode 100644
index 0000000..eb47ea1
--- /dev/null
+++ b/renderScript/BasicRenderScript/screenshots/icon-web.png
Binary files differ
diff --git a/renderScript/BasicRenderScript/screenshots/main.png b/renderScript/BasicRenderScript/screenshots/main.png
new file mode 100644
index 0000000..a9f2a72
--- /dev/null
+++ b/renderScript/BasicRenderScript/screenshots/main.png
Binary files differ
diff --git a/renderScript/BasicRenderScript/template-params.xml b/renderScript/BasicRenderScript/template-params.xml
index cf35d31..37be82e 100644
--- a/renderScript/BasicRenderScript/template-params.xml
+++ b/renderScript/BasicRenderScript/template-params.xml
@@ -29,14 +29,15 @@
 
     <defaultConfig>
             renderscriptTargetApi 18
-            renderscriptSupportMode true
+            renderscriptSupportModeEnabled true
     </defaultConfig>
 
     <strings>
         <intro>
             <![CDATA[
-            BasicRenderScript sample demonstrates basic steps how to use renderScript.
-			In the sample, it performs graphical filter operation on a image with renderScript.
+            This sample demonstrates using RenderScript to perform basic image manipulation. Specifically, it allows users
+            to dynamically adjust the saturation for an image using a slider. A custom RenderScript kernel performs the saturation
+            adjustment, running the computation on the device's GPU or other compute hardware as deemed appropriate by the system.
             ]]>
         </intro>
     </strings>
@@ -44,4 +45,48 @@
     <template src="base"/>
     <common src="media"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>RenderScript</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>EXPERT</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.renderscript.RenderScript</android>
+            <android>android.renderscript.Allocation</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates using RenderScript to perform basic image manipulation. Specifically, it allows users
+to dynamically adjust the saturation for an image using a slider. A custom RenderScript kernel performs the saturation
+adjustment, running the computation on the device's GPU or other compute hardware as deemed appropriate by the system.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+[RenderScript][1] is a framework for running computationally intensive tasks at high performance on Android. RenderScript is
+primarily oriented for use with data-parallel computation, although serial computationally intensive workloads can
+benefit as well.
+
+The RenderScript runtime will parallelize work across all processors available on a device, such as multi-core CPUs,
+GPUs, or DSPs, allowing you to focus on expressing algorithms rather than scheduling work or load balancing.
+RenderScript is especially useful for applications performing image processing, computational photography, or computer
+vision.
+
+This sample demonstrates the fundamentals of writing a RenderScript compute kernel, and using it to perform basic image
+manipulation. In this case, each pixel is transformed based on a liner interpolation between its original
+RGB value and it's luminance (black-and-white) value, weighted based on the user's specified saturation target.
+
+[1]: http://developer.android.com/guide/topics/renderscript/compute.html
+[2]: http://developer.android.com/reference/renderscript/rs__cl_8rsh.html#a254612a612ff7539b01a1478e03d8697
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/renderScript/RenderScriptIntrinsic/gradle/wrapper/gradle-wrapper.properties b/renderScript/RenderScriptIntrinsic/gradle/wrapper/gradle-wrapper.properties
index 42b0b0f..28f01d8 100644
--- a/renderScript/RenderScriptIntrinsic/gradle/wrapper/gradle-wrapper.properties
+++ b/renderScript/RenderScriptIntrinsic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/renderScript/RenderScriptIntrinsic/screenshots/icon-web.png b/renderScript/RenderScriptIntrinsic/screenshots/icon-web.png
new file mode 100644
index 0000000..87f9dac
--- /dev/null
+++ b/renderScript/RenderScriptIntrinsic/screenshots/icon-web.png
Binary files differ
diff --git a/renderScript/RenderScriptIntrinsic/screenshots/main.png b/renderScript/RenderScriptIntrinsic/screenshots/main.png
new file mode 100644
index 0000000..1a81389
--- /dev/null
+++ b/renderScript/RenderScriptIntrinsic/screenshots/main.png
Binary files differ
diff --git a/renderScript/RenderScriptIntrinsic/template-params.xml b/renderScript/RenderScriptIntrinsic/template-params.xml
index fe67f2a..8b80bb2 100644
--- a/renderScript/RenderScriptIntrinsic/template-params.xml
+++ b/renderScript/RenderScriptIntrinsic/template-params.xml
@@ -15,8 +15,6 @@
  limitations under the License.
 -->
 
-
-
 <sample>
     <name>RenderScriptIntrinsic</name>
     <group>RenderScript</group>
@@ -29,7 +27,7 @@
 
     <defaultConfig>
         renderscriptTargetApi 18
-        renderscriptSupportMode true
+        renderscriptSupportModeEnabled true
     </defaultConfig>
 
     <strings>
@@ -45,4 +43,59 @@
     <template src="base"/>
     <common src="logger"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>RenderScript</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>EXPERT</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.renderscript.RenderScript</android>
+            <android>android.renderscript.ScriptIntrinsicBlur</android>
+            <android>android.renderscript.ScriptIntrinsicConvolve5x5</android>
+            <android>android.renderscript.ScriptIntrinsicColorMatrix</android>
+            <android></android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+RenderScriptIntrinsic sample that demonstrates how to use RenderScript intrinsics.
+Creates several RenderScript intrinsics and shows a filtering result with various parameters.
+Also shows how to extends RedioButton with StateListDrawable.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+[RenderScript][1] is a framework for running computationally intensive tasks at high performance on Android. RenderScript is
+primarily oriented for use with data-parallel computation, although serial computationally intensive workloads can
+benefit as well.
+
+RenderScript **intrinsics** are built-in functions that perform well-defined operations often seen
+in image processing. Intrinsics provide extremely high-performance implementations of standard
+functions with a minimal amount of code.
+
+This sample shows how to access and use the blur, convolve, and matrix intrinsics:
+
+```java
+mScriptBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+mScriptConvolve = ScriptIntrinsicConvolve5x5.create(mRS,
+        Element.U8_4(mRS));
+mScriptMatrix = ScriptIntrinsicColorMatrix.create(mRS,
+        Element.U8_4(mRS));
+```
+
+RenderScript intrinsics will usually be the fastest possible way for a developer to perform these operations. The
+Android team works closely with our partners to ensure that the intrinsics perform as fast as possible on their
+architectures — often far beyond anything that can be achieved in a general-purpose language.
+
+[1]: http://developer.android.com/guide/topics/renderscript/compute.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/security/keystore/BasicAndroidKeyStore/gradle/wrapper/gradle-wrapper.properties b/security/keystore/BasicAndroidKeyStore/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/security/keystore/BasicAndroidKeyStore/gradle/wrapper/gradle-wrapper.properties
+++ b/security/keystore/BasicAndroidKeyStore/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/security/keystore/BasicAndroidKeyStore/packaging.yaml b/security/keystore/BasicAndroidKeyStore/packaging.yaml
deleted file mode 100644
index a2cbd14..0000000
--- a/security/keystore/BasicAndroidKeyStore/packaging.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# GOOGLE SAMPLE PACKAGING DATA
-#
-# This file is used by Google as part of our samples packaging process.
-# End users may safely ignore this file. It has no relevance to other systems.
----
-
-status:       PUBLISHED
-technologies: [Android]
-categories:   [Security]
-languages:    [Java]
-solutions:    [Mobile]
-github:       googlesamples/android-BasicAndroidKeyStore
-level:        BEGINNER
-icon:         BasicAndroidKeyStoreSample/src/main/res/drawable-xxhdpi/ic_launcher.png
-license:      apache2-android
diff --git a/security/keystore/BasicAndroidKeyStore/screenshots/big_icon.png b/security/keystore/BasicAndroidKeyStore/screenshots/big_icon.png
new file mode 100644
index 0000000..004d80c
--- /dev/null
+++ b/security/keystore/BasicAndroidKeyStore/screenshots/big_icon.png
Binary files differ
diff --git a/security/keystore/BasicAndroidKeyStore/screenshots/screenshot1.png b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot1.png
new file mode 100644
index 0000000..e09a209
--- /dev/null
+++ b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot1.png
Binary files differ
diff --git a/security/keystore/BasicAndroidKeyStore/screenshots/screenshot2.png b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot2.png
new file mode 100644
index 0000000..24bfa43
--- /dev/null
+++ b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot2.png
Binary files differ
diff --git a/security/keystore/BasicAndroidKeyStore/screenshots/screenshot3.png b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot3.png
new file mode 100644
index 0000000..aafbbfb
--- /dev/null
+++ b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot3.png
Binary files differ
diff --git a/security/keystore/BasicAndroidKeyStore/screenshots/screenshot4.png b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot4.png
new file mode 100644
index 0000000..3f77b54
--- /dev/null
+++ b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot4.png
Binary files differ
diff --git a/security/keystore/BasicAndroidKeyStore/screenshots/screenshot5.png b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot5.png
new file mode 100644
index 0000000..4965dc5
--- /dev/null
+++ b/security/keystore/BasicAndroidKeyStore/screenshots/screenshot5.png
Binary files differ
diff --git a/security/keystore/BasicAndroidKeyStore/template-params.xml b/security/keystore/BasicAndroidKeyStore/template-params.xml
index d603dfa..e2fddf6 100644
--- a/security/keystore/BasicAndroidKeyStore/template-params.xml
+++ b/security/keystore/BasicAndroidKeyStore/template-params.xml
@@ -24,14 +24,15 @@
 
     <strings>
         <intro>
-            <![CDATA[
-                Welcome to the <b>Basic Android Key Store</b> sample!\n\n
-                This sample demonstrates how to use the Android Key Store to safely create and store
-                encryption keys that only your application can access.  You can also sign data
-                using those keys.\n\n
-                To create a new KeyPair, click \"Create\".\n\n
-                To sign some data using a KeyPair, click \"Sign\".\n\n
-                To verify the data using the signature provided, click \"Verify\".\n\n            ]]>
+<![CDATA[
+Welcome to the <b>Basic Android Key Store</b> sample!\n\n
+This sample demonstrates how to use the Android Key Store to safely create and store
+encryption keys that only your application can access.  You can also sign data
+using those keys.\n\n
+To create a new KeyPair, click \"Create\".\n\n
+To sign some data using a KeyPair, click \"Sign\".\n\n
+To verify the data using the signature provided, click \"Verify\".\n\n
+]]>
         </intro>
     </strings>
 
@@ -40,4 +41,44 @@
     <common src="activities"/>
     <common src="logger"/>
 
+    <metadata>
+    <status>PUBLISHED</status>
+    <categories>Security</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>ADVANCED</level>
+    <icon>screenshots/big-icon.png</icon>
+    <screenshots>
+        <img>screenshots/screenshot1.png</img>
+        <img>screenshots/screenshot2.png</img>
+        <img>screenshots/screenshot3.png</img>
+        <img>screenshots/screenshot4.png</img>
+        <img>screenshots/screenshot5.png</img>
+    </screenshots>
+    <api_refs>
+        <android>android.security.KeyPairGeneratorSpec</android>
+    </api_refs>
+    <description>
+<![CDATA[
+An advanced sample displaying the creation and usage of data integrity mechanisms
+]]>
+    </description>
+    <intro>
+<![CDATA[
+This sample demonstrates how to use the Android [KeyStore][1] to safely create and store
+encryption keys that only your application can access.
+
+A [KeyPair][2] consisting of a [PrivateKey][3] and a [PublicKey][4] is being generated.
+The private key then is being used to sign and verify a String.
+
+Next to that appropriate exception handling for potential errors is being displayed.
+
+[1]: https://developer.android.com/reference/java/security/KeyStore.html
+[2]: https://developer.android.com/reference/java/security/KeyPair.html
+[3]: https://developer.android.com/reference/java/security/PrivateKey.html
+[4]: https://developer.android.com/reference/java/security/PublicKey.html
+]]>
+    </intro>
+</metadata>
 </sample>
diff --git a/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties
+++ b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/sensors/BatchStepSensor/packaging.yaml b/sensors/BatchStepSensor/packaging.yaml
index 1002b36..c78b852 100644
--- a/sensors/BatchStepSensor/packaging.yaml
+++ b/sensors/BatchStepSensor/packaging.yaml
@@ -11,5 +11,5 @@
 solutions:    [Mobile]
 github:       googlesamples/android-BatchStepSensor
 level:        BEGINNER
-icon:         BatchStepSensorSample/src/main/res/drawable-xxhdpi/ic_launcher.png
+icon:         BatchStepSensorSample/screenshots/big_icon.png
 license:      apache2-android
diff --git a/sensors/BatchStepSensor/screenshots/big_icon.png b/sensors/BatchStepSensor/screenshots/big_icon.png
new file mode 100644
index 0000000..7f06890
--- /dev/null
+++ b/sensors/BatchStepSensor/screenshots/big_icon.png
Binary files differ
diff --git a/sensors/BatchStepSensor/screenshots/screenshot1.png b/sensors/BatchStepSensor/screenshots/screenshot1.png
new file mode 100644
index 0000000..3c202a5
--- /dev/null
+++ b/sensors/BatchStepSensor/screenshots/screenshot1.png
Binary files differ
diff --git a/sensors/BatchStepSensor/screenshots/screenshot2.png b/sensors/BatchStepSensor/screenshots/screenshot2.png
new file mode 100644
index 0000000..84865c2
--- /dev/null
+++ b/sensors/BatchStepSensor/screenshots/screenshot2.png
Binary files differ
diff --git a/sensors/BatchStepSensor/screenshots/screenshot3.png b/sensors/BatchStepSensor/screenshots/screenshot3.png
new file mode 100644
index 0000000..402ea41
--- /dev/null
+++ b/sensors/BatchStepSensor/screenshots/screenshot3.png
Binary files differ
diff --git a/sensors/BatchStepSensor/screenshots/screenshot4.png b/sensors/BatchStepSensor/screenshots/screenshot4.png
new file mode 100644
index 0000000..17043a9
--- /dev/null
+++ b/sensors/BatchStepSensor/screenshots/screenshot4.png
Binary files differ
diff --git a/sensors/BatchStepSensor/screenshots/screenshot5.png b/sensors/BatchStepSensor/screenshots/screenshot5.png
new file mode 100644
index 0000000..c448d03
--- /dev/null
+++ b/sensors/BatchStepSensor/screenshots/screenshot5.png
Binary files differ
diff --git a/sensors/BatchStepSensor/template-params.xml b/sensors/BatchStepSensor/template-params.xml
index a07579d..0f2ffbb 100644
--- a/sensors/BatchStepSensor/template-params.xml
+++ b/sensors/BatchStepSensor/template-params.xml
@@ -14,33 +14,27 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
-
-
 <sample>
     <name>BatchStepSensor</name>
     <group>Sensors</group>
     <package>com.example.android.batchstepsensor</package>
-
-
-
     <!-- change minSdk if needed-->
     <minSdk>19</minSdk>
-
-
     <strings>
         <intro>
             <![CDATA[
-            This sample demonstrates the use of the two step sensors (step detector and counter) and
-            sensor batching.\n\n It shows how to register a SensorEventListener with and without
-            batching and shows how these events are received.\n\nThe Step Detector sensor fires an
+            <p>This sample demonstrates the use of the two step sensors (step detector and counter) and
+            sensor batching.</p>
+            <p>It shows how to register a SensorEventListener with and without
+            batching and shows how these events are received.</p>
+            <p>The Step Detector sensor fires an
             event when a step is detected, while the step counter returns the total number of
             steps since a listener was first registered for this sensor.
             Both sensors only count steps while a listener is registered. This sample only covers the
             basic case, where a listener is only registered while the app is running. Likewise,
             batched sensors can be used in the background (when the CPU is suspended), which
             requires manually flushing the sensor event queue before it overflows, which is not
-            covered in this sample.
+            covered in this sample.</p>
             ]]>
         </intro>
     </strings>
@@ -49,5 +43,58 @@
     <template src="CardStream"/>
     <common src="logger"/>
     <common src="activities"/>
+    <metadata>
+    <status>PUBLISHED</status>
+    <categories>Sensors</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>ADVANCED</level>
+    <icon>screenshots/big_icon.png</icon>
+    <screenshots>
+        <img>screenshots/screenshot1.png</img>
+        <img>screenshots/screenshot2.png</img>
+        <img>screenshots/screenshot3.png</img>
+        <img>screenshots/screenshot4.png</img>
+        <img>screenshots/screenshot5.png</img>
+        <img>screenshots/screenshot6.png</img>
+      </screenshots>
+    <api_refs>
+        <android>android.hardware.Sensor</android>
+        <android>android.hardware.SensorEvent</android>
+        <android>android.hardware.SensorEventListener</android>
+        <android>android.hardware.SensorManager</android>
+    </api_refs>
+    <description>
+<![CDATA[
+Sample demonstrating how to set up SensorEventListeners for step
+detectors and step counters.
+]]>
+    </description>
+
+    <intro>
+<![CDATA[
+This sample demonstrates the use of the two step [sensors][1] (step detector and counter) and
+sensor batching.
+
+It shows how to register a [SensorEventListener][2] with and without
+batching and shows how these events are received.
+
+The Step Detector sensor fires an
+event when a step is detected, while the step counter returns the total number of
+steps since a listener was first registered for this sensor.
+
+Both sensors only count steps while a listener is registered. This sample only covers the
+basic case, where a listener is only registered while the app is running. Likewise,
+batched sensors can be used in the background (when the CPU is suspended), which
+requires manually flushing the [sensor event][3] queue before it overflows, which is not
+covered in this sample.
+
+[1]: https://developer.android.com/reference/android/hardware/Sensor.html
+[2]: https://developer.android.com/reference/android/hardware/SensorEventListener.html
+[3]: https://developer.android.com/reference/android/hardware/SensorEvent.html
+]]>
+    </intro>
+</metadata>
 
 </sample>
diff --git a/ui/notifications/BasicNotifications/Application/.gitignore b/system/AppUsageStatistics/Application/.gitignore
similarity index 100%
copy from ui/notifications/BasicNotifications/Application/.gitignore
copy to system/AppUsageStatistics/Application/.gitignore
diff --git a/ui/notifications/BasicNotifications/Application/proguard-project.txt b/system/AppUsageStatistics/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/BasicNotifications/Application/proguard-project.txt
copy to system/AppUsageStatistics/Application/proguard-project.txt
diff --git a/system/AppUsageStatistics/Application/src/main/AndroidManifest.xml b/system/AppUsageStatistics/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2726b4a
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.appusagestatistics"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/Theme.AppCompat.Light">
+
+        <activity android:name=".AppUsageStatisticsActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsActivity.java b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsActivity.java
new file mode 100644
index 0000000..4def465
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsActivity.java
@@ -0,0 +1,37 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.appusagestatistics;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+
+/**
+ * Launcher Activity for the App Usage Statistics sample app.
+ */
+public class AppUsageStatisticsActivity extends ActionBarActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_app_usage_statistics);
+        if (savedInstanceState == null) {
+            getSupportFragmentManager().beginTransaction()
+                    .add(R.id.container, AppUsageStatisticsFragment.newInstance())
+                    .commit();
+        }
+    }
+}
\ No newline at end of file
diff --git a/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java
new file mode 100644
index 0000000..9f54d02
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/AppUsageStatisticsFragment.java
@@ -0,0 +1,233 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.appusagestatistics;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Fragment that demonstrates how to use App Usage Statistics API.
+ */
+public class AppUsageStatisticsFragment extends Fragment {
+
+    private static final String TAG = AppUsageStatisticsFragment.class.getSimpleName();
+
+    //VisibleForTesting for variables below
+    UsageStatsManager mUsageStatsManager;
+    UsageListAdapter mUsageListAdapter;
+    RecyclerView mRecyclerView;
+    RecyclerView.LayoutManager mLayoutManager;
+    Button mOpenUsageSettingButton;
+    Spinner mSpinner;
+
+    /**
+     * Use this factory method to create a new instance of
+     * this fragment using the provided parameters.
+     *
+     * @return A new instance of fragment {@link AppUsageStatisticsFragment}.
+     */
+    public static AppUsageStatisticsFragment newInstance() {
+        AppUsageStatisticsFragment fragment = new AppUsageStatisticsFragment();
+        return fragment;
+    }
+
+    public AppUsageStatisticsFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mUsageStatsManager = (UsageStatsManager) getActivity()
+                .getSystemService("usagestats"); //Context.USAGE_STATS_SERVICE
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.fragment_app_usage_statistics, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View rootView, Bundle savedInstanceState) {
+        super.onViewCreated(rootView, savedInstanceState);
+
+        mLayoutManager = new LinearLayoutManager(getActivity());
+        mUsageListAdapter = new UsageListAdapter();
+        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_app_usage);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        mRecyclerView.scrollToPosition(0);
+        mRecyclerView.setAdapter(mUsageListAdapter);
+        mOpenUsageSettingButton = (Button) rootView.findViewById(R.id.button_open_usage_setting);
+        mSpinner = (Spinner) rootView.findViewById(R.id.spinner_time_span);
+        SpinnerAdapter spinnerAdapter = ArrayAdapter.createFromResource(getActivity(),
+                R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
+        mSpinner.setAdapter(spinnerAdapter);
+        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+
+            String[] strings = getResources().getStringArray(R.array.action_list);
+
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                StatsUsageInterval statsUsageInterval = StatsUsageInterval
+                        .getValue(strings[position]);
+                if (statsUsageInterval != null) {
+                    List<UsageStats> usageStatsList =
+                            getUsageStatistics(statsUsageInterval.mInterval);
+                    Collections.sort(usageStatsList, new LastTimeLaunchedComparatorDesc());
+                    updateAppsList(usageStatsList);
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+    }
+
+    /**
+     * Returns the {@link #mRecyclerView} including the time span specified by the
+     * intervalType argument.
+     *
+     * @param intervalType The time interval by which the stats are aggregated.
+     *                     Corresponding to the value of {@link UsageStatsManager}.
+     *                     E.g. {@link UsageStatsManager#INTERVAL_DAILY}, {@link
+     *                     UsageStatsManager#INTERVAL_WEEKLY},
+     *
+     * @return A list of {@link android.app.usage.UsageStats}.
+     */
+    public List<UsageStats> getUsageStatistics(int intervalType) {
+        // Get the app statistics since one year ago from the current time.
+        Calendar cal = Calendar.getInstance();
+        cal.add(Calendar.YEAR, -1);
+
+        List<UsageStats> queryUsageStats = mUsageStatsManager
+                .queryUsageStats(intervalType, cal.getTimeInMillis(),
+                        System.currentTimeMillis());
+
+        if (queryUsageStats.size() == 0) {
+            Log.i(TAG, "The user may not allow the access to apps usage. ");
+            Toast.makeText(getActivity(),
+                    getString(R.string.explanation_access_to_appusage_is_not_enabled),
+                    Toast.LENGTH_LONG).show();
+            mOpenUsageSettingButton.setVisibility(View.VISIBLE);
+            mOpenUsageSettingButton.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
+                }
+            });
+        }
+        return queryUsageStats;
+    }
+
+    /**
+     * Updates the {@link #mRecyclerView} with the list of {@link UsageStats} passed as an argument.
+     *
+     * @param usageStatsList A list of {@link UsageStats} from which update the
+     *                       {@link #mRecyclerView}.
+     */
+    //VisibleForTesting
+    void updateAppsList(List<UsageStats> usageStatsList) {
+        List<CustomUsageStats> customUsageStatsList = new ArrayList<>();
+        for (int i = 0; i < usageStatsList.size(); i++) {
+            CustomUsageStats customUsageStats = new CustomUsageStats();
+            customUsageStats.usageStats = usageStatsList.get(i);
+            try {
+                Drawable appIcon = getActivity().getPackageManager()
+                        .getApplicationIcon(customUsageStats.usageStats.getPackageName());
+                customUsageStats.appIcon = appIcon;
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, String.format("App Icon is not found for %s",
+                        customUsageStats.usageStats.getPackageName()));
+                customUsageStats.appIcon = getActivity()
+                        .getDrawable(R.drawable.ic_default_app_launcher);
+            }
+            customUsageStatsList.add(customUsageStats);
+        }
+        mUsageListAdapter.setCustomUsageStatsList(customUsageStatsList);
+        mUsageListAdapter.notifyDataSetChanged();
+        mRecyclerView.scrollToPosition(0);
+    }
+
+    /**
+     * The {@link Comparator} to sort a collection of {@link UsageStats} sorted by the timestamp
+     * last time the app was used in the descendant order.
+     */
+    private static class LastTimeLaunchedComparatorDesc implements Comparator<UsageStats> {
+
+        @Override
+        public int compare(UsageStats left, UsageStats right) {
+            return (int) (right.getLastTimeUsed() - left.getLastTimeUsed());
+        }
+    }
+
+    /**
+     * Enum represents the intervals for {@link android.app.usage.UsageStatsManager} so that
+     * values for intervals can be found by a String representation.
+     *
+     */
+    //VisibleForTesting
+    static enum StatsUsageInterval {
+        DAILY("Daily", UsageStatsManager.INTERVAL_DAILY),
+        WEEKLY("Weekly", UsageStatsManager.INTERVAL_WEEKLY),
+        MONTHLY("Monthly", UsageStatsManager.INTERVAL_MONTHLY),
+        YEARLY("Yearly", UsageStatsManager.INTERVAL_YEARLY);
+
+        private int mInterval;
+        private String mStringRepresentation;
+
+        StatsUsageInterval(String stringRepresentation, int interval) {
+            mStringRepresentation = stringRepresentation;
+            mInterval = interval;
+        }
+
+        static StatsUsageInterval getValue(String stringRepresentation) {
+            for (StatsUsageInterval statsUsageInterval : values()) {
+                if (statsUsageInterval.mStringRepresentation.equals(stringRepresentation)) {
+                    return statsUsageInterval;
+                }
+            }
+            return null;
+        }
+    }
+}
diff --git a/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/CustomUsageStats.java b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/CustomUsageStats.java
new file mode 100644
index 0000000..b5b15c4
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/CustomUsageStats.java
@@ -0,0 +1,28 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.appusagestatistics;
+
+import android.app.usage.UsageStats;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Entity class represents usage stats and app icon.
+ */
+public class CustomUsageStats {
+    public UsageStats usageStats;
+    public Drawable appIcon;
+}
diff --git a/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/UsageListAdapter.java b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/UsageListAdapter.java
new file mode 100644
index 0000000..ab1d037
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/java/com/example/android/appusagestatistics/UsageListAdapter.java
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.appusagestatistics;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Provide views to RecyclerView with the directory entries.
+ */
+public class UsageListAdapter extends RecyclerView.Adapter<UsageListAdapter.ViewHolder> {
+
+    private List<CustomUsageStats> mCustomUsageStatsList = new ArrayList<>();
+    private DateFormat mDateFormat = new SimpleDateFormat();
+
+    /**
+     * Provide a reference to the type of views that you are using (custom ViewHolder)
+     */
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        private final TextView mPackageName;
+        private final TextView mLastTimeUsed;
+        private final ImageView mAppIcon;
+
+        public ViewHolder(View v) {
+            super(v);
+            mPackageName = (TextView) v.findViewById(R.id.textview_package_name);
+            mLastTimeUsed = (TextView) v.findViewById(R.id.textview_last_time_used);
+            mAppIcon = (ImageView) v.findViewById(R.id.app_icon);
+        }
+
+        public TextView getLastTimeUsed() {
+            return mLastTimeUsed;
+        }
+
+        public TextView getPackageName() {
+            return mPackageName;
+        }
+
+        public ImageView getAppIcon() {
+            return mAppIcon;
+        }
+    }
+
+    public UsageListAdapter() {
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+        View v = LayoutInflater.from(viewGroup.getContext())
+                .inflate(R.layout.usage_row, viewGroup, false);
+        return new ViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
+        viewHolder.getPackageName().setText(
+                mCustomUsageStatsList.get(position).usageStats.getPackageName());
+        long lastTimeUsed = mCustomUsageStatsList.get(position).usageStats.getLastTimeUsed();
+        viewHolder.getLastTimeUsed().setText(mDateFormat.format(new Date(lastTimeUsed)));
+        viewHolder.getAppIcon().setImageDrawable(mCustomUsageStatsList.get(position).appIcon);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mCustomUsageStatsList.size();
+    }
+
+    public void setCustomUsageStatsList(List<CustomUsageStats> customUsageStats) {
+        mCustomUsageStatsList = customUsageStats;
+    }
+}
\ No newline at end of file
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-hdpi/ic_default_app_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-hdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-hdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-hdpi/ic_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..3cf5e8b
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-mdpi/ic_default_app_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-mdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-mdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-mdpi/ic_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..51553c5
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-xhdpi/ic_default_app_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-xhdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-xhdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..5248000
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-xxhdpi/ic_default_app_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-xxhdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-xxhdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/system/AppUsageStatistics/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..7f16200
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/system/AppUsageStatistics/Application/src/main/res/layout/activity_app_usage_statistics.xml b/system/AppUsageStatistics/Application/src/main/res/layout/activity_app_usage_statistics.xml
new file mode 100644
index 0000000..2627c8b
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/layout/activity_app_usage_statistics.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/container"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             tools:context="com.example.android.appusagestatistics.AppUsageStatisticsActivity"
+             tools:ignore="MergeRootFrame" />
diff --git a/system/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml b/system/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml
new file mode 100644
index 0000000..1d567b7
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/layout/fragment_app_usage_statistics.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:gravity="center_vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:padding="@dimen/margin_medium">
+
+    <Button android:id="@+id/button_open_usage_setting"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/open_app_usage_setting"
+            android:visibility="gone"
+            />
+
+    <LinearLayout android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:orientation="horizontal"
+            >
+        <TextView android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:text="@string/time_span"
+                />
+
+        <Spinner android:id="@+id/spinner_time_span"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+    <android.support.v7.widget.RecyclerView
+            android:id="@+id/recyclerview_app_usage"
+            android:layout_marginLeft="@dimen/margin_small"
+            android:layout_marginRight="@dimen/margin_small"
+            android:scrollbars="vertical"
+            android:drawSelectorOnTop="true"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/system/AppUsageStatistics/Application/src/main/res/layout/usage_row.xml b/system/AppUsageStatistics/Application/src/main/res/layout/usage_row.xml
new file mode 100644
index 0000000..c16f039
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/layout/usage_row.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/usage_row_height"
+        >
+
+    <LinearLayout android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:orientation="horizontal"
+                  android:layout_centerVertical="true"
+                  android:gravity="center_vertical"
+            >
+        <ImageView android:id="@+id/app_icon"
+                   android:layout_width="wrap_content"
+                   android:layout_height="wrap_content"
+                />
+        <LinearLayout android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:layout_marginLeft="@dimen/margin_medium"
+                      android:orientation="vertical"
+                >
+
+            <TextView
+                    android:id="@+id/textview_package_name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    style="@style/PackageNameFont"
+                    />
+
+            <LinearLayout android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:orientation="horizontal"
+                    >
+
+                <TextView
+                        android:id="@+id/textview_last_time_used_label"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/last_time_used"
+                        />
+
+                <TextView
+                        android:id="@+id/textview_last_time_used"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        />
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+
+    <View android:id="@+id/divider"
+          android:layout_width="match_parent"
+          android:layout_height="1dp"
+          android:background="#bbbbbb"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/system/AppUsageStatistics/Application/src/main/res/values/dimens.xml b/system/AppUsageStatistics/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..d1741c3
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+    <dimen name="usage_row_height">72dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/system/AppUsageStatistics/Application/src/main/res/values/navigation_items.xml b/system/AppUsageStatistics/Application/src/main/res/values/navigation_items.xml
new file mode 100644
index 0000000..b36a4a5
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/values/navigation_items.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <string-array name="action_list">
+        <item>Daily</item>
+        <item>Weekly</item>
+        <item>Monthly</item>
+        <item>Yearly</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/system/AppUsageStatistics/Application/src/main/res/values/strings.xml b/system/AppUsageStatistics/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..df769b5
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <string name="open_app_usage_setting">Open Apps with usage access settings</string>
+    <string name="last_time_used">"Last time used: "</string>
+    <string name="time_span">"Time span: "</string>
+    <string name="explanation_access_to_appusage_is_not_enabled">Failed to retrieve app usage
+        statistics. You may need to enable access
+        for this app through Settings > Security > Apps with usage access
+    </string>
+</resources>
diff --git a/system/AppUsageStatistics/Application/src/main/res/values/styles.xml b/system/AppUsageStatistics/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..d400276
--- /dev/null
+++ b/system/AppUsageStatistics/Application/src/main/res/values/styles.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <style name="PackageNameFont" parent="@android:style/TextAppearance.Small">
+        <item name="android:textColor">#000000</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/system/AppUsageStatistics/Application/tests/src/com/example/android/appusagestatistics/AppUsageStatisticsActivityTests.java b/system/AppUsageStatistics/Application/tests/src/com/example/android/appusagestatistics/AppUsageStatisticsActivityTests.java
new file mode 100644
index 0000000..f2e3ea9
--- /dev/null
+++ b/system/AppUsageStatistics/Application/tests/src/com/example/android/appusagestatistics/AppUsageStatisticsActivityTests.java
@@ -0,0 +1,70 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package com.example.android.appusagestatistics;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * Tests for {@link AppUsageStatisticsActivity}.
+ */
+public class AppUsageStatisticsActivityTests
+        extends ActivityInstrumentationTestCase2<AppUsageStatisticsActivity> {
+
+    private AppUsageStatisticsActivity mTestActivity;
+    private AppUsageStatisticsFragment mTestFragment;
+
+    public AppUsageStatisticsActivityTests() {
+        super(AppUsageStatisticsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (AppUsageStatisticsFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(0);
+    }
+
+    /**
+     * Test if the test fixture has been set up correctly.
+     */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+}
diff --git a/system/AppUsageStatistics/Application/tests/src/com/example/android/appusagestatistics/AppUsageStatisticsFragmentTests.java b/system/AppUsageStatistics/Application/tests/src/com/example/android/appusagestatistics/AppUsageStatisticsFragmentTests.java
new file mode 100644
index 0000000..d8adb1c
--- /dev/null
+++ b/system/AppUsageStatistics/Application/tests/src/com/example/android/appusagestatistics/AppUsageStatisticsFragmentTests.java
@@ -0,0 +1,97 @@
+/*
+* Copyright 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.example.android.appusagestatistics;
+
+import com.example.android.appusagestatistics.AppUsageStatisticsFragment.StatsUsageInterval;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.List;
+
+/**
+ * Tests for {@link com.example.android.appusagestatistics.AppUsageStatisticsFragment}.
+ */
+public class AppUsageStatisticsFragmentTests
+        extends ActivityInstrumentationTestCase2<AppUsageStatisticsActivity> {
+
+    private AppUsageStatisticsActivity mTestActivity;
+    private AppUsageStatisticsFragment mTestFragment;
+
+    public AppUsageStatisticsFragmentTests() {
+        super(AppUsageStatisticsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (AppUsageStatisticsFragment)
+                mTestActivity.getSupportFragmentManager().getFragments().get(0);
+    }
+
+    /**
+     * Test if the test fixture has been set up correctly.
+     */
+    public void testPreconditions() {
+        assertNotNull(mTestFragment.mUsageStatsManager);
+        assertNotNull(mTestFragment.mUsageListAdapter);
+        assertNotNull(mTestFragment.mRecyclerView);
+        assertNotNull(mTestFragment.mLayoutManager);
+        assertNotNull(mTestFragment.mOpenUsageSettingButton);
+        assertNotNull(mTestFragment.mSpinner);
+    }
+
+    public void testStatsUsageInterval_getValue() {
+        assertTrue(StatsUsageInterval.DAILY == StatsUsageInterval.getValue("Daily"));
+        assertTrue(StatsUsageInterval.WEEKLY == StatsUsageInterval.getValue("Weekly"));
+        assertTrue(StatsUsageInterval.MONTHLY == StatsUsageInterval.getValue("Monthly"));
+        assertTrue(StatsUsageInterval.YEARLY == StatsUsageInterval.getValue("Yearly"));
+        assertNull(StatsUsageInterval.getValue("NonExistent"));
+    }
+
+    public void testGetUsageStatistics() {
+        List<UsageStats> usageStatsList = mTestFragment
+                .getUsageStatistics(UsageStatsManager.INTERVAL_DAILY);
+
+        // Whether the usageStatsList has any UsageStats depends on if the app is granted
+        // the access to App usage statistics.
+        // Only check non null here.
+        assertNotNull(usageStatsList);
+    }
+
+    public void testUpdateAppsList() {
+        List<UsageStats> usageStatsList = mTestFragment
+                .getUsageStatistics(UsageStatsManager.INTERVAL_DAILY);
+
+        mTestFragment.updateAppsList(usageStatsList);
+        getInstrumentation().waitForIdleSync();
+
+        // The result depends on if the app is granted the access to App usage statistics.
+        if (usageStatsList.size() == 0) {
+            assertTrue(mTestFragment.mUsageListAdapter.getItemCount() == 0);
+        } else {
+            assertTrue(mTestFragment.mUsageListAdapter.getItemCount() > 0);
+        }
+    }
+}
diff --git a/system/AppUsageStatistics/build.gradle b/system/AppUsageStatistics/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/system/AppUsageStatistics/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/system/AppUsageStatistics/buildSrc/build.gradle b/system/AppUsageStatistics/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/system/AppUsageStatistics/buildSrc/build.gradle
@@ -0,0 +1,15 @@
+repositories {
+    mavenCentral()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties b/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..8a27ebf
--- /dev/null
+++ b/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Dec 07 22:52:45 JST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/system/AppUsageStatistics/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to system/AppUsageStatistics/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/system/AppUsageStatistics/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to system/AppUsageStatistics/gradlew.bat
diff --git a/system/AppUsageStatistics/screenshots/screenshot-1.png b/system/AppUsageStatistics/screenshots/screenshot-1.png
new file mode 100644
index 0000000..ab0da86
--- /dev/null
+++ b/system/AppUsageStatistics/screenshots/screenshot-1.png
Binary files differ
diff --git a/system/AppUsageStatistics/screenshots/screenshot-2.png b/system/AppUsageStatistics/screenshots/screenshot-2.png
new file mode 100644
index 0000000..7663ebd
--- /dev/null
+++ b/system/AppUsageStatistics/screenshots/screenshot-2.png
Binary files differ
diff --git a/system/AppUsageStatistics/screenshots/web-icon.png b/system/AppUsageStatistics/screenshots/web-icon.png
new file mode 100755
index 0000000..68af9d0
--- /dev/null
+++ b/system/AppUsageStatistics/screenshots/web-icon.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/system/AppUsageStatistics/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to system/AppUsageStatistics/settings.gradle
diff --git a/system/AppUsageStatistics/template-params.xml b/system/AppUsageStatistics/template-params.xml
new file mode 100644
index 0000000..38f5781
--- /dev/null
+++ b/system/AppUsageStatistics/template-params.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<sample>
+    <name>AppUsageStatistics</name>
+    <group>System</group>
+    <package>com.example.android.appusagestatistics</package>
+
+    <minSdk>21</minSdk>
+
+    <dependency>com.android.support:recyclerview-v7:+</dependency>
+    <dependency>com.android.support:appcompat-v7:21.+</dependency>
+
+    <strings>
+        <intro>
+            <![CDATA[
+            This sample explains how to use App usage statistics API, which was introduced
+            in Android 5.0.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base" />
+
+    <metadata>
+        <status>DRAFT</status>
+        <categories>System</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/web-icon.png</icon>
+        <screenshots>
+            <img>screenshots/screenshot-1.png</img>
+            <img>screenshots/screenshot-2.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.usage.UsageStats</android>
+            <android>android.app.usage.UsageStatsManager</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic app showing how to use App usage statistics API to let users collect statistics related
+to usage of the applications.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+The [App usage statistics][1] API allows app developers to collect statistics related to usage of
+the applications. This API provides more detailed usage information than the deprecated
+[getRecentTasks()][2] method.
+
+This example illustrates how to use the App usage statistics API by showing the applications sorted
+by the timestamp of the last time each app was used.
+
+To use this API, you must first declare the `android.permission.PACKAGE_USAGE_STATS` permission
+in your manifest. The user must also enable access for this app through
+`Settings > Security > Apps with usage access`.
+
+To collect the statistics of the app usage, you need to first get the instance of
+[UsageStatsManager][3] by the following code:
+
+```java
+mUsageStatsManager = (UsageStatsManager) getActivity()
+       .getSystemService(Context.USAGE_STATS_SERVICE);
+```
+
+Then you can retrieve the statistics of the app usage by the following method:
+
+```java
+Calendar cal = Calendar.getInstance();
+cal.add(Calendar.YEAR, -1);
+List<UsageStats> queryUsageStats = mUsageStatsManager
+        .queryUsageStats(UsageStatsManager.INTERVAL_DAILY, cal.getTimeInMillis(),
+                System.currentTimeMillis());
+```
+
+The first argument of the [queryUsageStats()][4] is used for the time interval by which the
+stats are aggregated. The second and the third arguments are used for specifying the beginning
+and the end of the range of the stats to include in the results.
+
+[1]: https://developer.android.com/reference/android/app/usage/package-summary.html
+[2]: https://developer.android.com/reference/android/app/ActivityManager.html#getRecentTasks(int%2C%20int)
+[3]: https://developer.android.com/reference/android/app/usage/UsageStatsManager.html
+[4]: https://developer.android.com/reference/android/app/usage/UsageStatsManager.html#queryUsageStats(int%2C%20long%2C%20long)
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/testing/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties b/testing/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/testing/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties
+++ b/testing/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/DrawableTinting/Application/build.gradle b/ui/DrawableTinting/Application/build.gradle
index 5fa47b7..4509c6e 100644
--- a/ui/DrawableTinting/Application/build.gradle
+++ b/ui/DrawableTinting/Application/build.gradle
@@ -4,7 +4,7 @@
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.12.+'
+        classpath 'com.android.tools.build:gradle:1.0.0-rc1'
     }
 }
 
diff --git a/ui/DrawableTinting/gradle/wrapper/gradle-wrapper.properties b/ui/DrawableTinting/gradle/wrapper/gradle-wrapper.properties
index 56f685a..0c71e76 100644
--- a/ui/DrawableTinting/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/DrawableTinting/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/Interpolator/after.png b/ui/Interpolator/after.png
new file mode 100644
index 0000000..0373014
--- /dev/null
+++ b/ui/Interpolator/after.png
Binary files differ
diff --git a/ui/Interpolator/before.png b/ui/Interpolator/before.png
new file mode 100644
index 0000000..e28e4f9
--- /dev/null
+++ b/ui/Interpolator/before.png
Binary files differ
diff --git a/ui/Interpolator/gradle/wrapper/gradle-wrapper.properties b/ui/Interpolator/gradle/wrapper/gradle-wrapper.properties
index 56f685a..0c71e76 100644
--- a/ui/Interpolator/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/Interpolator/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/Interpolator/screenshots/icon-web.png b/ui/Interpolator/screenshots/icon-web.png
new file mode 100644
index 0000000..172851b
--- /dev/null
+++ b/ui/Interpolator/screenshots/icon-web.png
Binary files differ
diff --git a/ui/Interpolator/template-params.xml b/ui/Interpolator/template-params.xml
index 268ee8a..2433b5a 100644
--- a/ui/Interpolator/template-params.xml
+++ b/ui/Interpolator/template-params.xml
@@ -40,5 +40,46 @@
 
     <common src="logger"/>
     <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Design, Transitions, Input</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/before.png</img>
+            <img>screenshots/after.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.animation.ObjectAnimator</android>
+            <android>android.view.animation.Interpolator</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates the use of animation interpolators and path animations for
+`Material Design`.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+Material design is a comprehensive guide for visual, motion, and interaction
+design across platforms and devices. Starting with API level 21,
+[Android includes support for material design][1] apps.
+
+This sample demonstrates the use of `Material Design` animation interpolators
+and path animations. It uses an [ObjectAnimator][2] to animate two view properties,
+`Scale_X` and `Scale_Y`. Users can pick from a set of interpolator options. They
+can also define the duration of the animation.
+
+[1]: https://developer.android.com/training/material/index.html
+[2]: https://developer.android.com/guide/topics/graphics/prop-animation.html#object-animator
+
+]]>
+        </intro>
+    </metadata>
+
+
 
 </sample>
diff --git a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-hdpi/ic_launcher.png b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-hdpi/ic_launcher.png
index 6c0b5ee..c8342cb 100644
--- a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-mdpi/ic_launcher.png b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-mdpi/ic_launcher.png
index 4ce0b82..0e883dd 100644
--- a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 6ded707..4096b65 100644
--- a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index 74ae891..b00fc9b 100644
--- a/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/ui/accessibility/BasicAccessibility/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/accessibility/BasicAccessibility/gradle/wrapper/gradle-wrapper.properties b/ui/accessibility/BasicAccessibility/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/accessibility/BasicAccessibility/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/accessibility/BasicAccessibility/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/accessibility/BasicAccessibility/screenshots/icon-web.png b/ui/accessibility/BasicAccessibility/screenshots/icon-web.png
new file mode 100644
index 0000000..7164cad
--- /dev/null
+++ b/ui/accessibility/BasicAccessibility/screenshots/icon-web.png
Binary files differ
diff --git a/ui/accessibility/BasicAccessibility/screenshots/main.png b/ui/accessibility/BasicAccessibility/screenshots/main.png
new file mode 100644
index 0000000..0a0c1b2
--- /dev/null
+++ b/ui/accessibility/BasicAccessibility/screenshots/main.png
Binary files differ
diff --git a/ui/accessibility/BasicAccessibility/template-params.xml b/ui/accessibility/BasicAccessibility/template-params.xml
index af9d815..56b36eb 100644
--- a/ui/accessibility/BasicAccessibility/template-params.xml
+++ b/ui/accessibility/BasicAccessibility/template-params.xml
@@ -31,6 +31,41 @@
         </intro>
     </strings>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.View</android>
+            <android>android.view.accessibility.AccessibilityEvent</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates how to create an accessible application, using a mix of different widgets
+demonstrating different ways of adding accessibility markup to a UI.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample demonstrates how to create an accessible application, showing
+how to add accessibility to both framework and custom widgets.
+
+To run this sample as intended you will need to turn on TalkBack (or another text to speach system)
+on your Android device. Go to Settings > Accessibility > TalkBack to turn TalkBack on.
+
+When you run the sample with TalkBack mode on, TalkBack will read aloud the
+description of the in-focus widget.
+]]>
+        </intro>
+    </metadata>
+
     <template src="base"/>
     <common src="logger"/>
 
diff --git a/ui/actionbar/DoneBar/gradle/wrapper/gradle-wrapper.properties b/ui/actionbar/DoneBar/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/actionbar/DoneBar/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/actionbar/DoneBar/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/actionbar/DoneBar/screenshots/1-main.png b/ui/actionbar/DoneBar/screenshots/1-main.png
new file mode 100644
index 0000000..fbcb5e9
--- /dev/null
+++ b/ui/actionbar/DoneBar/screenshots/1-main.png
Binary files differ
diff --git a/ui/actionbar/DoneBar/screenshots/2-done-bar.png b/ui/actionbar/DoneBar/screenshots/2-done-bar.png
new file mode 100644
index 0000000..4979907
--- /dev/null
+++ b/ui/actionbar/DoneBar/screenshots/2-done-bar.png
Binary files differ
diff --git a/ui/actionbar/DoneBar/screenshots/3-done-button.png b/ui/actionbar/DoneBar/screenshots/3-done-button.png
new file mode 100644
index 0000000..50a4928
--- /dev/null
+++ b/ui/actionbar/DoneBar/screenshots/3-done-button.png
Binary files differ
diff --git a/ui/actionbar/DoneBar/screenshots/icon-web.png b/ui/actionbar/DoneBar/screenshots/icon-web.png
new file mode 100644
index 0000000..e43f833
--- /dev/null
+++ b/ui/actionbar/DoneBar/screenshots/icon-web.png
Binary files differ
diff --git a/ui/actionbar/DoneBar/template-params.xml b/ui/actionbar/DoneBar/template-params.xml
index c5d1741..ba0adfb 100644
--- a/ui/actionbar/DoneBar/template-params.xml
+++ b/ui/actionbar/DoneBar/template-params.xml
@@ -56,4 +56,48 @@
 
     <template src="base"/>
     <template src="ActivityCards"/>
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-done-bar.png</img>
+            <img>screenshots/3-done-button.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.ActionBar</android>
+        </api_refs>
+        <description>
+This sample shows how to create a custom view in the ActionBar to show a done button, using
+2 alternative layouts. This is well suited for simple data entry activities, where the only
+options for the user are to cancel or confirm the data changes.
+        </description>
+        <intro>
+API 11 introduced the [ActionBar][1] as a navigational UI element. In most cases, a custom view isn't required
+or even recommended, as using a menu xml file is all that is needed to add action icons to it. However, it is
+possible to use a custom view and it is useful in some cases.
+
+This sample contains two activities with custom views in their action bar, each with a "done" button. These are
+designed to be used with simple data entry screen where the only options for the user are to cancel or confirm
+the changes.
+
+1. Create a layout for the [ActionBar][2].
+2. In the activity, before setting the content view, inflate the view using the LayoutInflater.
+3. Use `getActionBar()` to get the ActionBar, and set the display options with `ActionBar.DISPLAY_SHOW_CUSTOM`.
+4. Set the custom view by calling `setCustomView(customActionBarView, new ActionBar.LayoutParams(
+ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT))` on the ActionBar.
+5. You can now set the content view of the Activity.
+
+
+[1]: http://developer.android.com/design/patterns/actionbar.html
+[2]: http://developer.android.com/reference/android/app/ActionBar.html
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/actionbarcompat/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/actionbarcompat/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/actionbarcompat/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/big_icon.png b/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/big_icon.png
new file mode 100644
index 0000000..5939283
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/big_icon.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/screenshot1.png b/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/screenshot1.png
new file mode 100644
index 0000000..6c7d8ba
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/screenshot1.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/screenshot2.png b/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/screenshot2.png
new file mode 100644
index 0000000..7c7ca17
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-Basic/screenshots/screenshot2.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-Basic/template-params.xml b/ui/actionbarcompat/ActionBarCompat-Basic/template-params.xml
index 91d60b2..4ef1123 100644
--- a/ui/actionbarcompat/ActionBarCompat-Basic/template-params.xml
+++ b/ui/actionbarcompat/ActionBarCompat-Basic/template-params.xml
@@ -19,7 +19,7 @@
     <group>UI</group>
     <package>com.example.android.actionbarcompat.basic</package>
 
-    <dependency>com.android.support:appcompat-v7:18.0.+</dependency>
+    <dependency>com.android.support:appcompat-v7:21.0.2</dependency>
 
     <!-- change minSdk if needed-->
     <minSdk>7</minSdk>
@@ -37,4 +37,62 @@
 
     <template src="base"/>
 
+<metadata>
+    <status>PUBLISHED</status>
+    <categories>Getting Started, UI</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>BEGINNER</level>
+    <!-- Dimensions: 512x512, PNG fomrat -->
+    <icon>screenshots/big-icon.png</icon>
+    <!-- Path to screenshots. Use <img> tags for each. -->
+    <screenshots>
+        <img>screenshots/screenshot1.png</img>
+        <img>screenshots/screenshot2.png</img>
+    </screenshots>
+    <!-- List of APIs that this sample should be cross-referenced under. Use <android>
+    for fully-qualified Framework class names ("android:" namespace).
+
+    Use <ext> for custom namespaces, if needed. See "Samples Index API" documentation
+    for more details. -->
+    <api_refs>
+        <android>android.app.ActionBar</android>
+        <android>android.support.v7.app.ActionBarActivity</android>
+        <android>android.support.v4.view.MenuItemCompat</android>
+    </api_refs>
+    <description>
+<![CDATA[
+This sample shows you how to use ActionBarCompat to create a basic Activity
+which displays action items. It covers inflating items from a menu resource,
+as well as adding an item in code.
+]]>
+    </description>
+    <intro>
+<![CDATA[
+Android 3.0 introduced the “action bar” control, a toolbar that is expected
+to be present in most types of applications. This control identifies the user
+location, and provides user actions and navigation modes.
+Using the action bar offers your users a familiar interface across applications
+that the system gracefully adapts for different screen configurations.
+
+Instantiating an action bar can be done by using the [ActionBar][1] API provided
+in API 11 or above. Support for older devices is provided by the
+[support library’s ActionBar][2] implementation, compatible back to API 7.
+This sample demonstrates using the support library APIs.
+
+Using an ActionBar with the support library requires the following steps:
+
+1. Create your activity by extending ActionBarActivity.
+2. Use (or extend) one of the Theme.AppCompat themes for your activity.
+
+Once this is done, action items will be created for any options menu items that
+would otherwise be created during when `[onCreateOptionsMenu()][3]` is called.
+
+[1]: http://developer.android.com/reference/android/app/ActionBar.html
+[2]: http://developer.android.com/reference/android/support/v7/app/ActionBar.html
+[3]: http://developer.android.com/reference/android/app/Activity.html#onCreateOptionsMenu(android.view.Menu)
+]]>
+    </intro>
+</metadata>
 </sample>
diff --git a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/1-main.png b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/1-main.png
new file mode 100644
index 0000000..f69ed96
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/1-main.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/2-popup.png b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/2-popup.png
new file mode 100644
index 0000000..b03862d
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/2-popup.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/icon-web.png b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/icon-web.png
new file mode 100644
index 0000000..5220b83
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/screenshots/icon-web.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/template-params.xml b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/template-params.xml
index 57ea914..d1985dd 100644
--- a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/template-params.xml
+++ b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/template-params.xml
@@ -19,7 +19,7 @@
     <group>UI</group>
     <package>com.example.android.actionbarcompat.listpopupmenu</package>
 
-    <dependency>com.android.support:appcompat-v7:+</dependency>
+    <dependency>com.android.support:appcompat-v7:21.0.2</dependency>
 
     <!-- change minSdk if needed-->
     <minSdk>7</minSdk>
@@ -35,4 +35,38 @@
 
     <template src="base"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-popup.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.v7.widget.PopupMenu</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample shows how to display a pop up menu using PopupMenu from the v7 appcompat library.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample displays a list of items and for each item, an icon can be clicked. When it is clicked, a pop up menu is shown, placed below the item, using the [PopupMenu][1] from the v7 appcompat support library.
+
+The sample uses [ListFragment][2] from the v4 support library to display the list. It shows how to instantiate and display the [PopupMenu][1], as well as how to use `setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener())` to process the user actions on the [PopupMenu][1].
+
+[1]: https://developer.android.com/reference/android/support/v7/widget/PopupMenu.html
+[2]: https://developer.android.com/reference/android/support/v4/app/ListFragment.html
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/template-params.xml b/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/template-params.xml
index b0d371e..03f6877 100644
--- a/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/template-params.xml
+++ b/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/template-params.xml
@@ -22,7 +22,7 @@
     <group>UI</group>
     <package>com.example.android.actionbarcompat.listviewmodalselect</package>
 
-    <dependency>com.android.support:appcompat-v7:18.0.+</dependency>
+    <dependency>com.android.support:appcompat-v7:21.0.2</dependency>
 
     <!-- change minSdk if needed-->
     <minSdk>7</minSdk>
diff --git a/ui/actionbarcompat/ActionBarCompat-SearchView/build.gradle b/ui/actionbarcompat/ActionBarCompat-SearchView/build.gradle
index 5161a84..248a459 100644
--- a/ui/actionbarcompat/ActionBarCompat-SearchView/build.gradle
+++ b/ui/actionbarcompat/ActionBarCompat-SearchView/build.gradle
@@ -4,7 +4,7 @@
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.5.+'
+        classpath 'com.android.tools.build:gradle:1.0.0-rc1'
     }
 }
 
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/Application/src/main/java/com/example/android/actionbarcompat/shareactionprovider/MainActivity.java b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/Application/src/main/java/com/example/android/actionbarcompat/shareactionprovider/MainActivity.java
index b8cc900..545764c 100644
--- a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/Application/src/main/java/com/example/android/actionbarcompat/shareactionprovider/MainActivity.java
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/Application/src/main/java/com/example/android/actionbarcompat/shareactionprovider/MainActivity.java
@@ -83,6 +83,10 @@
         // Now get the ShareActionProvider from the item
         mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);
 
+        // Get the ViewPager's current item position and set its ShareIntent.
+        int currentViewPagerItem = ((ViewPager) findViewById(R.id.viewpager)).getCurrentItem();
+        setShareIntent(currentViewPagerItem);
+
         return super.onCreateOptionsMenu(menu);
     }
     // END_INCLUDE(get_sap)
@@ -151,6 +155,19 @@
         }
     };
 
+    private void setShareIntent(int position) {
+        // BEGIN_INCLUDE(update_sap)
+        if (mShareActionProvider != null) {
+            // Get the currently selected item, and retrieve it's share intent
+            ContentItem item = mItems.get(position);
+            Intent shareIntent = item.getShareIntent(MainActivity.this);
+
+            // Now update the ShareActionProvider with the new share intent
+            mShareActionProvider.setShareIntent(shareIntent);
+        }
+        // END_INCLUDE(update_sap)
+    }
+
     /**
      * A OnPageChangeListener used to update the ShareActionProvider's share intent when a new item
      * is selected in the ViewPager.
@@ -165,16 +182,7 @@
 
         @Override
         public void onPageSelected(int position) {
-            // BEGIN_INCLUDE(update_sap)
-            if (mShareActionProvider != null) {
-                // Get the currently selected item, and retrieve it's share intent
-                ContentItem item = mItems.get(position);
-                Intent shareIntent = item.getShareIntent(MainActivity.this);
-
-                // Now update the ShareActionProvider with the new share intent
-                mShareActionProvider.setShareIntent(shareIntent);
-            }
-            // END_INCLUDE(update_sap)
+            setShareIntent(position);
         }
 
         @Override
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/1-image.png b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/1-image.png
new file mode 100644
index 0000000..3947911
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/1-image.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/2-text.png b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/2-text.png
new file mode 100644
index 0000000..abf7e7d
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/2-text.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/icon-web.png b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/icon-web.png
new file mode 100644
index 0000000..c16d884
--- /dev/null
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/screenshots/icon-web.png
Binary files differ
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml
index 4378f28..177f448 100644
--- a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml
@@ -19,7 +19,7 @@
     <group>UI</group>
     <package>com.example.android.actionbarcompat.shareactionprovider</package>
 
-    <dependency>com.android.support:appcompat-v7:18.0.+</dependency>
+    <dependency>com.android.support:appcompat-v7:21.0.2</dependency>
 
     <!-- change minSdk if needed-->
     <minSdk>7</minSdk>
@@ -35,4 +35,41 @@
 
     <template src="base"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, ActionBar</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-gridview.png</img>
+            <img>screenshots/2-detail.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.v7.widget.ShareActionProvider</android>
+            <android>android.support.v7.app.ActionBarActivity</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample shows you how a provide a context-sensitive ShareActionProvider with
+ActionBarCompat, backwards compatible to API v7.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+The sample contains a [`ViewPager`][1] which displays content of differing types: image and
+text. When a new item is selected in the ViewPager, the [`ShareActionProvider`][2] is updated with
+a share intent specific to that content.
+
+This Activity extends from [`ActionBarActivity`][3], which provides all of the function
+necessary to display a compatible Action Bar on devices running Android v2.1+.
+
+[1]: http://developer.android.com/reference/android/support/v4/view/ViewPager.html
+[2]: http://developer.android.com/reference/android/widget/ShareActionProvider.html
+[3]: https://developer.android.com/reference/android/support/v7/app/ActionBarActivity.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/actionbarcompat/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/actionbarcompat/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/actionbarcompat/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/libs/volley.jar b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/libs/volley.jar
deleted file mode 100644
index a38dac4..0000000
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/libs/volley.jar
+++ /dev/null
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/DetailActivity.java b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/DetailActivity.java
index 741cd26..17d1b2a 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/DetailActivity.java
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/DetailActivity.java
@@ -16,13 +16,14 @@
 
 package com.example.android.activityscenetransitionbasic;
 
-import com.android.volley.toolbox.ImageLoader;
-import com.android.volley.toolbox.NetworkImageView;
-import com.android.volley.toolbox.Volley;
+import com.squareup.picasso.Picasso;
 
 import android.app.Activity;
-import android.graphics.Bitmap;
+import android.os.Build;
 import android.os.Bundle;
+import android.support.v4.view.ViewCompat;
+import android.transition.Transition;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 /**
@@ -40,23 +41,20 @@
     // View name of the header title. Used for activity scene transitions
     public static final String VIEW_NAME_HEADER_TITLE = "detail:header:title";
 
-    private NetworkImageView mHeaderImageView;
+    private ImageView mHeaderImageView;
     private TextView mHeaderTitle;
 
-    private ImageLoader mImageLoader;
+    private Item mItem;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.details);
 
-        // Construct an ImageLoader instance so that we can load images from the network
-        mImageLoader = new ImageLoader(Volley.newRequestQueue(this), ImageMemoryCache.INSTANCE);
-
         // Retrieve the correct Item instance, using the ID provided in the Intent
-        Item item = Item.getItem(getIntent().getIntExtra(EXTRA_PARAM_ID, 0));
+        mItem = Item.getItem(getIntent().getIntExtra(EXTRA_PARAM_ID, 0));
 
-        mHeaderImageView = (NetworkImageView) findViewById(R.id.imageview_header);
+        mHeaderImageView = (ImageView) findViewById(R.id.imageview_header);
         mHeaderTitle = (TextView) findViewById(R.id.textview_title);
 
         // BEGIN_INCLUDE(detail_set_view_name)
@@ -65,31 +63,97 @@
          * This could be done in the layout XML, but exposing it via static variables allows easy
          * querying from other Activities
          */
-        mHeaderImageView.setViewName(VIEW_NAME_HEADER_IMAGE);
-        mHeaderTitle.setViewName(VIEW_NAME_HEADER_TITLE);
+        ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
+        ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
         // END_INCLUDE(detail_set_view_name)
 
-        loadItem(item);
+        loadItem();
     }
 
-    private void loadItem(Item item) {
+    private void loadItem() {
         // Set the title TextView to the item's name and author
-        mHeaderTitle.setText(getString(R.string.image_header, item.getName(), item.getAuthor()));
+        mHeaderTitle.setText(getString(R.string.image_header, mItem.getName(), mItem.getAuthor()));
 
-        final ImageMemoryCache cache = ImageMemoryCache.INSTANCE;
-        Bitmap thumbnailImage = cache.getBitmapFromUrl(item.getThumbnailUrl());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && addTransitionListener()) {
+            // If we're running on Lollipop and we have added a listener to the shared element
+            // transition, load the thumbnail. The listener will load the full-size image when
+            // the transition is complete.
+            loadThumbnail();
+        } else {
+            // If all other cases we should just load the full-size image now
+            loadFullSizeImage();
+        }
+    }
 
-        // Check to see if we already have the thumbnail sized image in the cache. If so, start
-        // loading the full size image and display the thumbnail as a placeholder.
-        if (thumbnailImage != null) {
-            mHeaderImageView.setImageUrl(item.getPhotoUrl(), mImageLoader);
-            mHeaderImageView.setImageBitmap(thumbnailImage);
-            return;
+    /**
+     * Load the item's thumbnail image into our {@link ImageView}.
+     */
+    private void loadThumbnail() {
+        Picasso.with(mHeaderImageView.getContext())
+                .load(mItem.getThumbnailUrl())
+                .noFade()
+                .into(mHeaderImageView);
+    }
+
+    /**
+     * Load the item's full-size image into our {@link ImageView}.
+     */
+    private void loadFullSizeImage() {
+        Picasso.with(mHeaderImageView.getContext())
+                .load(mItem.getPhotoUrl())
+                .noFade()
+                .noPlaceholder()
+                .into(mHeaderImageView);
+    }
+
+    /**
+     * Try and add a {@link Transition.TransitionListener} to the entering shared element
+     * {@link Transition}. We do this so that we can load the full-size image after the transition
+     * has completed.
+     *
+     * @return true if we were successful in adding a listener to the enter transition
+     */
+    private boolean addTransitionListener() {
+        final Transition transition = getWindow().getSharedElementEnterTransition();
+
+        if (transition != null) {
+            // There is an entering shared element transition so add a listener to it
+            transition.addListener(new Transition.TransitionListener() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    // As the transition has ended, we can now load the full-size image
+                    loadFullSizeImage();
+
+                    // Make sure we remove ourselves as a listener
+                    transition.removeListener(this);
+                }
+
+                @Override
+                public void onTransitionStart(Transition transition) {
+                    // No-op
+                }
+
+                @Override
+                public void onTransitionCancel(Transition transition) {
+                    // Make sure we remove ourselves as a listener
+                    transition.removeListener(this);
+                }
+
+                @Override
+                public void onTransitionPause(Transition transition) {
+                    // No-op
+                }
+
+                @Override
+                public void onTransitionResume(Transition transition) {
+                    // No-op
+                }
+            });
+            return true;
         }
 
-        // If we get here then we do not have either the full size or the thumbnail in the cache.
-        // Here we just load the full size and make do.
-        mHeaderImageView.setImageUrl(item.getPhotoUrl(), mImageLoader);
+        // If we reach here then we have not added a listener
+        return false;
     }
 
 }
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/ImageMemoryCache.java b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/ImageMemoryCache.java
deleted file mode 100644
index 53d47ba..0000000
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/ImageMemoryCache.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.activityscenetransitionbasic;
-
-import com.android.volley.toolbox.ImageLoader;
-
-import android.graphics.Bitmap;
-import android.util.LruCache;
-
-import java.util.Map;
-
-/**
- * An image memory cache implementation for Volley which allows the retrieval of entires via a URL.
- * Volley internally inserts items with a key which is a combination of a the size of the image,
- * and the url.
- *
- * This class provide the method {@link #getBitmapFromUrl(String)} which allows the retrieval of
- * a bitmap solely on the URL.
- */
-public class ImageMemoryCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
-
-    /**
-     * Singleton instance which has it's maximum size set to be 1/8th of the allowed memory size.
-     */
-    public static final ImageMemoryCache INSTANCE = new ImageMemoryCache(
-            (int) (Runtime.getRuntime().maxMemory() / 8));
-
-    // Cache the last created snapshot
-    private Map<String, Bitmap> mLastSnapshot;
-
-    private ImageMemoryCache(int maxSize) {
-        super(maxSize);
-    }
-
-    public Bitmap getBitmapFromUrl(String url) {
-        // If we do not have a snapshot to use, generate one
-        if (mLastSnapshot == null) {
-            mLastSnapshot = snapshot();
-        }
-
-        // Iterate through the snapshot to find any entries which match our url
-        for (Map.Entry<String, Bitmap> entry : mLastSnapshot.entrySet()) {
-            if (url.equals(extractUrl(entry.getKey()))) {
-                // We've found an entry with the same url, return the bitmap
-                return entry.getValue();
-            }
-        }
-
-        // We didn't find an entry, so return null
-        return null;
-    }
-
-    @Override
-    public Bitmap getBitmap(String key) {
-        return get(key);
-    }
-
-    @Override
-    public void putBitmap(String key, Bitmap bitmap) {
-        put(key, bitmap);
-
-        // An entry has been added, so invalidate the snapshot
-        mLastSnapshot = null;
-    }
-
-    @Override
-    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
-        super.entryRemoved(evicted, key, oldValue, newValue);
-
-        // An entry has been removed, so invalidate the snapshot
-        mLastSnapshot = null;
-    }
-
-    private static String extractUrl(String key) {
-        return key.substring(key.indexOf("http"));
-    }
-
-    @Override
-    protected int sizeOf(String key, Bitmap value) {
-        return value.getAllocationByteCount();
-    }
-}
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/MainActivity.java b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/MainActivity.java
index 3630990..9eed44a 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/MainActivity.java
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/java/com/example/android/activityscenetransitionbasic/MainActivity.java
@@ -16,20 +16,20 @@
 
 package com.example.android.activityscenetransitionbasic;
 
-import com.android.volley.toolbox.ImageLoader;
-import com.android.volley.toolbox.NetworkImageView;
-import com.android.volley.toolbox.Volley;
+import com.squareup.picasso.Picasso;
 
 import android.app.Activity;
-import android.app.ActivityOptions;
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.Pair;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.GridView;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 /**
@@ -42,16 +42,11 @@
     private GridView mGridView;
     private GridAdapter mAdapter;
 
-    private ImageLoader mImageLoader;
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.grid);
 
-        // Retrieve the ImageLoader we are going to use for NetworkImageView
-        mImageLoader = new ImageLoader(Volley.newRequestQueue(this), ImageMemoryCache.INSTANCE);
-
         // Setup the GridView and set the adapter
         mGridView = (GridView) findViewById(R.id.grid);
         mGridView.setOnItemClickListener(this);
@@ -74,23 +69,21 @@
         // BEGIN_INCLUDE(start_activity)
         /**
          * Now create an {@link android.app.ActivityOptions} instance using the
-         * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity, android.util.Pair[])} factory method.
+         * {@link ActivityOptionsCompat#makeSceneTransitionAnimation(Activity, Pair[])} factory
+         * method.
          */
-        ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
+        ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
                 this,
 
                 // Now we provide a list of Pair items which contain the view we can transitioning
                 // from, and the name of the view it is transitioning to, in the launched activity
-                new Pair<View, String>(
-                        view.findViewById(R.id.imageview_item),
+                new Pair<View, String>(view.findViewById(R.id.imageview_item),
                         DetailActivity.VIEW_NAME_HEADER_IMAGE),
-                new Pair<View, String>(
-                        view.findViewById(R.id.textview_name),
-                        DetailActivity.VIEW_NAME_HEADER_TITLE)
-        );
+                new Pair<View, String>(view.findViewById(R.id.textview_name),
+                        DetailActivity.VIEW_NAME_HEADER_TITLE));
 
         // Now we can start the Activity, providing the activity options as a bundle
-        startActivity(intent, activityOptions.toBundle());
+        ActivityCompat.startActivity(this, intent, activityOptions.toBundle());
         // END_INCLUDE(start_activity)
     }
 
@@ -123,22 +116,13 @@
             final Item item = getItem(position);
 
             // Load the thumbnail image
-            NetworkImageView image = (NetworkImageView) view.findViewById(R.id.imageview_item);
-            image.setImageUrl(item.getThumbnailUrl(), mImageLoader);
+            ImageView image = (ImageView) view.findViewById(R.id.imageview_item);
+            Picasso.with(image.getContext()).load(item.getThumbnailUrl()).into(image);
 
             // Set the TextView's contents
             TextView name = (TextView) view.findViewById(R.id.textview_name);
             name.setText(item.getName());
 
-            // BEGIN_INCLUDE(grid_set_view_name)
-            /**
-             * As we're in an adapter we need to set each view's name dynamically, using the
-             * item's ID so that the names are unique.
-             */
-            image.setViewName("grid:image:" + item.getId());
-            name.setViewName("grid:name:" + item.getId());
-            // END_INCLUDE(grid_set_view_name)
-
             return view;
         }
     }
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/details.xml b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/details.xml
index e61212e..8ca4634 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/details.xml
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/details.xml
@@ -27,7 +27,7 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content">
 
-            <com.android.volley.toolbox.NetworkImageView
+            <ImageView
                   android:id="@+id/imageview_header"
                   android:layout_width="match_parent"
                   android:layout_height="match_parent"
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/grid_item.xml b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/grid_item.xml
index 1d28dba..e79f0eb 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/grid_item.xml
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/layout/grid_item.xml
@@ -23,7 +23,7 @@
           android:layout_width="match_parent"
           android:layout_height="wrap_content">
 
-        <com.android.volley.toolbox.NetworkImageView
+        <ImageView
               android:id="@+id/imageview_item"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/transition/grid_detail_transition.xml b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/transition/grid_detail_transition.xml
index 40be3d4..3091cd2 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/transition/grid_detail_transition.xml
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/transition/grid_detail_transition.xml
@@ -16,33 +16,15 @@
 
 <!--
     The transitions which us used for the entrance and exit of shared elements. Here we declare
-    two different transitions which are targeting to specific views.
+    two different transitions which are targeting specific views.
 -->
 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
 
     <!-- changeBounds is used for the TextViews which are shared -->
-    <changeBounds>
-        <!--
-            Set this transitions target IDs to be those which point to the TextViews in both the
-            starting and result Activities
-        -->
-        <targets>
-            <target android:targetId="@id/textview_name" />
-            <target android:targetId="@id/textview_title" />
-        </targets>
-    </changeBounds>
+    <changeBounds />
 
-    <!-- moveImage is used for the ImageViews which are shared -->
-    <moveImage>
-        <!--
-            Set this transitions target IDs to be those which point to the ImageViews in both the
-            starting and result Activities
-        -->
-        <targets>
-            <target android:targetId="@id/imageview_header" />
-            <target android:targetId="@id/imageview_item" />
-        </targets>
-    </moveImage>
+    <!-- changeImageTransform is used for the ImageViews which are shared -->
+    <changeImageTransform />
 
 </transitionSet>
 
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values-v21/styles.xml b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values-v21/styles.xml
index fd212b3..4fee48d 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values-v21/styles.xml
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values-v21/styles.xml
@@ -17,9 +17,6 @@
 <resources>
 
     <style name="Theme.Sample" parent="Theme.Base">
-        <!-- Set the windowContentTransitions flag to enable Activity scene transitions -->
-        <item name="android:windowContentTransitions">true</item>
-
         <!-- Set the transitions which are used for the entrance and exit of shared elements -->
         <item name="android:windowSharedElementEnterTransition">
             @transition/grid_detail_transition
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values/strings.xml b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values/strings.xml
index 2aa0d3b..669c127 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values/strings.xml
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/Application/src/main/res/values/strings.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
 <resources>
 
     <string name="bacon_ipsum">
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties b/ui/activityscenetransition/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties
index eca7f2f..46b1748 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/1-main.png b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/1-main.png
new file mode 100644
index 0000000..539e3e4
--- /dev/null
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/1-main.png
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/2-transition.png b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/2-transition.png
new file mode 100644
index 0000000..96d27d0
--- /dev/null
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/2-transition.png
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/3-transition.png b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/3-transition.png
new file mode 100644
index 0000000..08e8f54
--- /dev/null
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/3-transition.png
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/4-detail.png b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/4-detail.png
new file mode 100644
index 0000000..91d65be
--- /dev/null
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/4-detail.png
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/icon-web.png b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/icon-web.png
new file mode 100644
index 0000000..715c319
--- /dev/null
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/screenshots/icon-web.png
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/template-params.xml b/ui/activityscenetransition/ActivitySceneTransitionBasic/template-params.xml
index ae954d4..85e24b3 100644
--- a/ui/activityscenetransition/ActivitySceneTransitionBasic/template-params.xml
+++ b/ui/activityscenetransition/ActivitySceneTransitionBasic/template-params.xml
@@ -25,20 +25,58 @@
     <minSdk>21</minSdk>
     <compileSdkVersion>21</compileSdkVersion>
 
-    <dependency_external>'libs/volley.jar'</dependency_external>
+    <dependency>com.squareup.picasso:picasso:2.4.0</dependency>
 
     <strings>
         <intro>
             <![CDATA[
             Demonstrates how to the use Activity scene transitions when transitions
-            from one Activity to another. Uses a combination of moveImage and changeBounds
-            to nicely transition a grid of images to an Activity with a large image and detail
-            text.
+            from one Activity to another. Uses a combination of changeImageTransform and
+            changeBounds to nicely transition a grid of images to an Activity with a large image
+            and detail text.
             ]]>
         </intro>
     </strings>
 
+    <attribution>
+        <copyright>Copyright 2013 Square, Inc.</copyright>
+        <license>APACHE2</license>
+    </attribution>
+
     <template src="base"/>
     <common src="logger"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-transition.png</img>
+            <img>screenshots/3-transition.png</img>
+            <img>screenshots/4-detail.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.transition.Transition</android>
+            <android>android.app.ActivityOptions</android>
+        </api_refs>
+        <description>
+This sample shows how to use scene transitions from one Activity to another in Lollipop.
+Uses a combination of changeImageTransform and changeBounds to transition a grid of images
+to an Activity with a large image and detail text.
+        </description>
+        <intro>
+Android Lollipop has introduced the ability to transition between activities by using a shared element.
+This sample demonstrates how to do this using the theme of your application.
+
+See [Defining Custom Animations][1] for all the details on how to do this.
+
+[1]: https://developer.android.com/training/material/animations.html#Transitions
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-hdpi/ic_launcher.png b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-hdpi/ic_launcher.png
index b1efaf4..15e44c5 100644
--- a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-mdpi/ic_launcher.png b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-mdpi/ic_launcher.png
index f5f9244..ba0bd22 100644
--- a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..42d49b8 100644
--- a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..3624eef 100644
--- a/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/ui/activitytasks/DocumentCentricApps/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/gradle/wrapper/gradle-wrapper.properties b/ui/activitytasks/DocumentCentricApps/gradle/wrapper/gradle-wrapper.properties
index 56f685a..0c71e76 100644
--- a/ui/activitytasks/DocumentCentricApps/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/activitytasks/DocumentCentricApps/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/activitytasks/DocumentCentricApps/screenshots/1-activity.png b/ui/activitytasks/DocumentCentricApps/screenshots/1-activity.png
new file mode 100644
index 0000000..9c6c411
--- /dev/null
+++ b/ui/activitytasks/DocumentCentricApps/screenshots/1-activity.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/screenshots/2-overview.png b/ui/activitytasks/DocumentCentricApps/screenshots/2-overview.png
new file mode 100644
index 0000000..36ad9df
--- /dev/null
+++ b/ui/activitytasks/DocumentCentricApps/screenshots/2-overview.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/screenshots/big_icon.png b/ui/activitytasks/DocumentCentricApps/screenshots/big_icon.png
new file mode 100644
index 0000000..84ce4cf
--- /dev/null
+++ b/ui/activitytasks/DocumentCentricApps/screenshots/big_icon.png
Binary files differ
diff --git a/ui/activitytasks/DocumentCentricApps/template-params.xml b/ui/activitytasks/DocumentCentricApps/template-params.xml
index 589e9f1..0df82b9 100644
--- a/ui/activitytasks/DocumentCentricApps/template-params.xml
+++ b/ui/activitytasks/DocumentCentricApps/template-params.xml
@@ -15,8 +15,6 @@
  limitations under the License.
 -->
 
-
-
 <sample>
     <name>DocumentCentricRecents</name>
     <group>UI</group>
@@ -38,4 +36,44 @@
 
     <template src="base"/>
 
+<metadata>
+    <status>PUBLISHED</status>
+    <categories>UI</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>INTERMEDIATE</level>
+    <icon>screenshots/big_icon.png</icon>
+    <screenshots>
+        <img>screenshots/1-activity.png</img>
+        <img>screenshots/2-overview.png</img>
+    </screenshots>
+    <api_refs>
+        <android>android.app.Activity</android>
+        <android>android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT</android>
+    </api_refs>
+    <description>
+<![CDATA[
+Sample demonstrating the basic usage of the new 'Document Centric Apps' API.
+It let's you create new documents in the system overview menu and persists its
+state through reboots.
+]]>
+    </description>
+    <intro>
+<![CDATA[
+This sample shows the basic usage of the new Document-Centric Apps API. The new
+API modifies the meaning of the Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET flag, which is
+now deprecated. In versions before L it serves to define a boundary between the main task and a
+subtask. The subtask holds a different thumbnail and all activities in it are finished when the
+task is reset. In L this flag causes a full break with the task that launched it. As such it has
+been renamed to Intent#FLAG_ACTIVITY_NEW_DOCUMENT.
+
+This sample mainly uses Intent flags in code. But Activities can also specify in their manifests
+that they shall always be launched into a new task in the above manner using the new activity
+attribute documentLaunchMode which may take on one of three values, “intoExisting” equivalent to
+NEW_DOCUMENT, “always” equivalent to NEW_DOCUMENT | MULTIPLE_TASK, “none” the default, and
+“never” which will negate the effect of any attempt to launch the activity with NEW_DOCUMENT.
+]]>
+    </intro>
+</metadata>
 </sample>
diff --git a/ui/activitytasks/DocumentCentricRelinquishIdentity/gradle/wrapper/gradle-wrapper.properties b/ui/activitytasks/DocumentCentricRelinquishIdentity/gradle/wrapper/gradle-wrapper.properties
index 56f685a..0c71e76 100644
--- a/ui/activitytasks/DocumentCentricRelinquishIdentity/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/activitytasks/DocumentCentricRelinquishIdentity/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/graphics/DisplayingBitmaps/gradle/wrapper/gradle-wrapper.properties b/ui/graphics/DisplayingBitmaps/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/graphics/DisplayingBitmaps/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/graphics/DisplayingBitmaps/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/graphics/DisplayingBitmaps/screenshots/1-gridview.png b/ui/graphics/DisplayingBitmaps/screenshots/1-gridview.png
new file mode 100644
index 0000000..11f9ab7
--- /dev/null
+++ b/ui/graphics/DisplayingBitmaps/screenshots/1-gridview.png
Binary files differ
diff --git a/ui/graphics/DisplayingBitmaps/screenshots/2-detail.png b/ui/graphics/DisplayingBitmaps/screenshots/2-detail.png
new file mode 100644
index 0000000..32babd9
--- /dev/null
+++ b/ui/graphics/DisplayingBitmaps/screenshots/2-detail.png
Binary files differ
diff --git a/ui/graphics/DisplayingBitmaps/screenshots/icon-web.png b/ui/graphics/DisplayingBitmaps/screenshots/icon-web.png
new file mode 100644
index 0000000..7bad768
--- /dev/null
+++ b/ui/graphics/DisplayingBitmaps/screenshots/icon-web.png
Binary files differ
diff --git a/ui/graphics/DisplayingBitmaps/template-params.xml b/ui/graphics/DisplayingBitmaps/template-params.xml
index b849f97..5a25b22 100644
--- a/ui/graphics/DisplayingBitmaps/template-params.xml
+++ b/ui/graphics/DisplayingBitmaps/template-params.xml
@@ -39,4 +39,43 @@
     <template src="base"/>
     <common src="logger"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Background, Views</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-gridview.png</img>
+            <img>screenshots/2-detail.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.widget.ImageView</android>
+            <android>android.widget.GridView</android>
+            <android>android.graphics.BitmapFactory</android>
+            <android>android.os.AsyncTask</android>
+            <android>android.util.LruCache</android>
+            <android>android.support.v4.view.ViewPager</android>
+        </api_refs>
+        <description>
+<![CDATA[
+Sample demonstrating how to load large bitmaps efficiently off the main UI thread,
+caching bitmaps (both in memory and on disk), managing bitmap memory and displaying
+bitmaps in UI elements such as ViewPager and ListView/GridView.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This is a sample application for the Android Training class [Displaying Bitmaps Efficiently][1].
+
+It demonstrates how to load large bitmaps efficiently off the main UI thread, caching
+bitmaps (both in memory and on disk), managing bitmap memory and displaying bitmaps
+in UI elements such as ViewPager and ListView/GridView.
+
+[1]: http://developer.android.com/training/displaying-bitmaps/
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/graphics/PdfRendererBasic/gradle/wrapper/gradle-wrapper.properties b/ui/graphics/PdfRendererBasic/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/graphics/PdfRendererBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/graphics/PdfRendererBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/graphics/PdfRendererBasic/screenshots/icon-web.png b/ui/graphics/PdfRendererBasic/screenshots/icon-web.png
new file mode 100755
index 0000000..5ed38dc
--- /dev/null
+++ b/ui/graphics/PdfRendererBasic/screenshots/icon-web.png
Binary files differ
diff --git a/ui/graphics/PdfRendererBasic/screenshots/main.png b/ui/graphics/PdfRendererBasic/screenshots/main.png
new file mode 100644
index 0000000..faff891
--- /dev/null
+++ b/ui/graphics/PdfRendererBasic/screenshots/main.png
Binary files differ
diff --git a/ui/graphics/PdfRendererBasic/template-params.xml b/ui/graphics/PdfRendererBasic/template-params.xml
index a87261d..f8607b2 100644
--- a/ui/graphics/PdfRendererBasic/template-params.xml
+++ b/ui/graphics/PdfRendererBasic/template-params.xml
@@ -43,4 +43,55 @@
 
     <template src="base"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Graphics</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.graphics.pdf.PdfRenderer</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates how to display PDF document on screen using
+the PdfRenderer introduced in Android 5.0 Lollipop.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+You can now render PDF document pages into bitmap images for printing by using
+the new [PdfRenderer][1] class. You must specify a [ParcelFileDescriptor][2]
+that is seekable (that is, the content can be randomly accessed) on which the
+system writes the the printable content. Your app can obtain a page for
+rendering with [openPage()][3], then call [render()][4] to turn the opened
+[PdfRenderer.Page][5] into a bitmap.
+
+This sample loads the PDF from assets. Contents of assets are compressed by
+default, but we disable it since PdfRenderer class cannot handle it.
+
+```groovy
+android {
+    aaptOptions {
+        noCompress "pdf"
+    }
+}
+```
+
+[1]: https://developer.android.com/reference/android/graphics/pdf/PdfRenderer.html
+[2]: https://developer.android.com/reference/android/os/ParcelFileDescriptor.html
+[3]: https://developer.android.com/reference/android/graphics/pdf/PdfRenderer.html#openPage(int)
+[4]: https://developer.android.com/reference/android/graphics/pdf/PdfRenderer.Page.html#render(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Matrix, int)
+[5]: https://developer.android.com/reference/android/graphics/pdf/PdfRenderer.Page.html
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-hdpi/ic_launcher.png b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-hdpi/ic_launcher.png
index b1efaf4..bb97e43 100644
--- a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-mdpi/ic_launcher.png b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-mdpi/ic_launcher.png
index f5f9244..4ee8ad3 100644
--- a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..b3b78e9 100644
--- a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..82a4165 100644
--- a/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/ui/holo/BorderlessButtons/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/holo/BorderlessButtons/README.md b/ui/holo/BorderlessButtons/README.md
index 7db92f5..3177580 100644
--- a/ui/holo/BorderlessButtons/README.md
+++ b/ui/holo/BorderlessButtons/README.md
@@ -1,16 +1,37 @@
 Android BorderlessButtons Sample
-==============================
+===================================
 
-This sample demonstrates the use of borderless buttons, bottom button bars
-(OK and Cancel) and dividers to establish visual structure.
+This sample demonstrates the borderless button styling from the Holo visual language.
+Styling is applied in the XML resource layout definitions, referecing the styling attributes
+from the Holo theme.
+
+Introduction
+------------
+
+This sample shows how to style widgets in an Activity using [borderless elements][1].
+This includes a ListView with custom rows and buttons, using dividing lines to create
+visual separatation.
+
+Style definitions are provided by the Holo theme, which includes references to the 
+'?android:buttonBarButtonStyle' for buttons and references to the appropriate divider 
+attribute and style 'android:dividerVertical'.
+
+Styles are directly applied to widgets in their XML layout definitions.
+
+[1]: http://developer.android.com/design/building-blocks/buttons.html#borderless
 
 Pre-requisites
 --------------
 
-- Android SDK v20
-- Android Build Tools v20
+- Android SDK v21
+- Android Build Tools v21.1.1
 - Android Support Repository
 
+Screenshots
+-------------
+
+<img src="screenshots/1-activity.png" height="400" alt="Screenshot"/> 
+
 Getting Started
 ---------------
 
@@ -41,7 +62,7 @@
 use this file except in compliance with the License.  You may obtain a copy of
 the License at
 
-  http://www.apache.org/licenses/LICENSE-2.0
+http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/ui/holo/BorderlessButtons/gradle/wrapper/gradle-wrapper.properties b/ui/holo/BorderlessButtons/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/holo/BorderlessButtons/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/holo/BorderlessButtons/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/holo/BorderlessButtons/screenshots/1-activity.png.png b/ui/holo/BorderlessButtons/screenshots/1-activity.png.png
new file mode 100644
index 0000000..6b85b13
--- /dev/null
+++ b/ui/holo/BorderlessButtons/screenshots/1-activity.png.png
Binary files differ
diff --git a/ui/holo/BorderlessButtons/screenshots/big_icon.png b/ui/holo/BorderlessButtons/screenshots/big_icon.png
new file mode 100644
index 0000000..b0fae79
--- /dev/null
+++ b/ui/holo/BorderlessButtons/screenshots/big_icon.png
Binary files differ
diff --git a/ui/holo/BorderlessButtons/template-params.xml b/ui/holo/BorderlessButtons/template-params.xml
index c0d9aeb..09274c1 100644
--- a/ui/holo/BorderlessButtons/template-params.xml
+++ b/ui/holo/BorderlessButtons/template-params.xml
@@ -34,4 +34,44 @@
     <template src="base"/>
     <common src="logger"/>
 
+<metadata>
+    <status>PUBLISHED</status>
+    <categories>Design, UI</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>INTERMEDIATE</level>
+    <icon>screenshots/big_icon.png</icon>
+    <screenshots>
+        <img>screenshots/1-activity.png</img>
+    </screenshots>
+    <api_refs>
+        <android>android.widget.Button</android>
+        <android>android.widget.ListView</android>
+        <android>android.app.ListActivity</android>
+    </api_refs>
+
+    <description>
+<![CDATA[
+This sample demonstrates the borderless button styling from the Holo visual language.
+Styling is applied in the XML resource layout definitions, referecing the styling attributes
+from the Holo theme.
+]]>
+    </description>
+    <intro>
+<![CDATA[
+This sample shows how to style widgets in an Activity using [borderless elements][1].
+This includes a ListView with custom rows and buttons, using dividing lines to create
+visual separatation.
+
+Style definitions are provided by the Holo theme, which includes references to the 
+'?android:buttonBarButtonStyle' for buttons and references to the appropriate divider 
+attribute and style 'android:dividerVertical'.
+
+Styles are directly applied to widgets in their XML layout definitions.
+
+[1]: http://developer.android.com/design/building-blocks/buttons.html#borderless
+]]>
+    </intro>
+</metadata>
 </sample>
diff --git a/ui/lists/CustomChoiceList/gradle/wrapper/gradle-wrapper.properties b/ui/lists/CustomChoiceList/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/lists/CustomChoiceList/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/lists/CustomChoiceList/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/lists/CustomChoiceList/screenshots/icon-web.png b/ui/lists/CustomChoiceList/screenshots/icon-web.png
new file mode 100644
index 0000000..9e61e21
--- /dev/null
+++ b/ui/lists/CustomChoiceList/screenshots/icon-web.png
Binary files differ
diff --git a/ui/lists/CustomChoiceList/screenshots/main.png b/ui/lists/CustomChoiceList/screenshots/main.png
new file mode 100644
index 0000000..f52ee6d
--- /dev/null
+++ b/ui/lists/CustomChoiceList/screenshots/main.png
Binary files differ
diff --git a/ui/lists/CustomChoiceList/template-params.xml b/ui/lists/CustomChoiceList/template-params.xml
index b58c0ac..35626fc 100644
--- a/ui/lists/CustomChoiceList/template-params.xml
+++ b/ui/lists/CustomChoiceList/template-params.xml
@@ -20,7 +20,7 @@
     <package>com.example.android.customchoicelist</package>
 
     <!-- change minSdk if needed-->
-    <minSdk>3</minSdk>
+    <minSdk>4</minSdk>
 
     <strings>
         <intro>
@@ -34,4 +34,66 @@
     <template src="base"/>
     <common src="logger"/>
 
+
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Widgets</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+            <img>screenshots/2-settings.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.widget.Checkable</android>
+            <android>android.widget.ListView</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates how to create custom checkable layouts, for use with ListView's choiceMode
+attribute.
+]]>
+        </description>
+
+        <!-- Multi-paragraph introduction to sample, from an educational point-of-view.
+        Makrdown formatting allowed. This will be used to generate a mini-article for the
+        sample on DAC. -->
+        <intro>
+<![CDATA[
+This sample demonstrates how to create custom single- or multi-choice [ListView][1] UIs on Android.
+
+When a ListView has a `android:choiceMode` attribute set, it will allow users to "choose" one or more items. For
+exmaple, refer to `res/layout/sample_main.xml` in this project:
+
+```xml
+<ListView android:id="@android:id/list"
+    android:layout_width="match_parent"
+    android:layout_height="0dp"
+    android:layout_weight="1"
+    android:paddingLeft="@dimen/page_margin"
+    android:paddingRight="@dimen/page_margin"
+    android:scrollbarStyle="outsideOverlay"
+    android:choiceMode="multipleChoice" />
+```
+
+The framework provides these default list item layouts that show standard radio buttons or check boxes next to a single
+line of text:
+
+- android.R.layout.simple_list_item_single_choice
+- android.R.layout.simple_list_item_multiple_choice.
+
+In some cases, you may want to customize this layout. When doing so, the root view must implement the Checkable
+interface. For an example, see this sample's `CheckableLinearLayout` class.
+
+Lastly, remember to use padding on your ListViews to adhere to the standard metrics described in the Android Design
+guidelines. When doing so, you should set the `android:scrollbarStyle` attribute such that the scrollbar doesn't inset.
+
+[1]: http://developer.android.com/reference/android/widget/ListView.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/notifications/BasicNotifications/build.gradle b/ui/notifications/BasicNotifications/build.gradle
deleted file mode 100644
index cca9ac3..0000000
--- a/ui/notifications/BasicNotifications/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-// BEGIN_EXCLUDE
-import com.example.android.samples.build.SampleGenPlugin
-apply plugin: SampleGenPlugin
-
-samplegen {
-  pathToBuild "../../../../../build"
-  pathToSamplesCommon "../../../common"
-}
-apply from: "../../../../../build/build.gradle"
-// END_EXCLUDE
diff --git a/ui/notifications/BasicNotifications/buildSrc/build.gradle b/ui/notifications/BasicNotifications/buildSrc/build.gradle
deleted file mode 100644
index e344a8c..0000000
--- a/ui/notifications/BasicNotifications/buildSrc/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    compile 'org.freemarker:freemarker:2.3.20'
-}
-
-sourceSets {
-    main {
-        groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
-        }
-    }
-}
-
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties b/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index d7f03cf..0000000
--- a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Apr 10 15:27:10 PDT 2013
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
diff --git a/ui/notifications/BasicNotifications/template-params.xml b/ui/notifications/BasicNotifications/template-params.xml
deleted file mode 100644
index 7118c24..0000000
--- a/ui/notifications/BasicNotifications/template-params.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<sample>
-    <name>BasicNotifications</name>
-    <group>UI</group>
-    <package>com.example.android.basicnotifications</package>
-    <!-- change minSdk if needed-->
-    <minSdk>8</minSdk>
-
-    <strings>
-        <intro>
-            <![CDATA[
-            This sample demonstrates how to display events in the system\'s notification bar. The
-            NotificationCompat API is used for compatibility with older devices, running Android
-            2.2 (Froyo) or newer.
-            ]]>
-        </intro>
-    </strings>
-
-    <template src="base"/>
-</sample>
diff --git a/ui/notifications/CustomNotifications/build.gradle b/ui/notifications/CustomNotifications/build.gradle
deleted file mode 100644
index cca9ac3..0000000
--- a/ui/notifications/CustomNotifications/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-// BEGIN_EXCLUDE
-import com.example.android.samples.build.SampleGenPlugin
-apply plugin: SampleGenPlugin
-
-samplegen {
-  pathToBuild "../../../../../build"
-  pathToSamplesCommon "../../../common"
-}
-apply from: "../../../../../build/build.gradle"
-// END_EXCLUDE
diff --git a/ui/notifications/CustomNotifications/buildSrc/build.gradle b/ui/notifications/CustomNotifications/buildSrc/build.gradle
deleted file mode 100644
index e344a8c..0000000
--- a/ui/notifications/CustomNotifications/buildSrc/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    compile 'org.freemarker:freemarker:2.3.20'
-}
-
-sourceSets {
-    main {
-        groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
-        }
-    }
-}
-
diff --git a/ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.properties b/ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index d7f03cf..0000000
--- a/ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Apr 10 15:27:10 PDT 2013
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
diff --git a/ui/notifications/CustomNotifications/template-params.xml b/ui/notifications/CustomNotifications/template-params.xml
deleted file mode 100644
index ff90ca3..0000000
--- a/ui/notifications/CustomNotifications/template-params.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<sample>
-    <name>CustomNotifications</name>
-    <group>UI</group>
-    <package>com.example.android.customnotifications</package>
-
-    <!-- change minSdk if needed-->
-    <minSdk>4</minSdk>
-
-    <strings>
-        <intro>
-            <![CDATA[
-            This sample demonstrates notifications with custom content views.
-            ]]>
-        </intro>
-    </strings>
-
-    <template src="base"/>
-
-</sample>
diff --git a/ui/notifications/LNotifications/CONTRIB.md b/ui/notifications/LNotifications/CONTRIB.md
deleted file mode 100644
index 8ddb52d..0000000
--- a/ui/notifications/LNotifications/CONTRIB.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# How to become a contributor and submit your own code
-
-## Contributor License Agreements
-
-We'd love to accept your sample apps and patches! Before we can take them, we
-have to jump a couple of legal hurdles.
-
-Please fill out either the individual or corporate Contributor License Agreement (CLA).
-
-  * If you are an individual writing original source code and you're sure you
-    own the intellectual property, then you'll need to sign an [individual CLA]
-    (https://developers.google.com/open-source/cla/individual).
-  * If you work for a company that wants to allow you to contribute your work,
-    then you'll need to sign a [corporate CLA]
-    (https://developers.google.com/open-source/cla/corporate).
-
-Follow either of the two links above to access the appropriate CLA and
-instructions for how to sign and return it. Once we receive it, we'll be able to
-accept your pull requests.
-
-## Contributing A Patch
-
-1. Submit an issue describing your proposed change to the repo in question.
-1. The repo owner will respond to your issue promptly.
-1. If your proposed change is accepted, and you haven't already done so, sign a
-   Contributor License Agreement (see details above).
-1. Fork the desired repo, develop and test your code changes.
-1. Ensure that your code adheres to the existing style in the sample to which
-   you are contributing. Refer to the
-   [Google Cloud Platform Samples Style Guide]
-   (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the
-   recommended coding standards for this organization.
-1. Ensure that your code has an appropriate set of unit tests which all pass.
-1. Submit a pull request.
-
diff --git a/ui/notifications/LNotifications/LICENSE b/ui/notifications/LNotifications/LICENSE
deleted file mode 100644
index c02ca2f..0000000
--- a/ui/notifications/LNotifications/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright 2014 Google Inc.
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.properties b/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index fbe0ffd..0000000
--- a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Aug 06 17:33:35 JST 2014
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
diff --git a/ui/notifications/LNotifications/template-params.xml b/ui/notifications/LNotifications/template-params.xml
deleted file mode 100644
index 43d5dc0..0000000
--- a/ui/notifications/LNotifications/template-params.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<sample>
-    <name>LNotifications Sample</name>
-    <group>UI</group>
-    <package>com.example.android.lnotifications</package>
-
-    <!-- change minSdk if needed-->
-    <minSdk>21</minSdk>
-
-    <strings>
-        <intro>
-            <![CDATA[
-                This sample demonstrates how new features for notifications introduced in Android L
-                are used such as Heads-Up notifications, visibility, people, category and priority
-                metadata. (Priority metadata has been present since Jelly Bean, but actually unused).
-            ]]>
-        </intro>
-    </strings>
-
-    <template src="base"/>
-
-</sample>
diff --git a/ui/transition/AdapterTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/AdapterTransition/gradle/wrapper/gradle-wrapper.properties
index 00daa77..0c71e76 100644
--- a/ui/transition/AdapterTransition/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/transition/AdapterTransition/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/transition/BasicTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/BasicTransition/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/transition/BasicTransition/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/transition/BasicTransition/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/transition/BasicTransition/screenshots/icon-web.png b/ui/transition/BasicTransition/screenshots/icon-web.png
new file mode 100644
index 0000000..943b3d7
--- /dev/null
+++ b/ui/transition/BasicTransition/screenshots/icon-web.png
Binary files differ
diff --git a/ui/transition/BasicTransition/screenshots/main.png b/ui/transition/BasicTransition/screenshots/main.png
new file mode 100644
index 0000000..f15c7af
--- /dev/null
+++ b/ui/transition/BasicTransition/screenshots/main.png
Binary files differ
diff --git a/ui/transition/BasicTransition/template-params.xml b/ui/transition/BasicTransition/template-params.xml
index 0da7567..d082c8d 100644
--- a/ui/transition/BasicTransition/template-params.xml
+++ b/ui/transition/BasicTransition/template-params.xml
@@ -21,7 +21,6 @@
 
     <!-- change minSdk if needed-->
     <minSdk>19</minSdk>
-    <compileSdkVersion>19</compileSdkVersion>
 
     <strings>
         <intro>
@@ -38,4 +37,55 @@
     <common src="activities"/>
     <common src="view"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Transition, UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>Application/src/main/big_icon.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.transition.TransitionManager</android>
+            <android>android.transition.Transition</android>
+            <android>android.transition.Scene</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic app showing how to use the Transition framework introduced in
+KitKat. The app shows radioboxes to select between different Scenes,
+and uses various ways to transition between them.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+A Scene is an encapsulation of the state of a view hierarchy,
+including the views in that hierarchy and the various values
+(layout-related and otherwise) that those views have. A scene can be
+defined by a layout hierarchy directly or by code which sets up the
+scene dynamically as it is entered.
+
+A Transition is a mechanism to automatically animate changes that
+occur when a new scene is entered. Some transition capabilities are
+automatic. That is, entering a scene may cause animations to run which
+fade out views that go away, changeBounds and resize existing views
+that change, and fade in views that become visible. There are
+additional transitions that can animate other attributes, such as
+color changes, and which can optionally be specified to take place
+during particular scene changes. Finally, developers can define their
+own Transition subclasses which monitor particular property changes
+and which run custom animations when those properties change values.
+
+TransitionManager is used to specify custom transitions for particular
+scene changes, and to cause scene changes with specific transitions to
+take place.
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/transition/CustomTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/CustomTransition/gradle/wrapper/gradle-wrapper.properties
index 684b39c..4cd80b2 100644
--- a/ui/transition/CustomTransition/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/transition/CustomTransition/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/transition/CustomTransition/screenshots/icon-web.png b/ui/transition/CustomTransition/screenshots/icon-web.png
new file mode 100755
index 0000000..51149ad
--- /dev/null
+++ b/ui/transition/CustomTransition/screenshots/icon-web.png
Binary files differ
diff --git a/ui/transition/CustomTransition/screenshots/main.png b/ui/transition/CustomTransition/screenshots/main.png
new file mode 100644
index 0000000..d016b60
--- /dev/null
+++ b/ui/transition/CustomTransition/screenshots/main.png
Binary files differ
diff --git a/ui/transition/CustomTransition/template-params.xml b/ui/transition/CustomTransition/template-params.xml
index 30f7c16..952b649 100644
--- a/ui/transition/CustomTransition/template-params.xml
+++ b/ui/transition/CustomTransition/template-params.xml
@@ -39,4 +39,54 @@
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Transition, UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.transition.TransitionManager</android>
+            <android>android.transition.Transition</android>
+            <android>android.transition.Scene</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample shows how to implement a custom Transition extending the
+standard Transition class.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+In order to create a custom Transition, you first need to override
+[captureStartValues][1] and [captureEndValues][2]. In those method, you have to
+extract all the relevant properties from a View. These methods are called for
+each of the Views in the hierarchy.
+
+Then, you need to override [createAnimator][3] and create an Animator based on
+the property values you extracted. This method will also be called for each of
+the Views in the hierarchy. If you want to skip some Views, simply return null.
+
+The instantiated custom Transition can be applied by passing it as the second
+parameter of [TransitionManager.go][4].
+
+```java
+TransitionManager.go(mScenes[mCurrentScene], mTransition);
+```
+
+[1]: https://developer.android.com/reference/android/transition/Transition.html#captureStartValues(android.transition.TransitionValues)
+[2]: https://developer.android.com/reference/android/transition/Transition.html#captureEndValues(android.transition.TransitionValues)
+[3]: https://developer.android.com/reference/android/transition/Transition.html#createAnimator(android.view.ViewGroup, android.transition.TransitionValues, android.transition.TransitionValues)
+[4]: https://developer.android.com/reference/android/transition/TransitionManager.html#go(android.transition.Scene, android.transition.Transition)
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/transition/FragmentTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/FragmentTransition/gradle/wrapper/gradle-wrapper.properties
index 00daa77..0c71e76 100644
--- a/ui/transition/FragmentTransition/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/transition/FragmentTransition/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/transition/FragmentTransition/screenshots/grid.png b/ui/transition/FragmentTransition/screenshots/grid.png
new file mode 100644
index 0000000..a974669
--- /dev/null
+++ b/ui/transition/FragmentTransition/screenshots/grid.png
Binary files differ
diff --git a/ui/transition/FragmentTransition/screenshots/icon-web.png b/ui/transition/FragmentTransition/screenshots/icon-web.png
new file mode 100755
index 0000000..b0d5dde
--- /dev/null
+++ b/ui/transition/FragmentTransition/screenshots/icon-web.png
Binary files differ
diff --git a/ui/transition/FragmentTransition/screenshots/main.png b/ui/transition/FragmentTransition/screenshots/main.png
new file mode 100644
index 0000000..dbd5c22
--- /dev/null
+++ b/ui/transition/FragmentTransition/screenshots/main.png
Binary files differ
diff --git a/ui/transition/FragmentTransition/template-params.xml b/ui/transition/FragmentTransition/template-params.xml
index 23523d2..2d2da3f 100644
--- a/ui/transition/FragmentTransition/template-params.xml
+++ b/ui/transition/FragmentTransition/template-params.xml
@@ -36,4 +36,50 @@
     <common src="activities"/>
     <common src="view"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Transition, UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/grid.png</img>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.transition.TransitionManager</android>
+            <android>android.transition.Transition</android>
+            <android>android.transition.Scene</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample demonstrates how to start a Transition after a Fragment Transaction.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample uses [the Transition framework][1] to show a nice visual effect on
+Fragment Transaction.
+
+Animation for fragment _transaction_ can be customized by overriding
+[onCreateAnimation][2]. In this sample, we set up an AnimationListener
+for this Animation, and start our Transition on [onAnimationEnd][3].
+
+Transition is started by calling [TransitionManager.go][4]. We don't
+instantiate a Scene for the initial state of Transition, as we never
+go back. One thing we need to be careful here is that we need to
+populate the content of Views after starting the Transition.
+
+[1]: https://developer.android.com/reference/android/transition/package-summary.html
+[2]: http://developer.android.com/reference/android/support/v4/app/Fragment.html#onCreateAnimation(int, boolean, int)
+[3]: http://developer.android.com/reference/android/view/animation/Animation.AnimationListener.html#onAnimationEnd(android.view.animation.Animation)
+[4]: http://developer.android.com/reference/android/transition/TransitionManager.html#go(android.transition.Scene)
+]]>
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/views/CardView/gradle/wrapper/gradle-wrapper.properties b/ui/views/CardView/gradle/wrapper/gradle-wrapper.properties
index 588fabb..78c4ddd 100644
--- a/ui/views/CardView/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/CardView/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/CardView/template-params.xml b/ui/views/CardView/template-params.xml
index 6ff29b1..dc79f3a 100644
--- a/ui/views/CardView/template-params.xml
+++ b/ui/views/CardView/template-params.xml
@@ -28,7 +28,7 @@
     <minSdk>21</minSdk>
     <compileSdkVersion>21</compileSdkVersion>
 
-    <dependency>com.android.support:cardview-v7:21.+</dependency>
+    <dependency>com.android.support:cardview-v7:21.0.2</dependency>
 
     <strings>
         <intro>
diff --git a/ui/views/Clipping/ClippingBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/Clipping/ClippingBasic/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/views/Clipping/ClippingBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/Clipping/ClippingBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/Clipping/ClippingBasic/screenshots/screenshot-1.png b/ui/views/Clipping/ClippingBasic/screenshots/screenshot-1.png
new file mode 100644
index 0000000..9a36e78
--- /dev/null
+++ b/ui/views/Clipping/ClippingBasic/screenshots/screenshot-1.png
Binary files differ
diff --git a/ui/views/Clipping/ClippingBasic/screenshots/screenshot-2.png b/ui/views/Clipping/ClippingBasic/screenshots/screenshot-2.png
new file mode 100644
index 0000000..2e2f10c
--- /dev/null
+++ b/ui/views/Clipping/ClippingBasic/screenshots/screenshot-2.png
Binary files differ
diff --git a/ui/views/Clipping/ClippingBasic/screenshots/web-icon.png b/ui/views/Clipping/ClippingBasic/screenshots/web-icon.png
new file mode 100755
index 0000000..e14cdfb
--- /dev/null
+++ b/ui/views/Clipping/ClippingBasic/screenshots/web-icon.png
Binary files differ
diff --git a/ui/views/Clipping/ClippingBasic/template-params.xml b/ui/views/Clipping/ClippingBasic/template-params.xml
index 957a591..a33790e 100644
--- a/ui/views/Clipping/ClippingBasic/template-params.xml
+++ b/ui/views/Clipping/ClippingBasic/template-params.xml
@@ -40,4 +40,67 @@
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI Views</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/web-icon.png</icon>
+        <screenshots>
+            <img>screenshots/screenshot-1.png</img>
+            <img>screenshots/screenshot-2.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.ViewOutlineProvider</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic app showing how to clip on a View using [ViewOutlineProvider][1] interface,
+by which a View builds its outline, used for shadowing and clipping.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+The [ViewOutlineProvider][1] interface offers you a method to populate the outline of a View.
+You need to implement a getOutline(android.view.View, android.graphics.Outline)
+method to clip a View in a specific shape.
+
+This example clips the outline of a View as a rounded rectangle by defining a class that
+ implements ViewOutlineProvider by following code:
+
+```java
+private class ClipOutlineProvider extends ViewOutlineProvider {
+    @Override
+    public void getOutline(View view, Outline outline) {
+        final int margin = Math.min(view.getWidth(), view.getHeight()) / 10;
+        outline.setRoundRect(margin, margin, view.getWidth() - margin,
+                view.getHeight() - margin, margin / 2);
+    }
+}
+```
+
+To clip a View by the defined outline, setting a OutlineProvider to a View
+to be clipped is needed like following:
+
+```java
+final View clippedView = view.findViewById(R.id.frame);
+clippedView.setOutlineProvider(mOutlineProvider);
+```
+
+You can toggle if the View is clipped by calling [setClipToOutline(boolean)][2]
+like following code:
+
+```java
+clippedView.setClipToOutline(true); // Setting false disable clipping
+```
+
+[1]: https://developer.android.com/reference/android/view/ViewOutlineProvider.html
+[2]: https://developer.android.com/reference/android/view/View.html#setClipToOutline(boolean)
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/views/Elevation/ElevationBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/Elevation/ElevationBasic/gradle/wrapper/gradle-wrapper.properties
index fe5a0cf..2aebf9c 100644
--- a/ui/views/Elevation/ElevationBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/Elevation/ElevationBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/Elevation/ElevationBasic/screenshots/fixed.png b/ui/views/Elevation/ElevationBasic/screenshots/fixed.png
new file mode 100644
index 0000000..9864b90
--- /dev/null
+++ b/ui/views/Elevation/ElevationBasic/screenshots/fixed.png
Binary files differ
diff --git a/ui/views/Elevation/ElevationBasic/screenshots/icon-web.png b/ui/views/Elevation/ElevationBasic/screenshots/icon-web.png
new file mode 100644
index 0000000..7f2dbe5
--- /dev/null
+++ b/ui/views/Elevation/ElevationBasic/screenshots/icon-web.png
Binary files differ
diff --git a/ui/views/Elevation/ElevationBasic/screenshots/raised.png b/ui/views/Elevation/ElevationBasic/screenshots/raised.png
new file mode 100644
index 0000000..af81493
--- /dev/null
+++ b/ui/views/Elevation/ElevationBasic/screenshots/raised.png
Binary files differ
diff --git a/ui/views/Elevation/ElevationBasic/template-params.xml b/ui/views/Elevation/ElevationBasic/template-params.xml
index 3ef4ec2..bcaa253 100644
--- a/ui/views/Elevation/ElevationBasic/template-params.xml
+++ b/ui/views/Elevation/ElevationBasic/template-params.xml
@@ -44,5 +44,59 @@
     <template src="FragmentView"/>
     <common src="logger"/>
     <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Input</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/fixed.png</img>
+            <img>screenshots/raised.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.MotionEvent</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates ways to move a view in the z-axis using
+`setTranslationZ()`. This method was introduced in API Level 21 ('Lollipop').
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample uses two shapes, a circle and a square, and it demonstrates two
+alternative ways to move a view in the z-axis. The first shape, the circle,
+has a fixed elevation, which is defined in XML. The second view, the square,
+changes its elevation using [setTranslationZ()][1] when a user touches it:
 
+    shape2.setOnTouchListener(new View.OnTouchListener() {
+        @Override
+        public boolean onTouch(View view, MotionEvent motionEvent) {
+            int action = motionEvent.getActionMasked();
+            /* Raise view on ACTION_DOWN and lower it on ACTION_UP. */
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                    Log.d(TAG, "ACTION_DOWN on view.");
+                    view.setTranslationZ(120);
+                    break;
+                case MotionEvent.ACTION_UP:
+                    Log.d(TAG, "ACTION_UP on view.");
+                    view.setTranslationZ(0);
+                    break;
+                default:
+                    return false;
+            }
+            return true;
+        }
+    });
+
+The elevation reverts back once the touch is removed.
+
+[1]: https://developer.android.com/training/material/shadows-clipping.html#Elevation
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties b/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
index eefbc0a..a51db8c 100644
--- a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/Elevation/ElevationDrag/gradle/wrapper/gradle-wrapper.properties b/ui/views/Elevation/ElevationDrag/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/views/Elevation/ElevationDrag/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/Elevation/ElevationDrag/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/Elevation/ElevationDrag/screenshots/1-main.png b/ui/views/Elevation/ElevationDrag/screenshots/1-main.png
new file mode 100644
index 0000000..c2e385f
--- /dev/null
+++ b/ui/views/Elevation/ElevationDrag/screenshots/1-main.png
Binary files differ
diff --git a/ui/views/Elevation/ElevationDrag/screenshots/icon-web.png b/ui/views/Elevation/ElevationDrag/screenshots/icon-web.png
new file mode 100644
index 0000000..b0a031a
--- /dev/null
+++ b/ui/views/Elevation/ElevationDrag/screenshots/icon-web.png
Binary files differ
diff --git a/ui/views/Elevation/ElevationDrag/template-params.xml b/ui/views/Elevation/ElevationDrag/template-params.xml
index 2c62cb2..9bd511c 100644
--- a/ui/views/Elevation/ElevationDrag/template-params.xml
+++ b/ui/views/Elevation/ElevationDrag/template-params.xml
@@ -43,5 +43,42 @@
     <template src="FragmentView"/>
     <common src="logger"/>
     <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Views</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.View</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates a drag and drop action on different shapes. Elevation and
+z-translation are used to render the shadows and the views are clipped using different
+Outlines.
+]]>
+        </description>
 
+        <intro>
+<![CDATA[
+This sample demonstrates a drag and drop action on different shapes. Elevation and
+z-translation are used to render the shadows and the views are clipped using different
+Outlines.
+
+Elevation is considered the static or initial position of a view on the z axis, while
+translationZ exists for transient states, like animations.
+
+`Z = elevation + translationZ`
+
+See [Assign Elevation to Your Views][1] for more info.
+[1]:https://developer.android.com/training/material/shadows-clipping.html#Elevation
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/Application/src/main/res/values/dimens.xml b/ui/views/FloatingActionButton/FloatingActionButtonBasic/Application/src/main/res/values/dimens.xml
index f063937..5a9d684 100644
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/Application/src/main/res/values/dimens.xml
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/Application/src/main/res/values/dimens.xml
@@ -15,8 +15,8 @@
  limitations under the License.
 -->
 <resources>
-    <dimen name="fab_elevation">2dp</dimen>
-    <dimen name="fab_press_translation_z">2dp</dimen>
+    <dimen name="fab_elevation">8dp</dimen>
+    <dimen name="fab_press_translation_z">9dp</dimen>
     <dimen name="fab_size">56dp</dimen>
     <dimen name="fab_size_small">40dp</dimen>
     <dimen name="fab_icon_size">24dp</dimen>
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties
index 893a63e..12dab4c 100644
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/wrapper/gradle-wrapper.properties
index fc0d638..a4c577a 100644
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/big_icon.png b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/big_icon.png
new file mode 100644
index 0000000..78b209b
--- /dev/null
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/big_icon.png
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot1.png b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot1.png
new file mode 100644
index 0000000..9952a93
--- /dev/null
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot1.png
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot2.png b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot2.png
new file mode 100644
index 0000000..2daa568
--- /dev/null
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot2.png
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot3.png b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot3.png
new file mode 100644
index 0000000..7828d30
--- /dev/null
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot3.png
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot4.png b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot4.png
new file mode 100644
index 0000000..023d6e2
--- /dev/null
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot4.png
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot5.png b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot5.png
new file mode 100644
index 0000000..20133d8
--- /dev/null
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/screenshots/screenshot5.png
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/template-params.xml b/ui/views/FloatingActionButton/FloatingActionButtonBasic/template-params.xml
index 0e97b7a..66533b7 100644
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/template-params.xml
+++ b/ui/views/FloatingActionButton/FloatingActionButtonBasic/template-params.xml
@@ -14,19 +14,12 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
-
-
 <sample>
     <name>FloatingActionButtonBasic</name>
     <group>UI</group>
     <package>com.example.android.floatingactionbuttonbasic</package>
-
-    <!-- change minSdk if needed-->
     <minSdk>21</minSdk>
     <compileSdkVersion>21</compileSdkVersion>
-
-
     <strings>
         <intro>
             <![CDATA[
@@ -40,5 +33,49 @@
     <template src="FragmentView"/>
     <common src="logger"/>
     <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Getting Started, UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>screenshots/big_icon.png</icon>
+        <screenshots>
+            <img>screenshots/screenshot1.png</img>
+            <img>screenshots/screenshot2.png</img>
+            <img>screenshots/screenshot3.png</img>
+            <img>screenshots/screenshot4.png</img>
+            <img>screenshots/screenshot5.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.graphics.Outline</android>
+            <android>android.view.ViewOutlineProvider</android>
+            <android>android.widget.Checkable</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample shows the two sizes of Floating Action Buttons and
+how to interact with them.
+]]>
+        </description>
 
+        <intro>
+<![CDATA[
+This sample shows how to implement a [Checkable][1] Floating Action Button.
+
+Floating action buttons are used for a special type of promoted action.
+They are distinguished by a circled icon floating above the UI and have
+special motion behaviors related to morphing, launching, and the transferring anchor point.
+
+Floating action buttons come in two sizes:
+the default, which should be used in most cases, and the mini,
+which should only be used to create visual continuity with other elements on the screen.
+
+Both sizes of Floating Action Buttons are displayed on screen.
+
+[1]: https://developer.android.com/reference/android/widget/Checkable.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/views/HorizontalPaging/Application/src/main/res/drawable-hdpi/ic_launcher.png b/ui/views/HorizontalPaging/Application/src/main/res/drawable-hdpi/ic_launcher.png
old mode 100755
new mode 100644
index 96a442e..8004277
--- a/ui/views/HorizontalPaging/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/ui/views/HorizontalPaging/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/Application/src/main/res/drawable-mdpi/ic_launcher.png b/ui/views/HorizontalPaging/Application/src/main/res/drawable-mdpi/ic_launcher.png
old mode 100755
new mode 100644
index 359047d..eb49a86
--- a/ui/views/HorizontalPaging/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/ui/views/HorizontalPaging/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/views/HorizontalPaging/Application/src/main/res/drawable-xhdpi/ic_launcher.png
old mode 100755
new mode 100644
index 71c6d76..1d159e9
--- a/ui/views/HorizontalPaging/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/ui/views/HorizontalPaging/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/views/HorizontalPaging/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..ba543fe
--- /dev/null
+++ b/ui/views/HorizontalPaging/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/gradle/wrapper/gradle-wrapper.properties b/ui/views/HorizontalPaging/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/views/HorizontalPaging/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/HorizontalPaging/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/HorizontalPaging/screenshots/1-tab1.png b/ui/views/HorizontalPaging/screenshots/1-tab1.png
new file mode 100644
index 0000000..10fe6a6
--- /dev/null
+++ b/ui/views/HorizontalPaging/screenshots/1-tab1.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/screenshots/2-tab2.png b/ui/views/HorizontalPaging/screenshots/2-tab2.png
new file mode 100644
index 0000000..1d8c66c
--- /dev/null
+++ b/ui/views/HorizontalPaging/screenshots/2-tab2.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/screenshots/icon-web.png b/ui/views/HorizontalPaging/screenshots/icon-web.png
new file mode 100644
index 0000000..9939329
--- /dev/null
+++ b/ui/views/HorizontalPaging/screenshots/icon-web.png
Binary files differ
diff --git a/ui/views/HorizontalPaging/template-params.xml b/ui/views/HorizontalPaging/template-params.xml
index e0546b1..ba748cc 100644
--- a/ui/views/HorizontalPaging/template-params.xml
+++ b/ui/views/HorizontalPaging/template-params.xml
@@ -33,4 +33,46 @@
 
     <template src="base"/>
 
+    <metadata>
+        <status>DEPRECATED</status>
+        <categories>UI</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/1-tab1.png</img>
+            <img>screenshots/2-tab2.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.v4.view.ViewPager</android>
+            <android>android.support.v4.app.FragmentPagerAdapter</android>
+        </api_refs>
+        <description>
+This sample shows how to implement tabs, using Fragments and a ViewPager.
+        </description>
+        <intro>
+This sample implements tabs using the deprecated [ActionBar.TabListener][1]. It uses [ViewPager][2] and
+[FragmentPagerAdapter][3] to handle swiping between tabs and displaying the selected tab content.
+
+
+1. Create an Activity that extends [FragmentActivity][4], with a [ViewPager][2] for its layout.
+2. Implement [ActionBar.TabListener][1] interface.
+3. Create a class that extends [FragmentPagerAdapter][3] and override its `getItem(int position)`,
+`getCount()` and `getPageTitle(int position)` methods.
+4. In the `onCreate(Bundle savedInstanceState)` method of your activity, set navigation mode to tabs for the
+ActionBar using `setNavigationMode(ActionBar.NAVIGATION_MODE_TABS)`. Note: This is DEPRECATED as of Android
+Lollipop.
+5. Set your custom [FragmentPagerAdapter][3] on your [ViewPager][2].
+6. Implement `setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener())` on your [ViewPager][2] to
+know the selected tab, so you can update your ActionBar with `setSelectedNavigationItem(position)`.
+
+[1]: http://developer.android.com/reference/android/support/v7/app/ActionBar.TabListener.html
+[2]: http://developer.android.com/reference/android/support/v4/view/ViewPager.html
+[3]: http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
+[4]: http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html
+        </intro>
+    </metadata>
+
 </sample>
diff --git a/ui/views/NavigationDrawer/gradle/wrapper/gradle-wrapper.properties b/ui/views/NavigationDrawer/gradle/wrapper/gradle-wrapper.properties
index c819e62..5f735f1 100644
--- a/ui/views/NavigationDrawer/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/NavigationDrawer/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/NavigationDrawer/template-params.xml b/ui/views/NavigationDrawer/template-params.xml
index 79e1f26..af92a68 100644
--- a/ui/views/NavigationDrawer/template-params.xml
+++ b/ui/views/NavigationDrawer/template-params.xml
@@ -29,9 +29,10 @@
     <compileSdkVersion>21</compileSdkVersion>
 
 
-    <dependency>com.android.support:support-v13:20.+</dependency>
-    <dependency>com.android.support:appcompat-v7:20.+</dependency>
-    <dependency>com.android.support:recyclerview-v7:+</dependency>
+    <dependency>com.android.support:support-v13:21.0.2</dependency>
+    <dependency>com.android.support:appcompat-v7:21.0.2</dependency>
+    <dependency>com.android.support:recyclerview-v7:21.0.2</dependency>
+    <dependency>com.android.support:cardview-v7:21.0.2</dependency>
 
     <strings>
         <intro>
diff --git a/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/CustomAdapter.java b/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/CustomAdapter.java
index f8e3bae..40f9375 100644
--- a/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/CustomAdapter.java
+++ b/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/CustomAdapter.java
@@ -34,21 +34,29 @@
 
     // BEGIN_INCLUDE(recyclerViewSampleViewHolder)
     /**
-     * Provide a reference to the type of views that you are using (custom viewholder)
+     * Provide a reference to the type of views that you are using (custom ViewHolder)
      */
     public static class ViewHolder extends RecyclerView.ViewHolder {
-        private final TextView mTextView;
+        private final TextView textView;
 
         public ViewHolder(View v) {
             super(v);
-            mTextView = (TextView) v.findViewById(R.id.textView);
+            // Define click listener for the ViewHolder's View.
+            v.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Log.d(TAG, "Element " + getPosition() + " clicked.");
+                }
+            });
+            textView = (TextView) v.findViewById(R.id.textView);
         }
 
-        public TextView getmTextView() {
-            return mTextView;
+        public TextView getTextView() {
+            return textView;
         }
     }
     // END_INCLUDE(recyclerViewSampleViewHolder)
+
     /**
      * Initialize the dataset of the Adapter.
      *
@@ -61,25 +69,24 @@
     // BEGIN_INCLUDE(recyclerViewOnCreateViewHolder)
     // Create new views (invoked by the layout manager)
     @Override
-    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
+    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
         // Create a new view.
         View v = LayoutInflater.from(viewGroup.getContext())
                 .inflate(R.layout.text_row_item, viewGroup, false);
 
-        ViewHolder vh = new ViewHolder(v);
-        return vh;
+        return new ViewHolder(v);
     }
     // END_INCLUDE(recyclerViewOnCreateViewHolder)
 
     // BEGIN_INCLUDE(recyclerViewOnBindViewHolder)
     // Replace the contents of a view (invoked by the layout manager)
     @Override
-    public void onBindViewHolder(ViewHolder viewHolder, int position) {
+    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
         Log.d(TAG, "Element " + position + " set.");
 
         // Get element from your dataset at this position and replace the contents of the view
         // with that element
-        viewHolder.getmTextView().setText(mDataSet[position]);
+        viewHolder.getTextView().setText(mDataSet[position]);
     }
     // END_INCLUDE(recyclerViewOnBindViewHolder)
 
diff --git a/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/RecyclerViewFragment.java b/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/RecyclerViewFragment.java
index 4f4a596..1c0fded 100644
--- a/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/RecyclerViewFragment.java
+++ b/ui/views/RecyclerView/Application/src/main/java/com/example/android/recyclerview/RecyclerViewFragment.java
@@ -18,21 +18,37 @@
 
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.RadioButton;
 
 /**
- * Demonstrates the use of RecyclerView with a LinearLayoutManager.
+ * Demonstrates the use of {@link RecyclerView} with a {@link LinearLayoutManager} and a
+ * {@link GridLayoutManager}.
  */
 public class RecyclerViewFragment extends Fragment {
 
     private static final String TAG = "RecyclerViewFragment";
+    private static final String KEY_LAYOUT_MANAGER = "layoutManager";
+    private static final int SPAN_COUNT = 2;
+    private static final int DATASET_COUNT = 60;
+
+    private enum LayoutManagerType {
+        GRID_LAYOUT_MANAGER,
+        LINEAR_LAYOUT_MANAGER
+    }
+
+    protected LayoutManagerType mCurrentLayoutManagerType;
+
+    protected RadioButton mLinearLayoutRadioButton;
+    protected RadioButton mGridLayoutRadioButton;
 
     protected RecyclerView mRecyclerView;
-    protected RecyclerView.Adapter mAdapter;
+    protected CustomAdapter mAdapter;
     protected RecyclerView.LayoutManager mLayoutManager;
     protected String[] mDataset;
 
@@ -58,23 +74,86 @@
         // to the way ListView would layout elements. The RecyclerView.LayoutManager defines how
         // elements are laid out.
         mLayoutManager = new LinearLayoutManager(getActivity());
-        mRecyclerView.setLayoutManager(mLayoutManager);
+
+        mCurrentLayoutManagerType = LayoutManagerType.LINEAR_LAYOUT_MANAGER;
+
+        if (savedInstanceState != null) {
+            // Restore saved layout manager type.
+            mCurrentLayoutManagerType = (LayoutManagerType) savedInstanceState
+                    .getSerializable(KEY_LAYOUT_MANAGER);
+        }
+        setRecyclerViewLayoutManager(mCurrentLayoutManagerType);
 
         mAdapter = new CustomAdapter(mDataset);
         // Set CustomAdapter as the adapter for RecyclerView.
         mRecyclerView.setAdapter(mAdapter);
         // END_INCLUDE(initializeRecyclerView)
 
+        mLinearLayoutRadioButton = (RadioButton) rootView.findViewById(R.id.linear_layout_rb);
+        mLinearLayoutRadioButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setRecyclerViewLayoutManager(LayoutManagerType.LINEAR_LAYOUT_MANAGER);
+            }
+        });
+
+        mGridLayoutRadioButton = (RadioButton) rootView.findViewById(R.id.grid_layout_rb);
+        mGridLayoutRadioButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setRecyclerViewLayoutManager(LayoutManagerType.GRID_LAYOUT_MANAGER);
+            }
+        });
+
         return rootView;
     }
 
     /**
+     * Set RecyclerView's LayoutManager to the one given.
+     *
+     * @param layoutManagerType Type of layout manager to switch to.
+     */
+    public void setRecyclerViewLayoutManager(LayoutManagerType layoutManagerType) {
+        int scrollPosition = 0;
+
+        // If a layout manager has already been set, get current scroll position.
+        if (mRecyclerView.getLayoutManager() != null) {
+            scrollPosition = ((LinearLayoutManager) mRecyclerView.getLayoutManager())
+                    .findFirstCompletelyVisibleItemPosition();
+        }
+
+        switch (layoutManagerType) {
+            case GRID_LAYOUT_MANAGER:
+                mLayoutManager = new GridLayoutManager(getActivity(), SPAN_COUNT);
+                mCurrentLayoutManagerType = LayoutManagerType.GRID_LAYOUT_MANAGER;
+                break;
+            case LINEAR_LAYOUT_MANAGER:
+                mLayoutManager = new LinearLayoutManager(getActivity());
+                mCurrentLayoutManagerType = LayoutManagerType.LINEAR_LAYOUT_MANAGER;
+                break;
+            default:
+                mLayoutManager = new LinearLayoutManager(getActivity());
+                mCurrentLayoutManagerType = LayoutManagerType.LINEAR_LAYOUT_MANAGER;
+        }
+
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        mRecyclerView.scrollToPosition(scrollPosition);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle savedInstanceState) {
+        // Save currently selected layout manager.
+        savedInstanceState.putSerializable(KEY_LAYOUT_MANAGER, mCurrentLayoutManagerType);
+        super.onSaveInstanceState(savedInstanceState);
+    }
+
+    /**
      * Generates Strings for RecyclerView's adapter. This data would usually come
      * from a local content provider or remote server.
      */
     private void initDataset() {
-        mDataset = new String[60];
-        for (int i=0; i < 60; i++) {
+        mDataset = new String[DATASET_COUNT];
+        for (int i = 0; i < DATASET_COUNT; i++) {
             mDataset[i] = "This is element #" + i;
         }
     }
diff --git a/ui/views/RecyclerView/Application/src/main/res/layout/recycler_view_frag.xml b/ui/views/RecyclerView/Application/src/main/res/layout/recycler_view_frag.xml
index 6682468..dda99ce 100644
--- a/ui/views/RecyclerView/Application/src/main/res/layout/recycler_view_frag.xml
+++ b/ui/views/RecyclerView/Application/src/main/res/layout/recycler_view_frag.xml
@@ -15,14 +15,28 @@
  limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
+    <RadioGroup
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:orientation="horizontal"
+        android:checkedButton="@+id/linear_layout_rb">
+        <RadioButton android:id="@+id/linear_layout_rb"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/linear_layout_manager"/>
+        <RadioButton android:id="@+id/grid_layout_rb"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/grid_layout_manager"/>
+    </RadioGroup>
 
     <android.support.v7.widget.RecyclerView
         android:id="@+id/recyclerView"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
-</FrameLayout>
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/ui/views/RecyclerView/Application/src/main/res/layout/text_row_item.xml b/ui/views/RecyclerView/Application/src/main/res/layout/text_row_item.xml
index d552e0e..9b94684 100644
--- a/ui/views/RecyclerView/Application/src/main/res/layout/text_row_item.xml
+++ b/ui/views/RecyclerView/Application/src/main/res/layout/text_row_item.xml
@@ -15,17 +15,16 @@
  limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_margin="@dimen/margin_small"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content">
-
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/list_item_height"
+    android:layout_marginLeft="@dimen/margin_medium"
+    android:layout_marginRight="@dimen/margin_medium"
+    android:gravity="center_vertical">
 
     <TextView
+        android:id="@+id/textView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/element_text"
-        android:id="@+id/textView"
-        android:layout_gravity="center_horizontal"/>
-</LinearLayout>
\ No newline at end of file
+        android:text="@string/element_text"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/ui/views/RecyclerView/Application/src/main/res/values/dimens.xml b/ui/views/RecyclerView/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..5af7e9e
--- /dev/null
+++ b/ui/views/RecyclerView/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+    <dimen name="list_item_height">72dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/ui/views/RecyclerView/Application/src/main/res/values/strings.xml b/ui/views/RecyclerView/Application/src/main/res/values/strings.xml
index 179529c..642e022 100644
--- a/ui/views/RecyclerView/Application/src/main/res/values/strings.xml
+++ b/ui/views/RecyclerView/Application/src/main/res/values/strings.xml
@@ -17,4 +17,6 @@
 
 <resources>
     <string name="element_text">Element</string>
+    <string name="grid_layout_manager">Grid Layout Manager</string>
+    <string name="linear_layout_manager">Linear Layout Manager</string>
 </resources>
\ No newline at end of file
diff --git a/ui/views/RecyclerView/gradle/wrapper/gradle-wrapper.properties b/ui/views/RecyclerView/gradle/wrapper/gradle-wrapper.properties
index 65fb80a..0e0523e 100644
--- a/ui/views/RecyclerView/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/RecyclerView/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/RecyclerView/template-params.xml b/ui/views/RecyclerView/template-params.xml
index 76fbd83..aff7d8b 100644
--- a/ui/views/RecyclerView/template-params.xml
+++ b/ui/views/RecyclerView/template-params.xml
@@ -22,7 +22,7 @@
     <group>UI</group>
     <package>com.example.android.recyclerview</package>
 
-    <dependency>com.android.support:recyclerview-v7:+</dependency>
+    <dependency>com.android.support:recyclerview-v7:21.0.2</dependency>
 
     <!-- change minSdk if needed-->
     <minSdk>7</minSdk>
@@ -31,11 +31,24 @@
     <strings>
         <intro>
             <![CDATA[
-            Demonstration of using RecyclerView with a LayoutManager to create a vertical ListView.
+                Demonstration of using RecyclerView with a LinearLayoutManager and GridLayoutManager
+                to create a vertical list. Tap \"SHOW LOG\" to view elements as they are bound to
+                their ViewHolder. The log also displays elements that you tap.
             ]]>
         </intro>
     </strings>
 
+    <colors>
+        <color>
+            <name>colorPrimary</name>
+            <hexval>#00BCD4</hexval>
+        </color>
+        <color>
+            <name>colorPrimaryDark</name>
+            <hexval>#00838F</hexval>
+        </color>
+    </colors>
+
     <template src="base"/>
     <template src="FragmentView"/>
     <common src="logger"/>
diff --git a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties b/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties
index 02df8c4..62a8a6c 100644
--- a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/RevealEffect/RevealEffectBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/RevealEffect/RevealEffectBasic/gradle/wrapper/gradle-wrapper.properties
index 19c6ed1..0c71e76 100644
--- a/ui/views/RevealEffect/RevealEffectBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/RevealEffect/RevealEffectBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/SlidingTabs/SlidingTabsBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/SlidingTabs/SlidingTabsBasic/gradle/wrapper/gradle-wrapper.properties
index 7dc3dff..c25449e 100644
--- a/ui/views/SlidingTabs/SlidingTabsBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/SlidingTabs/SlidingTabsBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,5 +3,5 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
 
diff --git a/ui/views/SlidingTabs/SlidingTabsColors/gradle/wrapper/gradle-wrapper.properties b/ui/views/SlidingTabs/SlidingTabsColors/gradle/wrapper/gradle-wrapper.properties
index 5fbce39..0c8edb4 100644
--- a/ui/views/SlidingTabs/SlidingTabsColors/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/SlidingTabs/SlidingTabsColors/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/SlidingTabs/SlidingTabsColors/screenshots/1-pre.png b/ui/views/SlidingTabs/SlidingTabsColors/screenshots/1-pre.png
new file mode 100644
index 0000000..38cd788
--- /dev/null
+++ b/ui/views/SlidingTabs/SlidingTabsColors/screenshots/1-pre.png
Binary files differ
diff --git a/ui/views/SlidingTabs/SlidingTabsColors/screenshots/2-morph.png b/ui/views/SlidingTabs/SlidingTabsColors/screenshots/2-morph.png
new file mode 100644
index 0000000..dad9c39
--- /dev/null
+++ b/ui/views/SlidingTabs/SlidingTabsColors/screenshots/2-morph.png
Binary files differ
diff --git a/ui/views/SlidingTabs/SlidingTabsColors/screenshots/3-post.png b/ui/views/SlidingTabs/SlidingTabsColors/screenshots/3-post.png
new file mode 100644
index 0000000..4c594a9
--- /dev/null
+++ b/ui/views/SlidingTabs/SlidingTabsColors/screenshots/3-post.png
Binary files differ
diff --git a/ui/views/SlidingTabs/SlidingTabsColors/template-params.xml b/ui/views/SlidingTabs/SlidingTabsColors/template-params.xml
index b249629..ecdad8c 100644
--- a/ui/views/SlidingTabs/SlidingTabsColors/template-params.xml
+++ b/ui/views/SlidingTabs/SlidingTabsColors/template-params.xml
@@ -42,4 +42,45 @@
     <common src="activities"/>
     <common src="view"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Views</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>Application/src/main/res/drawable-xxhdpi/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/1-pre.png</img>
+            <img>screenshots/2-morph.png</img>
+            <img>screenshots/3-post.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.widget.HorizontalScrollView</android>
+            <android>android.support.v4.view.ViewPager</android>
+        </api_refs>
+        <description>
+<![CDATA[
+A more advanced sample which shows how to use SlidingTabLayout to display a custom
+ViewPager title strip, with custom coloring for each tab.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample consists of a custom [HorizontalScrollView][1] called `SlidingTabLayout` that is used
+with a [ViewPager][2] to provide a tab indicator component which gives constant feedback as to
+the user's scroll progress.
+
+The colors can be customized in two ways. The first and simplest is to provide an
+array of colors and the alternative is via the `TabColorizer` interface which gives
+complete control over which color is used for any individual position.
+
+The views used as tabs can be customized by calling `setCustomTabView(int, int)` providing the
+layout ID of the custom layout.
+
+[1]: http://developer.android.com/reference/android/widget/HorizontalScrollView.html
+[2]: http://developer.android.com/reference/android/support/v4/view/ViewPager.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic/gradle/wrapper/gradle-wrapper.properties
index a9085ca..2727e36 100644
--- a/ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/gradle/wrapper/gradle-wrapper.properties b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/screenshots/icon-web.png b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/screenshots/icon-web.png
new file mode 100644
index 0000000..f9819f2
--- /dev/null
+++ b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/screenshots/icon-web.png
Binary files differ
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/screenshots/refresh.png b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/screenshots/refresh.png
new file mode 100644
index 0000000..6af7af2
--- /dev/null
+++ b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/screenshots/refresh.png
Binary files differ
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/template-params.xml b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/template-params.xml
index e209c33..4210182 100644
--- a/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/template-params.xml
+++ b/ui/views/SwipeRefreshLayout/SwipeRefreshListFragment/template-params.xml
@@ -21,10 +21,7 @@
     <name>SwipeRefreshListFragment</name>
     <group>UI</group>
     <package>com.example.android.swiperefreshlistfragment</package>
-
-    <!-- change minSdk if needed-->
     <minSdk>10</minSdk>
-
     <strings>
         <intro>
             <![CDATA[
@@ -43,4 +40,43 @@
     <common src="view"/>
     <common src="dummydata"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Input</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/refresh.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.v4.widget.SwipeRefreshLayout</android>
+            <android>android.widget.ArrayAdapter</android>
+            <android>android.widget.ListAdapter</android>
+            <android>android.support.v4.app.ListFragment</android>
+        </api_refs>
+        <description>
+<![CDATA[
+A sample which shows how to use `SwipeRefreshLayout` to add
+'swipe-to-refresh' gesture to a ListView, enabling the ability to trigger a refresh
+from swiping down on that view.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+A sample which shows how to use
+[SwipeRefreshLayout][1] within a `ListFragment` to add the 'swipe-to-refresh'
+gesture to a `ListView`. The sample provides this functionality by defining a
+re-usable `SwipeRefreshListFragment` class.
+
+The sample displays a list of cheeses. When the user initiates a downward swipe,
+that list refreshes with randomly generated items. Selecting the `Refresh`
+action from the action bar also refreshes the list.
+
+[1]: https://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews/gradle/wrapper/gradle-wrapper.properties b/ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/views/TextSwitcher/gradle/wrapper/gradle-wrapper.properties b/ui/views/TextSwitcher/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/views/TextSwitcher/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/TextSwitcher/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/window/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/ui/window/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties
index 7817462..faba5b7 100644
--- a/ui/window/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/window/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/window/AdvancedImmersiveMode/screenshots/icon-web.png b/ui/window/AdvancedImmersiveMode/screenshots/icon-web.png
new file mode 100644
index 0000000..fc00d92
--- /dev/null
+++ b/ui/window/AdvancedImmersiveMode/screenshots/icon-web.png
Binary files differ
diff --git a/ui/window/AdvancedImmersiveMode/screenshots/immersion.png b/ui/window/AdvancedImmersiveMode/screenshots/immersion.png
new file mode 100644
index 0000000..a6c310b
--- /dev/null
+++ b/ui/window/AdvancedImmersiveMode/screenshots/immersion.png
Binary files differ
diff --git a/ui/window/AdvancedImmersiveMode/template-params.xml b/ui/window/AdvancedImmersiveMode/template-params.xml
index c081c66..6e19aa2 100644
--- a/ui/window/AdvancedImmersiveMode/template-params.xml
+++ b/ui/window/AdvancedImmersiveMode/template-params.xml
@@ -19,10 +19,7 @@
     <group>UI</group>
     <package>com.example.android.advancedimmersivemode</package>
 
-
-    <!-- change minSdk if needed-->
-    <minSdk>4</minSdk>
-    <compileSdkVersion>19</compileSdkVersion>
+    <minSdk>19</minSdk>
 
     <strings>
         <intro>
@@ -40,5 +37,46 @@
     <template src="FragmentView"/>
     <common src="logger"/>
     <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Window</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/immersion.png</img>
+            <img>screenshots/leanback.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.view.Window</android>
+        </api_refs>
 
+        <description>
+<![CDATA[
+Immersive Mode, added in Android 4.4, improves the "hide full screen" and
+"hide nav bar" modes by letting users swipe the bars in and out.  This sample
+lets the user experiment with immersive mode by seeing how it interacts
+with some of the other UI flags related to full-screen apps.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+Android 4.4 (API Level 19) introduces a new `SYSTEM_UI_FLAG_IMMERSIVE`
+flag for [setSystemUiVisibility()][1] that lets your app go truly "full
+screen." This flag, when combined with the `SYSTEM_UI_FLAG_HIDE_NAVIGATION` and
+`SYSTEM_UI_FLAG_FULLSCREEN` flags, hides the navigation and status bars
+and lets your app capture all touch events on the screen.
+
+When immersive full-screen mode is enabled, your activity continues
+to receive all touch events. The user can reveal the system bars with
+an inward swipe along the region where the system bars normally
+appear.
+
+[1]: http://developer.android.com/reference/android/view/View.html#setSystemUiVisibility(int)
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-hdpi/ic_launcher.png b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-hdpi/ic_launcher.png
index b1efaf4..63dfb38 100644
--- a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-mdpi/ic_launcher.png b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-mdpi/ic_launcher.png
index f5f9244..f7c06d0 100644
--- a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..22d9247 100644
--- a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..4c4c037 100644
--- a/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/ui/window/BasicImmersiveMode/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/README.md b/ui/window/BasicImmersiveMode/README.md
index 66f74b1..87ec7b8 100644
--- a/ui/window/BasicImmersiveMode/README.md
+++ b/ui/window/BasicImmersiveMode/README.md
@@ -1,17 +1,32 @@
 Android BasicImmersiveMode Sample
-==============================
+===================================
 
-"Immersive Mode" is a new UI mode which improves "hide full screen" and
-"hide nav bar" modes, by letting users swipe the bars in and out.This sample
-demonstrates how to enable and disable immersive mode programmatically.
+Sample demonstrating the use of immersive mode to hide the system and navigation bars for
+full screen applications.
+
+Introduction
+------------
+
+'Immersive Mode' is a new UI mode which improves 'hide full screen' and 'hide nav bar' 
+modes, by letting users swipe the bars in and out.
+
+This sample demonstrates how to enable and disable immersive mode programmatically.
+
+Immersive mode was introduced in Android 4.4 (Api Level 19). It is toggled using the 
+SYSTEM_UI_FLAG_IMMERSIVE system ui flag. When combined with the SYSTEM_UI_FLAG_HIDE_NAVIGATION and SYSTEM_UI_FLAG_FULLSCREEN  flags, hides the navigation and status bars and lets your app capture all touch events on the screen.
 
 Pre-requisites
 --------------
 
-- Android SDK v20
-- Android Build Tools v20
+- Android SDK v21
+- Android Build Tools v21.1.1
 - Android Support Repository
 
+Screenshots
+-------------
+
+<img src="screenshots/1-activity.png" height="400" alt="Screenshot"/> <img src="screenshots/2-immersive.png" height="400" alt="Screenshot"/> 
+
 Getting Started
 ---------------
 
@@ -42,7 +57,7 @@
 use this file except in compliance with the License.  You may obtain a copy of
 the License at
 
-  http://www.apache.org/licenses/LICENSE-2.0
+http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
diff --git a/ui/window/BasicImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/ui/window/BasicImmersiveMode/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/window/BasicImmersiveMode/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/window/BasicImmersiveMode/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/window/BasicImmersiveMode/screenshots/1-activity.png b/ui/window/BasicImmersiveMode/screenshots/1-activity.png
new file mode 100644
index 0000000..c7bfeca
--- /dev/null
+++ b/ui/window/BasicImmersiveMode/screenshots/1-activity.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/screenshots/2-immersive.png b/ui/window/BasicImmersiveMode/screenshots/2-immersive.png
new file mode 100644
index 0000000..61cef5c
--- /dev/null
+++ b/ui/window/BasicImmersiveMode/screenshots/2-immersive.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/screenshots/big_icon.png b/ui/window/BasicImmersiveMode/screenshots/big_icon.png
new file mode 100644
index 0000000..f8e74be
--- /dev/null
+++ b/ui/window/BasicImmersiveMode/screenshots/big_icon.png
Binary files differ
diff --git a/ui/window/BasicImmersiveMode/template-params.xml b/ui/window/BasicImmersiveMode/template-params.xml
index 1335908..ec26964 100644
--- a/ui/window/BasicImmersiveMode/template-params.xml
+++ b/ui/window/BasicImmersiveMode/template-params.xml
@@ -19,11 +19,8 @@
     <group>UI</group>
     <package>com.example.android.basicimmersivemode</package>
     
-
-
     <!-- change minSdk if needed-->
     <minSdk>19</minSdk>
-    <compileSdkVersion>19</compileSdkVersion>
 
     <strings>
         <intro>
@@ -41,4 +38,39 @@
     <common src="logger"/>
     <common src="activities"/>
 
+<metadata>
+    <status>PUBLISHED</status>
+    <categories>UI</categories>
+    <technologies>Android</technologies>
+    <languages>Java</languages>
+    <solutions>Mobile</solutions>
+    <level>INTERMEDIATE</level>
+    <icon>screenshots/big_icon.png</icon>
+    <screenshots>
+        <img>screenshots/1-activity.png</img>
+        <img>screenshots/2-immersive.png</img>
+    </screenshots>
+    <api_refs>
+        <android>android.view.View</android>
+        <android>android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY</android>
+
+    </api_refs>
+    <description>
+<![CDATA[
+Sample demonstrating the use of immersive mode to hide the system and navigation bars for
+full screen applications.
+]]>
+    </description>
+    <intro>
+<![CDATA[
+'Immersive Mode' is a new UI mode which improves 'hide full screen' and 'hide nav bar' 
+modes, by letting users swipe the bars in and out.
+
+This sample demonstrates how to enable and disable immersive mode programmatically.
+
+Immersive mode was introduced in Android 4.4 (Api Level 19). It is toggled using the 
+SYSTEM_UI_FLAG_IMMERSIVE system ui flag. When combined with the SYSTEM_UI_FLAG_HIDE_NAVIGATION and SYSTEM_UI_FLAG_FULLSCREEN  flags, hides the navigation and status bars and lets your app capture all touch events on the screen.
+]]>
+    </intro>
+</metadata>
 </sample>
diff --git a/ui/window/ImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/ui/window/ImmersiveMode/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/ui/window/ImmersiveMode/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/window/ImmersiveMode/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/window/ImmersiveMode/template-params.xml b/ui/window/ImmersiveMode/template-params.xml
index e053d26..8959099 100644
--- a/ui/window/ImmersiveMode/template-params.xml
+++ b/ui/window/ImmersiveMode/template-params.xml
@@ -19,8 +19,6 @@
     <group>UI</group>
     <package>com.example.android.immersivemode</package>
     
-    <compileSdkVersion>19</compileSdkVersion>
-
     <!--TODO: change minSdk if needed-->
     <minSdk>4</minSdk>
 
diff --git a/views/TextLinkify/gradle/wrapper/gradle-wrapper.properties b/views/TextLinkify/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/views/TextLinkify/gradle/wrapper/gradle-wrapper.properties
+++ b/views/TextLinkify/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/views/TextLinkify/screenshots/icon-web.png b/views/TextLinkify/screenshots/icon-web.png
new file mode 100644
index 0000000..47f96e4
--- /dev/null
+++ b/views/TextLinkify/screenshots/icon-web.png
Binary files differ
diff --git a/views/TextLinkify/screenshots/main.png b/views/TextLinkify/screenshots/main.png
new file mode 100644
index 0000000..5f483f5
--- /dev/null
+++ b/views/TextLinkify/screenshots/main.png
Binary files differ
diff --git a/views/TextLinkify/template-params.xml b/views/TextLinkify/template-params.xml
index c703b8b..72c3437 100644
--- a/views/TextLinkify/template-params.xml
+++ b/views/TextLinkify/template-params.xml
@@ -35,4 +35,57 @@
     <template src="base"/>
     <common src="logger"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Views</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/main.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.widget.TextView</android>
+            <android>android.text.util.Linkify</android>
+            <android>android.text.Html</android>
+            <android>android.text.SpannableString</android>
+            <android>android.text.style.StyleSpan</android>
+            <android>android.text.style.URLSpan</android>
+            <android>android.text.SpannableString</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This sample illustrates how links can be added to a TextView. This can be done either
+automatically by setting the "autoLink" property or explicitly.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample demonstrates how clickable links can be added to a `TextView` in Android.
+
+This can be done in three ways:
+
+- **Automatically:** Text added to a TextView can automatically be linkified by enabling
+autoLinking. In XML, use the `android:autoLink` property, programatically call
+[android.widget.TextView#setAutoLinkMask(int)][1] using an option from
+[android.text.util.Linkify][2].
+- **Parsing a String as HTML:** See [android.text.Html#fromHtml(String)][3].
+- **Manually by constructing a [android.text.SpannableString][4]:** Consisting of
+[android.text.style.StyleSpan][5] and [android.text.style.URLSpan][6] objects that
+are contained within a [android.text.SpannableString][7].
+
+[1]: http://developer.android.com/reference/android/widget/TextView.html#setAutoLinkMask(int)
+[2]: http://developer.android.com/reference/android/text/util/Linkify.html
+[3]: http://developer.android.com/reference/android/text/Html.html#fromHtml(java.lang.String)
+[4]: http://developer.android.com/reference/android/text/SpannableString.html
+[5]: http://developer.android.com/reference/android/text/style/StyleSpan.html
+[6]: http://developer.android.com/reference/android/text/style/URLSpan.html
+[7]: http://developer.android.com/reference/android/text/SpannableString.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml b/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml
index b5bebc3..aa8a14a 100644
--- a/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.example.android.agendadata">
+          package="com.example.android.wearable.agendadata">
 
-    <uses-sdk android:minSdkVersion="19"
-        android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+        android:targetSdkVersion="21" />
 
     <uses-permission android:name="android.permission.READ_CALENDAR" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -34,7 +34,7 @@
                 android:value="@integer/google_play_services_version" />
 
         <activity
-                android:name="com.example.android.agendadata.MainActivity"
+                android:name="com.example.android.wearable.agendadata.MainActivity"
                 android:label="@string/app_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
@@ -43,6 +43,6 @@
             </intent-filter>
         </activity>
 
-        <service android:name="com.example.android.agendadata.CalendarQueryService"/>
+        <service android:name="com.example.android.wearable.agendadata.CalendarQueryService"/>
     </application>
 </manifest>
diff --git a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/CalendarQueryService.java b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/CalendarQueryService.java
deleted file mode 100644
index fe7fa27..0000000
--- a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/CalendarQueryService.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.agendadata;
-
-
-import static com.example.android.agendadata.Constants.TAG;
-import static com.example.android.agendadata.Constants.CONNECTION_TIME_OUT_MS;
-import static com.example.android.agendadata.Constants.CAL_DATA_ITEM_PATH_PREFIX;
-import static com.example.android.agendadata.Constants.ALL_DAY;
-import static com.example.android.agendadata.Constants.BEGIN;
-import static com.example.android.agendadata.Constants.DATA_ITEM_URI;
-import static com.example.android.agendadata.Constants.DESCRIPTION;
-import static com.example.android.agendadata.Constants.END;
-import static com.example.android.agendadata.Constants.EVENT_ID;
-import static com.example.android.agendadata.Constants.ID;
-import static com.example.android.agendadata.Constants.PROFILE_PIC;
-import static com.example.android.agendadata.Constants.TITLE;
-
-import android.app.IntentService;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.text.format.Time;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
-import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
-import com.google.android.gms.wearable.Asset;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Queries calendar events using Android Calendar Provider API and creates a data item for each
- * event.
- */
-public class CalendarQueryService extends IntentService
-        implements ConnectionCallbacks, OnConnectionFailedListener {
-
-    private static final String[] INSTANCE_PROJECTION = {
-            CalendarContract.Instances._ID,
-            CalendarContract.Instances.EVENT_ID,
-            CalendarContract.Instances.TITLE,
-            CalendarContract.Instances.BEGIN,
-            CalendarContract.Instances.END,
-            CalendarContract.Instances.ALL_DAY,
-            CalendarContract.Instances.DESCRIPTION,
-            CalendarContract.Instances.ORGANIZER
-    };
-
-    private static final String[] CONTACT_PROJECTION = new String[] { Data._ID, Data.CONTACT_ID };
-    private static final String CONTACT_SELECTION = Email.ADDRESS + " = ?";
-
-    private GoogleApiClient mGoogleApiClient;
-
-    public CalendarQueryService() {
-        super(CalendarQueryService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
-        // Query calendar events in the next 24 hours.
-        Time time = new Time();
-        time.setToNow();
-        long beginTime = time.toMillis(true);
-        time.monthDay++;
-        time.normalize(true);
-        long endTime = time.normalize(true);
-
-        List<Event> events = queryEvents(this, beginTime, endTime);
-        for (Event event : events) {
-            final PutDataMapRequest putDataMapRequest = event.toPutDataMapRequest();
-            if (mGoogleApiClient.isConnected()) {
-                Wearable.DataApi.putDataItem(
-                    mGoogleApiClient, putDataMapRequest.asPutDataRequest()).await();
-            } else {
-                Log.e(TAG, "Failed to send data item: " + putDataMapRequest
-                         + " - Client disconnected from Google Play Services");
-            }
-        }
-        mGoogleApiClient.disconnect();
-    }
-
-    private static String makeDataItemPath(long eventId, long beginTime) {
-        return CAL_DATA_ITEM_PATH_PREFIX + eventId + "/" + beginTime;
-    }
-
-    private static List<Event> queryEvents(Context context, long beginTime, long endTime) {
-        ContentResolver contentResolver = context.getContentResolver();
-        Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();
-        ContentUris.appendId(builder, beginTime);
-        ContentUris.appendId(builder, endTime);
-
-        Cursor cursor = contentResolver.query(builder.build(), INSTANCE_PROJECTION,
-                null /* selection */, null /* selectionArgs */, null /* sortOrder */);
-        try {
-            int idIdx = cursor.getColumnIndex(CalendarContract.Instances._ID);
-            int eventIdIdx = cursor.getColumnIndex(CalendarContract.Instances.EVENT_ID);
-            int titleIdx = cursor.getColumnIndex(CalendarContract.Instances.TITLE);
-            int beginIdx = cursor.getColumnIndex(CalendarContract.Instances.BEGIN);
-            int endIdx = cursor.getColumnIndex(CalendarContract.Instances.END);
-            int allDayIdx = cursor.getColumnIndex(CalendarContract.Instances.ALL_DAY);
-            int descIdx = cursor.getColumnIndex(CalendarContract.Instances.DESCRIPTION);
-            int ownerEmailIdx = cursor.getColumnIndex(CalendarContract.Instances.ORGANIZER);
-
-            List<Event> events = new ArrayList<Event>(cursor.getCount());
-            while (cursor.moveToNext()) {
-                Event event = new Event();
-                event.id = cursor.getLong(idIdx);
-                event.eventId = cursor.getLong(eventIdIdx);
-                event.title = cursor.getString(titleIdx);
-                event.begin = cursor.getLong(beginIdx);
-                event.end = cursor.getLong(endIdx);
-                event.allDay = cursor.getInt(allDayIdx) != 0;
-                event.description = cursor.getString(descIdx);
-                String ownerEmail = cursor.getString(ownerEmailIdx);
-                Cursor contactCursor = contentResolver.query(Data.CONTENT_URI,
-                        CONTACT_PROJECTION, CONTACT_SELECTION, new String[] {ownerEmail}, null);
-                int ownerIdIdx = contactCursor.getColumnIndex(Data.CONTACT_ID);
-                long ownerId = -1;
-                if (contactCursor.moveToFirst()) {
-                    ownerId = contactCursor.getLong(ownerIdIdx);
-                }
-                contactCursor.close();
-                // Use event organizer's profile picture as the notification background.
-                event.ownerProfilePic = getProfilePicture(contentResolver, context, ownerId);
-                events.add(event);
-            }
-            return events;
-        } finally {
-            cursor.close();
-        }
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-    }
-
-    private static Asset getDefaultProfile(Resources res) {
-        Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.nobody);
-        return Asset.createFromBytes(toByteArray(bitmap));
-    }
-
-    private static Asset getProfilePicture(ContentResolver contentResolver, Context context,
-                                           long contactId) {
-        if (contactId != -1) {
-            // Try to retrieve the profile picture for the given contact.
-            Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
-            InputStream inputStream = Contacts.openContactPhotoInputStream(contentResolver,
-                    contactUri, true /*preferHighres*/);
-
-            if (null != inputStream) {
-                try {
-                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
-                    if (bitmap != null) {
-                        return Asset.createFromBytes(toByteArray(bitmap));
-                    } else {
-                        Log.e(TAG, "Cannot decode profile picture for contact " + contactId);
-                    }
-                } finally {
-                    closeQuietly(inputStream);
-                }
-            }
-        }
-        // Use a default background image if the user has no profile picture or there was an error.
-        return getDefaultProfile(context.getResources());
-    }
-
-    private static byte[] toByteArray(Bitmap bitmap) {
-        ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
-        byte[] byteArray = stream.toByteArray();
-        closeQuietly(stream);
-        return byteArray;
-    }
-
-    private static void closeQuietly(Closeable closeable) {
-        try {
-            closeable.close();
-        } catch (IOException e) {
-            Log.e(TAG, "IOException while closing closeable.", e);
-        }
-    }
-
-    private static class Event {
-
-        public long id;
-        public long eventId;
-        public String title;
-        public long begin;
-        public long end;
-        public boolean allDay;
-        public String description;
-        public Asset ownerProfilePic;
-
-        public PutDataMapRequest toPutDataMapRequest(){
-            final PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(
-                    makeDataItemPath(eventId, begin));
-            DataMap data = putDataMapRequest.getDataMap();
-            data.putString(DATA_ITEM_URI, putDataMapRequest.getUri().toString());
-            data.putLong(ID, id);
-            data.putLong(EVENT_ID, eventId);
-            data.putString(TITLE, title);
-            data.putLong(BEGIN, begin);
-            data.putLong(END, end);
-            data.putBoolean(ALL_DAY, allDay);
-            data.putString(DESCRIPTION, description);
-            data.putAsset(PROFILE_PIC, ownerProfilePic);
-
-            return putDataMapRequest;
-        }
-    }
-}
diff --git a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/Constants.java b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/Constants.java
deleted file mode 100644
index d66e7ca..0000000
--- a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/Constants.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.agendadata;
-
-/** Constants used in companion app. */
-public final class Constants {
-    private Constants() {
-    }
-
-    public static final String TAG = "AgendaDataSample";
-
-    public static final String CAL_DATA_ITEM_PATH_PREFIX = "/event";
-    // Timeout for making a connection to GoogleApiClient (in milliseconds).
-    public static final long CONNECTION_TIME_OUT_MS = 100;
-
-    public static final String EVENT_ID = "event_id";
-    public static final String ID = "id";
-    public static final String TITLE = "title";
-    public static final String DESCRIPTION = "description";
-    public static final String BEGIN = "begin";
-    public static final String END = "end";
-    public static final String DATA_ITEM_URI = "data_item_uri";
-    public static final String ALL_DAY = "all_day";
-    public static final String PROFILE_PIC = "profile_pic";
-}
diff --git a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/MainActivity.java b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/MainActivity.java
deleted file mode 100644
index e999fde..0000000
--- a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/agendadata/MainActivity.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.agendadata;
-
-import static com.example.android.agendadata.Constants.TAG;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
-import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
-import com.google.android.gms.common.api.ResultCallback;
-import com.google.android.gms.common.data.FreezableUtils;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.DataItem;
-import com.google.android.gms.wearable.DataItemBuffer;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.List;
-
-public class MainActivity extends Activity implements NodeApi.NodeListener, ConnectionCallbacks,
-        OnConnectionFailedListener {
-
-    /** Request code for launching the Intent to resolve Google Play services errors. */
-    private static final int REQUEST_RESOLVE_ERROR = 1000;
-
-    private GoogleApiClient mGoogleApiClient;
-    private boolean mResolvingError = false;
-
-    private TextView mLogTextView;
-    ScrollView mScroller;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.main);
-        mLogTextView = (TextView) findViewById(R.id.log);
-        mScroller = (ScrollView) findViewById(R.id.scroller);
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        if (!mResolvingError) {
-            mGoogleApiClient.connect();
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        if (mGoogleApiClient.isConnected()) {
-            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
-        }
-        mGoogleApiClient.disconnect();
-        super.onStop();
-    }
-
-    public void onGetEventsClicked(View v) {
-        startService(new Intent(this, CalendarQueryService.class));
-    }
-
-    public void onDeleteEventsClicked(View v) {
-        if (mGoogleApiClient.isConnected()) {
-            Wearable.DataApi.getDataItems(mGoogleApiClient)
-                    .setResultCallback(new ResultCallback<DataItemBuffer>() {
-                        @Override
-                        public void onResult(DataItemBuffer result) {
-                            if (result.getStatus().isSuccess()) {
-                                deleteDataItems(result);
-                            } else {
-                                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                                    Log.d(TAG, "onDeleteEventsClicked(): failed to get Data Items");
-                                }
-                            }
-                            result.close();
-                        }
-                    });
-        } else {
-            Log.e(TAG, "Failed to delete data items"
-                     + " - Client disconnected from Google Play Services");
-        }
-    }
-
-    private void deleteDataItems(DataItemBuffer dataItems) {
-        if (mGoogleApiClient.isConnected()) {
-            // Store the DataItem URIs in a List and close the buffer. Then use these URIs
-            // to delete the DataItems.
-            final List<DataItem> dataItemList = FreezableUtils.freezeIterable(dataItems);
-            dataItems.close();
-            for (final DataItem dataItem : dataItemList) {
-                final Uri dataItemUri = dataItem.getUri();
-                // In a real calendar application, this might delete the corresponding calendar
-                // event from the calendar data provider. In this sample, we simply delete the
-                // DataItem, but leave the phone's calendar data intact.
-                Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
-                        .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
-                            @Override
-                            public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
-                                if (deleteResult.getStatus().isSuccess()) {
-                                    appendLog("Successfully deleted data item: " + dataItemUri);
-                                } else {
-                                    appendLog("Failed to delete data item:" + dataItemUri);
-                                }
-                            }
-                        });
-            }
-        } else {
-            Log.e(TAG, "Failed to delete data items"
-                     + " - Client disconnected from Google Play Services");
-        }
-    }
-
-    private void appendLog(final String s) {
-        mLogTextView.post(new Runnable() {
-            @Override
-            public void run() {
-                mLogTextView.append(s);
-                mLogTextView.append("\n");
-                mScroller.fullScroll(View.FOCUS_DOWN);
-            }
-        });
-    }
-
-    @Override
-    public void onPeerConnected(Node peer) {
-        appendLog("Device connected");
-    }
-
-    @Override
-    public void onPeerDisconnected(Node peer) {
-        appendLog("Device disconnected");
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Connected to Google Api Service");
-        }
-        mResolvingError = false;
-        Wearable.NodeApi.addListener(mGoogleApiClient, this);
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-        // Ignore
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Disconnected from Google Api Service");
-        }
-        if (null != Wearable.NodeApi) {
-            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
-        }
-        if (mResolvingError) {
-            // Already attempting to resolve an error.
-            return;
-        } else if (result.hasResolution()) {
-            try {
-                mResolvingError = true;
-                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
-            } catch (IntentSender.SendIntentException e) {
-                // There was an error with the resolution intent. Try again.
-                mGoogleApiClient.connect();
-            }
-        } else {
-            mResolvingError = false;
-        }
-    }
-}
diff --git a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/CalendarQueryService.java b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/CalendarQueryService.java
new file mode 100644
index 0000000..c39a5ed
--- /dev/null
+++ b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/CalendarQueryService.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.agendadata;
+
+
+import static com.example.android.wearable.agendadata.Constants.TAG;
+import static com.example.android.wearable.agendadata.Constants.CONNECTION_TIME_OUT_MS;
+import static com.example.android.wearable.agendadata.Constants.CAL_DATA_ITEM_PATH_PREFIX;
+import static com.example.android.wearable.agendadata.Constants.ALL_DAY;
+import static com.example.android.wearable.agendadata.Constants.BEGIN;
+import static com.example.android.wearable.agendadata.Constants.DATA_ITEM_URI;
+import static com.example.android.wearable.agendadata.Constants.DESCRIPTION;
+import static com.example.android.wearable.agendadata.Constants.END;
+import static com.example.android.wearable.agendadata.Constants.EVENT_ID;
+import static com.example.android.wearable.agendadata.Constants.ID;
+import static com.example.android.wearable.agendadata.Constants.PROFILE_PIC;
+import static com.example.android.wearable.agendadata.Constants.TITLE;
+
+import android.app.IntentService;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.CalendarContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.text.format.Time;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Queries calendar events using Android Calendar Provider API and creates a data item for each
+ * event.
+ */
+public class CalendarQueryService extends IntentService
+        implements ConnectionCallbacks, OnConnectionFailedListener {
+
+    private static final String[] INSTANCE_PROJECTION = {
+            CalendarContract.Instances._ID,
+            CalendarContract.Instances.EVENT_ID,
+            CalendarContract.Instances.TITLE,
+            CalendarContract.Instances.BEGIN,
+            CalendarContract.Instances.END,
+            CalendarContract.Instances.ALL_DAY,
+            CalendarContract.Instances.DESCRIPTION,
+            CalendarContract.Instances.ORGANIZER
+    };
+
+    private static final String[] CONTACT_PROJECTION = new String[] { Data._ID, Data.CONTACT_ID };
+    private static final String CONTACT_SELECTION = Email.ADDRESS + " = ?";
+
+    private GoogleApiClient mGoogleApiClient;
+
+    public CalendarQueryService() {
+        super(CalendarQueryService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+        // Query calendar events in the next 24 hours.
+        Time time = new Time();
+        time.setToNow();
+        long beginTime = time.toMillis(true);
+        time.monthDay++;
+        time.normalize(true);
+        long endTime = time.normalize(true);
+
+        List<Event> events = queryEvents(this, beginTime, endTime);
+        for (Event event : events) {
+            final PutDataMapRequest putDataMapRequest = event.toPutDataMapRequest();
+            if (mGoogleApiClient.isConnected()) {
+                Wearable.DataApi.putDataItem(
+                    mGoogleApiClient, putDataMapRequest.asPutDataRequest()).await();
+            } else {
+                Log.e(TAG, "Failed to send data item: " + putDataMapRequest
+                         + " - Client disconnected from Google Play Services");
+            }
+        }
+        mGoogleApiClient.disconnect();
+    }
+
+    private static String makeDataItemPath(long eventId, long beginTime) {
+        return CAL_DATA_ITEM_PATH_PREFIX + eventId + "/" + beginTime;
+    }
+
+    private static List<Event> queryEvents(Context context, long beginTime, long endTime) {
+        ContentResolver contentResolver = context.getContentResolver();
+        Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();
+        ContentUris.appendId(builder, beginTime);
+        ContentUris.appendId(builder, endTime);
+
+        Cursor cursor = contentResolver.query(builder.build(), INSTANCE_PROJECTION,
+                null /* selection */, null /* selectionArgs */, null /* sortOrder */);
+        try {
+            int idIdx = cursor.getColumnIndex(CalendarContract.Instances._ID);
+            int eventIdIdx = cursor.getColumnIndex(CalendarContract.Instances.EVENT_ID);
+            int titleIdx = cursor.getColumnIndex(CalendarContract.Instances.TITLE);
+            int beginIdx = cursor.getColumnIndex(CalendarContract.Instances.BEGIN);
+            int endIdx = cursor.getColumnIndex(CalendarContract.Instances.END);
+            int allDayIdx = cursor.getColumnIndex(CalendarContract.Instances.ALL_DAY);
+            int descIdx = cursor.getColumnIndex(CalendarContract.Instances.DESCRIPTION);
+            int ownerEmailIdx = cursor.getColumnIndex(CalendarContract.Instances.ORGANIZER);
+
+            List<Event> events = new ArrayList<Event>(cursor.getCount());
+            while (cursor.moveToNext()) {
+                Event event = new Event();
+                event.id = cursor.getLong(idIdx);
+                event.eventId = cursor.getLong(eventIdIdx);
+                event.title = cursor.getString(titleIdx);
+                event.begin = cursor.getLong(beginIdx);
+                event.end = cursor.getLong(endIdx);
+                event.allDay = cursor.getInt(allDayIdx) != 0;
+                event.description = cursor.getString(descIdx);
+                String ownerEmail = cursor.getString(ownerEmailIdx);
+                Cursor contactCursor = contentResolver.query(Data.CONTENT_URI,
+                        CONTACT_PROJECTION, CONTACT_SELECTION, new String[] {ownerEmail}, null);
+                int ownerIdIdx = contactCursor.getColumnIndex(Data.CONTACT_ID);
+                long ownerId = -1;
+                if (contactCursor.moveToFirst()) {
+                    ownerId = contactCursor.getLong(ownerIdIdx);
+                }
+                contactCursor.close();
+                // Use event organizer's profile picture as the notification background.
+                event.ownerProfilePic = getProfilePicture(contentResolver, context, ownerId);
+                events.add(event);
+            }
+            return events;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+    }
+
+    private static Asset getDefaultProfile(Resources res) {
+        Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.nobody);
+        return Asset.createFromBytes(toByteArray(bitmap));
+    }
+
+    private static Asset getProfilePicture(ContentResolver contentResolver, Context context,
+                                           long contactId) {
+        if (contactId != -1) {
+            // Try to retrieve the profile picture for the given contact.
+            Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+            InputStream inputStream = Contacts.openContactPhotoInputStream(contentResolver,
+                    contactUri, true /*preferHighres*/);
+
+            if (null != inputStream) {
+                try {
+                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
+                    if (bitmap != null) {
+                        return Asset.createFromBytes(toByteArray(bitmap));
+                    } else {
+                        Log.e(TAG, "Cannot decode profile picture for contact " + contactId);
+                    }
+                } finally {
+                    closeQuietly(inputStream);
+                }
+            }
+        }
+        // Use a default background image if the user has no profile picture or there was an error.
+        return getDefaultProfile(context.getResources());
+    }
+
+    private static byte[] toByteArray(Bitmap bitmap) {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
+        byte[] byteArray = stream.toByteArray();
+        closeQuietly(stream);
+        return byteArray;
+    }
+
+    private static void closeQuietly(Closeable closeable) {
+        try {
+            closeable.close();
+        } catch (IOException e) {
+            Log.e(TAG, "IOException while closing closeable.", e);
+        }
+    }
+
+    private static class Event {
+
+        public long id;
+        public long eventId;
+        public String title;
+        public long begin;
+        public long end;
+        public boolean allDay;
+        public String description;
+        public Asset ownerProfilePic;
+
+        public PutDataMapRequest toPutDataMapRequest(){
+            final PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(
+                    makeDataItemPath(eventId, begin));
+            DataMap data = putDataMapRequest.getDataMap();
+            data.putString(DATA_ITEM_URI, putDataMapRequest.getUri().toString());
+            data.putLong(ID, id);
+            data.putLong(EVENT_ID, eventId);
+            data.putString(TITLE, title);
+            data.putLong(BEGIN, begin);
+            data.putLong(END, end);
+            data.putBoolean(ALL_DAY, allDay);
+            data.putString(DESCRIPTION, description);
+            data.putAsset(PROFILE_PIC, ownerProfilePic);
+
+            return putDataMapRequest;
+        }
+    }
+}
diff --git a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/Constants.java b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/Constants.java
new file mode 100644
index 0000000..6dc6952
--- /dev/null
+++ b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/Constants.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.agendadata;
+
+/** Constants used in companion app. */
+public final class Constants {
+    private Constants() {
+    }
+
+    public static final String TAG = "AgendaDataSample";
+
+    public static final String CAL_DATA_ITEM_PATH_PREFIX = "/event";
+    // Timeout for making a connection to GoogleApiClient (in milliseconds).
+    public static final long CONNECTION_TIME_OUT_MS = 100;
+
+    public static final String EVENT_ID = "event_id";
+    public static final String ID = "id";
+    public static final String TITLE = "title";
+    public static final String DESCRIPTION = "description";
+    public static final String BEGIN = "begin";
+    public static final String END = "end";
+    public static final String DATA_ITEM_URI = "data_item_uri";
+    public static final String ALL_DAY = "all_day";
+    public static final String PROFILE_PIC = "profile_pic";
+}
diff --git a/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/MainActivity.java b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/MainActivity.java
new file mode 100644
index 0000000..8e4063d
--- /dev/null
+++ b/wearable/wear/AgendaData/Application/src/main/java/com/example/android/wearable/agendadata/MainActivity.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.agendadata;
+
+import static com.example.android.wearable.agendadata.Constants.TAG;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataItemBuffer;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.List;
+
+public class MainActivity extends Activity implements NodeApi.NodeListener, ConnectionCallbacks,
+        OnConnectionFailedListener {
+
+    /** Request code for launching the Intent to resolve Google Play services errors. */
+    private static final int REQUEST_RESOLVE_ERROR = 1000;
+
+    private GoogleApiClient mGoogleApiClient;
+    private boolean mResolvingError = false;
+
+    private TextView mLogTextView;
+    ScrollView mScroller;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main);
+        mLogTextView = (TextView) findViewById(R.id.log);
+        mScroller = (ScrollView) findViewById(R.id.scroller);
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        if (!mResolvingError) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        if (mGoogleApiClient.isConnected()) {
+            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+        }
+        mGoogleApiClient.disconnect();
+        super.onStop();
+    }
+
+    public void onGetEventsClicked(View v) {
+        startService(new Intent(this, CalendarQueryService.class));
+    }
+
+    public void onDeleteEventsClicked(View v) {
+        if (mGoogleApiClient.isConnected()) {
+            Wearable.DataApi.getDataItems(mGoogleApiClient)
+                    .setResultCallback(new ResultCallback<DataItemBuffer>() {
+                        @Override
+                        public void onResult(DataItemBuffer result) {
+                            if (result.getStatus().isSuccess()) {
+                                deleteDataItems(result);
+                            } else {
+                                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                    Log.d(TAG, "onDeleteEventsClicked(): failed to get Data Items");
+                                }
+                            }
+                            result.close();
+                        }
+                    });
+        } else {
+            Log.e(TAG, "Failed to delete data items"
+                     + " - Client disconnected from Google Play Services");
+        }
+    }
+
+    private void deleteDataItems(DataItemBuffer dataItems) {
+        if (mGoogleApiClient.isConnected()) {
+            // Store the DataItem URIs in a List and close the buffer. Then use these URIs
+            // to delete the DataItems.
+            final List<DataItem> dataItemList = FreezableUtils.freezeIterable(dataItems);
+            dataItems.close();
+            for (final DataItem dataItem : dataItemList) {
+                final Uri dataItemUri = dataItem.getUri();
+                // In a real calendar application, this might delete the corresponding calendar
+                // event from the calendar data provider. In this sample, we simply delete the
+                // DataItem, but leave the phone's calendar data intact.
+                Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
+                        .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
+                            @Override
+                            public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
+                                if (deleteResult.getStatus().isSuccess()) {
+                                    appendLog("Successfully deleted data item: " + dataItemUri);
+                                } else {
+                                    appendLog("Failed to delete data item:" + dataItemUri);
+                                }
+                            }
+                        });
+            }
+        } else {
+            Log.e(TAG, "Failed to delete data items"
+                     + " - Client disconnected from Google Play Services");
+        }
+    }
+
+    private void appendLog(final String s) {
+        mLogTextView.post(new Runnable() {
+            @Override
+            public void run() {
+                mLogTextView.append(s);
+                mLogTextView.append("\n");
+                mScroller.fullScroll(View.FOCUS_DOWN);
+            }
+        });
+    }
+
+    @Override
+    public void onPeerConnected(Node peer) {
+        appendLog("Device connected");
+    }
+
+    @Override
+    public void onPeerDisconnected(Node peer) {
+        appendLog("Device disconnected");
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Connected to Google Api Service");
+        }
+        mResolvingError = false;
+        Wearable.NodeApi.addListener(mGoogleApiClient, this);
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+        // Ignore
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Disconnected from Google Api Service");
+        }
+        if (null != Wearable.NodeApi) {
+            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+        }
+        if (mResolvingError) {
+            // Already attempting to resolve an error.
+            return;
+        } else if (result.hasResolution()) {
+            try {
+                mResolvingError = true;
+                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
+            } catch (IntentSender.SendIntentException e) {
+                // There was an error with the resolution intent. Try again.
+                mGoogleApiClient.connect();
+            }
+        } else {
+            mResolvingError = false;
+        }
+    }
+}
diff --git a/wearable/wear/AgendaData/Shared/src/main/AndroidManifest.xml b/wearable/wear/AgendaData/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 07bc153..0000000
--- a/wearable/wear/AgendaData/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.agendadata.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/AgendaData/Shared/src/main/res/values/strings.xml b/wearable/wear/AgendaData/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/AgendaData/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml b/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml
index c91f09c..dcab622 100644
--- a/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.agendadata" >
+        package="com.example.android.wearable.agendadata" >
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="20" />
+        android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -32,13 +32,13 @@
                 android:value="@integer/google_play_services_version" />
 
         <service
-                android:name="com.example.android.agendadata.HomeListenerService" >
+                android:name="com.example.android.wearable.agendadata.HomeListenerService" >
             <intent-filter>
                 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
             </intent-filter>
         </service>
 
-        <service android:name="com.example.android.agendadata.DeleteService"/>
+        <service android:name="com.example.android.wearable.agendadata.DeleteService"/>
 
         <activity android:name="android.support.wearable.activity.ConfirmationActivity"/>
 
diff --git a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/Constants.java b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/Constants.java
deleted file mode 100644
index 5b51751..0000000
--- a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/Constants.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.agendadata;
-
-/** Constants used in wearable app. */
-public final class Constants {
-
-    private Constants() {
-    }
-
-    public static final String TAG = "AgendaDataSample";
-
-    public static final String TITLE = "title";
-    public static final String DESCRIPTION = "description";
-    public static final String BEGIN = "begin";
-    public static final String END = "end";
-    public static final String ALL_DAY = "all_day";
-    public static final String PROFILE_PIC = "profile_pic";
-
-    public static final String EXTRA_SILENT = "silent";
-}
diff --git a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/DeleteService.java b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/DeleteService.java
deleted file mode 100644
index 7c6bb9b..0000000
--- a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/DeleteService.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.agendadata;
-
-import static com.example.android.agendadata.Constants.TAG;
-import static com.example.android.agendadata.Constants.EXTRA_SILENT;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.wearable.activity.ConfirmationActivity;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Handles "Delete" button on calendar event card.
- */
-public class DeleteService extends IntentService implements GoogleApiClient.ConnectionCallbacks,
-        GoogleApiClient.OnConnectionFailedListener {
-
-    /* Timeout for making a connection to GoogleApiClient (in milliseconds) */
-    private static final long TIME_OUT = 100;
-
-    private GoogleApiClient mGoogleApiClient;
-
-    public DeleteService() {
-        super(DeleteService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        mGoogleApiClient.blockingConnect(TIME_OUT, TimeUnit.MILLISECONDS);
-        Uri dataItemUri = intent.getData();
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "DeleteService.onHandleIntent=" + dataItemUri);
-        }
-        if (mGoogleApiClient.isConnected()) {
-            DataApi.DeleteDataItemsResult result = Wearable.DataApi
-                    .deleteDataItems(mGoogleApiClient, dataItemUri).await();
-            if (result.getStatus().isSuccess() && !intent.getBooleanExtra(EXTRA_SILENT, false)) {
-                // Show the success animation on the watch unless Silent extra is true.
-                startConfirmationActivity(ConfirmationActivity.SUCCESS_ANIMATION,
-                                          getString(R.string.delete_successful));
-            } else {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "DeleteService.onHandleIntent: Failed to delete dataItem:"
-                            + dataItemUri);
-                }
-                // Show the failure animation on the watch unless Silent extra is true.
-                if (!intent.getBooleanExtra(EXTRA_SILENT, false)) {
-                    startConfirmationActivity(ConfirmationActivity.FAILURE_ANIMATION,
-                                              getString(R.string.delete_unsuccessful));
-                }
-            }
-        } else {
-            Log.e(TAG, "Failed to delete data item: " + dataItemUri
-                    + " - Client disconnected from Google Play Services");
-            // Show the failure animation on the watch unless Silent extra is true.
-            if (!intent.getBooleanExtra(EXTRA_SILENT, false)) {
-                startConfirmationActivity(ConfirmationActivity.FAILURE_ANIMATION,
-                        getString(R.string.delete_unsuccessful));
-            }
-        }
-        mGoogleApiClient.disconnect();
-    }
-
-    private void startConfirmationActivity(int animationType, String message) {
-        Intent confirmationActivity = new Intent(this, ConfirmationActivity.class)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION)
-                .putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, animationType)
-                .putExtra(ConfirmationActivity.EXTRA_MESSAGE, message);
-        startActivity(confirmationActivity);
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-    }
-
-}
diff --git a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/HomeListenerService.java b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/HomeListenerService.java
deleted file mode 100644
index 7a46911..0000000
--- a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/agendadata/HomeListenerService.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.agendadata;
-
-import static com.example.android.agendadata.Constants.TAG;
-import static com.example.android.agendadata.Constants.EXTRA_SILENT;
-
-import static com.example.android.agendadata.Constants.ALL_DAY;
-import static com.example.android.agendadata.Constants.BEGIN;
-import static com.example.android.agendadata.Constants.DESCRIPTION;
-import static com.example.android.agendadata.Constants.END;
-import static com.example.android.agendadata.Constants.PROFILE_PIC;
-import static com.example.android.agendadata.Constants.TITLE;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.util.Log;
-
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.Asset;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataItem;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Wearable;
-import com.google.android.gms.wearable.WearableListenerService;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Listens to DataItem events on the home device.
- */
-public class HomeListenerService extends WearableListenerService {
-
-    private static final Map<Uri, Integer> sNotificationIdByDataItemUri =
-            new HashMap<Uri, Integer>();
-    private static int sNotificationId = 1;
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName());
-        }
-        for (DataEvent event : dataEvents) {
-            if (event.getType() == DataEvent.TYPE_DELETED) {
-                deleteDataItem(event.getDataItem());
-            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
-                UpdateNotificationForDataItem(event.getDataItem());
-            }
-        }
-        dataEvents.close();
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this.getApplicationContext())
-                .addApi(Wearable.API)
-                .build();
-        mGoogleApiClient.connect();
-    }
-
-    /**
-     * Posts a local notification to show calendar card.
-     */
-    private void UpdateNotificationForDataItem(DataItem dataItem) {
-        DataMapItem mapDataItem = DataMapItem.fromDataItem(dataItem);
-        DataMap data = mapDataItem.getDataMap();
-
-        String description = data.getString(DESCRIPTION);
-        if (TextUtils.isEmpty(description)) {
-            description = "";
-        } else {
-            // Add a space between the description and the time of the event.
-            description += " ";
-        }
-        String contentText;
-        if (data.getBoolean(ALL_DAY)) {
-            contentText = getString(R.string.desc_all_day, description);
-        } else {
-            String startTime = DateFormat.getTimeFormat(this).format(new Date(data.getLong(BEGIN)));
-            String endTime = DateFormat.getTimeFormat(this).format(new Date(data.getLong(END)));
-            contentText = getString(R.string.desc_time_period, description, startTime, endTime);
-        }
-
-        Intent deleteOperation = new Intent(this, DeleteService.class);
-        // Use a unique identifier for the delete action.
-        String deleteAction = "action_delete" + dataItem.getUri().toString() + sNotificationId;
-        deleteOperation.setAction(deleteAction);
-        deleteOperation.setData(dataItem.getUri());
-        PendingIntent deleteIntent = PendingIntent.getService(this, 0, deleteOperation,
-                PendingIntent.FLAG_ONE_SHOT);
-        PendingIntent silentDeleteIntent = PendingIntent.getService(this, 1,
-                deleteOperation.putExtra(EXTRA_SILENT, true), PendingIntent.FLAG_ONE_SHOT);
-
-        Notification.Builder notificationBuilder = new Notification.Builder(this)
-                .setContentTitle(data.getString(TITLE))
-                .setContentText(contentText)
-                .setSmallIcon(R.drawable.ic_launcher)
-                .addAction(R.drawable.ic_menu_delete, getText(R.string.delete), deleteIntent)
-                .setDeleteIntent(silentDeleteIntent)  // Delete DataItem if notification dismissed.
-                .setLocalOnly(true)
-                .setPriority(Notification.PRIORITY_MIN);
-
-        // Set the event owner's profile picture as the notification background.
-        Asset asset = data.getAsset(PROFILE_PIC);
-        if (null != asset) {
-            if (mGoogleApiClient.isConnected()) {
-                DataApi.GetFdForAssetResult assetFdResult =
-                        Wearable.DataApi.getFdForAsset(mGoogleApiClient, asset).await();
-                if (assetFdResult.getStatus().isSuccess()) {
-                    Bitmap profilePic = BitmapFactory.decodeStream(assetFdResult.getInputStream());
-                    notificationBuilder.extend(new Notification.WearableExtender()
-                            .setBackground(profilePic));
-                } else if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "asset fetch failed with statusCode: "
-                            + assetFdResult.getStatus().getStatusCode());
-                }
-            } else {
-                Log.e(TAG, "Failed to set notification background"
-                         + " - Client disconnected from Google Play Services");
-            }
-        }
-        Notification card = notificationBuilder.build();
-
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .notify(sNotificationId, card);
-
-        sNotificationIdByDataItemUri.put(dataItem.getUri(), sNotificationId++);
-    }
-
-    /**
-     * Deletes the calendar card associated with the data item.
-     */
-    private void deleteDataItem(DataItem dataItem) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "onDataItemDeleted:DataItem=" + dataItem.getUri());
-        }
-        Integer notificationId = sNotificationIdByDataItemUri.remove(dataItem.getUri());
-        if (notificationId != null) {
-            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(notificationId);
-        }
-    }
-
-    @Override
-    public void onMessageReceived(MessageEvent messageEvent) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "onMessageReceived: " + messageEvent.getPath()
-                     + " " + messageEvent.getData() + " for " + getPackageName());
-        }
-    }
-
-}
diff --git a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/Constants.java b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/Constants.java
new file mode 100644
index 0000000..176a21e
--- /dev/null
+++ b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/Constants.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.agendadata;
+
+/** Constants used in wearable app. */
+public final class Constants {
+
+    private Constants() {
+    }
+
+    public static final String TAG = "AgendaDataSample";
+
+    public static final String TITLE = "title";
+    public static final String DESCRIPTION = "description";
+    public static final String BEGIN = "begin";
+    public static final String END = "end";
+    public static final String ALL_DAY = "all_day";
+    public static final String PROFILE_PIC = "profile_pic";
+
+    public static final String EXTRA_SILENT = "silent";
+}
diff --git a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/DeleteService.java b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/DeleteService.java
new file mode 100644
index 0000000..86aafe0
--- /dev/null
+++ b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/DeleteService.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.agendadata;
+
+import static com.example.android.wearable.agendadata.Constants.TAG;
+import static com.example.android.wearable.agendadata.Constants.EXTRA_SILENT;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.wearable.activity.ConfirmationActivity;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Handles "Delete" button on calendar event card.
+ */
+public class DeleteService extends IntentService implements GoogleApiClient.ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener {
+
+    /* Timeout for making a connection to GoogleApiClient (in milliseconds) */
+    private static final long TIME_OUT = 100;
+
+    private GoogleApiClient mGoogleApiClient;
+
+    public DeleteService() {
+        super(DeleteService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        mGoogleApiClient.blockingConnect(TIME_OUT, TimeUnit.MILLISECONDS);
+        Uri dataItemUri = intent.getData();
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "DeleteService.onHandleIntent=" + dataItemUri);
+        }
+        if (mGoogleApiClient.isConnected()) {
+            DataApi.DeleteDataItemsResult result = Wearable.DataApi
+                    .deleteDataItems(mGoogleApiClient, dataItemUri).await();
+            if (result.getStatus().isSuccess() && !intent.getBooleanExtra(EXTRA_SILENT, false)) {
+                // Show the success animation on the watch unless Silent extra is true.
+                startConfirmationActivity(ConfirmationActivity.SUCCESS_ANIMATION,
+                                          getString(R.string.delete_successful));
+            } else {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "DeleteService.onHandleIntent: Failed to delete dataItem:"
+                            + dataItemUri);
+                }
+                // Show the failure animation on the watch unless Silent extra is true.
+                if (!intent.getBooleanExtra(EXTRA_SILENT, false)) {
+                    startConfirmationActivity(ConfirmationActivity.FAILURE_ANIMATION,
+                                              getString(R.string.delete_unsuccessful));
+                }
+            }
+        } else {
+            Log.e(TAG, "Failed to delete data item: " + dataItemUri
+                    + " - Client disconnected from Google Play Services");
+            // Show the failure animation on the watch unless Silent extra is true.
+            if (!intent.getBooleanExtra(EXTRA_SILENT, false)) {
+                startConfirmationActivity(ConfirmationActivity.FAILURE_ANIMATION,
+                        getString(R.string.delete_unsuccessful));
+            }
+        }
+        mGoogleApiClient.disconnect();
+    }
+
+    private void startConfirmationActivity(int animationType, String message) {
+        Intent confirmationActivity = new Intent(this, ConfirmationActivity.class)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION)
+                .putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, animationType)
+                .putExtra(ConfirmationActivity.EXTRA_MESSAGE, message);
+        startActivity(confirmationActivity);
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+    }
+
+}
diff --git a/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/HomeListenerService.java b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/HomeListenerService.java
new file mode 100644
index 0000000..ef47977
--- /dev/null
+++ b/wearable/wear/AgendaData/Wearable/src/main/java/com/example/android/wearable/agendadata/HomeListenerService.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.agendadata;
+
+import static com.example.android.wearable.agendadata.Constants.TAG;
+import static com.example.android.wearable.agendadata.Constants.EXTRA_SILENT;
+
+import static com.example.android.wearable.agendadata.Constants.ALL_DAY;
+import static com.example.android.wearable.agendadata.Constants.BEGIN;
+import static com.example.android.wearable.agendadata.Constants.DESCRIPTION;
+import static com.example.android.wearable.agendadata.Constants.END;
+import static com.example.android.wearable.agendadata.Constants.PROFILE_PIC;
+import static com.example.android.wearable.agendadata.Constants.TITLE;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Listens to DataItem events on the home device.
+ */
+public class HomeListenerService extends WearableListenerService {
+
+    private static final Map<Uri, Integer> sNotificationIdByDataItemUri =
+            new HashMap<Uri, Integer>();
+    private static int sNotificationId = 1;
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName());
+        }
+        for (DataEvent event : dataEvents) {
+            if (event.getType() == DataEvent.TYPE_DELETED) {
+                deleteDataItem(event.getDataItem());
+            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
+                UpdateNotificationForDataItem(event.getDataItem());
+            }
+        }
+        dataEvents.close();
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this.getApplicationContext())
+                .addApi(Wearable.API)
+                .build();
+        mGoogleApiClient.connect();
+    }
+
+    /**
+     * Posts a local notification to show calendar card.
+     */
+    private void UpdateNotificationForDataItem(DataItem dataItem) {
+        DataMapItem mapDataItem = DataMapItem.fromDataItem(dataItem);
+        DataMap data = mapDataItem.getDataMap();
+
+        String description = data.getString(DESCRIPTION);
+        if (TextUtils.isEmpty(description)) {
+            description = "";
+        } else {
+            // Add a space between the description and the time of the event.
+            description += " ";
+        }
+        String contentText;
+        if (data.getBoolean(ALL_DAY)) {
+            contentText = getString(R.string.desc_all_day, description);
+        } else {
+            String startTime = DateFormat.getTimeFormat(this).format(new Date(data.getLong(BEGIN)));
+            String endTime = DateFormat.getTimeFormat(this).format(new Date(data.getLong(END)));
+            contentText = getString(R.string.desc_time_period, description, startTime, endTime);
+        }
+
+        Intent deleteOperation = new Intent(this, DeleteService.class);
+        // Use a unique identifier for the delete action.
+        String deleteAction = "action_delete" + dataItem.getUri().toString() + sNotificationId;
+        deleteOperation.setAction(deleteAction);
+        deleteOperation.setData(dataItem.getUri());
+        PendingIntent deleteIntent = PendingIntent.getService(this, 0, deleteOperation,
+                PendingIntent.FLAG_ONE_SHOT);
+        PendingIntent silentDeleteIntent = PendingIntent.getService(this, 1,
+                deleteOperation.putExtra(EXTRA_SILENT, true), PendingIntent.FLAG_ONE_SHOT);
+
+        Notification.Builder notificationBuilder = new Notification.Builder(this)
+                .setContentTitle(data.getString(TITLE))
+                .setContentText(contentText)
+                .setSmallIcon(R.drawable.ic_launcher)
+                .addAction(R.drawable.ic_menu_delete, getText(R.string.delete), deleteIntent)
+                .setDeleteIntent(silentDeleteIntent)  // Delete DataItem if notification dismissed.
+                .setLocalOnly(true)
+                .setPriority(Notification.PRIORITY_MIN);
+
+        // Set the event owner's profile picture as the notification background.
+        Asset asset = data.getAsset(PROFILE_PIC);
+        if (null != asset) {
+            if (mGoogleApiClient.isConnected()) {
+                DataApi.GetFdForAssetResult assetFdResult =
+                        Wearable.DataApi.getFdForAsset(mGoogleApiClient, asset).await();
+                if (assetFdResult.getStatus().isSuccess()) {
+                    Bitmap profilePic = BitmapFactory.decodeStream(assetFdResult.getInputStream());
+                    notificationBuilder.extend(new Notification.WearableExtender()
+                            .setBackground(profilePic));
+                } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "asset fetch failed with statusCode: "
+                            + assetFdResult.getStatus().getStatusCode());
+                }
+            } else {
+                Log.e(TAG, "Failed to set notification background"
+                         + " - Client disconnected from Google Play Services");
+            }
+        }
+        Notification card = notificationBuilder.build();
+
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .notify(sNotificationId, card);
+
+        sNotificationIdByDataItemUri.put(dataItem.getUri(), sNotificationId++);
+    }
+
+    /**
+     * Deletes the calendar card associated with the data item.
+     */
+    private void deleteDataItem(DataItem dataItem) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "onDataItemDeleted:DataItem=" + dataItem.getUri());
+        }
+        Integer notificationId = sNotificationIdByDataItemUri.remove(dataItem.getUri());
+        if (notificationId != null) {
+            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(notificationId);
+        }
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent messageEvent) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onMessageReceived: " + messageEvent.getPath()
+                     + " " + messageEvent.getData() + " for " + getPackageName());
+        }
+    }
+
+}
diff --git a/wearable/wear/AgendaData/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/AgendaData/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/AgendaData/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/AgendaData/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/AgendaData/screenshots/companion_agenda_data.png b/wearable/wear/AgendaData/screenshots/companion_agenda_data.png
new file mode 100644
index 0000000..c39503b
--- /dev/null
+++ b/wearable/wear/AgendaData/screenshots/companion_agenda_data.png
Binary files differ
diff --git a/wearable/wear/AgendaData/screenshots/dummy_calendar_event.png b/wearable/wear/AgendaData/screenshots/dummy_calendar_event.png
new file mode 100644
index 0000000..074c56b
--- /dev/null
+++ b/wearable/wear/AgendaData/screenshots/dummy_calendar_event.png
Binary files differ
diff --git a/wearable/wear/AgendaData/settings.gradle b/wearable/wear/AgendaData/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/AgendaData/settings.gradle
+++ b/wearable/wear/AgendaData/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/AgendaData/template-params.xml b/wearable/wear/AgendaData/template-params.xml
index 54b5d52..83126ca 100644
--- a/wearable/wear/AgendaData/template-params.xml
+++ b/wearable/wear/AgendaData/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>AgendaData</name>
     <group>Wearable</group>
-    <package>com.example.android.agendadata</package>
+    <package>com.example.android.wearable.agendadata</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -42,7 +42,42 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/companion_agenda_data.png</img>
+            <img>screenshots/dummy_calendar_event.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.IntentService</android>
+            <ext>com.google.android.gms.wearable.DataApi</ext>
+            <ext>com.google.android.gms.wearable.Node</ext>
+        </api_refs>
+        <description>
+<![CDATA[
+Sample demonstrating sync of calendar events to a wearable by the press of a button.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+Using the Wearable [DataApi][1] allows to transmit data such as event time,
+description, and background image.
 
+The sent [DataItems][2] can be deleted individually via an action on the event notifications,
+or all at once via a button on the companion.
+
+When deleted using the notification action, a ConfirmationActivity is used to indicate
+success or failure. The sample shows implementations for both the success as well failure case.
+
+[1]: https://developer.android.com/reference/com/google/android/gms/wearable/DataApi.html
+[2]: https://developer.android.com/reference/com/google/android/gms/wearable/DataItem.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml b/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml
index f382b25..6f7e81e 100644
--- a/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.datalayer" >
+        package="com.example.android.wearable.datalayer" >
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.camera" android:required="false" />
 
diff --git a/wearable/wear/DataLayer/Application/src/main/java/com/example/android/datalayer/MainActivity.java b/wearable/wear/DataLayer/Application/src/main/java/com/example/android/datalayer/MainActivity.java
deleted file mode 100644
index d94261d..0000000
--- a/wearable/wear/DataLayer/Application/src/main/java/com/example/android/datalayer/MainActivity.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.datalayer;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.ResultCallback;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
-import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
-import com.google.android.gms.common.data.FreezableUtils;
-import com.google.android.gms.wearable.Asset;
-import com.google.android.gms.wearable.DataApi.DataItemResult;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.MessageApi.SendMessageResult;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.MessageApi;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.PutDataRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Receives its own events using a listener API designed for foreground activities. Updates a data
- * item every second while it is open. Also allows user to take a photo and send that as an asset to
- * the paired wearable.
- */
-public class MainActivity extends Activity implements DataApi.DataListener,
-        MessageApi.MessageListener, NodeApi.NodeListener, ConnectionCallbacks,
-        OnConnectionFailedListener {
-
-    private static final String TAG = "MainActivity";
-
-    /** Request code for launching the Intent to resolve Google Play services errors. */
-    private static final int REQUEST_RESOLVE_ERROR = 1000;
-
-    private static final String START_ACTIVITY_PATH = "/start-activity";
-    private static final String COUNT_PATH = "/count";
-    private static final String IMAGE_PATH = "/image";
-    private static final String IMAGE_KEY = "photo";
-    private static final String COUNT_KEY = "count";
-
-    private GoogleApiClient mGoogleApiClient;
-    private boolean mResolvingError = false;
-    private boolean mCameraSupported = false;
-
-    private ListView mDataItemList;
-    private Button mTakePhotoBtn;
-    private Button mSendPhotoBtn;
-    private ImageView mThumbView;
-    private Bitmap mImageBitmap;
-    private View mStartActivityBtn;
-
-    private DataItemAdapter mDataItemListAdapter;
-    private Handler mHandler;
-
-    // Send DataItems.
-    private ScheduledExecutorService mGeneratorExecutor;
-    private ScheduledFuture<?> mDataItemGeneratorFuture;
-
-    static final int REQUEST_IMAGE_CAPTURE = 1;
-
-    @Override
-    public void onCreate(Bundle b) {
-        super.onCreate(b);
-        mHandler = new Handler();
-        LOGD(TAG, "onCreate");
-        mCameraSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
-        setContentView(R.layout.main_activity);
-        setupViews();
-
-        // Stores DataItems received by the local broadcaster or from the paired watch.
-        mDataItemListAdapter = new DataItemAdapter(this, android.R.layout.simple_list_item_1);
-        mDataItemList.setAdapter(mDataItemListAdapter);
-
-        mGeneratorExecutor = new ScheduledThreadPoolExecutor(1);
-
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
-            Bundle extras = data.getExtras();
-            mImageBitmap = (Bitmap) extras.get("data");
-            mThumbView.setImageBitmap(mImageBitmap);
-        }
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        if (!mResolvingError) {
-            mGoogleApiClient.connect();
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mDataItemGeneratorFuture = mGeneratorExecutor.scheduleWithFixedDelay(
-                new DataItemGenerator(), 1, 5, TimeUnit.SECONDS);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        mDataItemGeneratorFuture.cancel(true /* mayInterruptIfRunning */);
-    }
-
-    @Override
-    protected void onStop() {
-        if (!mResolvingError) {
-            Wearable.DataApi.removeListener(mGoogleApiClient, this);
-            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
-            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
-            mGoogleApiClient.disconnect();
-        }
-        super.onStop();
-    }
-
-    @Override //ConnectionCallbacks
-    public void onConnected(Bundle connectionHint) {
-        LOGD(TAG, "Google API Client was connected");
-        mResolvingError = false;
-        mStartActivityBtn.setEnabled(true);
-        mSendPhotoBtn.setEnabled(mCameraSupported);
-        Wearable.DataApi.addListener(mGoogleApiClient, this);
-        Wearable.MessageApi.addListener(mGoogleApiClient, this);
-        Wearable.NodeApi.addListener(mGoogleApiClient, this);
-    }
-
-    @Override //ConnectionCallbacks
-    public void onConnectionSuspended(int cause) {
-        LOGD(TAG, "Connection to Google API client was suspended");
-        mStartActivityBtn.setEnabled(false);
-        mSendPhotoBtn.setEnabled(false);
-    }
-
-    @Override //OnConnectionFailedListener
-    public void onConnectionFailed(ConnectionResult result) {
-        if (mResolvingError) {
-            // Already attempting to resolve an error.
-            return;
-        } else if (result.hasResolution()) {
-            try {
-                mResolvingError = true;
-                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
-            } catch (IntentSender.SendIntentException e) {
-                // There was an error with the resolution intent. Try again.
-                mGoogleApiClient.connect();
-            }
-        } else {
-            Log.e(TAG, "Connection to Google API client has failed");
-            mResolvingError = false;
-            mStartActivityBtn.setEnabled(false);
-            mSendPhotoBtn.setEnabled(false);
-            Wearable.DataApi.removeListener(mGoogleApiClient, this);
-            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
-            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
-        }
-    }
-
-    @Override //DataListener
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        LOGD(TAG, "onDataChanged: " + dataEvents);
-        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
-        dataEvents.close();
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                for (DataEvent event : events) {
-                    if (event.getType() == DataEvent.TYPE_CHANGED) {
-                        mDataItemListAdapter.add(
-                                new Event("DataItem Changed", event.getDataItem().toString()));
-                    } else if (event.getType() == DataEvent.TYPE_DELETED) {
-                        mDataItemListAdapter.add(
-                                new Event("DataItem Deleted", event.getDataItem().toString()));
-                    }
-                }
-            }
-        });
-    }
-
-    @Override //MessageListener
-    public void onMessageReceived(final MessageEvent messageEvent) {
-        LOGD(TAG, "onMessageReceived() A message from watch was received:" + messageEvent
-                .getRequestId() + " " + messageEvent.getPath());
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mDataItemListAdapter.add(new Event("Message from watch", messageEvent.toString()));
-            }
-        });
-
-    }
-
-    @Override //NodeListener
-    public void onPeerConnected(final Node peer) {
-        LOGD(TAG, "onPeerConnected: " + peer);
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mDataItemListAdapter.add(new Event("Connected", peer.toString()));
-            }
-        });
-
-    }
-
-    @Override //NodeListener
-    public void onPeerDisconnected(final Node peer) {
-        LOGD(TAG, "onPeerDisconnected: " + peer);
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mDataItemListAdapter.add(new Event("Disconnected", peer.toString()));
-            }
-        });
-    }
-
-    /**
-     * A View Adapter for presenting the Event objects in a list
-     */
-    private static class DataItemAdapter extends ArrayAdapter<Event> {
-
-        private final Context mContext;
-
-        public DataItemAdapter(Context context, int unusedResource) {
-            super(context, unusedResource);
-            mContext = context;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            ViewHolder holder;
-            if (convertView == null) {
-                holder = new ViewHolder();
-                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                        Context.LAYOUT_INFLATER_SERVICE);
-                convertView = inflater.inflate(android.R.layout.two_line_list_item, null);
-                convertView.setTag(holder);
-                holder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
-                holder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
-            }
-            Event event = getItem(position);
-            holder.text1.setText(event.title);
-            holder.text2.setText(event.text);
-            return convertView;
-        }
-
-        private class ViewHolder {
-
-            TextView text1;
-            TextView text2;
-        }
-    }
-
-    private class Event {
-
-        String title;
-        String text;
-
-        public Event(String title, String text) {
-            this.title = title;
-            this.text = text;
-        }
-    }
-
-    private Collection<String> getNodes() {
-        HashSet<String> results = new HashSet<String>();
-        NodeApi.GetConnectedNodesResult nodes =
-                Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
-
-        for (Node node : nodes.getNodes()) {
-            results.add(node.getId());
-        }
-
-        return results;
-    }
-
-    private void sendStartActivityMessage(String node) {
-        Wearable.MessageApi.sendMessage(
-                mGoogleApiClient, node, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
-                new ResultCallback<SendMessageResult>() {
-                    @Override
-                    public void onResult(SendMessageResult sendMessageResult) {
-                        if (!sendMessageResult.getStatus().isSuccess()) {
-                            Log.e(TAG, "Failed to send message with status code: "
-                                    + sendMessageResult.getStatus().getStatusCode());
-                        }
-                    }
-                }
-        );
-    }
-
-    private class StartWearableActivityTask extends AsyncTask<Void, Void, Void> {
-
-        @Override
-        protected Void doInBackground(Void... args) {
-            Collection<String> nodes = getNodes();
-            for (String node : nodes) {
-                sendStartActivityMessage(node);
-            }
-            return null;
-        }
-    }
-
-    /** Sends an RPC to start a fullscreen Activity on the wearable. */
-    public void onStartWearableActivityClick(View view) {
-        LOGD(TAG, "Generating RPC");
-
-        // Trigger an AsyncTask that will query for a list of connected nodes and send a
-        // "start-activity" message to each connected node.
-        new StartWearableActivityTask().execute();
-    }
-
-    /** Generates a DataItem based on an incrementing count. */
-    private class DataItemGenerator implements Runnable {
-
-        private int count = 0;
-
-        @Override
-        public void run() {
-            PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(COUNT_PATH);
-            putDataMapRequest.getDataMap().putInt(COUNT_KEY, count++);
-            PutDataRequest request = putDataMapRequest.asPutDataRequest();
-
-            LOGD(TAG, "Generating DataItem: " + request);
-            if (!mGoogleApiClient.isConnected()) {
-                return;
-            }
-            Wearable.DataApi.putDataItem(mGoogleApiClient, request)
-                    .setResultCallback(new ResultCallback<DataItemResult>() {
-                        @Override
-                        public void onResult(DataItemResult dataItemResult) {
-                            if (!dataItemResult.getStatus().isSuccess()) {
-                                Log.e(TAG, "ERROR: failed to putDataItem, status code: "
-                                        + dataItemResult.getStatus().getStatusCode());
-                            }
-                        }
-                    });
-        }
-    }
-
-    /**
-     * Dispatches an {@link android.content.Intent} to take a photo. Result will be returned back
-     * in onActivityResult().
-     */
-    private void dispatchTakePictureIntent() {
-        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
-        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
-            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
-        }
-    }
-
-    /**
-     * Builds an {@link com.google.android.gms.wearable.Asset} from a bitmap. The image that we get
-     * back from the camera in "data" is a thumbnail size. Typically, your image should not exceed
-     * 320x320 and if you want to have zoom and parallax effect in your app, limit the size of your
-     * image to 640x400. Resize your image before transferring to your wearable device.
-     */
-    private static Asset toAsset(Bitmap bitmap) {
-        ByteArrayOutputStream byteStream = null;
-        try {
-            byteStream = new ByteArrayOutputStream();
-            bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
-            return Asset.createFromBytes(byteStream.toByteArray());
-        } finally {
-            if (null != byteStream) {
-                try {
-                    byteStream.close();
-                } catch (IOException e) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    /**
-     * Sends the asset that was created form the photo we took by adding it to the Data Item store.
-     */
-    private void sendPhoto(Asset asset) {
-        PutDataMapRequest dataMap = PutDataMapRequest.create(IMAGE_PATH);
-        dataMap.getDataMap().putAsset(IMAGE_KEY, asset);
-        dataMap.getDataMap().putLong("time", new Date().getTime());
-        PutDataRequest request = dataMap.asPutDataRequest();
-        Wearable.DataApi.putDataItem(mGoogleApiClient, request)
-                .setResultCallback(new ResultCallback<DataItemResult>() {
-                    @Override
-                    public void onResult(DataItemResult dataItemResult) {
-                        LOGD(TAG, "Sending image was successful: " + dataItemResult.getStatus()
-                                .isSuccess());
-                    }
-                });
-
-    }
-
-    public void onTakePhotoClick(View view) {
-        dispatchTakePictureIntent();
-    }
-
-    public void onSendPhotoClick(View view) {
-        if (null != mImageBitmap && mGoogleApiClient.isConnected()) {
-            sendPhoto(toAsset(mImageBitmap));
-        }
-    }
-
-    /**
-     * Sets up UI components and their callback handlers.
-     */
-    private void setupViews() {
-        mTakePhotoBtn = (Button) findViewById(R.id.takePhoto);
-        mSendPhotoBtn = (Button) findViewById(R.id.sendPhoto);
-
-        // Shows the image received from the handset
-        mThumbView = (ImageView) findViewById(R.id.imageView);
-        mDataItemList = (ListView) findViewById(R.id.data_item_list);
-
-        mStartActivityBtn = findViewById(R.id.start_wearable_activity);
-    }
-
-    /**
-     * As simple wrapper around Log.d
-     */
-    private static void LOGD(final String tag, String message) {
-        if (Log.isLoggable(tag, Log.DEBUG)) {
-            Log.d(tag, message);
-        }
-    }
-
-}
diff --git a/wearable/wear/DataLayer/Application/src/main/java/com/example/android/wearable/datalayer/MainActivity.java b/wearable/wear/DataLayer/Application/src/main/java/com/example/android/wearable/datalayer/MainActivity.java
new file mode 100644
index 0000000..23bc696
--- /dev/null
+++ b/wearable/wear/DataLayer/Application/src/main/java/com/example/android/wearable/datalayer/MainActivity.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.datalayer;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.DataApi.DataItemResult;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.MessageApi.SendMessageResult;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.MessageApi;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Receives its own events using a listener API designed for foreground activities. Updates a data
+ * item every second while it is open. Also allows user to take a photo and send that as an asset to
+ * the paired wearable.
+ */
+public class MainActivity extends Activity implements DataApi.DataListener,
+        MessageApi.MessageListener, NodeApi.NodeListener, ConnectionCallbacks,
+        OnConnectionFailedListener {
+
+    private static final String TAG = "MainActivity";
+
+    /** Request code for launching the Intent to resolve Google Play services errors. */
+    private static final int REQUEST_RESOLVE_ERROR = 1000;
+
+    private static final String START_ACTIVITY_PATH = "/start-activity";
+    private static final String COUNT_PATH = "/count";
+    private static final String IMAGE_PATH = "/image";
+    private static final String IMAGE_KEY = "photo";
+    private static final String COUNT_KEY = "count";
+
+    private GoogleApiClient mGoogleApiClient;
+    private boolean mResolvingError = false;
+    private boolean mCameraSupported = false;
+
+    private ListView mDataItemList;
+    private Button mTakePhotoBtn;
+    private Button mSendPhotoBtn;
+    private ImageView mThumbView;
+    private Bitmap mImageBitmap;
+    private View mStartActivityBtn;
+
+    private DataItemAdapter mDataItemListAdapter;
+    private Handler mHandler;
+
+    // Send DataItems.
+    private ScheduledExecutorService mGeneratorExecutor;
+    private ScheduledFuture<?> mDataItemGeneratorFuture;
+
+    static final int REQUEST_IMAGE_CAPTURE = 1;
+
+    @Override
+    public void onCreate(Bundle b) {
+        super.onCreate(b);
+        mHandler = new Handler();
+        LOGD(TAG, "onCreate");
+        mCameraSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
+        setContentView(R.layout.main_activity);
+        setupViews();
+
+        // Stores DataItems received by the local broadcaster or from the paired watch.
+        mDataItemListAdapter = new DataItemAdapter(this, android.R.layout.simple_list_item_1);
+        mDataItemList.setAdapter(mDataItemListAdapter);
+
+        mGeneratorExecutor = new ScheduledThreadPoolExecutor(1);
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
+            Bundle extras = data.getExtras();
+            mImageBitmap = (Bitmap) extras.get("data");
+            mThumbView.setImageBitmap(mImageBitmap);
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        if (!mResolvingError) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mDataItemGeneratorFuture = mGeneratorExecutor.scheduleWithFixedDelay(
+                new DataItemGenerator(), 1, 5, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mDataItemGeneratorFuture.cancel(true /* mayInterruptIfRunning */);
+    }
+
+    @Override
+    protected void onStop() {
+        if (!mResolvingError) {
+            Wearable.DataApi.removeListener(mGoogleApiClient, this);
+            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+            mGoogleApiClient.disconnect();
+        }
+        super.onStop();
+    }
+
+    @Override //ConnectionCallbacks
+    public void onConnected(Bundle connectionHint) {
+        LOGD(TAG, "Google API Client was connected");
+        mResolvingError = false;
+        mStartActivityBtn.setEnabled(true);
+        mSendPhotoBtn.setEnabled(mCameraSupported);
+        Wearable.DataApi.addListener(mGoogleApiClient, this);
+        Wearable.MessageApi.addListener(mGoogleApiClient, this);
+        Wearable.NodeApi.addListener(mGoogleApiClient, this);
+    }
+
+    @Override //ConnectionCallbacks
+    public void onConnectionSuspended(int cause) {
+        LOGD(TAG, "Connection to Google API client was suspended");
+        mStartActivityBtn.setEnabled(false);
+        mSendPhotoBtn.setEnabled(false);
+    }
+
+    @Override //OnConnectionFailedListener
+    public void onConnectionFailed(ConnectionResult result) {
+        if (mResolvingError) {
+            // Already attempting to resolve an error.
+            return;
+        } else if (result.hasResolution()) {
+            try {
+                mResolvingError = true;
+                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
+            } catch (IntentSender.SendIntentException e) {
+                // There was an error with the resolution intent. Try again.
+                mGoogleApiClient.connect();
+            }
+        } else {
+            Log.e(TAG, "Connection to Google API client has failed");
+            mResolvingError = false;
+            mStartActivityBtn.setEnabled(false);
+            mSendPhotoBtn.setEnabled(false);
+            Wearable.DataApi.removeListener(mGoogleApiClient, this);
+            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+            Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+        }
+    }
+
+    @Override //DataListener
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        LOGD(TAG, "onDataChanged: " + dataEvents);
+        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
+        dataEvents.close();
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (DataEvent event : events) {
+                    if (event.getType() == DataEvent.TYPE_CHANGED) {
+                        mDataItemListAdapter.add(
+                                new Event("DataItem Changed", event.getDataItem().toString()));
+                    } else if (event.getType() == DataEvent.TYPE_DELETED) {
+                        mDataItemListAdapter.add(
+                                new Event("DataItem Deleted", event.getDataItem().toString()));
+                    }
+                }
+            }
+        });
+    }
+
+    @Override //MessageListener
+    public void onMessageReceived(final MessageEvent messageEvent) {
+        LOGD(TAG, "onMessageReceived() A message from watch was received:" + messageEvent
+                .getRequestId() + " " + messageEvent.getPath());
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mDataItemListAdapter.add(new Event("Message from watch", messageEvent.toString()));
+            }
+        });
+
+    }
+
+    @Override //NodeListener
+    public void onPeerConnected(final Node peer) {
+        LOGD(TAG, "onPeerConnected: " + peer);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mDataItemListAdapter.add(new Event("Connected", peer.toString()));
+            }
+        });
+
+    }
+
+    @Override //NodeListener
+    public void onPeerDisconnected(final Node peer) {
+        LOGD(TAG, "onPeerDisconnected: " + peer);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mDataItemListAdapter.add(new Event("Disconnected", peer.toString()));
+            }
+        });
+    }
+
+    /**
+     * A View Adapter for presenting the Event objects in a list
+     */
+    private static class DataItemAdapter extends ArrayAdapter<Event> {
+
+        private final Context mContext;
+
+        public DataItemAdapter(Context context, int unusedResource) {
+            super(context, unusedResource);
+            mContext = context;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewHolder holder;
+            if (convertView == null) {
+                holder = new ViewHolder();
+                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                        Context.LAYOUT_INFLATER_SERVICE);
+                convertView = inflater.inflate(android.R.layout.two_line_list_item, null);
+                convertView.setTag(holder);
+                holder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
+                holder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+            Event event = getItem(position);
+            holder.text1.setText(event.title);
+            holder.text2.setText(event.text);
+            return convertView;
+        }
+
+        private class ViewHolder {
+
+            TextView text1;
+            TextView text2;
+        }
+    }
+
+    private class Event {
+
+        String title;
+        String text;
+
+        public Event(String title, String text) {
+            this.title = title;
+            this.text = text;
+        }
+    }
+
+    private Collection<String> getNodes() {
+        HashSet<String> results = new HashSet<String>();
+        NodeApi.GetConnectedNodesResult nodes =
+                Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
+
+        for (Node node : nodes.getNodes()) {
+            results.add(node.getId());
+        }
+
+        return results;
+    }
+
+    private void sendStartActivityMessage(String node) {
+        Wearable.MessageApi.sendMessage(
+                mGoogleApiClient, node, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
+                new ResultCallback<SendMessageResult>() {
+                    @Override
+                    public void onResult(SendMessageResult sendMessageResult) {
+                        if (!sendMessageResult.getStatus().isSuccess()) {
+                            Log.e(TAG, "Failed to send message with status code: "
+                                    + sendMessageResult.getStatus().getStatusCode());
+                        }
+                    }
+                }
+        );
+    }
+
+    private class StartWearableActivityTask extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... args) {
+            Collection<String> nodes = getNodes();
+            for (String node : nodes) {
+                sendStartActivityMessage(node);
+            }
+            return null;
+        }
+    }
+
+    /** Sends an RPC to start a fullscreen Activity on the wearable. */
+    public void onStartWearableActivityClick(View view) {
+        LOGD(TAG, "Generating RPC");
+
+        // Trigger an AsyncTask that will query for a list of connected nodes and send a
+        // "start-activity" message to each connected node.
+        new StartWearableActivityTask().execute();
+    }
+
+    /** Generates a DataItem based on an incrementing count. */
+    private class DataItemGenerator implements Runnable {
+
+        private int count = 0;
+
+        @Override
+        public void run() {
+            PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(COUNT_PATH);
+            putDataMapRequest.getDataMap().putInt(COUNT_KEY, count++);
+            PutDataRequest request = putDataMapRequest.asPutDataRequest();
+
+            LOGD(TAG, "Generating DataItem: " + request);
+            if (!mGoogleApiClient.isConnected()) {
+                return;
+            }
+            Wearable.DataApi.putDataItem(mGoogleApiClient, request)
+                    .setResultCallback(new ResultCallback<DataItemResult>() {
+                        @Override
+                        public void onResult(DataItemResult dataItemResult) {
+                            if (!dataItemResult.getStatus().isSuccess()) {
+                                Log.e(TAG, "ERROR: failed to putDataItem, status code: "
+                                        + dataItemResult.getStatus().getStatusCode());
+                            }
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Dispatches an {@link android.content.Intent} to take a photo. Result will be returned back
+     * in onActivityResult().
+     */
+    private void dispatchTakePictureIntent() {
+        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
+            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
+        }
+    }
+
+    /**
+     * Builds an {@link com.google.android.gms.wearable.Asset} from a bitmap. The image that we get
+     * back from the camera in "data" is a thumbnail size. Typically, your image should not exceed
+     * 320x320 and if you want to have zoom and parallax effect in your app, limit the size of your
+     * image to 640x400. Resize your image before transferring to your wearable device.
+     */
+    private static Asset toAsset(Bitmap bitmap) {
+        ByteArrayOutputStream byteStream = null;
+        try {
+            byteStream = new ByteArrayOutputStream();
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
+            return Asset.createFromBytes(byteStream.toByteArray());
+        } finally {
+            if (null != byteStream) {
+                try {
+                    byteStream.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Sends the asset that was created form the photo we took by adding it to the Data Item store.
+     */
+    private void sendPhoto(Asset asset) {
+        PutDataMapRequest dataMap = PutDataMapRequest.create(IMAGE_PATH);
+        dataMap.getDataMap().putAsset(IMAGE_KEY, asset);
+        dataMap.getDataMap().putLong("time", new Date().getTime());
+        PutDataRequest request = dataMap.asPutDataRequest();
+        Wearable.DataApi.putDataItem(mGoogleApiClient, request)
+                .setResultCallback(new ResultCallback<DataItemResult>() {
+                    @Override
+                    public void onResult(DataItemResult dataItemResult) {
+                        LOGD(TAG, "Sending image was successful: " + dataItemResult.getStatus()
+                                .isSuccess());
+                    }
+                });
+
+    }
+
+    public void onTakePhotoClick(View view) {
+        dispatchTakePictureIntent();
+    }
+
+    public void onSendPhotoClick(View view) {
+        if (null != mImageBitmap && mGoogleApiClient.isConnected()) {
+            sendPhoto(toAsset(mImageBitmap));
+        }
+    }
+
+    /**
+     * Sets up UI components and their callback handlers.
+     */
+    private void setupViews() {
+        mTakePhotoBtn = (Button) findViewById(R.id.takePhoto);
+        mSendPhotoBtn = (Button) findViewById(R.id.sendPhoto);
+
+        // Shows the image received from the handset
+        mThumbView = (ImageView) findViewById(R.id.imageView);
+        mDataItemList = (ListView) findViewById(R.id.data_item_list);
+
+        mStartActivityBtn = findViewById(R.id.start_wearable_activity);
+    }
+
+    /**
+     * As simple wrapper around Log.d
+     */
+    private static void LOGD(final String tag, String message) {
+        if (Log.isLoggable(tag, Log.DEBUG)) {
+            Log.d(tag, message);
+        }
+    }
+
+}
diff --git a/wearable/wear/DataLayer/Shared/src/main/AndroidManifest.xml b/wearable/wear/DataLayer/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index b7d03bf..0000000
--- a/wearable/wear/DataLayer/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.datalayer.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/DataLayer/Shared/src/main/res/values/strings.xml b/wearable/wear/DataLayer/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/DataLayer/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/DataLayer/Wearable/src/main/AndroidManifest.xml b/wearable/wear/DataLayer/Wearable/src/main/AndroidManifest.xml
index 7bde39a..4cc9034 100644
--- a/wearable/wear/DataLayer/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/DataLayer/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.datalayer" >
+        package="com.example.android.wearable.datalayer" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -47,7 +47,7 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
             <intent-filter>
-                <action android:name="com.example.android.datalayer.EXAMPLE"/>
+                <action android:name="com.example.android.wearable.datalayer.EXAMPLE"/>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
diff --git a/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/datalayer/DataLayerListenerService.java b/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/datalayer/DataLayerListenerService.java
deleted file mode 100644
index 9356f39..0000000
--- a/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/datalayer/DataLayerListenerService.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.datalayer;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.data.FreezableUtils;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.Wearable;
-import com.google.android.gms.wearable.WearableListenerService;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Listens to DataItems and Messages from the local node.
- */
-public class DataLayerListenerService extends WearableListenerService {
-
-    private static final String TAG = "DataLayerListenerServic";
-
-    private static final String START_ACTIVITY_PATH = "/start-activity";
-    private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
-    public static final String COUNT_PATH = "/count";
-    public static final String IMAGE_PATH = "/image";
-    public static final String IMAGE_KEY = "photo";
-    private static final String COUNT_KEY = "count";
-    private static final int MAX_LOG_TAG_LENGTH = 23;
-    GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .build();
-        mGoogleApiClient.connect();
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        LOGD(TAG, "onDataChanged: " + dataEvents);
-        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
-        dataEvents.close();
-        if(!mGoogleApiClient.isConnected()) {
-            ConnectionResult connectionResult = mGoogleApiClient
-                    .blockingConnect(30, TimeUnit.SECONDS);
-            if (!connectionResult.isSuccess()) {
-                Log.e(TAG, "DataLayerListenerService failed to connect to GoogleApiClient.");
-                return;
-            }
-        }
-
-        // Loop through the events and send a message back to the node that created the data item.
-        for (DataEvent event : events) {
-            Uri uri = event.getDataItem().getUri();
-            String path = uri.getPath();
-            if (COUNT_PATH.equals(path)) {
-                // Get the node id of the node that created the data item from the host portion of
-                // the uri.
-                String nodeId = uri.getHost();
-                // Set the data of the message to be the bytes of the Uri.
-                byte[] payload = uri.toString().getBytes();
-
-                // Send the rpc
-                Wearable.MessageApi.sendMessage(mGoogleApiClient, nodeId, DATA_ITEM_RECEIVED_PATH,
-                        payload);
-            }
-        }
-    }
-
-    @Override
-    public void onMessageReceived(MessageEvent messageEvent) {
-        LOGD(TAG, "onMessageReceived: " + messageEvent);
-
-        // Check to see if the message is to start an activity
-        if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
-            Intent startIntent = new Intent(this, MainActivity.class);
-            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            startActivity(startIntent);
-        }
-    }
-
-    @Override
-    public void onPeerConnected(Node peer) {
-        LOGD(TAG, "onPeerConnected: " + peer);
-    }
-
-    @Override
-    public void onPeerDisconnected(Node peer) {
-        LOGD(TAG, "onPeerDisconnected: " + peer);
-    }
-
-    public static void LOGD(final String tag, String message) {
-        if (Log.isLoggable(tag, Log.DEBUG)) {
-            Log.d(tag, message);
-        }
-    }
-}
diff --git a/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/datalayer/MainActivity.java b/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/datalayer/MainActivity.java
deleted file mode 100644
index 5f763b8..0000000
--- a/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/datalayer/MainActivity.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.datalayer;
-
-import static com.example.android.datalayer.DataLayerListenerService.LOGD;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
-import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
-import com.google.android.gms.common.data.FreezableUtils;
-import com.google.android.gms.wearable.Asset;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.MessageApi;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.Wearable;
-
-import java.io.InputStream;
-import java.util.List;
-
-/**
- * Shows events and photo from the Wearable APIs.
- */
-public class MainActivity extends Activity implements ConnectionCallbacks,
-        OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener,
-        NodeApi.NodeListener {
-
-    private static final String TAG = "MainActivity";
-
-    private GoogleApiClient mGoogleApiClient;
-    private ListView mDataItemList;
-    private TextView mIntroText;
-    private DataItemAdapter mDataItemListAdapter;
-    private View mLayout;
-    private Handler mHandler;
-
-    @Override
-    public void onCreate(Bundle b) {
-        super.onCreate(b);
-        mHandler = new Handler();
-        LOGD(TAG, "onCreate");
-        setContentView(R.layout.main_activity);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        mDataItemList = (ListView) findViewById(R.id.dataItem_list);
-        mIntroText = (TextView) findViewById(R.id.intro);
-        mLayout = findViewById(R.id.layout);
-
-        // Stores data events received by the local broadcaster.
-        mDataItemListAdapter = new DataItemAdapter(this, android.R.layout.simple_list_item_1);
-        mDataItemList.setAdapter(mDataItemListAdapter);
-
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mGoogleApiClient.connect();
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        Wearable.DataApi.removeListener(mGoogleApiClient, this);
-        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
-        Wearable.NodeApi.removeListener(mGoogleApiClient, this);
-        mGoogleApiClient.disconnect();
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-        LOGD(TAG, "onConnected(): Successfully connected to Google API client");
-        Wearable.DataApi.addListener(mGoogleApiClient, this);
-        Wearable.MessageApi.addListener(mGoogleApiClient, this);
-        Wearable.NodeApi.addListener(mGoogleApiClient, this);
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-        LOGD(TAG, "onConnectionSuspended(): Connection to Google API client was suspended");
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-        Log.e(TAG, "onConnectionFailed(): Failed to connect, with result: " + result);
-    }
-
-    private void generateEvent(final String title, final String text) {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mIntroText.setVisibility(View.INVISIBLE);
-                mDataItemListAdapter.add(new Event(title, text));
-            }
-        });
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        LOGD(TAG, "onDataChanged(): " + dataEvents);
-
-        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
-        dataEvents.close();
-        for (DataEvent event : events) {
-            if (event.getType() == DataEvent.TYPE_CHANGED) {
-                String path = event.getDataItem().getUri().getPath();
-                if (DataLayerListenerService.IMAGE_PATH.equals(path)) {
-                    DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
-                    Asset photo = dataMapItem.getDataMap()
-                            .getAsset(DataLayerListenerService.IMAGE_KEY);
-                    final Bitmap bitmap = loadBitmapFromAsset(mGoogleApiClient, photo);
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            Log.d(TAG, "Setting background image..");
-                            mLayout.setBackground(new BitmapDrawable(getResources(), bitmap));
-                        }
-                    });
-
-                } else if (DataLayerListenerService.COUNT_PATH.equals(path)) {
-                    LOGD(TAG, "Data Changed for COUNT_PATH");
-                    generateEvent("DataItem Changed", event.getDataItem().toString());
-                } else {
-                    LOGD(TAG, "Unrecognized path: " + path);
-                }
-
-            } else if (event.getType() == DataEvent.TYPE_DELETED) {
-                generateEvent("DataItem Deleted", event.getDataItem().toString());
-            } else {
-                generateEvent("Unknown data event type", "Type = " + event.getType());
-            }
-        }
-    }
-
-    /**
-     * Extracts {@link android.graphics.Bitmap} data from the
-     * {@link com.google.android.gms.wearable.Asset}
-     */
-    private Bitmap loadBitmapFromAsset(GoogleApiClient apiClient, Asset asset) {
-        if (asset == null) {
-            throw new IllegalArgumentException("Asset must be non-null");
-        }
-
-        InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
-                apiClient, asset).await().getInputStream();
-
-        if (assetInputStream == null) {
-            Log.w(TAG, "Requested an unknown Asset.");
-            return null;
-        }
-        return BitmapFactory.decodeStream(assetInputStream);
-    }
-
-    @Override
-    public void onMessageReceived(MessageEvent event) {
-        LOGD(TAG, "onMessageReceived: " + event);
-        generateEvent("Message", event.toString());
-    }
-
-    @Override
-    public void onPeerConnected(Node node) {
-        generateEvent("Node Connected", node.getId());
-    }
-
-    @Override
-    public void onPeerDisconnected(Node node) {
-        generateEvent("Node Disconnected", node.getId());
-    }
-
-    private static class DataItemAdapter extends ArrayAdapter<Event> {
-
-        private final Context mContext;
-
-        public DataItemAdapter(Context context, int unusedResource) {
-            super(context, unusedResource);
-            mContext = context;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            ViewHolder holder;
-            if (convertView == null) {
-                holder = new ViewHolder();
-                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
-                        Context.LAYOUT_INFLATER_SERVICE);
-                convertView = inflater.inflate(android.R.layout.two_line_list_item, null);
-                convertView.setTag(holder);
-                holder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
-                holder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
-            }
-            Event event = getItem(position);
-            holder.text1.setText(event.title);
-            holder.text2.setText(event.text);
-            return convertView;
-        }
-
-        private class ViewHolder {
-
-            TextView text1;
-            TextView text2;
-        }
-    }
-
-    private class Event {
-
-        String title;
-        String text;
-
-        public Event(String title, String text) {
-            this.title = title;
-            this.text = text;
-        }
-    }
-}
diff --git a/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/wearable/datalayer/DataLayerListenerService.java b/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/wearable/datalayer/DataLayerListenerService.java
new file mode 100644
index 0000000..64abaf5
--- /dev/null
+++ b/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/wearable/datalayer/DataLayerListenerService.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.datalayer;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens to DataItems and Messages from the local node.
+ */
+public class DataLayerListenerService extends WearableListenerService {
+
+    private static final String TAG = "DataLayerListenerServic";
+
+    private static final String START_ACTIVITY_PATH = "/start-activity";
+    private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
+    public static final String COUNT_PATH = "/count";
+    public static final String IMAGE_PATH = "/image";
+    public static final String IMAGE_KEY = "photo";
+    private static final String COUNT_KEY = "count";
+    private static final int MAX_LOG_TAG_LENGTH = 23;
+    GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .build();
+        mGoogleApiClient.connect();
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        LOGD(TAG, "onDataChanged: " + dataEvents);
+        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
+        dataEvents.close();
+        if(!mGoogleApiClient.isConnected()) {
+            ConnectionResult connectionResult = mGoogleApiClient
+                    .blockingConnect(30, TimeUnit.SECONDS);
+            if (!connectionResult.isSuccess()) {
+                Log.e(TAG, "DataLayerListenerService failed to connect to GoogleApiClient.");
+                return;
+            }
+        }
+
+        // Loop through the events and send a message back to the node that created the data item.
+        for (DataEvent event : events) {
+            Uri uri = event.getDataItem().getUri();
+            String path = uri.getPath();
+            if (COUNT_PATH.equals(path)) {
+                // Get the node id of the node that created the data item from the host portion of
+                // the uri.
+                String nodeId = uri.getHost();
+                // Set the data of the message to be the bytes of the Uri.
+                byte[] payload = uri.toString().getBytes();
+
+                // Send the rpc
+                Wearable.MessageApi.sendMessage(mGoogleApiClient, nodeId, DATA_ITEM_RECEIVED_PATH,
+                        payload);
+            }
+        }
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent messageEvent) {
+        LOGD(TAG, "onMessageReceived: " + messageEvent);
+
+        // Check to see if the message is to start an activity
+        if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
+            Intent startIntent = new Intent(this, MainActivity.class);
+            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(startIntent);
+        }
+    }
+
+    @Override
+    public void onPeerConnected(Node peer) {
+        LOGD(TAG, "onPeerConnected: " + peer);
+    }
+
+    @Override
+    public void onPeerDisconnected(Node peer) {
+        LOGD(TAG, "onPeerDisconnected: " + peer);
+    }
+
+    public static void LOGD(final String tag, String message) {
+        if (Log.isLoggable(tag, Log.DEBUG)) {
+            Log.d(tag, message);
+        }
+    }
+}
diff --git a/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/wearable/datalayer/MainActivity.java b/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/wearable/datalayer/MainActivity.java
new file mode 100644
index 0000000..b312135
--- /dev/null
+++ b/wearable/wear/DataLayer/Wearable/src/main/java/com/example/android/wearable/datalayer/MainActivity.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.datalayer;
+
+import static com.example.android.wearable.datalayer.DataLayerListenerService.LOGD;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.MessageApi;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Shows events and photo from the Wearable APIs.
+ */
+public class MainActivity extends Activity implements ConnectionCallbacks,
+        OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener,
+        NodeApi.NodeListener {
+
+    private static final String TAG = "MainActivity";
+
+    private GoogleApiClient mGoogleApiClient;
+    private ListView mDataItemList;
+    private TextView mIntroText;
+    private DataItemAdapter mDataItemListAdapter;
+    private View mLayout;
+    private Handler mHandler;
+
+    @Override
+    public void onCreate(Bundle b) {
+        super.onCreate(b);
+        mHandler = new Handler();
+        LOGD(TAG, "onCreate");
+        setContentView(R.layout.main_activity);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        mDataItemList = (ListView) findViewById(R.id.dataItem_list);
+        mIntroText = (TextView) findViewById(R.id.intro);
+        mLayout = findViewById(R.id.layout);
+
+        // Stores data events received by the local broadcaster.
+        mDataItemListAdapter = new DataItemAdapter(this, android.R.layout.simple_list_item_1);
+        mDataItemList.setAdapter(mDataItemListAdapter);
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGoogleApiClient.connect();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Wearable.DataApi.removeListener(mGoogleApiClient, this);
+        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+        Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+        mGoogleApiClient.disconnect();
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+        LOGD(TAG, "onConnected(): Successfully connected to Google API client");
+        Wearable.DataApi.addListener(mGoogleApiClient, this);
+        Wearable.MessageApi.addListener(mGoogleApiClient, this);
+        Wearable.NodeApi.addListener(mGoogleApiClient, this);
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+        LOGD(TAG, "onConnectionSuspended(): Connection to Google API client was suspended");
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+        Log.e(TAG, "onConnectionFailed(): Failed to connect, with result: " + result);
+    }
+
+    private void generateEvent(final String title, final String text) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mIntroText.setVisibility(View.INVISIBLE);
+                mDataItemListAdapter.add(new Event(title, text));
+            }
+        });
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        LOGD(TAG, "onDataChanged(): " + dataEvents);
+
+        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
+        dataEvents.close();
+        for (DataEvent event : events) {
+            if (event.getType() == DataEvent.TYPE_CHANGED) {
+                String path = event.getDataItem().getUri().getPath();
+                if (DataLayerListenerService.IMAGE_PATH.equals(path)) {
+                    DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
+                    Asset photo = dataMapItem.getDataMap()
+                            .getAsset(DataLayerListenerService.IMAGE_KEY);
+                    final Bitmap bitmap = loadBitmapFromAsset(mGoogleApiClient, photo);
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            Log.d(TAG, "Setting background image..");
+                            mLayout.setBackground(new BitmapDrawable(getResources(), bitmap));
+                        }
+                    });
+
+                } else if (DataLayerListenerService.COUNT_PATH.equals(path)) {
+                    LOGD(TAG, "Data Changed for COUNT_PATH");
+                    generateEvent("DataItem Changed", event.getDataItem().toString());
+                } else {
+                    LOGD(TAG, "Unrecognized path: " + path);
+                }
+
+            } else if (event.getType() == DataEvent.TYPE_DELETED) {
+                generateEvent("DataItem Deleted", event.getDataItem().toString());
+            } else {
+                generateEvent("Unknown data event type", "Type = " + event.getType());
+            }
+        }
+    }
+
+    /**
+     * Extracts {@link android.graphics.Bitmap} data from the
+     * {@link com.google.android.gms.wearable.Asset}
+     */
+    private Bitmap loadBitmapFromAsset(GoogleApiClient apiClient, Asset asset) {
+        if (asset == null) {
+            throw new IllegalArgumentException("Asset must be non-null");
+        }
+
+        InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
+                apiClient, asset).await().getInputStream();
+
+        if (assetInputStream == null) {
+            Log.w(TAG, "Requested an unknown Asset.");
+            return null;
+        }
+        return BitmapFactory.decodeStream(assetInputStream);
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent event) {
+        LOGD(TAG, "onMessageReceived: " + event);
+        generateEvent("Message", event.toString());
+    }
+
+    @Override
+    public void onPeerConnected(Node node) {
+        generateEvent("Node Connected", node.getId());
+    }
+
+    @Override
+    public void onPeerDisconnected(Node node) {
+        generateEvent("Node Disconnected", node.getId());
+    }
+
+    private static class DataItemAdapter extends ArrayAdapter<Event> {
+
+        private final Context mContext;
+
+        public DataItemAdapter(Context context, int unusedResource) {
+            super(context, unusedResource);
+            mContext = context;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewHolder holder;
+            if (convertView == null) {
+                holder = new ViewHolder();
+                LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                        Context.LAYOUT_INFLATER_SERVICE);
+                convertView = inflater.inflate(android.R.layout.two_line_list_item, null);
+                convertView.setTag(holder);
+                holder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
+                holder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+            Event event = getItem(position);
+            holder.text1.setText(event.title);
+            holder.text2.setText(event.text);
+            return convertView;
+        }
+
+        private class ViewHolder {
+
+            TextView text1;
+            TextView text2;
+        }
+    }
+
+    private class Event {
+
+        String title;
+        String text;
+
+        public Event(String title, String text) {
+            this.title = title;
+            this.text = text;
+        }
+    }
+}
diff --git a/wearable/wear/DataLayer/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/DataLayer/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/DataLayer/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/DataLayer/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/DataLayer/screenshots/phone_image.png b/wearable/wear/DataLayer/screenshots/phone_image.png
new file mode 100644
index 0000000..e045b65
--- /dev/null
+++ b/wearable/wear/DataLayer/screenshots/phone_image.png
Binary files differ
diff --git a/wearable/wear/DataLayer/screenshots/wearable_background_image.png b/wearable/wear/DataLayer/screenshots/wearable_background_image.png
new file mode 100644
index 0000000..7826180
--- /dev/null
+++ b/wearable/wear/DataLayer/screenshots/wearable_background_image.png
Binary files differ
diff --git a/wearable/wear/DataLayer/settings.gradle b/wearable/wear/DataLayer/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/DataLayer/settings.gradle
+++ b/wearable/wear/DataLayer/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/DataLayer/template-params.xml b/wearable/wear/DataLayer/template-params.xml
index 0f62756..6c68e1d 100644
--- a/wearable/wear/DataLayer/template-params.xml
+++ b/wearable/wear/DataLayer/template-params.xml
@@ -14,33 +14,64 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
-
-
 <sample>
     <name>DataLayer</name>
     <group>Wearable</group>
-    <package>com.example.android.datalayer</package>
-
-
-
+    <package>com.example.android.wearable.datalayer</package>
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
-
-
+    <minSdk>18</minSdk>
     <strings>
         <intro>
             <![CDATA[
-            You can cause the wearable to start an activity by pressing a button in the
-            companion app UI. You can also take a picture on the companion device, which will be transmitted as
-            an Asset for display on the wearable.
+            Sample demonstrating the usage of the GoogleApiClient in order to send data
+            from a handheld device to a wearable. The data transmitted is a picture taken by
+            the user of the sample.
             ]]>
         </intro>
     </strings>
-
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>UI, Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>Application/src/main/res/drawable-xxhdpi/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/phone_image.png</img>
+            <img>screenshots/wearable_background_image.png</img>
+        </screenshots>
+        <api_refs>
+            <ext>com.google.android.gms.wearable.DataApi</ext>
+            <ext>com.google.android.gms.wearable.DataEvent</ext>
+            <ext>com.google.android.gms.wearable.WearableListenerService</ext>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates how to work with a WearableListenerService,
+to produce and consume DataEvents and effectively work with the DataLayer.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample demonstrates how to make a handheld and an Android Wear device communicate
+using the [DataApi][2].
+It does this by sending a picture between connected devices.
 
+An Activity is being used for both the connected devices which implement their parts of
+the required interfaces.
+
+It showcases how to use an [WearableListenerService][1] to consume DataEvents
+as well as implementations for various required listeners when using the [DataApi][2],
+[MessageApi][3] and [NodeApi][4].
+
+[1]: https://developer.android.com/reference/com/google/android/gms/wearable/WearableListenerService.html
+[2]: https://developer.android.com/reference/com/google/android/gms/wearable/DataApi.html
+[3]: https://developer.android.com/reference/com/google/android/gms/wearable/MessageApi.html
+[4]: https://developer.android.com/reference/com/google/android/gms/wearable/NodeApi.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml b/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml
index 115ebec..c9fdccb 100644
--- a/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.delayedconfirmation" >
+        package="com.example.android.wearable.delayedconfirmation" >
 
-    <uses-sdk android:minSdkVersion="19"
-        android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+        android:targetSdkVersion="21" />
 
     <application
             android:allowBackup="true"
diff --git a/wearable/wear/DelayedConfirmation/Application/src/main/java/com/example/android/delayedconfirmation/MainActivity.java b/wearable/wear/DelayedConfirmation/Application/src/main/java/com/example/android/delayedconfirmation/MainActivity.java
deleted file mode 100644
index 6fe681f..0000000
--- a/wearable/wear/DelayedConfirmation/Application/src/main/java/com/example/android/delayedconfirmation/MainActivity.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.delayedconfirmation;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-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.wearable.MessageApi;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.Wearable;
-
-/**
- * Has a single button, used to start the Wearable MainActivity.
- */
-public class MainActivity extends Activity implements MessageApi.MessageListener,
-        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
-
-    private static final String TAG = "DelayedConfirmation";
-    private static final String START_ACTIVITY_PATH = "/start-activity";
-    private static final String TIMER_SELECTED_PATH = "/timer_selected";
-    private static final String TIMER_FINISHED_PATH = "/timer_finished";
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate(Bundle b) {
-        super.onCreate(b);
-        setContentView(R.layout.main_activity);
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        if (!mGoogleApiClient.isConnected()) {
-            mGoogleApiClient.connect();
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        if (mGoogleApiClient.isConnected()) {
-            mGoogleApiClient.disconnect();
-        }
-        super.onDestroy();
-    }
-
-    @Override
-    public void onConnected(Bundle bundle) {
-        Wearable.MessageApi.addListener(mGoogleApiClient, this);
-    }
-
-    @Override
-    public void onConnectionSuspended(int i) {
-        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
-    }
-
-    @Override
-    public void onMessageReceived(final MessageEvent messageEvent) {
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                if (messageEvent.getPath().equals(TIMER_SELECTED_PATH)) {
-                    Toast.makeText(getApplicationContext(), R.string.toast_timer_selected,
-                            Toast.LENGTH_SHORT).show();
-                } else if (messageEvent.getPath().equals(TIMER_FINISHED_PATH)) {
-                    Toast.makeText(getApplicationContext(), R.string.toast_timer_finished,
-                            Toast.LENGTH_SHORT).show();
-                }
-            }
-        });
-
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-        Log.e(TAG, "Failed to connect to Google Api Client with error code "
-                + connectionResult.getErrorCode());
-    }
-
-    /**
-     * Sends a message to Wearable MainActivity when button is pressed.
-     */
-    public void onStartWearableActivityClick(View view) {
-        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
-                new ResultCallback<NodeApi.GetConnectedNodesResult>() {
-            @Override
-            public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
-                for (final Node node : getConnectedNodesResult.getNodes()) {
-                    Wearable.MessageApi.sendMessage(
-                            mGoogleApiClient, node.getId(), START_ACTIVITY_PATH, new byte[0])
-                            .setResultCallback(getSendMessageResultCallback());
-                }
-            }
-        });
-    }
-
-    private ResultCallback<MessageApi.SendMessageResult> getSendMessageResultCallback() {
-        return new ResultCallback<MessageApi.SendMessageResult>() {
-            @Override
-            public void onResult(MessageApi.SendMessageResult sendMessageResult) {
-                if (!sendMessageResult.getStatus().isSuccess()) {
-                    Log.e(TAG, "Failed to connect to Google Api Client with status "
-                            + sendMessageResult.getStatus());
-                }
-            }
-        };
-    }
-}
diff --git a/wearable/wear/DelayedConfirmation/Application/src/main/java/com/example/android/wearable/delayedconfirmation/MainActivity.java b/wearable/wear/DelayedConfirmation/Application/src/main/java/com/example/android/wearable/delayedconfirmation/MainActivity.java
new file mode 100644
index 0000000..3ffda53
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/Application/src/main/java/com/example/android/wearable/delayedconfirmation/MainActivity.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.delayedconfirmation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+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.wearable.MessageApi;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+
+/**
+ * Has a single button, used to start the Wearable MainActivity.
+ */
+public class MainActivity extends Activity implements MessageApi.MessageListener,
+        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "DelayedConfirmation";
+    private static final String START_ACTIVITY_PATH = "/start-activity";
+    private static final String TIMER_SELECTED_PATH = "/timer_selected";
+    private static final String TIMER_FINISHED_PATH = "/timer_finished";
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate(Bundle b) {
+        super.onCreate(b);
+        setContentView(R.layout.main_activity);
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (!mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.disconnect();
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    public void onConnected(Bundle bundle) {
+        Wearable.MessageApi.addListener(mGoogleApiClient, this);
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+    }
+
+    @Override
+    public void onMessageReceived(final MessageEvent messageEvent) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (messageEvent.getPath().equals(TIMER_SELECTED_PATH)) {
+                    Toast.makeText(getApplicationContext(), R.string.toast_timer_selected,
+                            Toast.LENGTH_SHORT).show();
+                } else if (messageEvent.getPath().equals(TIMER_FINISHED_PATH)) {
+                    Toast.makeText(getApplicationContext(), R.string.toast_timer_finished,
+                            Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        Log.e(TAG, "Failed to connect to Google Api Client with error code "
+                + connectionResult.getErrorCode());
+    }
+
+    /**
+     * Sends a message to Wearable MainActivity when button is pressed.
+     */
+    public void onStartWearableActivityClick(View view) {
+        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
+                new ResultCallback<NodeApi.GetConnectedNodesResult>() {
+            @Override
+            public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
+                for (final Node node : getConnectedNodesResult.getNodes()) {
+                    Wearable.MessageApi.sendMessage(
+                            mGoogleApiClient, node.getId(), START_ACTIVITY_PATH, new byte[0])
+                            .setResultCallback(getSendMessageResultCallback());
+                }
+            }
+        });
+    }
+
+    private ResultCallback<MessageApi.SendMessageResult> getSendMessageResultCallback() {
+        return new ResultCallback<MessageApi.SendMessageResult>() {
+            @Override
+            public void onResult(MessageApi.SendMessageResult sendMessageResult) {
+                if (!sendMessageResult.getStatus().isSuccess()) {
+                    Log.e(TAG, "Failed to connect to Google Api Client with status "
+                            + sendMessageResult.getStatus());
+                }
+            }
+        };
+    }
+}
diff --git a/wearable/wear/DelayedConfirmation/Shared/.gitignore b/wearable/wear/DelayedConfirmation/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/DelayedConfirmation/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/DelayedConfirmation/Shared/src/main/AndroidManifest.xml b/wearable/wear/DelayedConfirmation/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 2c9b669..0000000
--- a/wearable/wear/DelayedConfirmation/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.delayedconfirmation.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/DelayedConfirmation/Shared/src/main/res/values/strings.xml b/wearable/wear/DelayedConfirmation/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/DelayedConfirmation/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/DelayedConfirmation/Wearable/src/main/AndroidManifest.xml b/wearable/wear/DelayedConfirmation/Wearable/src/main/AndroidManifest.xml
index a220008..845c070 100644
--- a/wearable/wear/DelayedConfirmation/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/DelayedConfirmation/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.delayedconfirmation" >
+        package="com.example.android.wearable.delayedconfirmation" >
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="20" />
+        android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/delayedconfirmation/MainActivity.java b/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/delayedconfirmation/MainActivity.java
deleted file mode 100644
index 2e169c7..0000000
--- a/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/delayedconfirmation/MainActivity.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.delayedconfirmation;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.os.Bundle;
-import android.support.wearable.view.DelayedConfirmationView;
-import android.util.Log;
-import android.view.View;
-
-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.wearable.MessageApi;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.Wearable;
-
-
-public class MainActivity extends Activity implements
-        DelayedConfirmationView.DelayedConfirmationListener,
-        GoogleApiClient.OnConnectionFailedListener {
-
-    private static final String TAG = "DelayedConfirmation";
-    private static final int NUM_SECONDS = 5;
-
-    private static final String TIMER_SELECTED_PATH = "/timer_selected";
-    private static final String TIMER_FINISHED_PATH = "/timer_finished";
-
-    private DelayedConfirmationView delayedConfirmationView;
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate(Bundle b) {
-        super.onCreate(b);
-        setContentView(R.layout.main_activity);
-        delayedConfirmationView = (DelayedConfirmationView) findViewById(R.id.delayed_confirmation);
-        delayedConfirmationView.setTotalTimeMs(NUM_SECONDS * 1000);
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        if (!mGoogleApiClient.isConnected()) {
-            mGoogleApiClient.connect();
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        if (mGoogleApiClient.isConnected()) {
-            mGoogleApiClient.disconnect();
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * Starts the DelayedConfirmationView when user presses "Start Timer" button.
-     */
-    public void onStartTimer(View view) {
-        delayedConfirmationView.start();
-        delayedConfirmationView.setListener(this);
-    }
-
-    @Override
-    public void onTimerSelected(View v) {
-        v.setPressed(true);
-        Notification notification = new Notification.Builder(this)
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setContentTitle(getString(R.string.notification_title))
-                .setContentText(getString(R.string.notification_timer_selected))
-                .build();
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification);
-        sendMessageToCompanion(TIMER_SELECTED_PATH);
-        // Prevent onTimerFinished from being heard.
-        ((DelayedConfirmationView) v).setListener(null);
-        finish();
-    }
-
-    @Override
-    public void onTimerFinished(View v) {
-        Notification notification = new Notification.Builder(this)
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setContentTitle(getString(R.string.notification_title))
-                .setContentText(getString(R.string.notification_timer_finished))
-                .build();
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification);
-        sendMessageToCompanion(TIMER_FINISHED_PATH);
-        finish();
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-        Log.e(TAG, "Failed to connect to Google Api Client");
-    }
-
-    private void sendMessageToCompanion(final String path) {
-        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
-                new ResultCallback<NodeApi.GetConnectedNodesResult>() {
-                    @Override
-                    public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
-                        for (final Node node : getConnectedNodesResult.getNodes()) {
-                            Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path,
-                                    new byte[0]).setResultCallback(getSendMessageResultCallback());
-                        }
-                    }
-                }
-        );
-
-    }
-
-    private ResultCallback<MessageApi.SendMessageResult> getSendMessageResultCallback() {
-        return new ResultCallback<MessageApi.SendMessageResult>() {
-            @Override
-            public void onResult(MessageApi.SendMessageResult sendMessageResult) {
-                if (!sendMessageResult.getStatus().isSuccess()) {
-                    Log.e(TAG, "Failed to connect to Google Api Client with status "
-                            + sendMessageResult.getStatus());
-                }
-            }
-        };
-    }
-}
diff --git a/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/delayedconfirmation/WearableMessageListenerService.java b/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/delayedconfirmation/WearableMessageListenerService.java
deleted file mode 100644
index 0032fda..0000000
--- a/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/delayedconfirmation/WearableMessageListenerService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.delayedconfirmation;
-
-import android.content.Intent;
-
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.WearableListenerService;
-
-/**
- * Listens for a message telling it to start the Wearable MainActivity.
- */
-public class WearableMessageListenerService extends WearableListenerService {
-    private static final String START_ACTIVITY_PATH = "/start-activity";
-
-    @Override
-    public void onMessageReceived(MessageEvent event) {
-        if (event.getPath().equals(START_ACTIVITY_PATH)) {
-            Intent startIntent = new Intent(this, MainActivity.class);
-            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            startActivity(startIntent);
-        }
-    }
-}
diff --git a/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/wearable/delayedconfirmation/MainActivity.java b/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/wearable/delayedconfirmation/MainActivity.java
new file mode 100644
index 0000000..f9b126d
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/wearable/delayedconfirmation/MainActivity.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.delayedconfirmation;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.support.wearable.view.DelayedConfirmationView;
+import android.util.Log;
+import android.view.View;
+
+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.wearable.MessageApi;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+
+
+public class MainActivity extends Activity implements
+        DelayedConfirmationView.DelayedConfirmationListener,
+        GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "DelayedConfirmation";
+    private static final int NUM_SECONDS = 5;
+
+    private static final String TIMER_SELECTED_PATH = "/timer_selected";
+    private static final String TIMER_FINISHED_PATH = "/timer_finished";
+
+    private DelayedConfirmationView delayedConfirmationView;
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate(Bundle b) {
+        super.onCreate(b);
+        setContentView(R.layout.main_activity);
+        delayedConfirmationView = (DelayedConfirmationView) findViewById(R.id.delayed_confirmation);
+        delayedConfirmationView.setTotalTimeMs(NUM_SECONDS * 1000);
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (!mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.disconnect();
+        }
+        super.onDestroy();
+    }
+
+    /**
+     * Starts the DelayedConfirmationView when user presses "Start Timer" button.
+     */
+    public void onStartTimer(View view) {
+        delayedConfirmationView.start();
+        delayedConfirmationView.setListener(this);
+    }
+
+    @Override
+    public void onTimerSelected(View v) {
+        v.setPressed(true);
+        Notification notification = new Notification.Builder(this)
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setContentTitle(getString(R.string.notification_title))
+                .setContentText(getString(R.string.notification_timer_selected))
+                .build();
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification);
+        sendMessageToCompanion(TIMER_SELECTED_PATH);
+        // Prevent onTimerFinished from being heard.
+        ((DelayedConfirmationView) v).setListener(null);
+        finish();
+    }
+
+    @Override
+    public void onTimerFinished(View v) {
+        Notification notification = new Notification.Builder(this)
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setContentTitle(getString(R.string.notification_title))
+                .setContentText(getString(R.string.notification_timer_finished))
+                .build();
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(0, notification);
+        sendMessageToCompanion(TIMER_FINISHED_PATH);
+        finish();
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        Log.e(TAG, "Failed to connect to Google Api Client");
+    }
+
+    private void sendMessageToCompanion(final String path) {
+        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
+                new ResultCallback<NodeApi.GetConnectedNodesResult>() {
+                    @Override
+                    public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
+                        for (final Node node : getConnectedNodesResult.getNodes()) {
+                            Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path,
+                                    new byte[0]).setResultCallback(getSendMessageResultCallback());
+                        }
+                    }
+                }
+        );
+
+    }
+
+    private ResultCallback<MessageApi.SendMessageResult> getSendMessageResultCallback() {
+        return new ResultCallback<MessageApi.SendMessageResult>() {
+            @Override
+            public void onResult(MessageApi.SendMessageResult sendMessageResult) {
+                if (!sendMessageResult.getStatus().isSuccess()) {
+                    Log.e(TAG, "Failed to connect to Google Api Client with status "
+                            + sendMessageResult.getStatus());
+                }
+            }
+        };
+    }
+}
diff --git a/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/wearable/delayedconfirmation/WearableMessageListenerService.java b/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/wearable/delayedconfirmation/WearableMessageListenerService.java
new file mode 100644
index 0000000..bb2ec91
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/Wearable/src/main/java/com/example/android/wearable/delayedconfirmation/WearableMessageListenerService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.delayedconfirmation;
+
+import android.content.Intent;
+
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.WearableListenerService;
+
+/**
+ * Listens for a message telling it to start the Wearable MainActivity.
+ */
+public class WearableMessageListenerService extends WearableListenerService {
+    private static final String START_ACTIVITY_PATH = "/start-activity";
+
+    @Override
+    public void onMessageReceived(MessageEvent event) {
+        if (event.getPath().equals(START_ACTIVITY_PATH)) {
+            Intent startIntent = new Intent(this, MainActivity.class);
+            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(startIntent);
+        }
+    }
+}
diff --git a/wearable/wear/DelayedConfirmation/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/DelayedConfirmation/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/DelayedConfirmation/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/DelayedConfirmation/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/DelayedConfirmation/screenshots/companion_delayed_confirmation.png b/wearable/wear/DelayedConfirmation/screenshots/companion_delayed_confirmation.png
new file mode 100644
index 0000000..b1b61a4
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/screenshots/companion_delayed_confirmation.png
Binary files differ
diff --git a/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_box_inset_all.png b/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_box_inset_all.png
new file mode 100644
index 0000000..9119dd3
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_box_inset_all.png
Binary files differ
diff --git a/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_box_inset_top.png b/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_box_inset_top.png
new file mode 100644
index 0000000..6d2da70
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_box_inset_top.png
Binary files differ
diff --git a/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_notification.png b/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_notification.png
new file mode 100644
index 0000000..936bcaf
--- /dev/null
+++ b/wearable/wear/DelayedConfirmation/screenshots/delayed_confirmation_notification.png
Binary files differ
diff --git a/wearable/wear/DelayedConfirmation/settings.gradle b/wearable/wear/DelayedConfirmation/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/DelayedConfirmation/settings.gradle
+++ b/wearable/wear/DelayedConfirmation/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/DelayedConfirmation/template-params.xml b/wearable/wear/DelayedConfirmation/template-params.xml
index 3e1272b..302c610 100644
--- a/wearable/wear/DelayedConfirmation/template-params.xml
+++ b/wearable/wear/DelayedConfirmation/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>DelayedConfirmation</name>
     <group>Wearable</group>
-    <package>com.example.android.delayedconfirmation</package>
+    <package>com.example.android.wearable.delayedconfirmation</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -43,7 +43,5 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
 </sample>
diff --git a/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml b/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml
index e653fa9..8f35c56 100644
--- a/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.elizachat" >
+    package="com.example.android.wearable.elizachat" >
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application
         android:allowBackup="true"
@@ -36,8 +36,8 @@
         <service
             android:name=".ResponderService">
             <intent-filter>
-                <action android:name="com.example.android.elizachat.REPLY" />
-                <action android:name="com.example.android.elizachat.CONVERSATION" />
+                <action android:name="com.example.android.wearable.elizachat.REPLY" />
+                <action android:name="com.example.android.wearable.elizachat.CONVERSATION" />
             </intent-filter>
         </service>
     </application>
diff --git a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/ElizaResponder.java b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/ElizaResponder.java
deleted file mode 100644
index e87f6de..0000000
--- a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/ElizaResponder.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.elizachat;
-
-import android.text.TextUtils;
-
-/**
- * A basic chat bot based on ELIZA, one of the earliest programs to employ primitive natural
- * language processing. Like ELIZA, this bot can simulate a Rogerian psychotherapist using a
- * collection of vague and generic responses.
- */
-public class ElizaResponder {
-
-    private static final String[] CONVERSATION_KEYWORDS = {
-            "CAN YOU", "CAN I", "YOU ARE", "YOURE", "I DONT", "I FEEL", "WHY DONT YOU", "WHY CANT I",
-            "ARE YOU", "I CANT", "I AM", " IM ", "YOU", "I WANT", "WHAT", "HOW", "WHO", "WHERE",
-            "WHEN", "WHY", "NAME", "CAUSE", "SORRY", "DREAM", "HELLO", "HI", "MAYBE", "NO", "YOUR",
-            "ALWAYS", "THINK", "ALIKE", "YES", "FRIEND", "COMPUTER", "NOKEYFOUND" };
-
-    private static String[] WORDS_TO_REPLACE = {"ARE","AM","WERE","WAS","YOU","I","YOUR","MY",
-            "IVE","YOUVE","IM","YOURE", "YOU", "ME"};
-
-    private static String[] QUESTIONS = {
-            "DON'T YOU BELIEVE THAT I CAN.", "PERHAPS YOU WOULD LIKE TO BE ABLE TO.",
-            "YOU WANT ME TO BE ABLE TO*", "PERHAPS YOU DON'T WANT TO*",
-            "DO YOU WANT TO BE ABLE TO*", "WHAT MAKES YOU THINK I AM*",
-            "DOES IT PLEASE YOU TO BELIEVE I AM*", "PERHAPS YOU WOULD LIKE TO BE*",
-            "DO YOU SOMETIMES WISH YOU WERE*", "DON'T YOU REALLY*", "WHY DON'T YOU*",
-            "DO YOU WISH TO BE ABLE TO*", "DOES THAT TROUBLE YOU?",
-            "TELL ME MORE ABOUT SUCH FEELINGS*", "DO YOU OFTEN FEEL*",
-            "DO YOU ENJOY FEELING*", "DO YOU REALLY BELIEVE I DON'T*",
-            "PERHAPS IN TIME I WILL*", "DO YOU WANT ME TO*",
-            "DO YOU THINK YOU SHOULD BE ABLE TO*", "WHY CAN'T YOU*",
-            "WHAT MAKES YOU WONDER WHETHER OR NOT I AM*",
-            "WOULD YOU PREFER IF I WERE NOT*", "PERHAPS IN YOUR FANTASIES I AM*",
-            "HOW DO YOU KNOW YOU CAN'T*", "HAVE YOU TRIED?", "PERHAPS YOU CAN NOW*",
-            "DID YOU COME TO ME BECAUSE YOU ARE*", "HOW LONG HAVE YOU BEEN*",
-            "DO YOU BELIEVE IT IS NORMAL TO BE*", "DO YOU ENJOY BEING*",
-            "I AM MORE INTERESTED IN TALKING ABOUT YOU.", "OH, I*",
-            "ARE YOU REALLY SO INTERESTED IN SOMEONE LIKE ME?",
-            "WHAT WOULD IT MEAN TO YOU IF YOU GOT*", "WHY DO YOU WANT*",
-            "SUPPOSE YOU SOON GOT*", "WHAT IF YOU NEVER GOT*", "I SOMETIMES ALSO WANT*",
-            "WHY DO YOU ASK?", "DOES THAT QUESTION INTEREST YOU?",
-            "WHAT ANSWER WOULD PLEASE YOU THE MOST?", "WHAT DO YOU THINK?",
-            "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
-            "WHAT IS IT THAT YOU REALLY WANT TO KNOW?", "HAVE YOU ASKED ANYONE ELSE?",
-            "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
-            "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?", "WE CAN KEEP THIS ANONYMOUS.",
-            "NO NEED TO SPECIFY ANY NAMES-- PLEASE GO ON.", "IS THAT THE REAL REASON?",
-            "DON'T ANY OTHER REASONS COME TO MIND?",
-            "DOES THAT REASON EXPLAIN ANYTHING ELSE?", "WHAT OTHER REASONS MIGHT THERE BE?",
-            "PLEASE DON'T APOLOGIZE.", "APOLOGIES ARE NOT NECESSARY.",
-            "WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?", "NO NEED TO BE DEFENSIVE!",
-            "WHAT DOES THAT DREAM SUGGEST TO YOU?", "DO YOU DREAM OFTEN?",
-            "WHAT PERSONS APPEAR IN YOUR DREAMS?", "DO YOU HAVE PLEASANT DREAMS?",
-            "HOW DO YOU DO ... PLEASE STATE YOUR PROBLEM.", "YOU DON'T SEEM QUITE CERTAIN.",
-            "WHY THE UNCERTAIN TONE?", "LET'S TRY TO KEEP THIS POSITIVE.", "YOU AREN'T SURE?",
-            "DON'T YOU KNOW?", "IS THAT A DEFINITE NO OR MIGHT YOU CHANGE YOUR MIND?",
-            "I AM SENSING SOME NEGATIVITY.", "WHY NOT?", "ARE YOU SURE?", "WHY NO?",
-            "WHY ARE YOU CONCERNED ABOUT MY*", "WHAT ABOUT YOUR OWN*",
-            "CAN'T YOU THINK OF A SPECIFIC EXAMPLE?", "WHEN?", "WHAT ARE YOU THINKING OF?",
-            "REALLY. ALWAYS?", "DO YOU REALLY THINK SO?", "BUT YOU ARE NOT SURE YOU.",
-            "BELIEVE IN YOURSELF.", "IN WHAT WAY?", "WHAT RESEMBLANCE DO YOU SEE?",
-            "WHAT DOES THE SIMILARITY SUGGEST TO YOU?",
-            "WHAT OTHER CONNECTIONS DO YOU SEE?", "COULD THERE REALLY BE SOME CONNECTION?",
-            "HOW?", "YOU SEEM QUITE POSITIVE.", "ARE YOU SURE?", "I SEE.", "I UNDERSTAND.",
-            "TELL ME ABOUT YOUR FRIENDS.", "ARE YOU WORRIED ABOUT YOUR FRIENDS?",
-            "DO YOUR FRIENDS EVER GIVE YOU A HARD TIME?", "WHAT DO YOU LIKE ABOUT YOUR FRIENDS?",
-            "DO YOU LOVE YOUR FRIENDS?", "PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU.",
-            "DO COMPUTERS EXCITE YOU?", "ARE YOU TALKING ABOUT ME IN PARTICULAR?",
-            "HOW DO YOU LIKE YOUR WATCH?", "WHY DO YOU MENTION COMPUTERS?",
-            "DO YOU FIND MACHINES AS FASCINATING AS I DO?",
-            "DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?",
-            "WHAT ABOUT MACHINES EXCITES YOU THE MOST?",
-            "HEY THERE, HOW CAN I HELP YOU?",
-            "WHAT DOES THAT SUGGEST TO YOU?", "I SEE.",
-            "I'M NOT SURE I UNDERSTAND YOU FULLY.", "COME COME ELUCIDATE YOUR THOUGHTS.",
-            "CAN YOU ELABORATE ON THAT?", "THAT IS QUITE INTERESTING."};
-
-    private static char[] CONVERSATION_TO_RESPONSES_MAP = {
-            1,3,4,2,6,4,6,4,10,4,14,3,17,3,20,2,22,3,25,3,
-            28,4,28,4,32,3,35,5,40,9,40,9,40,9,40,9,40,9,40,9,
-            49,2,51,4,55,4,59,4,63,1,63,1,64,5,69,5,74,2,76,4,
-            80,3,83,7,90,3,93,6,99,7,106,6};
-
-    private int[] responseStarts = new int[36];
-    private int[] responseCurrentIndices = new int[36];
-    private int[] responseEnds = new int[36];
-    private String previousInput = null;
-
-    public ElizaResponder() {
-        for (int i = 0; i < CONVERSATION_TO_RESPONSES_MAP.length / 2; i++) {
-            responseStarts[i] = CONVERSATION_TO_RESPONSES_MAP[2 * i];
-            responseCurrentIndices[i] = CONVERSATION_TO_RESPONSES_MAP[2 * i];
-            responseEnds[i] = responseStarts[i] + CONVERSATION_TO_RESPONSES_MAP[2 * i  + 1];
-        }
-    }
-
-    public String elzTalk(String input) {
-        if (null == input) {
-            input = "";
-        }
-        String result = "";
-
-        input = " " + input.toUpperCase().replace("\'", "") + " ";
-
-        if (previousInput != null && input.equals(previousInput)) {
-            return "DIDN'T YOU JUST SAY THAT?\n";
-        }
-        previousInput = input;
-
-        int keywordIndex = 0;
-        for (; keywordIndex < CONVERSATION_KEYWORDS.length; ++keywordIndex) {
-            int index = input.indexOf(CONVERSATION_KEYWORDS[keywordIndex]);
-            if (index != -1) {
-                break;
-            }
-        }
-
-        String afterKeyword = "";
-        if (keywordIndex == CONVERSATION_KEYWORDS.length) {
-            keywordIndex = 35;
-        } else {
-            int index = input.indexOf(CONVERSATION_KEYWORDS[keywordIndex]);
-            afterKeyword = input.substring(index + CONVERSATION_KEYWORDS[keywordIndex].length());
-            String[] parts = afterKeyword.split("\\s+");
-            for (int i = 0; i < WORDS_TO_REPLACE.length / 2; i++) {
-                String first = WORDS_TO_REPLACE[i * 2];
-                String second = WORDS_TO_REPLACE[i * 2 + 1];
-                for (int j = 0; j < parts.length; ++j) {
-                    if (parts[j].equals(first)) {
-                        parts[j] = second;
-                    } else if (parts[j].equals(second)) {
-                        parts[j] = first;
-                    }
-                }
-            }
-            afterKeyword = TextUtils.join(" ", parts);
-        }
-
-        String question = QUESTIONS[responseCurrentIndices[keywordIndex] - 1];
-        responseCurrentIndices[keywordIndex] = responseCurrentIndices[keywordIndex] + 1;
-        if (responseCurrentIndices[keywordIndex] > responseEnds[keywordIndex]) {
-            responseCurrentIndices[keywordIndex] = responseStarts[keywordIndex];
-        }
-        result += question;
-        if (result.endsWith("*")) {
-            result = result.substring(0, result.length() - 1);
-            result += " " + afterKeyword;
-        }
-
-        return result;
-    }
-}
diff --git a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/MainActivity.java b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/MainActivity.java
deleted file mode 100644
index 421dcb8..0000000
--- a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/MainActivity.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.elizachat;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.support.v4.content.LocalBroadcastManager;
-import android.text.TextUtils;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.TextView;
-
-public class MainActivity extends Activity {
-
-    @SuppressWarnings("unused")
-    private static final String TAG = "MainActivity";
-
-    public static final String EXTRA_MESSAGE = "message";
-
-    public static final String ACTION_NOTIFY = "com.example.android.elizachat.NOTIFY";
-
-    public static final String ACTION_GET_CONVERSATION
-            = "com.example.android.elizachat.CONVERSATION";
-
-    private BroadcastReceiver mReceiver;
-
-    private TextView mHistoryView;
-
-    @Override
-    protected void onCreate(Bundle saved) {
-        super.onCreate(saved);
-        setContentView(R.layout.main);
-        mReceiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                processMessage(intent);
-            }
-        };
-        mHistoryView = (TextView) findViewById(R.id.history);
-        startResponderService();
-    }
-
-    private void startResponderService() {
-        Intent serviceIntent = new Intent(ResponderService.ACTION_INCOMING);
-        startService(serviceIntent);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
-                new IntentFilter(ACTION_NOTIFY));
-        mHistoryView.setText("");
-        Intent serviceIntent = new Intent(ACTION_GET_CONVERSATION);
-        startService(serviceIntent);
-
-    }
-
-    @Override
-    protected void onPause() {
-        LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
-        super.onPause();
-    }
-
-    private void processMessage(Intent intent) {
-        String text = intent.getStringExtra(EXTRA_MESSAGE);
-        if (!TextUtils.isEmpty(text)) {
-            mHistoryView.append("\n" + text);
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
-        getMenuInflater().inflate(R.menu.main, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.action_stop_service:
-                stopService(new Intent(this, ResponderService.class));
-                finish();
-                break;
-        }
-        return true;
-    }
-}
diff --git a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/ResponderService.java b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/ResponderService.java
deleted file mode 100644
index 6d6cd9a..0000000
--- a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/elizachat/ResponderService.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.elizachat;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.RemoteInput;
-import android.support.v4.content.LocalBroadcastManager;
-import android.support.v4.app.NotificationManagerCompat;
-import android.text.TextUtils;
-import android.util.Log;
-
-/**
- * A service that runs in the background and provides responses to the incoming messages from the
- * wearable. It also keeps a record of the chat session history, which it can provide upon request.
- */
-public class ResponderService extends Service {
-
-    public static final String ACTION_INCOMING = "com.example.android.elizachat.INCOMING";
-
-    public static final String ACTION_RESPONSE = "com.example.android.elizachat.REPLY";
-
-    public static final String EXTRA_REPLY = "reply";
-
-    private static final String TAG = "ResponderService";
-
-    private ElizaResponder mResponder;
-
-    private String mLastResponse = null;
-
-    private StringBuffer mCompleteConversation = new StringBuffer();
-
-    private LocalBroadcastManager mBroadcastManager;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Chat Service started");
-        }
-        mResponder = new ElizaResponder();
-        mBroadcastManager = LocalBroadcastManager.getInstance(this);
-        processIncoming(null);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (null == intent || null == intent.getAction()) {
-            return Service.START_STICKY;
-        }
-        String action = intent.getAction();
-        if (action.equals(ACTION_RESPONSE)) {
-            Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
-            CharSequence replyMessage = "";
-            if (remoteInputResults != null) {
-                replyMessage = remoteInputResults.getCharSequence(EXTRA_REPLY);
-            }
-            processIncoming(replyMessage.toString());
-        } else if (action.equals(MainActivity.ACTION_GET_CONVERSATION)) {
-            broadcastMessage(mCompleteConversation.toString());
-        }
-        return Service.START_STICKY;
-    }
-
-    private void showNotification() {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Sent: " + mLastResponse);
-        }
-        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
-                .setContentTitle(getString(R.string.eliza))
-                .setContentText(mLastResponse)
-                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bg_eliza))
-                .setSmallIcon(R.drawable.bg_eliza)
-                .setPriority(NotificationCompat.PRIORITY_MIN);
-
-        Intent intent = new Intent(ACTION_RESPONSE);
-        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
-                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT);
-        Notification notification = builder
-                .extend(new NotificationCompat.WearableExtender()
-                        .addAction(new NotificationCompat.Action.Builder(
-                                R.drawable.ic_full_reply, getString(R.string.reply), pendingIntent)
-                                .addRemoteInput(new RemoteInput.Builder(EXTRA_REPLY)
-                                        .setLabel(getString(R.string.reply))
-                                        .build())
-                                .build()))
-                .build();
-        NotificationManagerCompat.from(this).notify(0, notification);
-    }
-
-    private void processIncoming(String text) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Received: " + text);
-        }
-        mLastResponse = mResponder.elzTalk(text);
-        String line = TextUtils.isEmpty(text) ? mLastResponse : text + "\n" + mLastResponse;
-
-        // Send a new line of conversation to update the Activity, unless the incoming text was
-        // empty.
-        if (!TextUtils.isEmpty(text)) {
-            broadcastMessage(line);
-        }
-        NotificationManagerCompat.from(this).cancelAll();
-        showNotification();
-        mCompleteConversation.append("\n" + line);
-    }
-
-    private void broadcastMessage(String message) {
-        Intent intent = new Intent(MainActivity.ACTION_NOTIFY);
-        intent.putExtra(MainActivity.EXTRA_MESSAGE, message);
-        mBroadcastManager.sendBroadcast(intent);
-    }
-
-    @Override
-    public void onDestroy() {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Chat Service stopped");
-        }
-        NotificationManagerCompat.from(this).cancel(0);
-        mBroadcastManager = null;
-        super.onDestroy();
-    }
-}
diff --git a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/ElizaResponder.java b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/ElizaResponder.java
new file mode 100644
index 0000000..e6c0542
--- /dev/null
+++ b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/ElizaResponder.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.elizachat;
+
+import android.text.TextUtils;
+
+/**
+ * A basic chat bot based on ELIZA, one of the earliest programs to employ primitive natural
+ * language processing. Like ELIZA, this bot can simulate a Rogerian psychotherapist using a
+ * collection of vague and generic responses.
+ */
+public class ElizaResponder {
+
+    private static final String[] CONVERSATION_KEYWORDS = {
+            "CAN YOU", "CAN I", "YOU ARE", "YOURE", "I DONT", "I FEEL", "WHY DONT YOU", "WHY CANT I",
+            "ARE YOU", "I CANT", "I AM", " IM ", "YOU", "I WANT", "WHAT", "HOW", "WHO", "WHERE",
+            "WHEN", "WHY", "NAME", "CAUSE", "SORRY", "DREAM", "HELLO", "HI", "MAYBE", "NO", "YOUR",
+            "ALWAYS", "THINK", "ALIKE", "YES", "FRIEND", "COMPUTER", "NOKEYFOUND" };
+
+    private static String[] WORDS_TO_REPLACE = {"ARE","AM","WERE","WAS","YOU","I","YOUR","MY",
+            "IVE","YOUVE","IM","YOURE", "YOU", "ME"};
+
+    private static String[] QUESTIONS = {
+            "DON'T YOU BELIEVE THAT I CAN.", "PERHAPS YOU WOULD LIKE TO BE ABLE TO.",
+            "YOU WANT ME TO BE ABLE TO*", "PERHAPS YOU DON'T WANT TO*",
+            "DO YOU WANT TO BE ABLE TO*", "WHAT MAKES YOU THINK I AM*",
+            "DOES IT PLEASE YOU TO BELIEVE I AM*", "PERHAPS YOU WOULD LIKE TO BE*",
+            "DO YOU SOMETIMES WISH YOU WERE*", "DON'T YOU REALLY*", "WHY DON'T YOU*",
+            "DO YOU WISH TO BE ABLE TO*", "DOES THAT TROUBLE YOU?",
+            "TELL ME MORE ABOUT SUCH FEELINGS*", "DO YOU OFTEN FEEL*",
+            "DO YOU ENJOY FEELING*", "DO YOU REALLY BELIEVE I DON'T*",
+            "PERHAPS IN TIME I WILL*", "DO YOU WANT ME TO*",
+            "DO YOU THINK YOU SHOULD BE ABLE TO*", "WHY CAN'T YOU*",
+            "WHAT MAKES YOU WONDER WHETHER OR NOT I AM*",
+            "WOULD YOU PREFER IF I WERE NOT*", "PERHAPS IN YOUR FANTASIES I AM*",
+            "HOW DO YOU KNOW YOU CAN'T*", "HAVE YOU TRIED?", "PERHAPS YOU CAN NOW*",
+            "DID YOU COME TO ME BECAUSE YOU ARE*", "HOW LONG HAVE YOU BEEN*",
+            "DO YOU BELIEVE IT IS NORMAL TO BE*", "DO YOU ENJOY BEING*",
+            "I AM MORE INTERESTED IN TALKING ABOUT YOU.", "OH, I*",
+            "ARE YOU REALLY SO INTERESTED IN SOMEONE LIKE ME?",
+            "WHAT WOULD IT MEAN TO YOU IF YOU GOT*", "WHY DO YOU WANT*",
+            "SUPPOSE YOU SOON GOT*", "WHAT IF YOU NEVER GOT*", "I SOMETIMES ALSO WANT*",
+            "WHY DO YOU ASK?", "DOES THAT QUESTION INTEREST YOU?",
+            "WHAT ANSWER WOULD PLEASE YOU THE MOST?", "WHAT DO YOU THINK?",
+            "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
+            "WHAT IS IT THAT YOU REALLY WANT TO KNOW?", "HAVE YOU ASKED ANYONE ELSE?",
+            "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
+            "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?", "WE CAN KEEP THIS ANONYMOUS.",
+            "NO NEED TO SPECIFY ANY NAMES-- PLEASE GO ON.", "IS THAT THE REAL REASON?",
+            "DON'T ANY OTHER REASONS COME TO MIND?",
+            "DOES THAT REASON EXPLAIN ANYTHING ELSE?", "WHAT OTHER REASONS MIGHT THERE BE?",
+            "PLEASE DON'T APOLOGIZE.", "APOLOGIES ARE NOT NECESSARY.",
+            "WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?", "NO NEED TO BE DEFENSIVE!",
+            "WHAT DOES THAT DREAM SUGGEST TO YOU?", "DO YOU DREAM OFTEN?",
+            "WHAT PERSONS APPEAR IN YOUR DREAMS?", "DO YOU HAVE PLEASANT DREAMS?",
+            "HOW DO YOU DO ... PLEASE STATE YOUR PROBLEM.", "YOU DON'T SEEM QUITE CERTAIN.",
+            "WHY THE UNCERTAIN TONE?", "LET'S TRY TO KEEP THIS POSITIVE.", "YOU AREN'T SURE?",
+            "DON'T YOU KNOW?", "IS THAT A DEFINITE NO OR MIGHT YOU CHANGE YOUR MIND?",
+            "I AM SENSING SOME NEGATIVITY.", "WHY NOT?", "ARE YOU SURE?", "WHY NO?",
+            "WHY ARE YOU CONCERNED ABOUT MY*", "WHAT ABOUT YOUR OWN*",
+            "CAN'T YOU THINK OF A SPECIFIC EXAMPLE?", "WHEN?", "WHAT ARE YOU THINKING OF?",
+            "REALLY. ALWAYS?", "DO YOU REALLY THINK SO?", "BUT YOU ARE NOT SURE YOU.",
+            "BELIEVE IN YOURSELF.", "IN WHAT WAY?", "WHAT RESEMBLANCE DO YOU SEE?",
+            "WHAT DOES THE SIMILARITY SUGGEST TO YOU?",
+            "WHAT OTHER CONNECTIONS DO YOU SEE?", "COULD THERE REALLY BE SOME CONNECTION?",
+            "HOW?", "YOU SEEM QUITE POSITIVE.", "ARE YOU SURE?", "I SEE.", "I UNDERSTAND.",
+            "TELL ME ABOUT YOUR FRIENDS.", "ARE YOU WORRIED ABOUT YOUR FRIENDS?",
+            "DO YOUR FRIENDS EVER GIVE YOU A HARD TIME?", "WHAT DO YOU LIKE ABOUT YOUR FRIENDS?",
+            "DO YOU LOVE YOUR FRIENDS?", "PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU.",
+            "DO COMPUTERS EXCITE YOU?", "ARE YOU TALKING ABOUT ME IN PARTICULAR?",
+            "HOW DO YOU LIKE YOUR WATCH?", "WHY DO YOU MENTION COMPUTERS?",
+            "DO YOU FIND MACHINES AS FASCINATING AS I DO?",
+            "DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?",
+            "WHAT ABOUT MACHINES EXCITES YOU THE MOST?",
+            "HEY THERE, HOW CAN I HELP YOU?",
+            "WHAT DOES THAT SUGGEST TO YOU?", "I SEE.",
+            "I'M NOT SURE I UNDERSTAND YOU FULLY.", "COME COME ELUCIDATE YOUR THOUGHTS.",
+            "CAN YOU ELABORATE ON THAT?", "THAT IS QUITE INTERESTING."};
+
+    private static char[] CONVERSATION_TO_RESPONSES_MAP = {
+            1,3,4,2,6,4,6,4,10,4,14,3,17,3,20,2,22,3,25,3,
+            28,4,28,4,32,3,35,5,40,9,40,9,40,9,40,9,40,9,40,9,
+            49,2,51,4,55,4,59,4,63,1,63,1,64,5,69,5,74,2,76,4,
+            80,3,83,7,90,3,93,6,99,7,106,6};
+
+    private int[] responseStarts = new int[36];
+    private int[] responseCurrentIndices = new int[36];
+    private int[] responseEnds = new int[36];
+    private String previousInput = null;
+
+    public ElizaResponder() {
+        for (int i = 0; i < CONVERSATION_TO_RESPONSES_MAP.length / 2; i++) {
+            responseStarts[i] = CONVERSATION_TO_RESPONSES_MAP[2 * i];
+            responseCurrentIndices[i] = CONVERSATION_TO_RESPONSES_MAP[2 * i];
+            responseEnds[i] = responseStarts[i] + CONVERSATION_TO_RESPONSES_MAP[2 * i  + 1];
+        }
+    }
+
+    public String elzTalk(String input) {
+        if (null == input) {
+            input = "";
+        }
+        String result = "";
+
+        input = " " + input.toUpperCase().replace("\'", "") + " ";
+
+        if (previousInput != null && input.equals(previousInput)) {
+            return "DIDN'T YOU JUST SAY THAT?\n";
+        }
+        previousInput = input;
+
+        int keywordIndex = 0;
+        for (; keywordIndex < CONVERSATION_KEYWORDS.length; ++keywordIndex) {
+            int index = input.indexOf(CONVERSATION_KEYWORDS[keywordIndex]);
+            if (index != -1) {
+                break;
+            }
+        }
+
+        String afterKeyword = "";
+        if (keywordIndex == CONVERSATION_KEYWORDS.length) {
+            keywordIndex = 35;
+        } else {
+            int index = input.indexOf(CONVERSATION_KEYWORDS[keywordIndex]);
+            afterKeyword = input.substring(index + CONVERSATION_KEYWORDS[keywordIndex].length());
+            String[] parts = afterKeyword.split("\\s+");
+            for (int i = 0; i < WORDS_TO_REPLACE.length / 2; i++) {
+                String first = WORDS_TO_REPLACE[i * 2];
+                String second = WORDS_TO_REPLACE[i * 2 + 1];
+                for (int j = 0; j < parts.length; ++j) {
+                    if (parts[j].equals(first)) {
+                        parts[j] = second;
+                    } else if (parts[j].equals(second)) {
+                        parts[j] = first;
+                    }
+                }
+            }
+            afterKeyword = TextUtils.join(" ", parts);
+        }
+
+        String question = QUESTIONS[responseCurrentIndices[keywordIndex] - 1];
+        responseCurrentIndices[keywordIndex] = responseCurrentIndices[keywordIndex] + 1;
+        if (responseCurrentIndices[keywordIndex] > responseEnds[keywordIndex]) {
+            responseCurrentIndices[keywordIndex] = responseStarts[keywordIndex];
+        }
+        result += question;
+        if (result.endsWith("*")) {
+            result = result.substring(0, result.length() - 1);
+            result += " " + afterKeyword;
+        }
+
+        return result;
+    }
+}
diff --git a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/MainActivity.java b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/MainActivity.java
new file mode 100644
index 0000000..982e3de
--- /dev/null
+++ b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/MainActivity.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.elizachat;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+public class MainActivity extends Activity {
+
+    @SuppressWarnings("unused")
+    private static final String TAG = "MainActivity";
+
+    public static final String EXTRA_MESSAGE = "message";
+
+    public static final String ACTION_NOTIFY = "com.example.android.wearable.elizachat.NOTIFY";
+
+    public static final String ACTION_GET_CONVERSATION
+            = "com.example.android.wearable.elizachat.CONVERSATION";
+
+    private BroadcastReceiver mReceiver;
+
+    private TextView mHistoryView;
+
+    @Override
+    protected void onCreate(Bundle saved) {
+        super.onCreate(saved);
+        setContentView(R.layout.main);
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                processMessage(intent);
+            }
+        };
+        mHistoryView = (TextView) findViewById(R.id.history);
+        startResponderService();
+    }
+
+    private void startResponderService() {
+        Intent serviceIntent = new Intent(ResponderService.ACTION_INCOMING);
+        startService(serviceIntent);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
+                new IntentFilter(ACTION_NOTIFY));
+        mHistoryView.setText("");
+        Intent serviceIntent = new Intent(ACTION_GET_CONVERSATION);
+        startService(serviceIntent);
+
+    }
+
+    @Override
+    protected void onPause() {
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
+        super.onPause();
+    }
+
+    private void processMessage(Intent intent) {
+        String text = intent.getStringExtra(EXTRA_MESSAGE);
+        if (!TextUtils.isEmpty(text)) {
+            mHistoryView.append("\n" + text);
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_stop_service:
+                stopService(new Intent(this, ResponderService.class));
+                finish();
+                break;
+        }
+        return true;
+    }
+}
diff --git a/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/ResponderService.java b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/ResponderService.java
new file mode 100644
index 0000000..3bef19c
--- /dev/null
+++ b/wearable/wear/ElizaChat/Application/src/main/java/com/example/android/wearable/elizachat/ResponderService.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.elizachat;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.RemoteInput;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v4.app.NotificationManagerCompat;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * A service that runs in the background and provides responses to the incoming messages from the
+ * wearable. It also keeps a record of the chat session history, which it can provide upon request.
+ */
+public class ResponderService extends Service {
+
+    public static final String ACTION_INCOMING = "com.example.android.wearable.elizachat.INCOMING";
+
+    public static final String ACTION_RESPONSE = "com.example.android.wearable.elizachat.REPLY";
+
+    public static final String EXTRA_REPLY = "reply";
+
+    private static final String TAG = "ResponderService";
+
+    private ElizaResponder mResponder;
+
+    private String mLastResponse = null;
+
+    private StringBuffer mCompleteConversation = new StringBuffer();
+
+    private LocalBroadcastManager mBroadcastManager;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Chat Service started");
+        }
+        mResponder = new ElizaResponder();
+        mBroadcastManager = LocalBroadcastManager.getInstance(this);
+        processIncoming(null);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (null == intent || null == intent.getAction()) {
+            return Service.START_STICKY;
+        }
+        String action = intent.getAction();
+        if (action.equals(ACTION_RESPONSE)) {
+            Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
+            CharSequence replyMessage = "";
+            if (remoteInputResults != null) {
+                replyMessage = remoteInputResults.getCharSequence(EXTRA_REPLY);
+            }
+            processIncoming(replyMessage.toString());
+        } else if (action.equals(MainActivity.ACTION_GET_CONVERSATION)) {
+            broadcastMessage(mCompleteConversation.toString());
+        }
+        return Service.START_STICKY;
+    }
+
+    private void showNotification() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Sent: " + mLastResponse);
+        }
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
+                .setContentTitle(getString(R.string.eliza))
+                .setContentText(mLastResponse)
+                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bg_eliza))
+                .setSmallIcon(R.drawable.bg_eliza)
+                .setPriority(NotificationCompat.PRIORITY_MIN);
+
+        Intent intent = new Intent(ACTION_RESPONSE);
+        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT);
+        Notification notification = builder
+                .extend(new NotificationCompat.WearableExtender()
+                        .addAction(new NotificationCompat.Action.Builder(
+                                R.drawable.ic_full_reply, getString(R.string.reply), pendingIntent)
+                                .addRemoteInput(new RemoteInput.Builder(EXTRA_REPLY)
+                                        .setLabel(getString(R.string.reply))
+                                        .build())
+                                .build()))
+                .build();
+        NotificationManagerCompat.from(this).notify(0, notification);
+    }
+
+    private void processIncoming(String text) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Received: " + text);
+        }
+        mLastResponse = mResponder.elzTalk(text);
+        String line = TextUtils.isEmpty(text) ? mLastResponse : text + "\n" + mLastResponse;
+
+        // Send a new line of conversation to update the Activity, unless the incoming text was
+        // empty.
+        if (!TextUtils.isEmpty(text)) {
+            broadcastMessage(line);
+        }
+        NotificationManagerCompat.from(this).cancelAll();
+        showNotification();
+        mCompleteConversation.append("\n" + line);
+    }
+
+    private void broadcastMessage(String message) {
+        Intent intent = new Intent(MainActivity.ACTION_NOTIFY);
+        intent.putExtra(MainActivity.EXTRA_MESSAGE, message);
+        mBroadcastManager.sendBroadcast(intent);
+    }
+
+    @Override
+    public void onDestroy() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Chat Service stopped");
+        }
+        NotificationManagerCompat.from(this).cancel(0);
+        mBroadcastManager = null;
+        super.onDestroy();
+    }
+}
diff --git a/wearable/wear/ElizaChat/Shared/.gitignore b/wearable/wear/ElizaChat/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/ElizaChat/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/ElizaChat/Shared/src/main/AndroidManifest.xml b/wearable/wear/ElizaChat/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 9c02d59..0000000
--- a/wearable/wear/ElizaChat/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.elizachat.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/ElizaChat/Shared/src/main/res/values/strings.xml b/wearable/wear/ElizaChat/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/ElizaChat/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/ElizaChat/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/ElizaChat/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/ElizaChat/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/ElizaChat/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/ElizaChat/screenshots/companion_eliza_chat.png b/wearable/wear/ElizaChat/screenshots/companion_eliza_chat.png
new file mode 100644
index 0000000..b7fd266
--- /dev/null
+++ b/wearable/wear/ElizaChat/screenshots/companion_eliza_chat.png
Binary files differ
diff --git a/wearable/wear/ElizaChat/screenshots/companion_eliza_chat_response.png b/wearable/wear/ElizaChat/screenshots/companion_eliza_chat_response.png
new file mode 100644
index 0000000..cea063c
--- /dev/null
+++ b/wearable/wear/ElizaChat/screenshots/companion_eliza_chat_response.png
Binary files differ
diff --git a/wearable/wear/ElizaChat/screenshots/wearable_eliza_notification.png b/wearable/wear/ElizaChat/screenshots/wearable_eliza_notification.png
new file mode 100644
index 0000000..c5ced49
--- /dev/null
+++ b/wearable/wear/ElizaChat/screenshots/wearable_eliza_notification.png
Binary files differ
diff --git a/wearable/wear/ElizaChat/screenshots/wearable_voice_reply.png b/wearable/wear/ElizaChat/screenshots/wearable_voice_reply.png
new file mode 100644
index 0000000..032e037
--- /dev/null
+++ b/wearable/wear/ElizaChat/screenshots/wearable_voice_reply.png
Binary files differ
diff --git a/wearable/wear/ElizaChat/template-params.xml b/wearable/wear/ElizaChat/template-params.xml
index 87d417a..29ff39d 100644
--- a/wearable/wear/ElizaChat/template-params.xml
+++ b/wearable/wear/ElizaChat/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>ElizaChat</name>
     <group>Wearable</group>
-    <package>com.example.android.elizachat</package>
+    <package>com.example.android.wearable.elizachat</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -40,7 +40,90 @@
     </strings>
 
     <template src="base"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/wearable_eliza_notification.png</icon>
+        <screenshots>
+            <img>screenshots/companion_eliza_chat_response.png</img>
+            <img>screenshots/companion_eliza_chat.png</img>
+            <img>screenshots/wearable_eliza_notification.png</img>
+            <img>screenshots/wearable_voice_reply.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.v4.app.NotificationCompat.WearableExtender</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic sample showing how to add extensions to notifications on wearable using
+[NotificationCompat.WearableExtender][1] API by providing a chat experience.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+[NotificationCompat.WearableExtender][1] API offers you a functionality to
+ add wearable extensions to notifications like [Add the Voice Input as a Notification Action][2].
+
+This example also adds the wearable notifications as a voice input by the following code:
+
+```java
+NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
+        .setContentTitle(getString(R.string.eliza))
+        .setContentText(mLastResponse)
+        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bg_eliza))
+        .setSmallIcon(R.drawable.bg_eliza)
+        .setPriority(NotificationCompat.PRIORITY_MIN);
+
+Intent intent = new Intent(ACTION_RESPONSE);
+PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
+        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT);
+Notification notification = builder
+        .extend(new NotificationCompat.WearableExtender()
+                .addAction(new NotificationCompat.Action.Builder(
+                        R.drawable.ic_full_reply, getString(R.string.reply), pendingIntent)
+                        .addRemoteInput(new RemoteInput.Builder(EXTRA_REPLY)
+                                .setLabel(getString(R.string.reply))
+                                .build())
+                        .build()))
+        .build();
+NotificationManagerCompat.from(this).notify(0, notification);
+```
+
+When you issue this notification, users can swipe to the left to see the "Reply" action button.
+
+When you receive the response, you can do it by the following code:
+
+```java
+@Override
+public int onStartCommand(Intent intent, int flags, int startId) {
+    if (null == intent || null == intent.getAction()) {
+        return Service.START_STICKY;
+    }
+    String action = intent.getAction();
+    if (action.equals(ACTION_RESPONSE)) {
+        Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
+        CharSequence replyMessage = "";
+        if (remoteInputResults != null) {
+            replyMessage = remoteInputResults.getCharSequence(EXTRA_REPLY);
+        }
+        processIncoming(replyMessage.toString());
+    } else if (action.equals(MainActivity.ACTION_GET_CONVERSATION)) {
+        broadcastMessage(mCompleteConversation.toString());
+    }
+    return Service.START_STICKY;
+}
+```
+
+[1]: https://developer.android.com/reference/android/support/v4/app/NotificationCompat.WearableExtender.html
+[2]: https://developer.android.com/training/wearables/notifications/voice-input.html#AddAction
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/EmbeddedApp/Application/src/main/AndroidManifest.xml b/wearable/wear/EmbeddedApp/Application/src/main/AndroidManifest.xml
index 77595bc..ee47ffe 100644
--- a/wearable/wear/EmbeddedApp/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/EmbeddedApp/Application/src/main/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.embeddedapp" >
+    package="com.example.android.wearable.embeddedapp" >
     <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
     <application
         android:allowBackup="true"
diff --git a/wearable/wear/EmbeddedApp/Application/src/main/java/com/example/android/embeddedapp/PhoneActivity.java b/wearable/wear/EmbeddedApp/Application/src/main/java/com/example/android/embeddedapp/PhoneActivity.java
deleted file mode 100644
index de95938..0000000
--- a/wearable/wear/EmbeddedApp/Application/src/main/java/com/example/android/embeddedapp/PhoneActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.embeddedapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class PhoneActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_phone);
-    }
-}
diff --git a/wearable/wear/EmbeddedApp/Application/src/main/java/com/example/android/wearable/embeddedapp/PhoneActivity.java b/wearable/wear/EmbeddedApp/Application/src/main/java/com/example/android/wearable/embeddedapp/PhoneActivity.java
new file mode 100644
index 0000000..5a2f5ca
--- /dev/null
+++ b/wearable/wear/EmbeddedApp/Application/src/main/java/com/example/android/wearable/embeddedapp/PhoneActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.embeddedapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PhoneActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_phone);
+    }
+}
diff --git a/wearable/wear/EmbeddedApp/Shared/.gitignore b/wearable/wear/EmbeddedApp/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/EmbeddedApp/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/EmbeddedApp/Shared/src/main/AndroidManifest.xml b/wearable/wear/EmbeddedApp/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index f026bc5..0000000
--- a/wearable/wear/EmbeddedApp/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.embeddedapp.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/EmbeddedApp/Shared/src/main/res/values/strings.xml b/wearable/wear/EmbeddedApp/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/EmbeddedApp/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml b/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml
index fd07d3c..4863d66 100644
--- a/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.embeddedapp" >
+    package="com.example.android.wearable.embeddedapp" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/EmbeddedApp/Wearable/src/main/java/com/example/android/embeddedapp/WearableActivity.java b/wearable/wear/EmbeddedApp/Wearable/src/main/java/com/example/android/embeddedapp/WearableActivity.java
deleted file mode 100644
index 1ae0c1f..0000000
--- a/wearable/wear/EmbeddedApp/Wearable/src/main/java/com/example/android/embeddedapp/WearableActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.embeddedapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class WearableActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_wearable);
-    }
-}
diff --git a/wearable/wear/EmbeddedApp/Wearable/src/main/java/com/example/android/wearable/embeddedapp/WearableActivity.java b/wearable/wear/EmbeddedApp/Wearable/src/main/java/com/example/android/wearable/embeddedapp/WearableActivity.java
new file mode 100644
index 0000000..3703f34
--- /dev/null
+++ b/wearable/wear/EmbeddedApp/Wearable/src/main/java/com/example/android/wearable/embeddedapp/WearableActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.embeddedapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class WearableActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_wearable);
+    }
+}
diff --git a/wearable/wear/EmbeddedApp/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/EmbeddedApp/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/EmbeddedApp/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/EmbeddedApp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/EmbeddedApp/screenshots/embedded_wearable_app.png b/wearable/wear/EmbeddedApp/screenshots/embedded_wearable_app.png
new file mode 100644
index 0000000..414afe5
--- /dev/null
+++ b/wearable/wear/EmbeddedApp/screenshots/embedded_wearable_app.png
Binary files differ
diff --git a/wearable/wear/EmbeddedApp/screenshots/phone_app.png b/wearable/wear/EmbeddedApp/screenshots/phone_app.png
new file mode 100644
index 0000000..46c3d89
--- /dev/null
+++ b/wearable/wear/EmbeddedApp/screenshots/phone_app.png
Binary files differ
diff --git a/wearable/wear/EmbeddedApp/settings.gradle b/wearable/wear/EmbeddedApp/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/EmbeddedApp/settings.gradle
+++ b/wearable/wear/EmbeddedApp/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/EmbeddedApp/template-params.xml b/wearable/wear/EmbeddedApp/template-params.xml
index 7360fd2..6127940 100644
--- a/wearable/wear/EmbeddedApp/template-params.xml
+++ b/wearable/wear/EmbeddedApp/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>EmbeddedApp</name>
     <group>Wearable</group>
-    <package>com.example.android.embeddedapp</package>
+    <package>com.example.android.wearable.embeddedapp</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -38,7 +38,55 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>src/main/res/drawable-xxhdpi/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/embedded_wearable_app.png</img>
+            <img>screenshots/phone_app.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Activity</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+This simple app demonstrates how to embed a wearable app into a phone app.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+Wearable apps can be installed directly onto Android Wear devices during development, using either a direct ADB
+connection or ADB-over-Bluetooth. However, when releasing your app to end users, you must package your
+wearable APK inside of a traditional APK for distribution via a paired phone.
+
+When end users install this APK onto their phone, the wearable APK will be automatically detected, extracted, and pushed
+to their any paired wearable devices.
+
+This sample demonstrates how to properly package a wearable app for release in this manner. The wearable app is inside
+the `Wearable` directory, and the phone app (which will be used as a container for distribution) is the `Application`
+directory. There is nothing special about these apps, other than the `wearApp` dependency in the (host) phone app's
+`build.gradle` file:
+
+```groovy
+dependencies {
+    compile 'com.google.android.gms:play-services-wearable:6.5.+'
+    wearApp project(':Wearable')
+}
+```
+
+This dependency will automatically package the wearable APK during a **release build** (e.g. using the "Build > Generate
+Signed APK..." command in Android Studio). Note that this packaging is **not** performed for debug builds for
+performance reasons. During development, your wearable and phone apps must still be pushed individually to their
+respective devices using an ADB connection.
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml b/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml
index 5ca8c8a..8cb70d1 100644
--- a/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.example.android.findphone">
+          package="com.example.android.wearable.findphone">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <uses-permission android:name="android.permission.VIBRATE" />
     <application
@@ -32,7 +32,7 @@
                 android:value="@integer/google_play_services_version" />
 
         <service
-            android:name="com.example.android.findphone.SoundAlarmListenerService" >
+            android:name="com.example.android.wearable.findphone.SoundAlarmListenerService" >
             <intent-filter>
                 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
             </intent-filter>
diff --git a/wearable/wear/FindMyPhone/Application/src/main/java/com/example/android/findphone/SoundAlarmListenerService.java b/wearable/wear/FindMyPhone/Application/src/main/java/com/example/android/findphone/SoundAlarmListenerService.java
deleted file mode 100644
index 106a47a..0000000
--- a/wearable/wear/FindMyPhone/Application/src/main/java/com/example/android/findphone/SoundAlarmListenerService.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.findphone;
-
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.util.Log;
-
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.WearableListenerService;
-
-import java.io.IOException;
-
-/**
- * Listens for disconnection from home device.
- */
-public class SoundAlarmListenerService extends WearableListenerService {
-
-    private static final String TAG = "ExampleFindPhoneApp";
-
-    private static final String FIELD_ALARM_ON = "alarm_on";
-
-    private AudioManager mAudioManager;
-    private static int mOrigVolume;
-    private int mMaxVolume;
-    private Uri mAlarmSound;
-    private MediaPlayer mMediaPlayer;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
-        mOrigVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
-        mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
-        mAlarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
-        mMediaPlayer = new MediaPlayer();
-    }
-
-    @Override
-    public void onDestroy() {
-        // Reset the alarm volume to the user's original setting.
-        mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mOrigVolume, 0);
-        mMediaPlayer.release();
-        super.onDestroy();
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName());
-        }
-        for (DataEvent event : dataEvents) {
-            if (event.getType() == DataEvent.TYPE_DELETED) {
-                Log.i(TAG, event + " deleted");
-            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
-                Boolean alarmOn =
-                        DataMap.fromByteArray(event.getDataItem().getData()).get(FIELD_ALARM_ON);
-                if (alarmOn) {
-                    mOrigVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
-                    mMediaPlayer.reset();
-                    // Sound alarm at max volume.
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mMaxVolume, 0);
-                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
-                    try {
-                        mMediaPlayer.setDataSource(getApplicationContext(), mAlarmSound);
-                        mMediaPlayer.prepare();
-                    } catch (IOException e) {
-                        Log.e(TAG, "Failed to prepare media player to play alarm.", e);
-                    }
-                    mMediaPlayer.start();
-                } else {
-                    // Reset the alarm volume to the user's original setting.
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mOrigVolume, 0);
-                    if (mMediaPlayer.isPlaying()) {
-                        mMediaPlayer.stop();
-                    }
-                }
-            }
-        }
-        dataEvents.close();
-    }
-
-}
diff --git a/wearable/wear/FindMyPhone/Application/src/main/java/com/example/android/wearable/findphone/SoundAlarmListenerService.java b/wearable/wear/FindMyPhone/Application/src/main/java/com/example/android/wearable/findphone/SoundAlarmListenerService.java
new file mode 100644
index 0000000..c89db9d
--- /dev/null
+++ b/wearable/wear/FindMyPhone/Application/src/main/java/com/example/android/wearable/findphone/SoundAlarmListenerService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.findphone;
+
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.util.Log;
+
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.io.IOException;
+
+/**
+ * Listens for disconnection from home device.
+ */
+public class SoundAlarmListenerService extends WearableListenerService {
+
+    private static final String TAG = "ExampleFindPhoneApp";
+
+    private static final String FIELD_ALARM_ON = "alarm_on";
+
+    private AudioManager mAudioManager;
+    private static int mOrigVolume;
+    private int mMaxVolume;
+    private Uri mAlarmSound;
+    private MediaPlayer mMediaPlayer;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
+        mOrigVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
+        mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
+        mAlarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
+        mMediaPlayer = new MediaPlayer();
+    }
+
+    @Override
+    public void onDestroy() {
+        // Reset the alarm volume to the user's original setting.
+        mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mOrigVolume, 0);
+        mMediaPlayer.release();
+        super.onDestroy();
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName());
+        }
+        for (DataEvent event : dataEvents) {
+            if (event.getType() == DataEvent.TYPE_DELETED) {
+                Log.i(TAG, event + " deleted");
+            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
+                Boolean alarmOn =
+                        DataMap.fromByteArray(event.getDataItem().getData()).get(FIELD_ALARM_ON);
+                if (alarmOn) {
+                    mOrigVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
+                    mMediaPlayer.reset();
+                    // Sound alarm at max volume.
+                    mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mMaxVolume, 0);
+                    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
+                    try {
+                        mMediaPlayer.setDataSource(getApplicationContext(), mAlarmSound);
+                        mMediaPlayer.prepare();
+                    } catch (IOException e) {
+                        Log.e(TAG, "Failed to prepare media player to play alarm.", e);
+                    }
+                    mMediaPlayer.start();
+                } else {
+                    // Reset the alarm volume to the user's original setting.
+                    mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mOrigVolume, 0);
+                    if (mMediaPlayer.isPlaying()) {
+                        mMediaPlayer.stop();
+                    }
+                }
+            }
+        }
+        dataEvents.close();
+    }
+
+}
diff --git a/wearable/wear/FindMyPhone/Shared/.gitignore b/wearable/wear/FindMyPhone/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/FindMyPhone/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/FindMyPhone/Shared/src/main/AndroidManifest.xml b/wearable/wear/FindMyPhone/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 80efe2e..0000000
--- a/wearable/wear/FindMyPhone/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.findphone.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/FindMyPhone/Shared/src/main/res/values/strings.xml b/wearable/wear/FindMyPhone/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/FindMyPhone/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/AndroidManifest.xml b/wearable/wear/FindMyPhone/Wearable/src/main/AndroidManifest.xml
index b81dd33..18b5209 100644
--- a/wearable/wear/FindMyPhone/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/FindMyPhone/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.findphone" >
+    package="com.example.android.wearable.findphone" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-feature android:name="android.hardware.type.watch" />
@@ -39,7 +39,7 @@
             </intent-filter>
         </service>
 
-        <service android:name="com.example.android.findphone.FindPhoneService"/>
+        <service android:name="com.example.android.wearable.findphone.FindPhoneService"/>
 
         <activity
                 android:name=".FindPhoneActivity"
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/DisconnectListenerService.java b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/DisconnectListenerService.java
deleted file mode 100644
index 749a38d..0000000
--- a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/DisconnectListenerService.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.findphone;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-
-import com.google.android.gms.wearable.WearableListenerService;
-
-/**
- * Listens for disconnection from home device.
- */
-public class DisconnectListenerService extends WearableListenerService {
-
-    private static final String TAG = "ExampleFindPhoneApp";
-
-    private static final int FORGOT_PHONE_NOTIFICATION_ID = 1;
-
-    @Override
-    public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {
-        // Create a "forgot phone" notification when phone connection is broken.
-        Notification.Builder notificationBuilder = new Notification.Builder(this)
-                .setContentTitle(getString(R.string.left_phone_title))
-                .setContentText(getString(R.string.left_phone_content))
-                .setVibrate(new long[] {0, 200})  // Vibrate for 200 milliseconds.
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setLocalOnly(true)
-                .setPriority(Notification.PRIORITY_MAX);
-        Notification card = notificationBuilder.build();
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .notify(FORGOT_PHONE_NOTIFICATION_ID, card);
-    }
-
-    @Override
-    public void onPeerConnected(com.google.android.gms.wearable.Node peer) {
-        // Remove the "forgot phone" notification when connection is restored.
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .cancel(FORGOT_PHONE_NOTIFICATION_ID);
-    }
-
-}
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/FindPhoneActivity.java b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/FindPhoneActivity.java
deleted file mode 100644
index 8251bab..0000000
--- a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/FindPhoneActivity.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.findphone;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.style.RelativeSizeSpan;
-
-
-public class FindPhoneActivity extends Activity {
-
-    private static final int FIND_PHONE_NOTIFICATION_ID = 2;
-    private static Notification.Builder notification;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        // Create a notification with an action to toggle an alarm on the phone.
-        Intent toggleAlarmOperation = new Intent(this, FindPhoneService.class);
-        toggleAlarmOperation.setAction(FindPhoneService.ACTION_TOGGLE_ALARM);
-        PendingIntent toggleAlarmIntent = PendingIntent.getService(this, 0, toggleAlarmOperation,
-                PendingIntent.FLAG_CANCEL_CURRENT);
-        Action alarmAction = new Action(R.drawable.alarm_action_icon, "", toggleAlarmIntent);
-        // This intent turns off the alarm if the user dismisses the card from the wearable.
-        Intent cancelAlarmOperation = new Intent(this, FindPhoneService.class);
-        cancelAlarmOperation.setAction(FindPhoneService.ACTION_CANCEL_ALARM);
-        PendingIntent cancelAlarmIntent = PendingIntent.getService(this, 0, cancelAlarmOperation,
-                PendingIntent.FLAG_CANCEL_CURRENT);
-        // Use a spannable string for the notification title to resize it.
-        SpannableString title = new SpannableString(getString(R.string.app_name));
-        title.setSpan(new RelativeSizeSpan(0.85f), 0, title.length(), Spannable.SPAN_POINT_MARK);
-        notification = new Notification.Builder(this)
-                .setContentTitle(title)
-                .setContentText(getString(R.string.turn_alarm_on))
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setVibrate(new long[] {0, 50})  // Vibrate to bring card to top of stream.
-                .setDeleteIntent(cancelAlarmIntent)
-                .extend(new Notification.WearableExtender()
-                        .addAction(alarmAction)
-                        .setContentAction(0)
-                        .setHintHideIcon(true))
-                .setLocalOnly(true)
-                .setPriority(Notification.PRIORITY_MAX);
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .notify(FIND_PHONE_NOTIFICATION_ID, notification.build());
-
-        finish();
-    }
-
-    /**
-     * Updates the text on the wearable notification. This is used so the notification reflects the
-     * current state of the alarm on the phone. For instance, if the alarm is turned on, the
-     * notification text indicates that the user can tap it to turn it off, and vice-versa.
-     *
-     * @param context
-     * @param notificationText The new text to display on the wearable notification.
-     */
-    public static void updateNotification(Context context, String notificationText) {
-        notification.setContentText(notificationText);
-        ((NotificationManager) context.getSystemService(NOTIFICATION_SERVICE))
-                .notify(FIND_PHONE_NOTIFICATION_ID, notification.build());
-    }
-
-}
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/FindPhoneService.java b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/FindPhoneService.java
deleted file mode 100644
index cb9f961..0000000
--- a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/findphone/FindPhoneService.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.findphone;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.DataItemBuffer;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Creates a sound on the paired phone to find it.
- */
-public class FindPhoneService extends IntentService implements GoogleApiClient.ConnectionCallbacks,
-        GoogleApiClient.OnConnectionFailedListener {
-
-    private static final String TAG = "ExampleFindPhoneApp";
-
-    private static final String FIELD_ALARM_ON = "alarm_on";
-    private static final String PATH_SOUND_ALARM = "/sound_alarm";
-    public static final String ACTION_TOGGLE_ALARM = "action_toggle_alarm";
-    public static final String ACTION_CANCEL_ALARM = "action_alarm_off";
-
-    // Timeout for making a connection to GoogleApiClient (in milliseconds).
-    private static final long CONNECTION_TIME_OUT_MS = 100;
-    private GoogleApiClient mGoogleApiClient;
-
-    public FindPhoneService() {
-        super(FindPhoneService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "FindPhoneService.onHandleIntent");
-        }
-        if (mGoogleApiClient.isConnected()) {
-            // Set the alarm off by default.
-            boolean alarmOn = false;
-            if (intent.getAction().equals(ACTION_TOGGLE_ALARM)) {
-                // Get current state of the alarm.
-                DataItemBuffer result = Wearable.DataApi.getDataItems(mGoogleApiClient).await();
-                if (result.getStatus().isSuccess()) {
-                    if (result.getCount() == 1) {
-                        alarmOn = DataMap.fromByteArray(result.get(0).getData())
-                                .getBoolean(FIELD_ALARM_ON, false);
-                    } else {
-                        Log.e(TAG, "Unexpected number of DataItems found.\n"
-                                 + "\tExpected: 1\n"
-                                 + "\tActual: " + result.getCount());
-                    }
-                } else if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "onHandleIntent: failed to get current alarm state");
-                }
-                result.close();
-                // Toggle alarm.
-                alarmOn = !alarmOn;
-                // Change notification text based on new value of alarmOn.
-                String notificationText = alarmOn ? getString(R.string.turn_alarm_off)
-                                                  : getString(R.string.turn_alarm_on);
-                FindPhoneActivity.updateNotification(this, notificationText);
-            }
-            // Use alarmOn boolean to update the DataItem - phone will respond accordingly
-            // when it receives the change.
-            PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_SOUND_ALARM);
-            putDataMapRequest.getDataMap().putBoolean(FIELD_ALARM_ON, alarmOn);
-            Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest())
-                    .await();
-        } else {
-            Log.e(TAG, "Failed to toggle alarm on phone - Client disconnected from Google Play "
-                     + "Services");
-        }
-        mGoogleApiClient.disconnect();
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-    }
-
-}
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/DisconnectListenerService.java b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/DisconnectListenerService.java
new file mode 100644
index 0000000..42237de
--- /dev/null
+++ b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/DisconnectListenerService.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.findphone;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+
+import com.google.android.gms.wearable.WearableListenerService;
+
+/**
+ * Listens for disconnection from home device.
+ */
+public class DisconnectListenerService extends WearableListenerService {
+
+    private static final String TAG = "ExampleFindPhoneApp";
+
+    private static final int FORGOT_PHONE_NOTIFICATION_ID = 1;
+
+    @Override
+    public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {
+        // Create a "forgot phone" notification when phone connection is broken.
+        Notification.Builder notificationBuilder = new Notification.Builder(this)
+                .setContentTitle(getString(R.string.left_phone_title))
+                .setContentText(getString(R.string.left_phone_content))
+                .setVibrate(new long[] {0, 200})  // Vibrate for 200 milliseconds.
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setLocalOnly(true)
+                .setPriority(Notification.PRIORITY_MAX);
+        Notification card = notificationBuilder.build();
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .notify(FORGOT_PHONE_NOTIFICATION_ID, card);
+    }
+
+    @Override
+    public void onPeerConnected(com.google.android.gms.wearable.Node peer) {
+        // Remove the "forgot phone" notification when connection is restored.
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .cancel(FORGOT_PHONE_NOTIFICATION_ID);
+    }
+
+}
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/FindPhoneActivity.java b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/FindPhoneActivity.java
new file mode 100644
index 0000000..d4eb18b
--- /dev/null
+++ b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/FindPhoneActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.findphone;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.RelativeSizeSpan;
+
+
+public class FindPhoneActivity extends Activity {
+
+    private static final int FIND_PHONE_NOTIFICATION_ID = 2;
+    private static Notification.Builder notification;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Create a notification with an action to toggle an alarm on the phone.
+        Intent toggleAlarmOperation = new Intent(this, FindPhoneService.class);
+        toggleAlarmOperation.setAction(FindPhoneService.ACTION_TOGGLE_ALARM);
+        PendingIntent toggleAlarmIntent = PendingIntent.getService(this, 0, toggleAlarmOperation,
+                PendingIntent.FLAG_CANCEL_CURRENT);
+        Action alarmAction = new Action(R.drawable.alarm_action_icon, "", toggleAlarmIntent);
+        // This intent turns off the alarm if the user dismisses the card from the wearable.
+        Intent cancelAlarmOperation = new Intent(this, FindPhoneService.class);
+        cancelAlarmOperation.setAction(FindPhoneService.ACTION_CANCEL_ALARM);
+        PendingIntent cancelAlarmIntent = PendingIntent.getService(this, 0, cancelAlarmOperation,
+                PendingIntent.FLAG_CANCEL_CURRENT);
+        // Use a spannable string for the notification title to resize it.
+        SpannableString title = new SpannableString(getString(R.string.app_name));
+        title.setSpan(new RelativeSizeSpan(0.85f), 0, title.length(), Spannable.SPAN_POINT_MARK);
+        notification = new Notification.Builder(this)
+                .setContentTitle(title)
+                .setContentText(getString(R.string.turn_alarm_on))
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setVibrate(new long[] {0, 50})  // Vibrate to bring card to top of stream.
+                .setDeleteIntent(cancelAlarmIntent)
+                .extend(new Notification.WearableExtender()
+                        .addAction(alarmAction)
+                        .setContentAction(0)
+                        .setHintHideIcon(true))
+                .setLocalOnly(true)
+                .setPriority(Notification.PRIORITY_MAX);
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .notify(FIND_PHONE_NOTIFICATION_ID, notification.build());
+
+        finish();
+    }
+
+    /**
+     * Updates the text on the wearable notification. This is used so the notification reflects the
+     * current state of the alarm on the phone. For instance, if the alarm is turned on, the
+     * notification text indicates that the user can tap it to turn it off, and vice-versa.
+     *
+     * @param context
+     * @param notificationText The new text to display on the wearable notification.
+     */
+    public static void updateNotification(Context context, String notificationText) {
+        notification.setContentText(notificationText);
+        ((NotificationManager) context.getSystemService(NOTIFICATION_SERVICE))
+                .notify(FIND_PHONE_NOTIFICATION_ID, notification.build());
+    }
+
+}
diff --git a/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/FindPhoneService.java b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/FindPhoneService.java
new file mode 100644
index 0000000..e5fa6c0
--- /dev/null
+++ b/wearable/wear/FindMyPhone/Wearable/src/main/java/com/example/android/wearable/findphone/FindPhoneService.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.findphone;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataItemBuffer;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Creates a sound on the paired phone to find it.
+ */
+public class FindPhoneService extends IntentService implements GoogleApiClient.ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "ExampleFindPhoneApp";
+
+    private static final String FIELD_ALARM_ON = "alarm_on";
+    private static final String PATH_SOUND_ALARM = "/sound_alarm";
+    public static final String ACTION_TOGGLE_ALARM = "action_toggle_alarm";
+    public static final String ACTION_CANCEL_ALARM = "action_alarm_off";
+
+    // Timeout for making a connection to GoogleApiClient (in milliseconds).
+    private static final long CONNECTION_TIME_OUT_MS = 100;
+    private GoogleApiClient mGoogleApiClient;
+
+    public FindPhoneService() {
+        super(FindPhoneService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "FindPhoneService.onHandleIntent");
+        }
+        if (mGoogleApiClient.isConnected()) {
+            // Set the alarm off by default.
+            boolean alarmOn = false;
+            if (intent.getAction().equals(ACTION_TOGGLE_ALARM)) {
+                // Get current state of the alarm.
+                DataItemBuffer result = Wearable.DataApi.getDataItems(mGoogleApiClient).await();
+                if (result.getStatus().isSuccess()) {
+                    if (result.getCount() == 1) {
+                        alarmOn = DataMap.fromByteArray(result.get(0).getData())
+                                .getBoolean(FIELD_ALARM_ON, false);
+                    } else {
+                        Log.e(TAG, "Unexpected number of DataItems found.\n"
+                                 + "\tExpected: 1\n"
+                                 + "\tActual: " + result.getCount());
+                    }
+                } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "onHandleIntent: failed to get current alarm state");
+                }
+                result.close();
+                // Toggle alarm.
+                alarmOn = !alarmOn;
+                // Change notification text based on new value of alarmOn.
+                String notificationText = alarmOn ? getString(R.string.turn_alarm_off)
+                                                  : getString(R.string.turn_alarm_on);
+                FindPhoneActivity.updateNotification(this, notificationText);
+            }
+            // Use alarmOn boolean to update the DataItem - phone will respond accordingly
+            // when it receives the change.
+            PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_SOUND_ALARM);
+            putDataMapRequest.getDataMap().putBoolean(FIELD_ALARM_ON, alarmOn);
+            Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest())
+                    .await();
+        } else {
+            Log.e(TAG, "Failed to toggle alarm on phone - Client disconnected from Google Play "
+                     + "Services");
+        }
+        mGoogleApiClient.disconnect();
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+    }
+
+}
diff --git a/wearable/wear/FindMyPhone/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/FindMyPhone/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/FindMyPhone/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/FindMyPhone/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/FindMyPhone/screenshots/find_my_phone_activity.png b/wearable/wear/FindMyPhone/screenshots/find_my_phone_activity.png
new file mode 100644
index 0000000..8cff992
--- /dev/null
+++ b/wearable/wear/FindMyPhone/screenshots/find_my_phone_activity.png
Binary files differ
diff --git a/wearable/wear/FindMyPhone/screenshots/find_my_phone_notification.png b/wearable/wear/FindMyPhone/screenshots/find_my_phone_notification.png
new file mode 100644
index 0000000..a8ed867
--- /dev/null
+++ b/wearable/wear/FindMyPhone/screenshots/find_my_phone_notification.png
Binary files differ
diff --git a/wearable/wear/FindMyPhone/settings.gradle b/wearable/wear/FindMyPhone/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/FindMyPhone/settings.gradle
+++ b/wearable/wear/FindMyPhone/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/FindMyPhone/template-params.xml b/wearable/wear/FindMyPhone/template-params.xml
index fa5bb61..640467a 100644
--- a/wearable/wear/FindMyPhone/template-params.xml
+++ b/wearable/wear/FindMyPhone/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>FindMyPhone</name>
     <group>Wearable</group>
-    <package>com.example.android.findphone</package>
+    <package>com.example.android.wearable.findphone</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -41,7 +41,5 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
 </sample>
diff --git a/wearable/wear/Flashlight/Application/src/main/AndroidManifest.xml b/wearable/wear/Flashlight/Application/src/main/AndroidManifest.xml
index 2710313..925d11b 100644
--- a/wearable/wear/Flashlight/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Flashlight/Application/src/main/AndroidManifest.xml
@@ -16,12 +16,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.flashlight">
+    package="com.example.android.wearable.flashlight">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
         android:label="@string/app_name">
     </application>
 
diff --git a/wearable/wear/Flashlight/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/Flashlight/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/Flashlight/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Flashlight/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/Flashlight/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/Flashlight/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Flashlight/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/Flashlight/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/Flashlight/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Flashlight/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/Flashlight/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/Flashlight/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Flashlight/Shared/.gitignore b/wearable/wear/Flashlight/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/Flashlight/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/Flashlight/Shared/src/main/AndroidManifest.xml b/wearable/wear/Flashlight/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index c7893c7..0000000
--- a/wearable/wear/Flashlight/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.flashlight.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/Flashlight/Shared/src/main/res/values/strings.xml b/wearable/wear/Flashlight/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/Flashlight/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml
index f22e1ab..738ba9d 100644
--- a/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.example.android.flashlight" >
+          package="com.example.android.wearable.flashlight" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/flashlight/MainActivity.java b/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/flashlight/MainActivity.java
deleted file mode 100644
index 9e4bae5..0000000
--- a/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/flashlight/MainActivity.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.flashlight;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.os.Bundle;
-import android.support.v13.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.ViewPager.OnPageChangeListener;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * Let there be light.
- */
-public class MainActivity extends Activity {
-
-    private ViewPager mViewPager;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.main);
-        mViewPager = (ViewPager) findViewById(R.id.pager);
-        final LightFragmentAdapter adapter = new LightFragmentAdapter(getFragmentManager());
-        adapter.addFragment(new WhiteLightFragment());
-        final PartyLightFragment partyFragment = new PartyLightFragment();
-        adapter.addFragment(partyFragment);
-        mViewPager.setAdapter(adapter);
-        mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
-
-            @Override
-            public void onPageSelected(int position) {
-                if (position == 1) {
-                    partyFragment.startCycling();
-                } else {
-                    partyFragment.stopCycling();
-                }
-            }
-
-            @Override
-            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-            }
-
-            @Override
-            public void onPageScrollStateChanged(int state) {
-            }
-        });
-    }
-
-    static class LightFragmentAdapter extends FragmentPagerAdapter {
-        private ArrayList<Fragment> mFragments;
-
-        public LightFragmentAdapter(FragmentManager fm) {
-            super(fm);
-            mFragments = new ArrayList<Fragment>();
-        }
-
-        @Override
-        public Fragment getItem(int position) {
-            return mFragments.get(position);
-        }
-
-        @Override
-        public int getCount() {
-            return mFragments.size();
-        }
-
-        public void addFragment(Fragment fragment) {
-            mFragments.add(fragment);
-            // Update the pager when adding a fragment.
-            notifyDataSetChanged();
-        }
-    }
-
-    public static class WhiteLightFragment extends Fragment {
-
-        @Override
-        public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                Bundle savedInstanceState) {
-            return inflater.inflate(R.layout.white_light, container, false);
-        }
-    }
-
-    public static class PartyLightFragment extends Fragment {
-
-        private PartyLightView mView;
-
-        @Override
-        public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                Bundle savedInstanceState) {
-            mView = (PartyLightView) inflater.inflate(R.layout.party_light, container, false);
-            return mView;
-        }
-
-        public void startCycling() {
-            mView.startCycling();
-        }
-
-        public void stopCycling() {
-            mView.stopCycling();
-        }
-
-    }
-}
diff --git a/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/flashlight/PartyLightView.java b/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/flashlight/PartyLightView.java
deleted file mode 100644
index 02247d6..0000000
--- a/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/flashlight/PartyLightView.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.flashlight;
-
-import android.animation.ArgbEvaluator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * Flashing party lights!
- */
-public class PartyLightView extends View {
-
-    private int[] mColors = new int[] {
-            Color.RED,
-            Color.GREEN,
-            Color.BLUE,
-            Color.CYAN,
-            Color.MAGENTA
-    };
-
-    private int mFromColorIndex;
-    private int mToColorIndex;
-
-    /**
-     * Value b/t 0 and 1.
-     */
-    private float mProgress;
-
-    private ArgbEvaluator mEvaluator;
-
-    private int mCurrentColor;
-
-    private Handler mHandler;
-
-    public PartyLightView(Context context) {
-        super(context);
-        init();
-    }
-
-    public PartyLightView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        canvas.drawColor(mCurrentColor);
-        super.onDraw(canvas);
-    }
-
-    public void startCycling() {
-        mHandler.sendEmptyMessage(0);
-    }
-
-    public void stopCycling() {
-        mHandler.removeMessages(0);
-    }
-
-    private void init() {
-        mEvaluator = new ArgbEvaluator();
-        mHandler = new Handler() {
-
-            @Override
-            public void handleMessage(Message msg) {
-                mCurrentColor = getColor(mProgress, mColors[mFromColorIndex],
-                        mColors[mToColorIndex]);
-                postInvalidate();
-                mProgress += 0.1;
-                if (mProgress > 1.0) {
-                    mFromColorIndex = mToColorIndex;
-                    // Find a new color.
-                    mToColorIndex++;
-                    if (mToColorIndex >= mColors.length) {
-                        mToColorIndex = 0;
-                    }
-                }
-                mHandler.sendEmptyMessageDelayed(0, 100);
-            }
-        };
-    }
-
-    private int getColor(float fraction, int colorStart, int colorEnd) {
-        int startInt = colorStart;
-        int startA = (startInt >> 24) & 0xff;
-        int startR = (startInt >> 16) & 0xff;
-        int startG = (startInt >> 8) & 0xff;
-        int startB = startInt & 0xff;
-
-        int endInt = colorEnd;
-        int endA = (endInt >> 24) & 0xff;
-        int endR = (endInt >> 16) & 0xff;
-        int endG = (endInt >> 8) & 0xff;
-        int endB = endInt & 0xff;
-
-        return (startA + (int)(fraction * (endA - startA))) << 24 |
-                (startR + (int)(fraction * (endR - startR))) << 16 |
-                (startG + (int)(fraction * (endG - startG))) << 8 |
-                ((startB + (int)(fraction * (endB - startB))));
-    }
-}
diff --git a/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/wearable/flashlight/MainActivity.java b/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/wearable/flashlight/MainActivity.java
new file mode 100644
index 0000000..f87cae8
--- /dev/null
+++ b/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/wearable/flashlight/MainActivity.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.flashlight;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Let there be light.
+ */
+public class MainActivity extends Activity {
+
+    private ViewPager mViewPager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main);
+        mViewPager = (ViewPager) findViewById(R.id.pager);
+        final LightFragmentAdapter adapter = new LightFragmentAdapter(getFragmentManager());
+        adapter.addFragment(new WhiteLightFragment());
+        final PartyLightFragment partyFragment = new PartyLightFragment();
+        adapter.addFragment(partyFragment);
+        mViewPager.setAdapter(adapter);
+        mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
+
+            @Override
+            public void onPageSelected(int position) {
+                if (position == 1) {
+                    partyFragment.startCycling();
+                } else {
+                    partyFragment.stopCycling();
+                }
+            }
+
+            @Override
+            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            }
+
+            @Override
+            public void onPageScrollStateChanged(int state) {
+            }
+        });
+    }
+
+    static class LightFragmentAdapter extends FragmentPagerAdapter {
+        private ArrayList<Fragment> mFragments;
+
+        public LightFragmentAdapter(FragmentManager fm) {
+            super(fm);
+            mFragments = new ArrayList<Fragment>();
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            return mFragments.get(position);
+        }
+
+        @Override
+        public int getCount() {
+            return mFragments.size();
+        }
+
+        public void addFragment(Fragment fragment) {
+            mFragments.add(fragment);
+            // Update the pager when adding a fragment.
+            notifyDataSetChanged();
+        }
+    }
+
+    public static class WhiteLightFragment extends Fragment {
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            return inflater.inflate(R.layout.white_light, container, false);
+        }
+    }
+
+    public static class PartyLightFragment extends Fragment {
+
+        private PartyLightView mView;
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            mView = (PartyLightView) inflater.inflate(R.layout.party_light, container, false);
+            return mView;
+        }
+
+        public void startCycling() {
+            mView.startCycling();
+        }
+
+        public void stopCycling() {
+            mView.stopCycling();
+        }
+
+    }
+}
diff --git a/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/wearable/flashlight/PartyLightView.java b/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/wearable/flashlight/PartyLightView.java
new file mode 100644
index 0000000..33f061e
--- /dev/null
+++ b/wearable/wear/Flashlight/Wearable/src/main/java/com/example/android/wearable/flashlight/PartyLightView.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.flashlight;
+
+import android.animation.ArgbEvaluator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Flashing party lights!
+ */
+public class PartyLightView extends View {
+
+    private int[] mColors = new int[] {
+            Color.RED,
+            Color.GREEN,
+            Color.BLUE,
+            Color.CYAN,
+            Color.MAGENTA
+    };
+
+    private int mFromColorIndex;
+    private int mToColorIndex;
+
+    /**
+     * Value b/t 0 and 1.
+     */
+    private float mProgress;
+
+    private ArgbEvaluator mEvaluator;
+
+    private int mCurrentColor;
+
+    private Handler mHandler;
+
+    public PartyLightView(Context context) {
+        super(context);
+        init();
+    }
+
+    public PartyLightView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawColor(mCurrentColor);
+        super.onDraw(canvas);
+    }
+
+    public void startCycling() {
+        mHandler.sendEmptyMessage(0);
+    }
+
+    public void stopCycling() {
+        mHandler.removeMessages(0);
+    }
+
+    private void init() {
+        mEvaluator = new ArgbEvaluator();
+        mHandler = new Handler() {
+
+            @Override
+            public void handleMessage(Message msg) {
+                mCurrentColor = getColor(mProgress, mColors[mFromColorIndex],
+                        mColors[mToColorIndex]);
+                postInvalidate();
+                mProgress += 0.1;
+                if (mProgress > 1.0) {
+                    mFromColorIndex = mToColorIndex;
+                    // Find a new color.
+                    mToColorIndex++;
+                    if (mToColorIndex >= mColors.length) {
+                        mToColorIndex = 0;
+                    }
+                }
+                mHandler.sendEmptyMessageDelayed(0, 100);
+            }
+        };
+    }
+
+    private int getColor(float fraction, int colorStart, int colorEnd) {
+        int startInt = colorStart;
+        int startA = (startInt >> 24) & 0xff;
+        int startR = (startInt >> 16) & 0xff;
+        int startG = (startInt >> 8) & 0xff;
+        int startB = startInt & 0xff;
+
+        int endInt = colorEnd;
+        int endA = (endInt >> 24) & 0xff;
+        int endR = (endInt >> 16) & 0xff;
+        int endG = (endInt >> 8) & 0xff;
+        int endB = endInt & 0xff;
+
+        return (startA + (int)(fraction * (endA - startA))) << 24 |
+                (startR + (int)(fraction * (endR - startR))) << 16 |
+                (startG + (int)(fraction * (endG - startG))) << 8 |
+                ((startB + (int)(fraction * (endB - startB))));
+    }
+}
diff --git a/wearable/wear/Flashlight/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Flashlight/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/Flashlight/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/Flashlight/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/Flashlight/screenshots/flashlight.png b/wearable/wear/Flashlight/screenshots/flashlight.png
new file mode 100644
index 0000000..02fca20
--- /dev/null
+++ b/wearable/wear/Flashlight/screenshots/flashlight.png
Binary files differ
diff --git a/wearable/wear/Flashlight/screenshots/party_mode.gif b/wearable/wear/Flashlight/screenshots/party_mode.gif
new file mode 100644
index 0000000..8258b7d
--- /dev/null
+++ b/wearable/wear/Flashlight/screenshots/party_mode.gif
Binary files differ
diff --git a/wearable/wear/Flashlight/settings.gradle b/wearable/wear/Flashlight/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/Flashlight/settings.gradle
+++ b/wearable/wear/Flashlight/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/Flashlight/template-params.xml b/wearable/wear/Flashlight/template-params.xml
index 0468a4d..ca47208 100644
--- a/wearable/wear/Flashlight/template-params.xml
+++ b/wearable/wear/Flashlight/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>Flashlight</name>
     <group>Wearable</group>
-    <package>com.example.android.flashlight</package>
+    <package>com.example.android.wearable.flashlight</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -41,5 +41,4 @@
     <template src="Wear"/>
     <common src="logger"/>
     <common src="activities"/>
-
 </sample>
diff --git a/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml b/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml
index 519aa14..d07a265 100644
--- a/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.example.android.geofencing">
+          package="com.example.android.wearable.geofencing">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/Constants.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/Constants.java
deleted file mode 100644
index b90aa3b..0000000
--- a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/Constants.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import android.net.Uri;
-
-import com.google.android.gms.location.Geofence;
-
-/** Constants used in companion app. */
-public final class Constants {
-
-    private Constants() {
-    }
-
-    public static final String TAG = "ExampleGeofencingApp";
-
-    // Request code to attempt to resolve Google Play services connection failures.
-    public final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
-    // Timeout for making a connection to GoogleApiClient (in milliseconds).
-    public static final long CONNECTION_TIME_OUT_MS = 100;
-
-    // For the purposes of this demo, the geofences are hard-coded and should not expire.
-    // An app with dynamically-created geofences would want to include a reasonable expiration time.
-    public static final long GEOFENCE_EXPIRATION_TIME = Geofence.NEVER_EXPIRE;
-
-    // Geofence parameters for the Android building on Google's main campus in Mountain View.
-    public static final String ANDROID_BUILDING_ID = "1";
-    public static final double ANDROID_BUILDING_LATITUDE = 37.420092;
-    public static final double ANDROID_BUILDING_LONGITUDE = -122.083648;
-    public static final float ANDROID_BUILDING_RADIUS_METERS = 60.0f;
-
-    // Geofence parameters for the Yerba Buena Gardens near the Moscone Center in San Francisco.
-    public static final String YERBA_BUENA_ID = "2";
-    public static final double YERBA_BUENA_LATITUDE = 37.784886;
-    public static final double YERBA_BUENA_LONGITUDE = -122.402671;
-    public static final float YERBA_BUENA_RADIUS_METERS = 72.0f;
-
-
-    // The constants below are less interesting than those above.
-
-    // Path for the DataItem containing the last geofence id entered.
-    public static final String GEOFENCE_DATA_ITEM_PATH = "/geofenceid";
-    public static final Uri GEOFENCE_DATA_ITEM_URI =
-            new Uri.Builder().scheme("wear").path(GEOFENCE_DATA_ITEM_PATH).build();
-    public static final String KEY_GEOFENCE_ID = "geofence_id";
-
-    // Keys for flattened geofences stored in SharedPreferences.
-    public static final String KEY_LATITUDE = "com.example.wearable.geofencing.KEY_LATITUDE";
-    public static final String KEY_LONGITUDE = "com.example.wearable.geofencing.KEY_LONGITUDE";
-    public static final String KEY_RADIUS = "com.example.wearable.geofencing.KEY_RADIUS";
-    public static final String KEY_EXPIRATION_DURATION =
-            "com.example.wearable.geofencing.KEY_EXPIRATION_DURATION";
-    public static final String KEY_TRANSITION_TYPE =
-            "com.example.wearable.geofencing.KEY_TRANSITION_TYPE";
-    // The prefix for flattened geofence keys.
-    public static final String KEY_PREFIX = "com.example.wearable.geofencing.KEY";
-
-    // Invalid values, used to test geofence storage when retrieving geofences.
-    public static final long INVALID_LONG_VALUE = -999l;
-    public static final float INVALID_FLOAT_VALUE = -999.0f;
-    public static final int INVALID_INT_VALUE = -999;
-
-}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/GeofenceTransitionsIntentService.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/GeofenceTransitionsIntentService.java
deleted file mode 100644
index 1af7bc6..0000000
--- a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/GeofenceTransitionsIntentService.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import static com.example.android.geofencing.Constants.CONNECTION_TIME_OUT_MS;
-import static com.example.android.geofencing.Constants.GEOFENCE_DATA_ITEM_PATH;
-import static com.example.android.geofencing.Constants.GEOFENCE_DATA_ITEM_URI;
-import static com.example.android.geofencing.Constants.KEY_GEOFENCE_ID;
-import static com.example.android.geofencing.Constants.TAG;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.location.Geofence;
-import com.google.android.gms.location.LocationClient;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Listens for geofence transition changes.
- */
-public class GeofenceTransitionsIntentService extends IntentService
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
-
-    private GoogleApiClient mGoogleApiClient;
-
-    public GeofenceTransitionsIntentService() {
-        super(GeofenceTransitionsIntentService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    /**
-     * Handles incoming intents.
-     * @param intent The Intent sent by Location Services. This Intent is provided to Location
-     *               Services (inside a PendingIntent) when addGeofences() is called.
-     */
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        // First check for errors.
-        if (LocationClient.hasError(intent)) {
-            int errorCode = LocationClient.getErrorCode(intent);
-            Log.e(TAG, "Location Services error: " + errorCode);
-        } else {
-            // Get the type of geofence transition (i.e. enter or exit in this sample).
-            int transitionType = LocationClient.getGeofenceTransition(intent);
-            // Create a DataItem when a user enters one of the geofences. The wearable app will
-            // receive this and create a notification to prompt him/her to check in.
-            if (Geofence.GEOFENCE_TRANSITION_ENTER == transitionType) {
-                // Connect to the Google Api service in preparation for sending a DataItem.
-                mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
-                // Get the geofence id triggered. Note that only one geofence can be triggered at a
-                // time in this example, but in some cases you might want to consider the full list
-                // of geofences triggered.
-                String triggeredGeofenceId = LocationClient.getTriggeringGeofences(intent).get(0)
-                        .getRequestId();
-                // Create a DataItem with this geofence's id. The wearable can use this to create
-                // a notification.
-                final PutDataMapRequest putDataMapRequest =
-                        PutDataMapRequest.create(GEOFENCE_DATA_ITEM_PATH);
-                putDataMapRequest.getDataMap().putString(KEY_GEOFENCE_ID, triggeredGeofenceId);
-                if (mGoogleApiClient.isConnected()) {
-                    Wearable.DataApi.putDataItem(
-                        mGoogleApiClient, putDataMapRequest.asPutDataRequest()).await();
-                } else {
-                    Log.e(TAG, "Failed to send data item: " + putDataMapRequest
-                             + " - Client disconnected from Google Play Services");
-                }
-                mGoogleApiClient.disconnect();
-            } else if (Geofence.GEOFENCE_TRANSITION_EXIT == transitionType) {
-                // Delete the data item when leaving a geofence region.
-                mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
-                Wearable.DataApi.deleteDataItems(mGoogleApiClient, GEOFENCE_DATA_ITEM_URI).await();
-                mGoogleApiClient.disconnect();
-            }
-        }
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-    }
-
-}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/MainActivity.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/MainActivity.java
deleted file mode 100644
index 67dcd07..0000000
--- a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/MainActivity.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import static com.example.android.geofencing.Constants.ANDROID_BUILDING_ID;
-import static com.example.android.geofencing.Constants.ANDROID_BUILDING_LATITUDE;
-import static com.example.android.geofencing.Constants.ANDROID_BUILDING_LONGITUDE;
-import static com.example.android.geofencing.Constants.ANDROID_BUILDING_RADIUS_METERS;
-import static com.example.android.geofencing.Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST;
-import static com.example.android.geofencing.Constants.GEOFENCE_EXPIRATION_TIME;
-import static com.example.android.geofencing.Constants.TAG;
-import static com.example.android.geofencing.Constants.YERBA_BUENA_ID;
-import static com.example.android.geofencing.Constants.YERBA_BUENA_LATITUDE;
-import static com.example.android.geofencing.Constants.YERBA_BUENA_LONGITUDE;
-import static com.example.android.geofencing.Constants.YERBA_BUENA_RADIUS_METERS;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
-import com.google.android.gms.common.GooglePlayServicesUtil;
-import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
-import com.google.android.gms.location.Geofence;
-import com.google.android.gms.location.LocationClient;
-import com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener;
-import com.google.android.gms.location.LocationStatusCodes;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MainActivity extends Activity implements ConnectionCallbacks,
-        OnConnectionFailedListener, OnAddGeofencesResultListener {
-
-    // Internal List of Geofence objects. In a real app, these might be provided by an API based on
-    // locations within the user's proximity.
-    List<Geofence> mGeofenceList;
-
-    // These will store hard-coded geofences in this sample app.
-    private SimpleGeofence mAndroidBuildingGeofence;
-    private SimpleGeofence mYerbaBuenaGeofence;
-
-    // Persistent storage for geofences.
-    private SimpleGeofenceStore mGeofenceStorage;
-
-    private LocationClient mLocationClient;
-    // Stores the PendingIntent used to request geofence monitoring.
-    private PendingIntent mGeofenceRequestIntent;
-
-    // Defines the allowable request types (in this example, we only add geofences).
-    private enum REQUEST_TYPE {ADD}
-    private REQUEST_TYPE mRequestType;
-    // Flag that indicates if a request is underway.
-    private boolean mInProgress;
-
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // Rather than displayng this activity, simply display a toast indicating that the geofence
-        // service is being created. This should happen in less than a second.
-        Toast.makeText(this, getString(R.string.start_geofence_service), Toast.LENGTH_SHORT).show();
-
-        // Instantiate a new geofence storage area.
-        mGeofenceStorage = new SimpleGeofenceStore(this);
-        // Instantiate the current List of geofences.
-        mGeofenceList = new ArrayList<Geofence>();
-        // Start with the request flag set to false.
-        mInProgress = false;
-
-        createGeofences();
-        addGeofences();
-
-        finish();
-    }
-
-    /**
-     * In this sample, the geofences are predetermined and are hard-coded here. A real app might
-     * dynamically create geofences based on the user's location.
-     */
-    public void createGeofences() {
-        // Create internal "flattened" objects containing the geofence data.
-        mAndroidBuildingGeofence = new SimpleGeofence(
-                ANDROID_BUILDING_ID,                // geofenceId.
-                ANDROID_BUILDING_LATITUDE,
-                ANDROID_BUILDING_LONGITUDE,
-                ANDROID_BUILDING_RADIUS_METERS,
-                GEOFENCE_EXPIRATION_TIME,
-                Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT
-        );
-        mYerbaBuenaGeofence = new SimpleGeofence(
-                YERBA_BUENA_ID,                // geofenceId.
-                YERBA_BUENA_LATITUDE,
-                YERBA_BUENA_LONGITUDE,
-                YERBA_BUENA_RADIUS_METERS,
-                GEOFENCE_EXPIRATION_TIME,
-                Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT
-        );
-
-        // Store these flat versions in SharedPreferences and add them to the geofence list.
-        mGeofenceStorage.setGeofence(ANDROID_BUILDING_ID, mAndroidBuildingGeofence);
-        mGeofenceStorage.setGeofence(YERBA_BUENA_ID, mYerbaBuenaGeofence);
-        mGeofenceList.add(mAndroidBuildingGeofence.toGeofence());
-        mGeofenceList.add(mYerbaBuenaGeofence.toGeofence());
-    }
-
-    /**
-     * Start a request for geofence monitoring by calling LocationClient.connect().
-     */
-    public void addGeofences() {
-        // Start a request to add geofences.
-        mRequestType = REQUEST_TYPE.ADD;
-        // Test for Google Play services after setting the request type.
-        if (!isGooglePlayServicesAvailable()) {
-            Log.e(TAG, "Unable to add geofences - Google Play services unavailable.");
-            return;
-        }
-        // Create a new location client object. Since this activity class implements
-        // ConnectionCallbacks and OnConnectionFailedListener, it can be used as the listener for
-        // both parameters.
-        mLocationClient = new LocationClient(this, this, this);
-        // If a request is not already underway.
-        if (!mInProgress) {
-            // Indicate that a request is underway.
-            mInProgress = true;
-            // Request a connection from the client to Location Services.
-            mLocationClient.connect();
-        // A request is already underway, so disconnect the client and retry the request.
-        } else {
-            mLocationClient.disconnect();
-            mLocationClient.connect();
-        }
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-        mInProgress = false;
-        // If the error has a resolution, start a Google Play services activity to resolve it.
-        if (connectionResult.hasResolution()) {
-            try {
-                connectionResult.startResolutionForResult(this,
-                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
-            } catch (IntentSender.SendIntentException e) {
-                Log.e(TAG, "Exception while resolving connection error.", e);
-            }
-        } else {
-            int errorCode = connectionResult.getErrorCode();
-            Log.e(TAG, "Connection to Google Play services failed with error code " + errorCode);
-        }
-    }
-
-    /**
-     * Called by Location Services if the location client disconnects.
-     */
-    @Override
-    public void onDisconnected() {
-        // Turn off the request flag.
-        mInProgress = false;
-        // Destroy the current location client.
-        mLocationClient = null;
-    }
-
-    /**
-     * Once the connection is available, send a request to add the Geofences.
-     */
-    @Override
-    public void onConnected(Bundle connectionHint) {
-        // Use mRequestType to determine what action to take. Only ADD is used in this sample.
-        if (REQUEST_TYPE.ADD == mRequestType) {
-            // Get the PendingIntent for the geofence monitoring request.
-            mGeofenceRequestIntent = getGeofenceTransitionPendingIntent();
-            // Send a request to add the current geofences.
-            mLocationClient.addGeofences(mGeofenceList, mGeofenceRequestIntent, this);
-        }
-    }
-
-    /**
-     * Called when request to add geofences is complete, with a result status code.
-     */
-    @Override
-    public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) {
-        // Log if adding the geofences was successful.
-        if (LocationStatusCodes.SUCCESS == statusCode) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Added geofences successfully.");
-            }
-        } else {
-            Log.e(TAG, "Failed to add geofences. Status code: " + statusCode);
-        }
-        // Turn off the in progress flag and disconnect the client.
-        mInProgress = false;
-        mLocationClient.disconnect();
-    }
-
-    /**
-     * Checks if Google Play services is available.
-     * @return true if it is.
-     */
-    private boolean isGooglePlayServicesAvailable() {
-        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
-        if (ConnectionResult.SUCCESS == resultCode) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Google Play services is available.");
-            }
-            return true;
-        } else {
-            Log.e(TAG, "Google Play services is unavailable.");
-            return false;
-        }
-    }
-
-    /**
-     * Create a PendingIntent that triggers GeofenceTransitionIntentService when a geofence
-     * transition occurs.
-     */
-    private PendingIntent getGeofenceTransitionPendingIntent() {
-        Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
-        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-
-}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/SimpleGeofence.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/SimpleGeofence.java
deleted file mode 100644
index 8e17526..0000000
--- a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/SimpleGeofence.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import com.google.android.gms.location.Geofence;
-
-/**
- * A single Geofence object, defined by its center and radius.
- */
-public class SimpleGeofence {
-
-    // Instance variables
-    private final String mId;
-    private final double mLatitude;
-    private final double mLongitude;
-    private final float mRadius;
-    private long mExpirationDuration;
-    private int mTransitionType;
-
-    /**
-     * @param geofenceId The Geofence's request ID.
-     * @param latitude Latitude of the Geofence's center in degrees.
-     * @param longitude Longitude of the Geofence's center in degrees.
-     * @param radius Radius of the geofence circle in meters.
-     * @param expiration Geofence expiration duration.
-     * @param transition Type of Geofence transition.
-     */
-    public SimpleGeofence(String geofenceId, double latitude, double longitude, float radius,
-            long expiration, int transition) {
-        // Set the instance fields from the constructor.
-        this.mId = geofenceId;
-        this.mLatitude = latitude;
-        this.mLongitude = longitude;
-        this.mRadius = radius;
-        this.mExpirationDuration = expiration;
-        this.mTransitionType = transition;
-    }
-
-    // Instance field getters.
-    public String getId() {
-        return mId;
-    }
-    public double getLatitude() {
-        return mLatitude;
-    }
-    public double getLongitude() {
-        return mLongitude;
-    }
-    public float getRadius() {
-        return mRadius;
-    }
-    public long getExpirationDuration() {
-        return mExpirationDuration;
-    }
-    public int getTransitionType() {
-        return mTransitionType;
-    }
-
-    /**
-     * Creates a Location Services Geofence object from a SimpleGeofence.
-     * @return A Geofence object.
-     */
-    public Geofence toGeofence() {
-        // Build a new Geofence object.
-        return new Geofence.Builder()
-                .setRequestId(mId)
-                .setTransitionTypes(mTransitionType)
-                .setCircularRegion(mLatitude, mLongitude, mRadius)
-                .setExpirationDuration(mExpirationDuration)
-                .build();
-    }
-
-}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/SimpleGeofenceStore.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/SimpleGeofenceStore.java
deleted file mode 100644
index fcdf8f8..0000000
--- a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/geofencing/SimpleGeofenceStore.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import static com.example.android.geofencing.Constants.INVALID_FLOAT_VALUE;
-import static com.example.android.geofencing.Constants.INVALID_INT_VALUE;
-import static com.example.android.geofencing.Constants.INVALID_LONG_VALUE;
-import static com.example.android.geofencing.Constants.KEY_EXPIRATION_DURATION;
-import static com.example.android.geofencing.Constants.KEY_LATITUDE;
-import static com.example.android.geofencing.Constants.KEY_LONGITUDE;
-import static com.example.android.geofencing.Constants.KEY_PREFIX;
-import static com.example.android.geofencing.Constants.KEY_RADIUS;
-import static com.example.android.geofencing.Constants.KEY_TRANSITION_TYPE;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-/**
- * Storage for geofence values, implemented in SharedPreferences.
- */
-public class SimpleGeofenceStore {
-
-    // The SharedPreferences object in which geofences are stored.
-    private final SharedPreferences mPrefs;
-    // The name of the SharedPreferences.
-    private static final String SHARED_PREFERENCES = "SharedPreferences";
-
-    /**
-     * Create the SharedPreferences storage with private access only.
-     */
-    public SimpleGeofenceStore(Context context) {
-        mPrefs = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
-    }
-
-    /**
-     * Returns a stored geofence by its id, or returns null if it's not found.
-     * @param id The ID of a stored geofence.
-     * @return A SimpleGeofence defined by its center and radius, or null if the ID is invalid.
-     */
-    public SimpleGeofence getGeofence(String id) {
-        // Get the latitude for the geofence identified by id, or INVALID_FLOAT_VALUE if it doesn't
-        // exist (similarly for the other values that follow).
-        double lat = mPrefs.getFloat(getGeofenceFieldKey(id, KEY_LATITUDE),
-                INVALID_FLOAT_VALUE);
-        double lng = mPrefs.getFloat(getGeofenceFieldKey(id, KEY_LONGITUDE),
-                INVALID_FLOAT_VALUE);
-        float radius = mPrefs.getFloat(getGeofenceFieldKey(id, KEY_RADIUS),
-                INVALID_FLOAT_VALUE);
-        long expirationDuration =
-                mPrefs.getLong(getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
-                        INVALID_LONG_VALUE);
-        int transitionType = mPrefs.getInt(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
-                INVALID_INT_VALUE);
-        // If none of the values is incorrect, return the object.
-        if (lat != INVALID_FLOAT_VALUE
-                && lng != INVALID_FLOAT_VALUE
-                && radius != INVALID_FLOAT_VALUE
-                && expirationDuration != INVALID_LONG_VALUE
-                && transitionType != INVALID_INT_VALUE) {
-            return new SimpleGeofence(id, lat, lng, radius, expirationDuration, transitionType);
-        }
-        // Otherwise, return null.
-        return null;
-    }
-
-    /**
-     * Save a geofence.
-     * @param geofence The SimpleGeofence with the values you want to save in SharedPreferences.
-     */
-    public void setGeofence(String id, SimpleGeofence geofence) {
-        // Get a SharedPreferences editor instance. Among other things, SharedPreferences
-        // ensures that updates are atomic and non-concurrent.
-        SharedPreferences.Editor prefs = mPrefs.edit();
-        // Write the Geofence values to SharedPreferences.
-        prefs.putFloat(getGeofenceFieldKey(id, KEY_LATITUDE), (float) geofence.getLatitude());
-        prefs.putFloat(getGeofenceFieldKey(id, KEY_LONGITUDE), (float) geofence.getLongitude());
-        prefs.putFloat(getGeofenceFieldKey(id, KEY_RADIUS), geofence.getRadius());
-        prefs.putLong(getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
-                geofence.getExpirationDuration());
-        prefs.putInt(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
-                geofence.getTransitionType());
-        // Commit the changes.
-        prefs.commit();
-    }
-
-    /**
-     * Remove a flattened geofence object from storage by removing all of its keys.
-     */
-    public void clearGeofence(String id) {
-        SharedPreferences.Editor prefs = mPrefs.edit();
-        prefs.remove(getGeofenceFieldKey(id, KEY_LATITUDE));
-        prefs.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));
-        prefs.remove(getGeofenceFieldKey(id, KEY_RADIUS));
-        prefs.remove(getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION));
-        prefs.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));
-        prefs.commit();
-    }
-
-    /**
-     * Given a Geofence object's ID and the name of a field (for example, KEY_LATITUDE), return
-     * the key name of the object's values in SharedPreferences.
-     * @param id The ID of a Geofence object.
-     * @param fieldName The field represented by the key.
-     * @return The full key name of a value in SharedPreferences.
-     */
-    private String getGeofenceFieldKey(String id, String fieldName) {
-        return KEY_PREFIX + "_" + id + "_" + fieldName;
-    }
-
-}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/Constants.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/Constants.java
new file mode 100644
index 0000000..6da9151
--- /dev/null
+++ b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/Constants.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import android.net.Uri;
+
+import com.google.android.gms.location.Geofence;
+
+/** Constants used in companion app. */
+public final class Constants {
+
+    private Constants() {
+    }
+
+    public static final String TAG = "ExampleGeofencingApp";
+
+    // Request code to attempt to resolve Google Play services connection failures.
+    public final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
+    // Timeout for making a connection to GoogleApiClient (in milliseconds).
+    public static final long CONNECTION_TIME_OUT_MS = 100;
+
+    // For the purposes of this demo, the geofences are hard-coded and should not expire.
+    // An app with dynamically-created geofences would want to include a reasonable expiration time.
+    public static final long GEOFENCE_EXPIRATION_TIME = Geofence.NEVER_EXPIRE;
+
+    // Geofence parameters for the Android building on Google's main campus in Mountain View.
+    public static final String ANDROID_BUILDING_ID = "1";
+    public static final double ANDROID_BUILDING_LATITUDE = 37.420092;
+    public static final double ANDROID_BUILDING_LONGITUDE = -122.083648;
+    public static final float ANDROID_BUILDING_RADIUS_METERS = 60.0f;
+
+    // Geofence parameters for the Yerba Buena Gardens near the Moscone Center in San Francisco.
+    public static final String YERBA_BUENA_ID = "2";
+    public static final double YERBA_BUENA_LATITUDE = 37.784886;
+    public static final double YERBA_BUENA_LONGITUDE = -122.402671;
+    public static final float YERBA_BUENA_RADIUS_METERS = 72.0f;
+
+
+    // The constants below are less interesting than those above.
+
+    // Path for the DataItem containing the last geofence id entered.
+    public static final String GEOFENCE_DATA_ITEM_PATH = "/geofenceid";
+    public static final Uri GEOFENCE_DATA_ITEM_URI =
+            new Uri.Builder().scheme("wear").path(GEOFENCE_DATA_ITEM_PATH).build();
+    public static final String KEY_GEOFENCE_ID = "geofence_id";
+
+    // Keys for flattened geofences stored in SharedPreferences.
+    public static final String KEY_LATITUDE = "com.example.wearable.geofencing.KEY_LATITUDE";
+    public static final String KEY_LONGITUDE = "com.example.wearable.geofencing.KEY_LONGITUDE";
+    public static final String KEY_RADIUS = "com.example.wearable.geofencing.KEY_RADIUS";
+    public static final String KEY_EXPIRATION_DURATION =
+            "com.example.wearable.geofencing.KEY_EXPIRATION_DURATION";
+    public static final String KEY_TRANSITION_TYPE =
+            "com.example.wearable.geofencing.KEY_TRANSITION_TYPE";
+    // The prefix for flattened geofence keys.
+    public static final String KEY_PREFIX = "com.example.wearable.geofencing.KEY";
+
+    // Invalid values, used to test geofence storage when retrieving geofences.
+    public static final long INVALID_LONG_VALUE = -999l;
+    public static final float INVALID_FLOAT_VALUE = -999.0f;
+    public static final int INVALID_INT_VALUE = -999;
+
+}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/GeofenceTransitionsIntentService.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/GeofenceTransitionsIntentService.java
new file mode 100644
index 0000000..8ae0cbc
--- /dev/null
+++ b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/GeofenceTransitionsIntentService.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import static com.example.android.wearable.geofencing.Constants.CONNECTION_TIME_OUT_MS;
+import static com.example.android.wearable.geofencing.Constants.GEOFENCE_DATA_ITEM_PATH;
+import static com.example.android.wearable.geofencing.Constants.GEOFENCE_DATA_ITEM_URI;
+import static com.example.android.wearable.geofencing.Constants.KEY_GEOFENCE_ID;
+import static com.example.android.wearable.geofencing.Constants.TAG;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingEvent;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens for geofence transition changes.
+ */
+public class GeofenceTransitionsIntentService extends IntentService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+    private GoogleApiClient mGoogleApiClient;
+
+    public GeofenceTransitionsIntentService() {
+        super(GeofenceTransitionsIntentService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    /**
+     * Handles incoming intents.
+     * @param intent The Intent sent by Location Services. This Intent is provided to Location
+     * Services (inside a PendingIntent) when addGeofences() is called.
+     */
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        GeofencingEvent geoFenceEvent = GeofencingEvent.fromIntent(intent);
+        if (geoFenceEvent.hasError()) {
+            int errorCode = geoFenceEvent.getErrorCode();
+            Log.e(TAG, "Location Services error: " + errorCode);
+        } else {
+
+            int transitionType = geoFenceEvent.getGeofenceTransition();
+            if (Geofence.GEOFENCE_TRANSITION_ENTER == transitionType) {
+                // Connect to the Google Api service in preparation for sending a DataItem.
+                mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+                // Get the geofence id triggered. Note that only one geofence can be triggered at a
+                // time in this example, but in some cases you might want to consider the full list
+                // of geofences triggered.
+                String triggeredGeoFenceId = geoFenceEvent.getTriggeringGeofences().get(0)
+                        .getRequestId();
+                // Create a DataItem with this geofence's id. The wearable can use this to create
+                // a notification.
+                final PutDataMapRequest putDataMapRequest =
+                        PutDataMapRequest.create(GEOFENCE_DATA_ITEM_PATH);
+                putDataMapRequest.getDataMap().putString(KEY_GEOFENCE_ID, triggeredGeoFenceId);
+                if (mGoogleApiClient.isConnected()) {
+                    Wearable.DataApi.putDataItem(
+                            mGoogleApiClient, putDataMapRequest.asPutDataRequest()).await();
+                } else {
+                    Log.e(TAG, "Failed to send data item: " + putDataMapRequest
+                            + " - Client disconnected from Google Play Services");
+                }
+                Toast.makeText(this, getString(R.string.entering_geofence),
+                        Toast.LENGTH_SHORT).show();
+                mGoogleApiClient.disconnect();
+            } else if (Geofence.GEOFENCE_TRANSITION_EXIT == transitionType) {
+                // Delete the data item when leaving a geofence region.
+                mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+                Wearable.DataApi.deleteDataItems(mGoogleApiClient, GEOFENCE_DATA_ITEM_URI).await();
+                Toast.makeText(this, getString(R.string.exiting_geofence),
+                        Toast.LENGTH_SHORT).show();
+                mGoogleApiClient.disconnect();
+            }
+        }
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+    }
+
+}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/MainActivity.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/MainActivity.java
new file mode 100644
index 0000000..350c9c5
--- /dev/null
+++ b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/MainActivity.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_ID;
+import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_LATITUDE;
+import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_LONGITUDE;
+import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_RADIUS_METERS;
+import static com.example.android.wearable.geofencing.Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST;
+import static com.example.android.wearable.geofencing.Constants.GEOFENCE_EXPIRATION_TIME;
+import static com.example.android.wearable.geofencing.Constants.TAG;
+import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_ID;
+import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_LATITUDE;
+import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_LONGITUDE;
+import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_RADIUS_METERS;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
+import com.google.android.gms.common.GooglePlayServicesUtil;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.LocationServices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends Activity implements ConnectionCallbacks,
+        OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
+
+    // Internal List of Geofence objects. In a real app, these might be provided by an API based on
+    // locations within the user's proximity.
+    List<Geofence> mGeofenceList;
+
+    // These will store hard-coded geofences in this sample app.
+    private SimpleGeofence mAndroidBuildingGeofence;
+    private SimpleGeofence mYerbaBuenaGeofence;
+
+    // Persistent storage for geofences.
+    private SimpleGeofenceStore mGeofenceStorage;
+
+    private LocationServices mLocationService;
+    // Stores the PendingIntent used to request geofence monitoring.
+    private PendingIntent mGeofenceRequestIntent;
+    private GoogleApiClient mApiClient;
+
+    // Defines the allowable request types (in this example, we only add geofences).
+    private enum REQUEST_TYPE {ADD}
+    private REQUEST_TYPE mRequestType;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // Rather than displayng this activity, simply display a toast indicating that the geofence
+        // service is being created. This should happen in less than a second.
+        if (!isGooglePlayServicesAvailable()) {
+            Log.e(TAG, "Google Play services unavailable.");
+            finish();
+            return;
+        }
+
+        mApiClient = new GoogleApiClient.Builder(this)
+                .addApi(LocationServices.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+
+        mApiClient.connect();
+
+        // Instantiate a new geofence storage area.
+        mGeofenceStorage = new SimpleGeofenceStore(this);
+        // Instantiate the current List of geofences.
+        mGeofenceList = new ArrayList<Geofence>();
+        createGeofences();
+    }
+
+    /**
+     * In this sample, the geofences are predetermined and are hard-coded here. A real app might
+     * dynamically create geofences based on the user's location.
+     */
+    public void createGeofences() {
+        // Create internal "flattened" objects containing the geofence data.
+        mAndroidBuildingGeofence = new SimpleGeofence(
+                ANDROID_BUILDING_ID,                // geofenceId.
+                ANDROID_BUILDING_LATITUDE,
+                ANDROID_BUILDING_LONGITUDE,
+                ANDROID_BUILDING_RADIUS_METERS,
+                GEOFENCE_EXPIRATION_TIME,
+                Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT
+        );
+        mYerbaBuenaGeofence = new SimpleGeofence(
+                YERBA_BUENA_ID,                // geofenceId.
+                YERBA_BUENA_LATITUDE,
+                YERBA_BUENA_LONGITUDE,
+                YERBA_BUENA_RADIUS_METERS,
+                GEOFENCE_EXPIRATION_TIME,
+                Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT
+        );
+
+        // Store these flat versions in SharedPreferences and add them to the geofence list.
+        mGeofenceStorage.setGeofence(ANDROID_BUILDING_ID, mAndroidBuildingGeofence);
+        mGeofenceStorage.setGeofence(YERBA_BUENA_ID, mYerbaBuenaGeofence);
+        mGeofenceList.add(mAndroidBuildingGeofence.toGeofence());
+        mGeofenceList.add(mYerbaBuenaGeofence.toGeofence());
+    }
+
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        // If the error has a resolution, start a Google Play services activity to resolve it.
+        if (connectionResult.hasResolution()) {
+            try {
+                connectionResult.startResolutionForResult(this,
+                        CONNECTION_FAILURE_RESOLUTION_REQUEST);
+            } catch (IntentSender.SendIntentException e) {
+                Log.e(TAG, "Exception while resolving connection error.", e);
+            }
+        } else {
+            int errorCode = connectionResult.getErrorCode();
+            Log.e(TAG, "Connection to Google Play services failed with error code " + errorCode);
+        }
+    }
+
+    @Override
+    public void onDisconnected() {
+    }
+
+    /**
+     * Once the connection is available, send a request to add the Geofences.
+     */
+    @Override
+    public void onConnected(Bundle connectionHint) {
+        // Get the PendingIntent for the geofence monitoring request.
+        // Send a request to add the current geofences.
+        mGeofenceRequestIntent = getGeofenceTransitionPendingIntent();
+        LocationServices.GeofencingApi.addGeofences(mApiClient, mGeofenceList,
+                mGeofenceRequestIntent);
+        Toast.makeText(this, getString(R.string.start_geofence_service), Toast.LENGTH_SHORT).show();
+        finish();
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+        if (null != mGeofenceRequestIntent) {
+            LocationServices.GeofencingApi.removeGeofences(mApiClient, mGeofenceRequestIntent);
+        }
+    }
+
+
+    /**
+     * Checks if Google Play services is available.
+     * @return true if it is.
+     */
+    private boolean isGooglePlayServicesAvailable() {
+        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
+        if (ConnectionResult.SUCCESS == resultCode) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Google Play services is available.");
+            }
+            return true;
+        } else {
+            Log.e(TAG, "Google Play services is unavailable.");
+            return false;
+        }
+    }
+
+    /**
+     * Create a PendingIntent that triggers GeofenceTransitionIntentService when a geofence
+     * transition occurs.
+     */
+    private PendingIntent getGeofenceTransitionPendingIntent() {
+        Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
+        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/SimpleGeofence.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/SimpleGeofence.java
new file mode 100644
index 0000000..50f02c3
--- /dev/null
+++ b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/SimpleGeofence.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import com.google.android.gms.location.Geofence;
+
+/**
+ * A single Geofence object, defined by its center and radius.
+ */
+public class SimpleGeofence {
+
+    // Instance variables
+    private final String mId;
+    private final double mLatitude;
+    private final double mLongitude;
+    private final float mRadius;
+    private long mExpirationDuration;
+    private int mTransitionType;
+
+    /**
+     * @param geofenceId The Geofence's request ID.
+     * @param latitude Latitude of the Geofence's center in degrees.
+     * @param longitude Longitude of the Geofence's center in degrees.
+     * @param radius Radius of the geofence circle in meters.
+     * @param expiration Geofence expiration duration.
+     * @param transition Type of Geofence transition.
+     */
+    public SimpleGeofence(String geofenceId, double latitude, double longitude, float radius,
+            long expiration, int transition) {
+        // Set the instance fields from the constructor.
+        this.mId = geofenceId;
+        this.mLatitude = latitude;
+        this.mLongitude = longitude;
+        this.mRadius = radius;
+        this.mExpirationDuration = expiration;
+        this.mTransitionType = transition;
+    }
+
+    // Instance field getters.
+    public String getId() {
+        return mId;
+    }
+    public double getLatitude() {
+        return mLatitude;
+    }
+    public double getLongitude() {
+        return mLongitude;
+    }
+    public float getRadius() {
+        return mRadius;
+    }
+    public long getExpirationDuration() {
+        return mExpirationDuration;
+    }
+    public int getTransitionType() {
+        return mTransitionType;
+    }
+
+    /**
+     * Creates a Location Services Geofence object from a SimpleGeofence.
+     * @return A Geofence object.
+     */
+    public Geofence toGeofence() {
+        // Build a new Geofence object.
+        return new Geofence.Builder()
+                .setRequestId(mId)
+                .setTransitionTypes(mTransitionType)
+                .setCircularRegion(mLatitude, mLongitude, mRadius)
+                .setExpirationDuration(mExpirationDuration)
+                .build();
+    }
+
+}
diff --git a/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/SimpleGeofenceStore.java b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/SimpleGeofenceStore.java
new file mode 100644
index 0000000..47584f5
--- /dev/null
+++ b/wearable/wear/Geofencing/Application/src/main/java/com/example/android/wearable/geofencing/SimpleGeofenceStore.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import static com.example.android.wearable.geofencing.Constants.INVALID_FLOAT_VALUE;
+import static com.example.android.wearable.geofencing.Constants.INVALID_INT_VALUE;
+import static com.example.android.wearable.geofencing.Constants.INVALID_LONG_VALUE;
+import static com.example.android.wearable.geofencing.Constants.KEY_EXPIRATION_DURATION;
+import static com.example.android.wearable.geofencing.Constants.KEY_LATITUDE;
+import static com.example.android.wearable.geofencing.Constants.KEY_LONGITUDE;
+import static com.example.android.wearable.geofencing.Constants.KEY_PREFIX;
+import static com.example.android.wearable.geofencing.Constants.KEY_RADIUS;
+import static com.example.android.wearable.geofencing.Constants.KEY_TRANSITION_TYPE;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+/**
+ * Storage for geofence values, implemented in SharedPreferences.
+ */
+public class SimpleGeofenceStore {
+
+    // The SharedPreferences object in which geofences are stored.
+    private final SharedPreferences mPrefs;
+    // The name of the SharedPreferences.
+    private static final String SHARED_PREFERENCES = "SharedPreferences";
+
+    /**
+     * Create the SharedPreferences storage with private access only.
+     */
+    public SimpleGeofenceStore(Context context) {
+        mPrefs = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * Returns a stored geofence by its id, or returns null if it's not found.
+     * @param id The ID of a stored geofence.
+     * @return A SimpleGeofence defined by its center and radius, or null if the ID is invalid.
+     */
+    public SimpleGeofence getGeofence(String id) {
+        // Get the latitude for the geofence identified by id, or INVALID_FLOAT_VALUE if it doesn't
+        // exist (similarly for the other values that follow).
+        double lat = mPrefs.getFloat(getGeofenceFieldKey(id, KEY_LATITUDE),
+                INVALID_FLOAT_VALUE);
+        double lng = mPrefs.getFloat(getGeofenceFieldKey(id, KEY_LONGITUDE),
+                INVALID_FLOAT_VALUE);
+        float radius = mPrefs.getFloat(getGeofenceFieldKey(id, KEY_RADIUS),
+                INVALID_FLOAT_VALUE);
+        long expirationDuration =
+                mPrefs.getLong(getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
+                        INVALID_LONG_VALUE);
+        int transitionType = mPrefs.getInt(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
+                INVALID_INT_VALUE);
+        // If none of the values is incorrect, return the object.
+        if (lat != INVALID_FLOAT_VALUE
+                && lng != INVALID_FLOAT_VALUE
+                && radius != INVALID_FLOAT_VALUE
+                && expirationDuration != INVALID_LONG_VALUE
+                && transitionType != INVALID_INT_VALUE) {
+            return new SimpleGeofence(id, lat, lng, radius, expirationDuration, transitionType);
+        }
+        // Otherwise, return null.
+        return null;
+    }
+
+    /**
+     * Save a geofence.
+     * @param geofence The SimpleGeofence with the values you want to save in SharedPreferences.
+     */
+    public void setGeofence(String id, SimpleGeofence geofence) {
+        // Get a SharedPreferences editor instance. Among other things, SharedPreferences
+        // ensures that updates are atomic and non-concurrent.
+        SharedPreferences.Editor prefs = mPrefs.edit();
+        // Write the Geofence values to SharedPreferences.
+        prefs.putFloat(getGeofenceFieldKey(id, KEY_LATITUDE), (float) geofence.getLatitude());
+        prefs.putFloat(getGeofenceFieldKey(id, KEY_LONGITUDE), (float) geofence.getLongitude());
+        prefs.putFloat(getGeofenceFieldKey(id, KEY_RADIUS), geofence.getRadius());
+        prefs.putLong(getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION),
+                geofence.getExpirationDuration());
+        prefs.putInt(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE),
+                geofence.getTransitionType());
+        // Commit the changes.
+        prefs.commit();
+    }
+
+    /**
+     * Remove a flattened geofence object from storage by removing all of its keys.
+     */
+    public void clearGeofence(String id) {
+        SharedPreferences.Editor prefs = mPrefs.edit();
+        prefs.remove(getGeofenceFieldKey(id, KEY_LATITUDE));
+        prefs.remove(getGeofenceFieldKey(id, KEY_LONGITUDE));
+        prefs.remove(getGeofenceFieldKey(id, KEY_RADIUS));
+        prefs.remove(getGeofenceFieldKey(id, KEY_EXPIRATION_DURATION));
+        prefs.remove(getGeofenceFieldKey(id, KEY_TRANSITION_TYPE));
+        prefs.commit();
+    }
+
+    /**
+     * Given a Geofence object's ID and the name of a field (for example, KEY_LATITUDE), return
+     * the key name of the object's values in SharedPreferences.
+     * @param id The ID of a Geofence object.
+     * @param fieldName The field represented by the key.
+     * @return The full key name of a value in SharedPreferences.
+     */
+    private String getGeofenceFieldKey(String id, String fieldName) {
+        return KEY_PREFIX + "_" + id + "_" + fieldName;
+    }
+
+}
diff --git a/wearable/wear/Geofencing/Application/src/main/res/values/strings.xml b/wearable/wear/Geofencing/Application/src/main/res/values/strings.xml
index ad9717b..610661e 100644
--- a/wearable/wear/Geofencing/Application/src/main/res/values/strings.xml
+++ b/wearable/wear/Geofencing/Application/src/main/res/values/strings.xml
@@ -16,4 +16,6 @@
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="start_geofence_service">Starting geofence transition service</string>
+    <string name="entering_geofence">Entering the GeoFence zone</string>
+    <string name="exiting_geofence">Exiting the GeoFence zone</string>
 </resources>
diff --git a/wearable/wear/Geofencing/Shared/.gitignore b/wearable/wear/Geofencing/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/Geofencing/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/Geofencing/Shared/src/main/AndroidManifest.xml b/wearable/wear/Geofencing/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 2b60180..0000000
--- a/wearable/wear/Geofencing/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.geofencing.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/Geofencing/Shared/src/main/res/values/strings.xml b/wearable/wear/Geofencing/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/Geofencing/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml
index 85c41cd..082f396 100644
--- a/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.geofencing" >
+        package="com.example.android.wearable.geofencing" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -32,7 +32,7 @@
                 android:value="@integer/google_play_services_version" />
 
         <service
-                android:name="com.example.android.geofencing.HomeListenerService" >
+                android:name="com.example.android.wearable.geofencing.HomeListenerService" >
             <intent-filter>
                 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
             </intent-filter>
diff --git a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/CheckInAndDeleteDataItemsService.java b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/CheckInAndDeleteDataItemsService.java
deleted file mode 100644
index 1cc9baf..0000000
--- a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/CheckInAndDeleteDataItemsService.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.Wearable;
-
-import android.app.IntentService;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.wearable.activity.ConfirmationActivity;
-import android.util.Log;
-
-import java.util.concurrent.TimeUnit;
-
-import static com.example.android.geofencing.Constants.ACTION_CHECK_IN;
-import static com.example.android.geofencing.Constants.ACTION_DELETE_DATA_ITEM;
-import static com.example.android.geofencing.Constants.CONNECTION_TIME_OUT_MS;
-import static com.example.android.geofencing.Constants.NOTIFICATION_ID;
-import static com.example.android.geofencing.Constants.TAG;
-
-/**
- * Handles "Check In" action on the location-based notification. Also deletes orphan DataItems
- * when a notification is dismissed from the wearable.
- */
-public class CheckInAndDeleteDataItemsService extends IntentService
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
-
-    private GoogleApiClient mGoogleApiClient;
-
-    public CheckInAndDeleteDataItemsService() {
-        super(CheckInAndDeleteDataItemsService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        if (ACTION_CHECK_IN.equals(intent.getAction())) {
-            // In a real app, code for checking in would go here. For this sample, we will simply
-            // display a success animation.
-            startConfirmationActivity(ConfirmationActivity.SUCCESS_ANIMATION,
-                    getString(R.string.check_in_success));
-            // Dismiss the check-in notification.
-            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID);
-        } else if (!ACTION_DELETE_DATA_ITEM.equals(intent.getAction())) {
-            // The only possible actions should be checking in or dismissing the notification
-            // (which causes an intent with ACTION_DELETE_DATA_ITEM).
-            Log.e(TAG, "Unrecognized action: " + intent.getAction());
-            return;
-        }
-        // Regardless of the action, delete the DataItem (we are only be handling intents
-        // if the notification is dismissed or if the user has chosen to check in, either of which
-        // would be completed at this point).
-        mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
-        Uri dataItemUri = intent.getData();
-        if (mGoogleApiClient.isConnected()) {
-            DataApi.DeleteDataItemsResult result = Wearable.DataApi
-                    .deleteDataItems(mGoogleApiClient, dataItemUri).await();
-            if (!result.getStatus().isSuccess()) {
-                Log.e(TAG, "CheckInAndDeleteDataItemsService.onHandleIntent: "
-                         + "Failed to delete dataItem: " + dataItemUri);
-            } else if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Successfully deleted data item: " + dataItemUri);
-            }
-        } else {
-            Log.e(TAG, "Failed to delete data item: " + dataItemUri
-                     + " - Client disconnected from Google Play Services");
-        }
-        mGoogleApiClient.disconnect();
-    }
-
-    /**
-     * Helper method to create confirmation animations on the wearable.
-     * @param animationType Defined by constants in ConfirmationActivity.
-     * @param message The message to display with the animation.
-     */
-    private void startConfirmationActivity(int animationType, String message) {
-        Intent confirmationActivity = new Intent(this, ConfirmationActivity.class)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION)
-                .putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, animationType)
-                .putExtra(ConfirmationActivity.EXTRA_MESSAGE, message);
-        startActivity(confirmationActivity);
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-    }
-
-}
diff --git a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/Constants.java b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/Constants.java
deleted file mode 100644
index 23ee99e..0000000
--- a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/Constants.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-/** Constants used in wearable app. */
-public final class Constants {
-
-    private Constants() {
-    }
-
-    public static final String TAG = "ExampleGeofencingApp";
-
-    // Timeout for making a connection to GoogleApiClient (in milliseconds).
-    public static final long CONNECTION_TIME_OUT_MS = 100;
-
-    public static final int NOTIFICATION_ID = 1;
-    public static final String ANDROID_BUILDING_ID = "1";
-    public static final String YERBA_BUENA_ID = "2";
-
-    public static final String ACTION_CHECK_IN = "check_in";
-    public static final String ACTION_DELETE_DATA_ITEM = "delete_data_item";
-    public static final String KEY_GEOFENCE_ID = "geofence_id";
-
-}
diff --git a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/HomeListenerService.java b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/HomeListenerService.java
deleted file mode 100644
index b0b1b13..0000000
--- a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/geofencing/HomeListenerService.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.geofencing;
-
-import static com.example.android.geofencing.Constants.ACTION_CHECK_IN;
-import static com.example.android.geofencing.Constants.ACTION_DELETE_DATA_ITEM;
-import static com.example.android.geofencing.Constants.ANDROID_BUILDING_ID;
-import static com.example.android.geofencing.Constants.KEY_GEOFENCE_ID;
-import static com.example.android.geofencing.Constants.NOTIFICATION_ID;
-import static com.example.android.geofencing.Constants.TAG;
-import static com.example.android.geofencing.Constants.YERBA_BUENA_ID;
-
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.style.RelativeSizeSpan;
-import android.util.Log;
-
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataItem;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.Wearable;
-import com.google.android.gms.wearable.WearableListenerService;
-
-/**
- * Listens to DataItem events on the wearable device.
- */
-public class HomeListenerService extends WearableListenerService {
-
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this.getApplicationContext())
-                .addApi(Wearable.API)
-                .build();
-        mGoogleApiClient.connect();
-    }
-
-    /**
-     * Listen for DataItems added/deleted from the geofence service running on the companion.
-     */
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName());
-        }
-        for (DataEvent event : dataEvents) {
-            if (event.getType() == DataEvent.TYPE_DELETED) {
-                cancelNotificationForDataItem(event.getDataItem());
-            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
-                // The user has entered a geofence - post a notification!
-                String geofenceId = DataMap.fromByteArray(event.getDataItem().getData())
-                        .getString(KEY_GEOFENCE_ID);
-                postNotificationForGeofenceId(geofenceId, event.getDataItem().getUri());
-            }
-        }
-        dataEvents.close();
-    }
-
-    /**
-     * Deletes the check-in notification when the DataItem is deleted.
-     * @param dataItem Used only for logging in this sample, but could be used to identify which
-     *                 notification to cancel (in this case, there is at most 1 notification).
-     */
-    private void cancelNotificationForDataItem(DataItem dataItem) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "onDataItemDeleted:DataItem=" + dataItem.getUri());
-        }
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID);
-    }
-
-    /**
-     * Posts a local notification for the given geofence id, with an option to check in.
-     * @param geofenceId The geofence id that the user has triggered.
-     * @param dataItemUri The Uri for the DataItem that triggered this notification. Used to delete
-     *                    this DataItem when the notification is dismissed.
-     */
-    private void postNotificationForGeofenceId(String geofenceId, Uri dataItemUri) {
-        // Use the geofenceId to determine the title and background of the check-in notification.
-        // A SpannableString is used for the notification title for resizing capabilities.
-        SpannableString checkInTitle;
-        Bitmap notificationBackground;
-        if (ANDROID_BUILDING_ID.equals(geofenceId)) {
-            checkInTitle = new SpannableString(getText(R.string.android_building_title));
-            notificationBackground =
-                    BitmapFactory.decodeResource(getResources(), R.drawable.android_building);
-        } else if (YERBA_BUENA_ID.equals(geofenceId)) {
-            checkInTitle = new SpannableString(getText(R.string.yerba_buena_title));
-            notificationBackground =
-                    BitmapFactory.decodeResource(getResources(), R.drawable.yerba_buena);
-        } else {
-            Log.e(TAG, "Unrecognized geofence id: " + geofenceId);
-            return;
-        }
-        // Resize the title to avoid truncation.
-        checkInTitle.setSpan(new RelativeSizeSpan(0.8f), 0, checkInTitle.length(),
-                Spannable.SPAN_POINT_MARK);
-
-        Intent checkInOperation =
-                new Intent(this, CheckInAndDeleteDataItemsService.class).setData(dataItemUri);
-        PendingIntent checkInIntent = PendingIntent.getService(this, 0,
-                checkInOperation.setAction(ACTION_CHECK_IN), PendingIntent.FLAG_CANCEL_CURRENT);
-        PendingIntent deleteDataItemIntent = PendingIntent.getService(this, 1,
-                checkInOperation.setAction(ACTION_DELETE_DATA_ITEM),
-                PendingIntent.FLAG_CANCEL_CURRENT);
-        // This action will be embedded into the notification.
-        Action checkInAction = new Action(R.drawable.ic_action_check_in,
-                getText(R.string.check_in_prompt), checkInIntent);
-
-        Notification notification = new Notification.Builder(this)
-                .setContentTitle(checkInTitle)
-                .setContentText(getText(R.string.check_in_prompt))
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setDeleteIntent(deleteDataItemIntent)
-                .extend(new Notification.WearableExtender()
-                        .setBackground(notificationBackground)
-                        .addAction(checkInAction)
-                        .setContentAction(0)
-                        .setHintHideIcon(true))
-                .setLocalOnly(true)
-                .build();
-
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .notify(NOTIFICATION_ID, notification);
-    }
-
-}
diff --git a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/CheckInAndDeleteDataItemsService.java b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/CheckInAndDeleteDataItemsService.java
new file mode 100644
index 0000000..7b8ba11
--- /dev/null
+++ b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/CheckInAndDeleteDataItemsService.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.Wearable;
+
+import android.app.IntentService;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.wearable.activity.ConfirmationActivity;
+import android.util.Log;
+
+import java.util.concurrent.TimeUnit;
+
+import static com.example.android.wearable.geofencing.Constants.ACTION_CHECK_IN;
+import static com.example.android.wearable.geofencing.Constants.ACTION_DELETE_DATA_ITEM;
+import static com.example.android.wearable.geofencing.Constants.CONNECTION_TIME_OUT_MS;
+import static com.example.android.wearable.geofencing.Constants.NOTIFICATION_ID;
+import static com.example.android.wearable.geofencing.Constants.TAG;
+
+/**
+ * Handles "Check In" action on the location-based notification. Also deletes orphan DataItems
+ * when a notification is dismissed from the wearable.
+ */
+public class CheckInAndDeleteDataItemsService extends IntentService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+    private GoogleApiClient mGoogleApiClient;
+
+    public CheckInAndDeleteDataItemsService() {
+        super(CheckInAndDeleteDataItemsService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (ACTION_CHECK_IN.equals(intent.getAction())) {
+            // In a real app, code for checking in would go here. For this sample, we will simply
+            // display a success animation.
+            startConfirmationActivity(ConfirmationActivity.SUCCESS_ANIMATION,
+                    getString(R.string.check_in_success));
+            // Dismiss the check-in notification.
+            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID);
+        } else if (!ACTION_DELETE_DATA_ITEM.equals(intent.getAction())) {
+            // The only possible actions should be checking in or dismissing the notification
+            // (which causes an intent with ACTION_DELETE_DATA_ITEM).
+            Log.e(TAG, "Unrecognized action: " + intent.getAction());
+            return;
+        }
+        // Regardless of the action, delete the DataItem (we are only be handling intents
+        // if the notification is dismissed or if the user has chosen to check in, either of which
+        // would be completed at this point).
+        mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+        Uri dataItemUri = intent.getData();
+        if (mGoogleApiClient.isConnected()) {
+            DataApi.DeleteDataItemsResult result = Wearable.DataApi
+                    .deleteDataItems(mGoogleApiClient, dataItemUri).await();
+            if (!result.getStatus().isSuccess()) {
+                Log.e(TAG, "CheckInAndDeleteDataItemsService.onHandleIntent: "
+                         + "Failed to delete dataItem: " + dataItemUri);
+            } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Successfully deleted data item: " + dataItemUri);
+            }
+        } else {
+            Log.e(TAG, "Failed to delete data item: " + dataItemUri
+                     + " - Client disconnected from Google Play Services");
+        }
+        mGoogleApiClient.disconnect();
+    }
+
+    /**
+     * Helper method to create confirmation animations on the wearable.
+     * @param animationType Defined by constants in ConfirmationActivity.
+     * @param message The message to display with the animation.
+     */
+    private void startConfirmationActivity(int animationType, String message) {
+        Intent confirmationActivity = new Intent(this, ConfirmationActivity.class)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION)
+                .putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, animationType)
+                .putExtra(ConfirmationActivity.EXTRA_MESSAGE, message);
+        startActivity(confirmationActivity);
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+    }
+
+}
diff --git a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/Constants.java b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/Constants.java
new file mode 100644
index 0000000..ef6e8c6
--- /dev/null
+++ b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/Constants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+/** Constants used in wearable app. */
+public final class Constants {
+
+    private Constants() {
+    }
+
+    public static final String TAG = "ExampleGeofencingApp";
+
+    // Timeout for making a connection to GoogleApiClient (in milliseconds).
+    public static final long CONNECTION_TIME_OUT_MS = 100;
+
+    public static final int NOTIFICATION_ID = 1;
+    public static final String ANDROID_BUILDING_ID = "1";
+    public static final String YERBA_BUENA_ID = "2";
+
+    public static final String ACTION_CHECK_IN = "check_in";
+    public static final String ACTION_DELETE_DATA_ITEM = "delete_data_item";
+    public static final String KEY_GEOFENCE_ID = "geofence_id";
+
+}
diff --git a/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/HomeListenerService.java b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/HomeListenerService.java
new file mode 100644
index 0000000..415fc46
--- /dev/null
+++ b/wearable/wear/Geofencing/Wearable/src/main/java/com/example/android/wearable/geofencing/HomeListenerService.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.geofencing;
+
+import static com.example.android.wearable.geofencing.Constants.ACTION_CHECK_IN;
+import static com.example.android.wearable.geofencing.Constants.ACTION_DELETE_DATA_ITEM;
+import static com.example.android.wearable.geofencing.Constants.ANDROID_BUILDING_ID;
+import static com.example.android.wearable.geofencing.Constants.KEY_GEOFENCE_ID;
+import static com.example.android.wearable.geofencing.Constants.NOTIFICATION_ID;
+import static com.example.android.wearable.geofencing.Constants.TAG;
+import static com.example.android.wearable.geofencing.Constants.YERBA_BUENA_ID;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.RelativeSizeSpan;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+/**
+ * Listens to DataItem events on the wearable device.
+ */
+public class HomeListenerService extends WearableListenerService {
+
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this.getApplicationContext())
+                .addApi(Wearable.API)
+                .build();
+        mGoogleApiClient.connect();
+    }
+
+    /**
+     * Listen for DataItems added/deleted from the geofence service running on the companion.
+     */
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onDataChanged: " + dataEvents + " for " + getPackageName());
+        }
+        for (DataEvent event : dataEvents) {
+            if (event.getType() == DataEvent.TYPE_DELETED) {
+                cancelNotificationForDataItem(event.getDataItem());
+            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
+                // The user has entered a geofence - post a notification!
+                String geofenceId = DataMap.fromByteArray(event.getDataItem().getData())
+                        .getString(KEY_GEOFENCE_ID);
+                postNotificationForGeofenceId(geofenceId, event.getDataItem().getUri());
+            }
+        }
+        dataEvents.close();
+    }
+
+    /**
+     * Deletes the check-in notification when the DataItem is deleted.
+     * @param dataItem Used only for logging in this sample, but could be used to identify which
+     *                 notification to cancel (in this case, there is at most 1 notification).
+     */
+    private void cancelNotificationForDataItem(DataItem dataItem) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "onDataItemDeleted:DataItem=" + dataItem.getUri());
+        }
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(NOTIFICATION_ID);
+    }
+
+    /**
+     * Posts a local notification for the given geofence id, with an option to check in.
+     * @param geofenceId The geofence id that the user has triggered.
+     * @param dataItemUri The Uri for the DataItem that triggered this notification. Used to delete
+     *                    this DataItem when the notification is dismissed.
+     */
+    private void postNotificationForGeofenceId(String geofenceId, Uri dataItemUri) {
+        // Use the geofenceId to determine the title and background of the check-in notification.
+        // A SpannableString is used for the notification title for resizing capabilities.
+        SpannableString checkInTitle;
+        Bitmap notificationBackground;
+        if (ANDROID_BUILDING_ID.equals(geofenceId)) {
+            checkInTitle = new SpannableString(getText(R.string.android_building_title));
+            notificationBackground =
+                    BitmapFactory.decodeResource(getResources(), R.drawable.android_building);
+        } else if (YERBA_BUENA_ID.equals(geofenceId)) {
+            checkInTitle = new SpannableString(getText(R.string.yerba_buena_title));
+            notificationBackground =
+                    BitmapFactory.decodeResource(getResources(), R.drawable.yerba_buena);
+        } else {
+            Log.e(TAG, "Unrecognized geofence id: " + geofenceId);
+            return;
+        }
+        // Resize the title to avoid truncation.
+        checkInTitle.setSpan(new RelativeSizeSpan(0.8f), 0, checkInTitle.length(),
+                Spannable.SPAN_POINT_MARK);
+
+        Intent checkInOperation =
+                new Intent(this, CheckInAndDeleteDataItemsService.class).setData(dataItemUri);
+        PendingIntent checkInIntent = PendingIntent.getService(this, 0,
+                checkInOperation.setAction(ACTION_CHECK_IN), PendingIntent.FLAG_CANCEL_CURRENT);
+        PendingIntent deleteDataItemIntent = PendingIntent.getService(this, 1,
+                checkInOperation.setAction(ACTION_DELETE_DATA_ITEM),
+                PendingIntent.FLAG_CANCEL_CURRENT);
+        // This action will be embedded into the notification.
+        Action checkInAction = new Action(R.drawable.ic_action_check_in,
+                getText(R.string.check_in_prompt), checkInIntent);
+
+        Notification notification = new Notification.Builder(this)
+                .setContentTitle(checkInTitle)
+                .setContentText(getText(R.string.check_in_prompt))
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setDeleteIntent(deleteDataItemIntent)
+                .extend(new Notification.WearableExtender()
+                        .setBackground(notificationBackground)
+                        .addAction(checkInAction)
+                        .setContentAction(0)
+                        .setHintHideIcon(true))
+                .setLocalOnly(true)
+                .build();
+
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .notify(NOTIFICATION_ID, notification);
+    }
+
+}
diff --git a/wearable/wear/Geofencing/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Geofencing/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/Geofencing/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/Geofencing/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/Geofencing/screenshots/android_building_check_in.png b/wearable/wear/Geofencing/screenshots/android_building_check_in.png
new file mode 100644
index 0000000..5101e77
--- /dev/null
+++ b/wearable/wear/Geofencing/screenshots/android_building_check_in.png
Binary files differ
diff --git a/wearable/wear/Geofencing/screenshots/icon-web.png b/wearable/wear/Geofencing/screenshots/icon-web.png
new file mode 100644
index 0000000..81c1008
--- /dev/null
+++ b/wearable/wear/Geofencing/screenshots/icon-web.png
Binary files differ
diff --git a/wearable/wear/Geofencing/settings.gradle b/wearable/wear/Geofencing/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/Geofencing/settings.gradle
+++ b/wearable/wear/Geofencing/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/Geofencing/template-params.xml b/wearable/wear/Geofencing/template-params.xml
index b82b793..5530b02 100644
--- a/wearable/wear/Geofencing/template-params.xml
+++ b/wearable/wear/Geofencing/template-params.xml
@@ -20,12 +20,14 @@
 <sample>
     <name>Geofencing</name>
     <group>Wearable</group>
-    <package>com.example.android.geofencing</package>
+    <package>com.example.android.wearable.geofencing</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
+
+    <dependency>com.google.android.gms:play-services-location:6.5.+</dependency>
 
 
     <strings>
@@ -41,7 +43,54 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable, Sensors</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>ADVANCED</level>
+        <icon>screenshots/icon-web.png</icon>
+        <screenshots>
+            <img>screenshots/android_building_check_in.png</img>
+        </screenshots>
+        <api_refs>
+            <android>com.google.android.gms.location.Geofence</android>
+        </api_refs>
+        <description>
+<![CDATA[
+When the user enters the vicinity of the Android building (B44) or the Yerba Buena
+Gardens near the Moscone center in San Francisco, a notification silently appears on their
+wearable with an option to check in. This notification automatically disappears when they leave
+the area, and reappears the next time they are at one of these locations.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+Geofencing combines awareness of the user's current location with awareness of
+nearby features, defined as the user's proximity to locations that may be of
+interest. To mark a location of interest, you specify its latitude and longitude.
+To adjust the proximity for the location, you add a radius. The latitude,
+longitude, and radius define a geofence. You can have multiple active
+geofences at one time.
+
+To use geofencing, start by defining the geofences you want to monitor.
+Although you usually store geofence data in a local database or download
+it from the network, you need to send a geofence to Location Services as
+an instance of [Geofence][2], which you create with `Geofence.Builder`. Each
+Geofence object contains the following information:
+
+1. Latitude, longitude, and radius
+2. Expiration time
+3. Transition type
+4. Geofence ID
+
+Read more about geofences in [Creating and Monitoring Geofences][1].
+
+[1]:http://developer.android.com/training/location/geofencing.html
+[2]:http://developer.android.com/reference/com/google/android/gms/location/Geofence.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/GridViewPager/Application/src/main/AndroidManifest.xml b/wearable/wear/GridViewPager/Application/src/main/AndroidManifest.xml
index eaa91d7..b6092c5 100644
--- a/wearable/wear/GridViewPager/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/GridViewPager/Application/src/main/AndroidManifest.xml
@@ -16,12 +16,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.gridviewpager">
+    package="com.example.android.wearable.gridviewpager">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
         android:label="@string/app_name">
     </application>
 
diff --git a/wearable/wear/GridViewPager/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/GridViewPager/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/GridViewPager/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/GridViewPager/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/GridViewPager/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/GridViewPager/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/GridViewPager/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/GridViewPager/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/GridViewPager/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/GridViewPager/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/GridViewPager/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/GridViewPager/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/GridViewPager/Shared/.gitignore b/wearable/wear/GridViewPager/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/GridViewPager/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/GridViewPager/Shared/src/main/AndroidManifest.xml b/wearable/wear/GridViewPager/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 1be08aa..0000000
--- a/wearable/wear/GridViewPager/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.gridviewpager.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/GridViewPager/Shared/src/main/res/values/strings.xml b/wearable/wear/GridViewPager/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/GridViewPager/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml b/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml
index baadee2..5c362dc 100644
--- a/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.gridviewpager" >
+    package="com.example.android.wearable.gridviewpager" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -28,7 +28,7 @@
         android:label="@string/app_name"
         android:theme="@android:style/Theme.DeviceDefault.Light" >
         <activity
-            android:name="com.example.android.gridviewpager.MainActivity"
+            android:name="com.example.android.wearable.gridviewpager.MainActivity"
             android:label="@string/app_name" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/gridviewpager/MainActivity.java b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/gridviewpager/MainActivity.java
deleted file mode 100644
index 0ca304e..0000000
--- a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/gridviewpager/MainActivity.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.gridviewpager;
-
-import android.app.Activity;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.support.wearable.view.GridViewPager;
-import android.view.View;
-import android.view.View.OnApplyWindowInsetsListener;
-import android.view.WindowInsets;
-
-public class MainActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-        final Resources res = getResources();
-        final GridViewPager pager = (GridViewPager) findViewById(R.id.pager);
-        pager.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
-            @Override
-            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
-                // Adjust page margins:
-                //   A little extra horizontal spacing between pages looks a bit
-                //   less crowded on a round display.
-                final boolean round = insets.isRound();
-                int rowMargin = res.getDimensionPixelOffset(R.dimen.page_row_margin);
-                int colMargin = res.getDimensionPixelOffset(round ?
-                        R.dimen.page_column_margin_round : R.dimen.page_column_margin);
-                pager.setPageMargins(rowMargin, colMargin);
-
-                // GridViewPager relies on insets to properly handle
-                // layout for round displays. They must be explicitly
-                // applied since this listener has taken them over.
-                pager.onApplyWindowInsets(insets);
-                return insets;
-            }
-        });
-        pager.setAdapter(new SampleGridPagerAdapter(this, getFragmentManager()));
-    }
-}
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/gridviewpager/SampleGridPagerAdapter.java b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/gridviewpager/SampleGridPagerAdapter.java
deleted file mode 100644
index c9dde07..0000000
--- a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/gridviewpager/SampleGridPagerAdapter.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.gridviewpager;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.support.wearable.view.CardFragment;
-import android.support.wearable.view.FragmentGridPagerAdapter;
-import android.support.wearable.view.ImageReference;
-import android.view.Gravity;
-
-/**
- * Constructs fragments as requested by the GridViewPager. For each row a
- * different background is provided.
- */
-public class SampleGridPagerAdapter extends FragmentGridPagerAdapter {
-
-    private final Context mContext;
-
-    public SampleGridPagerAdapter(Context ctx, FragmentManager fm) {
-        super(fm);
-        mContext = ctx;
-    }
-
-    static final int[] BG_IMAGES = new int[] {
-            R.drawable.debug_background_1,
-            R.drawable.debug_background_2,
-            R.drawable.debug_background_3,
-            R.drawable.debug_background_4,
-            R.drawable.debug_background_5
-    };
-
-    /** A simple container for static data in each page */
-    private static class Page {
-        int titleRes;
-        int textRes;
-        int iconRes;
-        int cardGravity = Gravity.BOTTOM;
-        boolean expansionEnabled = true;
-        float expansionFactor = 1.0f;
-        int expansionDirection = CardFragment.EXPAND_DOWN;
-
-        public Page(int titleRes, int textRes, boolean expansion) {
-            this(titleRes, textRes, 0);
-            this.expansionEnabled = expansion;
-        }
-
-        public Page(int titleRes, int textRes, boolean expansion, float expansionFactor) {
-            this(titleRes, textRes, 0);
-            this.expansionEnabled = expansion;
-            this.expansionFactor = expansionFactor;
-        }
-
-        public Page(int titleRes, int textRes, int iconRes) {
-            this.titleRes = titleRes;
-            this.textRes = textRes;
-            this.iconRes = iconRes;
-        }
-
-        public Page(int titleRes, int textRes, int iconRes, int gravity) {
-            this.titleRes = titleRes;
-            this.textRes = textRes;
-            this.iconRes = iconRes;
-            this.cardGravity = gravity;
-        }
-    }
-
-    private final Page[][] PAGES = {
-            {
-                    new Page(R.string.welcome_title, R.string.welcome_text, R.drawable.bugdroid,
-                            Gravity.CENTER_VERTICAL),
-            },
-            {
-                    new Page(R.string.about_title, R.string.about_text, false),
-            },
-            {
-                    new Page(R.string.cards_title, R.string.cards_text, true, 2),
-                    new Page(R.string.expansion_title, R.string.expansion_text, true, 10),
-            },
-            {
-                    new Page(R.string.backgrounds_title, R.string.backgrounds_text, true, 2),
-                    new Page(R.string.columns_title, R.string.columns_text, true, 2)
-            },
-            {
-                    new Page(R.string.dismiss_title, R.string.dismiss_text, R.drawable.bugdroid,
-                            Gravity.CENTER_VERTICAL),
-            },
-
-    };
-
-    @Override
-    public Fragment getFragment(int row, int col) {
-        Page page = PAGES[row][col];
-        String title = page.titleRes != 0 ? mContext.getString(page.titleRes) : null;
-        String text = page.textRes != 0 ? mContext.getString(page.textRes) : null;
-        CardFragment fragment = CardFragment.create(title, text, page.iconRes);
-        // Advanced settings
-        fragment.setCardGravity(page.cardGravity);
-        fragment.setExpansionEnabled(page.expansionEnabled);
-        fragment.setExpansionDirection(page.expansionDirection);
-        fragment.setExpansionFactor(page.expansionFactor);
-        return fragment;
-    }
-
-    @Override
-    public ImageReference getBackground(int row, int column) {
-        return ImageReference.forDrawable(BG_IMAGES[row % BG_IMAGES.length]);
-    }
-
-    @Override
-    public int getRowCount() {
-        return PAGES.length;
-    }
-
-    @Override
-    public int getColumnCount(int rowNum) {
-        return PAGES[rowNum].length;
-    }
-}
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/CustomFragment.java b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/CustomFragment.java
new file mode 100644
index 0000000..aff3665
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/CustomFragment.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.gridviewpager;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class CustomFragment extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.custom_fragment, container, false);
+    }
+}
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/MainActivity.java b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/MainActivity.java
new file mode 100644
index 0000000..6abb58b
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/MainActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.gridviewpager;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.wearable.view.DotsPageIndicator;
+import android.support.wearable.view.GridViewPager;
+import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.WindowInsets;
+
+public class MainActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        final Resources res = getResources();
+        final GridViewPager pager = (GridViewPager) findViewById(R.id.pager);
+        pager.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
+            @Override
+            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+                // Adjust page margins:
+                //   A little extra horizontal spacing between pages looks a bit
+                //   less crowded on a round display.
+                final boolean round = insets.isRound();
+                int rowMargin = res.getDimensionPixelOffset(R.dimen.page_row_margin);
+                int colMargin = res.getDimensionPixelOffset(round ?
+                        R.dimen.page_column_margin_round : R.dimen.page_column_margin);
+                pager.setPageMargins(rowMargin, colMargin);
+
+                // GridViewPager relies on insets to properly handle
+                // layout for round displays. They must be explicitly
+                // applied since this listener has taken them over.
+                pager.onApplyWindowInsets(insets);
+                return insets;
+            }
+        });
+        pager.setAdapter(new SampleGridPagerAdapter(this, getFragmentManager()));
+        DotsPageIndicator dotsPageIndicator = (DotsPageIndicator) findViewById(R.id.page_indicator);
+        dotsPageIndicator.setPager(pager);
+    }
+}
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/SampleGridPagerAdapter.java b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/SampleGridPagerAdapter.java
new file mode 100644
index 0000000..b56cc6a
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/java/com/example/android/wearable/gridviewpager/SampleGridPagerAdapter.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.gridviewpager;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
+import android.os.AsyncTask;
+import android.support.v4.util.LruCache;
+import android.support.wearable.view.CardFragment;
+import android.support.wearable.view.FragmentGridPagerAdapter;
+import android.support.wearable.view.GridPagerAdapter;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Constructs fragments as requested by the GridViewPager. For each row a different background is
+ * provided.
+ * <p>
+ * Always avoid loading resources from the main thread. In this sample, the background images are
+ * loaded from an background task and then updated using {@link #notifyRowBackgroundChanged(int)}
+ * and {@link #notifyPageBackgroundChanged(int, int)}.
+ */
+public class SampleGridPagerAdapter extends FragmentGridPagerAdapter {
+    private static final int TRANSITION_DURATION_MILLIS = 100;
+
+    private final Context mContext;
+    private List<Row> mRows;
+    private ColorDrawable mDefaultBg;
+
+    private ColorDrawable mClearBg;
+
+    public SampleGridPagerAdapter(Context ctx, FragmentManager fm) {
+        super(fm);
+        mContext = ctx;
+
+        mRows = new ArrayList<SampleGridPagerAdapter.Row>();
+
+        mRows.add(new Row(cardFragment(R.string.welcome_title, R.string.welcome_text)));
+        mRows.add(new Row(cardFragment(R.string.about_title, R.string.about_text)));
+        mRows.add(new Row(
+                cardFragment(R.string.cards_title, R.string.cards_text),
+                cardFragment(R.string.expansion_title, R.string.expansion_text)));
+        mRows.add(new Row(
+                cardFragment(R.string.backgrounds_title, R.string.backgrounds_text),
+                cardFragment(R.string.columns_title, R.string.columns_text)));
+        mRows.add(new Row(new CustomFragment()));
+        mRows.add(new Row(cardFragment(R.string.dismiss_title, R.string.dismiss_text)));
+        mDefaultBg = new ColorDrawable(R.color.dark_grey);
+        mClearBg = new ColorDrawable(android.R.color.transparent);
+    }
+
+    LruCache<Integer, Drawable> mRowBackgrounds = new LruCache<Integer, Drawable>(3) {
+        @Override
+        protected Drawable create(final Integer row) {
+            int resid = BG_IMAGES[row % BG_IMAGES.length];
+            new DrawableLoadingTask(mContext) {
+                @Override
+                protected void onPostExecute(Drawable result) {
+                    TransitionDrawable background = new TransitionDrawable(new Drawable[] {
+                            mDefaultBg,
+                            result
+                    });
+                    mRowBackgrounds.put(row, background);
+                    notifyRowBackgroundChanged(row);
+                    background.startTransition(TRANSITION_DURATION_MILLIS);
+                }
+            }.execute(resid);
+            return mDefaultBg;
+        }
+    };
+
+    LruCache<Point, Drawable> mPageBackgrounds = new LruCache<Point, Drawable>(3) {
+        @Override
+        protected Drawable create(final Point page) {
+            // place bugdroid as the background at row 2, column 1
+            if (page.y == 2 && page.x == 1) {
+                int resid = R.drawable.bugdroid_large;
+                new DrawableLoadingTask(mContext) {
+                    @Override
+                    protected void onPostExecute(Drawable result) {
+                        TransitionDrawable background = new TransitionDrawable(new Drawable[] {
+                                mClearBg,
+                                result
+                        });
+                        mPageBackgrounds.put(page, background);
+                        notifyPageBackgroundChanged(page.y, page.x);
+                        background.startTransition(TRANSITION_DURATION_MILLIS);
+                    }
+                }.execute(resid);
+            }
+            return GridPagerAdapter.BACKGROUND_NONE;
+        }
+    };
+
+    private Fragment cardFragment(int titleRes, int textRes) {
+        Resources res = mContext.getResources();
+        CardFragment fragment =
+                CardFragment.create(res.getText(titleRes), res.getText(textRes));
+        // Add some extra bottom margin to leave room for the page indicator
+        fragment.setCardMarginBottom(
+                res.getDimensionPixelSize(R.dimen.card_margin_bottom));
+        return fragment;
+    }
+
+    static final int[] BG_IMAGES = new int[] {
+            R.drawable.debug_background_1,
+            R.drawable.debug_background_2,
+            R.drawable.debug_background_3,
+            R.drawable.debug_background_4,
+            R.drawable.debug_background_5
+    };
+
+    /** A convenient container for a row of fragments. */
+    private class Row {
+        final List<Fragment> columns = new ArrayList<Fragment>();
+
+        public Row(Fragment... fragments) {
+            for (Fragment f : fragments) {
+                add(f);
+            }
+        }
+
+        public void add(Fragment f) {
+            columns.add(f);
+        }
+
+        Fragment getColumn(int i) {
+            return columns.get(i);
+        }
+
+        public int getColumnCount() {
+            return columns.size();
+        }
+    }
+
+    @Override
+    public Fragment getFragment(int row, int col) {
+        Row adapterRow = mRows.get(row);
+        return adapterRow.getColumn(col);
+    }
+
+    @Override
+    public Drawable getBackgroundForRow(final int row) {
+        return mRowBackgrounds.get(row);
+    }
+
+    @Override
+    public Drawable getBackgroundForPage(final int row, final int column) {
+        return mPageBackgrounds.get(new Point(column, row));
+    }
+
+    @Override
+    public int getRowCount() {
+        return mRows.size();
+    }
+
+    @Override
+    public int getColumnCount(int rowNum) {
+        return mRows.get(rowNum).getColumnCount();
+    }
+
+    class DrawableLoadingTask extends AsyncTask<Integer, Void, Drawable> {
+        private static final String TAG = "Loader";
+        private Context context;
+
+        DrawableLoadingTask(Context context) {
+            this.context = context;
+        }
+
+        @Override
+        protected Drawable doInBackground(Integer... params) {
+            Log.d(TAG, "Loading asset 0x" + Integer.toHexString(params[0]));
+            return context.getResources().getDrawable(params[0]);
+        }
+    }
+}
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/drawable-nodpi/bugdroid_large.png b/wearable/wear/GridViewPager/Wearable/src/main/res/drawable-nodpi/bugdroid_large.png
new file mode 100644
index 0000000..7b393b2
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/drawable-nodpi/bugdroid_large.png
Binary files differ
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/drawable/gradient.xml b/wearable/wear/GridViewPager/Wearable/src/main/res/drawable/gradient.xml
new file mode 100644
index 0000000..7077043
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/drawable/gradient.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+
+    <gradient
+        android:angle="45"
+        android:endColor="#87CEEB"
+        android:centerColor="#768087"
+        android:startColor="#000"
+        android:type="linear" />
+
+</shape>
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/drawable/shape.xml b/wearable/wear/GridViewPager/Wearable/src/main/res/drawable/shape.xml
new file mode 100644
index 0000000..a6306c7
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/drawable/shape.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+
+    <stroke
+        android:dashGap="4dp"
+        android:dashWidth="10dp"
+        android:width="6dp"
+        android:color="@color/black" />
+
+    <solid android:color="@color/white" />
+
+    <padding
+        android:bottom="20dp"
+        android:left="20dp"
+        android:right="20dp"
+        android:top="20dp" />
+
+</shape>
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/layout/activity_main.xml b/wearable/wear/GridViewPager/Wearable/src/main/res/layout/activity_main.xml
index abc3b30..8c077ab 100644
--- a/wearable/wear/GridViewPager/Wearable/src/main/res/layout/activity_main.xml
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/layout/activity_main.xml
@@ -13,10 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<android.support.wearable.view.GridViewPager
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/pager"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:keepScreenOn="true" />
+    android:layout_height="match_parent" >
+
+    <android.support.wearable.view.GridViewPager
+        android:id="@+id/pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:keepScreenOn="true" />
+
+    <android.support.wearable.view.DotsPageIndicator
+        android:id="@+id/page_indicator"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal|bottom">
+    </android.support.wearable.view.DotsPageIndicator>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/layout/custom_fragment.xml b/wearable/wear/GridViewPager/Wearable/src/main/res/layout/custom_fragment.xml
new file mode 100644
index 0000000..13b02f2
--- /dev/null
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/layout/custom_fragment.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/gradient" >
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_margin="8dp"
+        android:background="@drawable/shape"
+        android:text="@string/custom_fragment_text" 
+        android:textColor="@color/primary_text_light"/>
+
+</FrameLayout>
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/values/dimens.xml b/wearable/wear/GridViewPager/Wearable/src/main/res/values/dimens.xml
index 47ab217..dd6ad2d 100644
--- a/wearable/wear/GridViewPager/Wearable/src/main/res/values/dimens.xml
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/values/dimens.xml
@@ -18,4 +18,5 @@
     <dimen name="page_row_margin">100dp</dimen>
     <dimen name="page_column_margin">10dp</dimen>
     <dimen name="page_column_margin_round">50dp</dimen>
+    <dimen name="card_margin_bottom">3dp</dimen>
 </resources>
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/res/values/strings.xml b/wearable/wear/GridViewPager/Wearable/src/main/res/values/strings.xml
index 404119b..ab4e663 100644
--- a/wearable/wear/GridViewPager/Wearable/src/main/res/values/strings.xml
+++ b/wearable/wear/GridViewPager/Wearable/src/main/res/values/strings.xml
@@ -18,39 +18,25 @@
 
     <string name="app_name">GridViewPager Sample</string>
     <string name="welcome_title">GridViewPager</string>
-    <string name="welcome_text">Welcome!</string>
+    <string name="welcome_text">Sample App</string>
 
     <string name="about_title">About</string>
-    <string name="about_text">Content is organized into multiple pages. Swipe
-        between cards to view other content.</string>
+    <string name="about_text">Content is organized into multiple pages. Swipe between cards to view other content.</string>
 
     <string name="cards_title">Cards</string>
-    <string name="cards_text">Each page is created using a CardFragment. A
-        layout is placed inside the card and the rest is handled
-        automatically.</string>
+    <string name="cards_text">Each page is created using a CardFragment. A layout is placed inside the card and the rest is handled automatically.</string>
 
     <string name="expansion_title">Expansion</string>
-    <string name="expansion_text">By default, each card will grow taller to
-        accommodate extra content. Tall cards can be scrolled through
-        like any scrollable view. The maximum height is controlled by
-        setExpansionFactor() with a default of 10 pages. After that
-        you\'ll notice a faded edge to indicate there is more. Expansion
-        can be disabled completely using setExpansionEnabled(false).
-        With this, the content will be clipped if it\'s taller than one
-        page.</string>
+    <string name="expansion_text">By default, each card will grow taller to accommodate extra content. Tall cards can be scrolled through like any scrollable view. The maximum height is controlled by setExpansionFactor() with a default of 10 pages. After that you\'ll notice a faded edge to indicate there is more. Expansion can be disabled completely using setExpansionEnabled(false). With this, the content will be clipped if it\'s taller than one page.</string>
 
     <string name="backgrounds_title">Backgrounds</string>
-    <string name="backgrounds_text">Backgrounds are supplied by the adapter\'s
-        getBackground method. Parallax and crossfade effects are applied
-        automatically.</string>
+    <string name="backgrounds_text">Backgrounds are supplied by the adapter\'s getBackground method. Parallax and crossfade effects are applied automatically.</string>
 
     <string name="columns_title">Columns</string>
-    <string name="columns_text">When moving between rows, the pager always
-        returns to column 0. This is adjustable in the adapter. See
-        method getCurrentColumnForRow().</string>
+    <string name="columns_text">When moving between rows, the pager always returns to column 0. This is adjustable in the adapter. See method getCurrentColumnForRow().</string>
 
     <string name="dismiss_title">Dismiss</string>
-    <string name="dismiss_text">To exit the application, swipe from left to
-        right.</string>
+    <string name="dismiss_text">To exit the application, swipe from left to right.</string>
+    <string name="custom_fragment_text">"GridViewPager will accept any fragment. This is an example of a custom fragment with it's own background."</string>
 
 </resources>
diff --git a/wearable/wear/GridViewPager/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/GridViewPager/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/GridViewPager/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/GridViewPager/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/GridViewPager/screenshots/grid_view_pager.gif b/wearable/wear/GridViewPager/screenshots/grid_view_pager.gif
new file mode 100644
index 0000000..f70dafc
--- /dev/null
+++ b/wearable/wear/GridViewPager/screenshots/grid_view_pager.gif
Binary files differ
diff --git a/wearable/wear/GridViewPager/settings.gradle b/wearable/wear/GridViewPager/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/GridViewPager/settings.gradle
+++ b/wearable/wear/GridViewPager/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/GridViewPager/template-params.xml b/wearable/wear/GridViewPager/template-params.xml
index efa3bad..0ac1ad3 100644
--- a/wearable/wear/GridViewPager/template-params.xml
+++ b/wearable/wear/GridViewPager/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>GridViewPager</name>
     <group>Wearable</group>
-    <package>com.example.android.gridviewpager</package>
+    <package>com.example.android.wearable.gridviewpager</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -38,7 +38,5 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
 </sample>
diff --git a/wearable/wear/JumpingJack/Application/src/main/AndroidManifest.xml b/wearable/wear/JumpingJack/Application/src/main/AndroidManifest.xml
index 652f895..8605fb0 100644
--- a/wearable/wear/JumpingJack/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/JumpingJack/Application/src/main/AndroidManifest.xml
@@ -16,12 +16,14 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.jumpingjack">
+    package="com.example.android.wearable.jumpingjack">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
+    <uses-permission android:name="android.permission.VIBRATE"/>
 
     <application android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
         android:label="@string/app_name">
     </application>
 
diff --git a/wearable/wear/JumpingJack/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/JumpingJack/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/JumpingJack/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/JumpingJack/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/JumpingJack/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/JumpingJack/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/JumpingJack/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/JumpingJack/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/JumpingJack/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/JumpingJack/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/JumpingJack/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/JumpingJack/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/JumpingJack/Shared/.gitignore b/wearable/wear/JumpingJack/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/JumpingJack/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/JumpingJack/Shared/src/main/AndroidManifest.xml b/wearable/wear/JumpingJack/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 7ba3326..0000000
--- a/wearable/wear/JumpingJack/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.jumpingjack.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/JumpingJack/Shared/src/main/res/values/strings.xml b/wearable/wear/JumpingJack/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/JumpingJack/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml b/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml
index e0448d9..02b7a4f 100644
--- a/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.example.android.jumpingjack">
+          package="com.example.android.wearable.jumpingjack">
 
    <uses-sdk android:minSdkVersion="20"
-             android:targetSdkVersion="20" />
+             android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/MainActivity.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/MainActivity.java
deleted file mode 100644
index 4fe2e79..0000000
--- a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/MainActivity.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.jumpingjack;
-
-import com.example.android.jumpingjack.fragments.CounterFragment;
-import com.example.android.jumpingjack.fragments.SettingsFragment;
-
-import android.app.Activity;
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.view.ViewPager;
-import android.util.Log;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * The main activity for the Jumping Jack application. This activity registers itself to receive
- * sensor values. Since on wearable devices a full screen activity is very short-lived, we set the
- * FLAG_KEEP_SCREEN_ON to give user adequate time for taking actions but since we don't want to
- * keep screen on for an extended period of time, there is a SCREEN_ON_TIMEOUT_MS that is enforced
- * if no interaction is discovered.
- *
- * This activity includes a {@link android.support.v4.view.ViewPager} with two pages, one that
- * shows the current count and one that allows user to reset the counter. the current value of the
- * counter is persisted so that upon re-launch, the counter picks up from the last value. At any
- * stage, user can set this counter to 0.
- */
-public class MainActivity extends Activity
-        implements SensorEventListener {
-
-    private static final String TAG = "JJMainActivity";
-
-    /** How long to keep the screen on when no activity is happening **/
-    private static final long SCREEN_ON_TIMEOUT_MS = 20000; // in milliseconds
-
-    /** an up-down movement that takes more than this will not be registered as such **/
-    private static final long TIME_THRESHOLD_NS = 2000000000; // in nanoseconds (= 2sec)
-
-    /**
-     * Earth gravity is around 9.8 m/s^2 but user may not completely direct his/her hand vertical
-     * during the exercise so we leave some room. Basically if the x-component of gravity, as
-     * measured by the Gravity sensor, changes with a variation (delta) > GRAVITY_THRESHOLD,
-     * we consider that a successful count.
-     */
-    private static final float GRAVITY_THRESHOLD = 7.0f;
-
-    private SensorManager mSensorManager;
-    private Sensor mSensor;
-    private long mLastTime = 0;
-    private boolean mUp = false;
-    private int mJumpCounter = 0;
-    private ViewPager mPager;
-    private CounterFragment mCounterPage;
-    private SettingsFragment mSettingPage;
-    private ImageView mSecondIndicator;
-    private ImageView mFirstIndicator;
-    private Timer mTimer;
-    private TimerTask mTimerTask;
-    private Handler mHandler;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.jj_layout);
-        setupViews();
-        mHandler = new Handler();
-        mJumpCounter = Utils.getCounterFromPreference(this);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        renewTimer();
-        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
-        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
-    }
-
-    private void setupViews() {
-        mPager = (ViewPager) findViewById(R.id.pager);
-        mFirstIndicator = (ImageView) findViewById(R.id.indicator_0);
-        mSecondIndicator = (ImageView) findViewById(R.id.indicator_1);
-        final PagerAdapter adapter = new PagerAdapter(getFragmentManager());
-        mCounterPage = new CounterFragment();
-        mSettingPage = new SettingsFragment(this);
-        adapter.addFragment(mCounterPage);
-        adapter.addFragment(mSettingPage);
-        setIndicator(0);
-        mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
-            @Override
-            public void onPageScrolled(int i, float v, int i2) {
-            }
-
-            @Override
-            public void onPageSelected(int i) {
-                setIndicator(i);
-                renewTimer();
-            }
-
-            @Override
-            public void onPageScrollStateChanged(int i) {
-            }
-        });
-
-        mPager.setAdapter(adapter);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        if (mSensorManager.registerListener(this, mSensor,
-                SensorManager.SENSOR_DELAY_NORMAL)) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Successfully registered for the sensor updates");
-            }
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        mSensorManager.unregisterListener(this);
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Unregistered for sensor events");
-        }
-    }
-
-    @Override
-    public void onSensorChanged(SensorEvent event) {
-        detectJump(event.values[0], event.timestamp);
-    }
-
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {
-    }
-
-    /**
-     * A simple algorithm to detect a successful up-down movement of hand(s). The algorithm is
-     * based on the assumption that when a person is wearing the watch, the x-component of gravity
-     * as measured by the Gravity Sensor is +9.8 when the hand is downward and -9.8 when the hand
-     * is upward (signs are reversed if the watch is worn on the right hand). Since the upward or
-     * downward may not be completely accurate, we leave some room and instead of 9.8, we use
-     * GRAVITY_THRESHOLD. We also consider the up <-> down movement successful if it takes less than
-     * TIME_THRESHOLD_NS.
-     */
-    private void detectJump(float xValue, long timestamp) {
-        if ((Math.abs(xValue) > GRAVITY_THRESHOLD)) {
-            if(timestamp - mLastTime < TIME_THRESHOLD_NS && mUp != (xValue > 0)) {
-                onJumpDetected(!mUp);
-            }
-            mUp = xValue > 0;
-            mLastTime = timestamp;
-        }
-    }
-
-    /**
-     * Called on detection of a successful down -> up or up -> down movement of hand.
-     */
-    private void onJumpDetected(boolean up) {
-        // we only count a pair of up and down as one successful movement
-        if (up) {
-            return;
-        }
-        mJumpCounter++;
-        setCounter(mJumpCounter);
-        renewTimer();
-    }
-
-    /**
-     * Updates the counter on UI, saves it to preferences and vibrates the watch when counter
-     * reaches a multiple of 10.
-     */
-    private void setCounter(int i) {
-        mCounterPage.setCounter(i);
-        Utils.saveCounterToPreference(this, i);
-        if (i > 0 && i % 10 == 0) {
-            Utils.vibrate(this, 0);
-        }
-    }
-
-    public void resetCounter() {
-        setCounter(0);
-        renewTimer();
-    }
-
-    /**
-     * Starts a timer to clear the flag FLAG_KEEP_SCREEN_ON.
-     */
-    private void renewTimer() {
-        if (null != mTimer) {
-            mTimer.cancel();
-        }
-        mTimerTask = new TimerTask() {
-            @Override
-            public void run() {
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG,
-                            "Removing the FLAG_KEEP_SCREEN_ON flag to allow going to background");
-                }
-                resetFlag();
-            }
-        };
-        mTimer = new Timer();
-        mTimer.schedule(mTimerTask, SCREEN_ON_TIMEOUT_MS);
-    }
-
-    /**
-     * Resets the FLAG_KEEP_SCREEN_ON flag so activity can go into background.
-     */
-    private void resetFlag() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "Resetting FLAG_KEEP_SCREEN_ON flag to allow going to background");
-                }
-                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-                finish();
-            }
-        });
-    }
-
-    /**
-     * Sets the page indicator for the ViewPager.
-     */
-    private void setIndicator(int i) {
-        switch (i) {
-            case 0:
-                mFirstIndicator.setImageResource(R.drawable.full_10);
-                mSecondIndicator.setImageResource(R.drawable.empty_10);
-                break;
-            case 1:
-                mFirstIndicator.setImageResource(R.drawable.empty_10);
-                mSecondIndicator.setImageResource(R.drawable.full_10);
-                break;
-        }
-    }
-
-
-}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/PagerAdapter.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/PagerAdapter.java
deleted file mode 100644
index 47fe86c..0000000
--- a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/PagerAdapter.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.jumpingjack;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.support.v13.app.FragmentPagerAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A simple adapter for the {@link android.support.v4.view.ViewPager}
- */
-public class PagerAdapter extends FragmentPagerAdapter {
-
-    List<Fragment> mFragments = null;
-
-    public PagerAdapter(FragmentManager fm) {
-        super(fm);
-        mFragments = new ArrayList<Fragment>();
-    }
-
-    @Override
-    public Fragment getItem(int position) {
-        return mFragments.get(position);
-    }
-
-    @Override
-    public int getCount() {
-        return mFragments.size();
-    }
-
-    public void addFragment(Fragment fragment) {
-        mFragments.add(fragment);
-        notifyDataSetChanged();
-    }
-}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/Utils.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/Utils.java
deleted file mode 100644
index 90fc296..0000000
--- a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/Utils.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.jumpingjack;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Vibrator;
-import android.preference.PreferenceManager;
-
-/**
- * A utility class for some helper methods.
- */
-public class Utils {
-
-    private static final int DEFAULT_VIBRATION_DURATION_MS = 200; // in millis
-    private static final String PREF_KEY_COUNTER = "counter";
-
-    /**
-     * Causes device to vibrate for the given duration (in millis). If duration is set to 0, then it
-     * will use the <code>DEFAULT_VIBRATION_DURATION_MS</code>.
-     */
-    public final static void vibrate(Context context, int duration) {
-        if (duration == 0) {
-            duration = DEFAULT_VIBRATION_DURATION_MS;
-        }
-        Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-        v.vibrate(duration);
-    }
-
-    /**
-     * Saves the counter value in the preference storage. If <code>value</code>
-     * is negative, then the value will be removed from the preferences.
-     */
-    public static void saveCounterToPreference(Context context, int value) {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
-        if (value < 0) {
-            // we want to remove
-            pref.edit().remove(PREF_KEY_COUNTER).apply();
-        } else {
-            pref.edit().putInt(PREF_KEY_COUNTER, value).apply();
-        }
-    }
-
-    /**
-     * Retrieves the value of counter from preference manager. If no value exists, it will return
-     * <code>0</code>.
-     */
-    public static int getCounterFromPreference(Context context) {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
-        return pref.getInt(PREF_KEY_COUNTER, 0);
-    }
-
-}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/fragments/CounterFragment.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/fragments/CounterFragment.java
deleted file mode 100644
index aa6fa53..0000000
--- a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/fragments/CounterFragment.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.jumpingjack.fragments;
-
-import com.example.android.jumpingjack.R;
-import com.example.android.jumpingjack.Utils;
-
-import android.app.Fragment;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * A simple fragment for showing the count
- */
-public class CounterFragment extends Fragment {
-
-    private static final long ANIMATION_INTERVAL_MS = 500; // in milliseconds
-    private TextView mCounterText;
-    private Timer mAnimationTimer;
-    private Handler mHandler;
-    private TimerTask mAnimationTask;
-    private boolean up = false;
-    private Drawable mDownDrawable;
-    private Drawable mUpDrawable;
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.counter_layout, container, false);
-        mDownDrawable = getResources().getDrawable(R.drawable.jump_down_50);
-        mUpDrawable = getResources().getDrawable(R.drawable.jump_up_50);
-        mCounterText = (TextView) view.findViewById(R.id.counter);
-        mCounterText.setCompoundDrawablesWithIntrinsicBounds(mUpDrawable, null, null, null);
-        setCounter(Utils.getCounterFromPreference(getActivity()));
-        mHandler = new Handler();
-        startAnimation();
-        return view;
-    }
-
-    private void startAnimation() {
-        mAnimationTask = new TimerTask() {
-            @Override
-            public void run() {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mCounterText.setCompoundDrawablesWithIntrinsicBounds(
-                                up ? mUpDrawable : mDownDrawable, null, null, null);
-                        up = !up;
-                    }
-                });
-            }
-        };
-        mAnimationTimer = new Timer();
-        mAnimationTimer.scheduleAtFixedRate(mAnimationTask, ANIMATION_INTERVAL_MS,
-                ANIMATION_INTERVAL_MS);
-    }
-
-    public void setCounter(String text) {
-        mCounterText.setText(text);
-    }
-
-    public void setCounter(int i) {
-        setCounter(i < 0 ? "0" : String.valueOf(i));
-    }
-
-    @Override
-    public void onDetach() {
-        mAnimationTimer.cancel();
-        super.onDetach();
-    }
-}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/fragments/SettingsFragment.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/fragments/SettingsFragment.java
deleted file mode 100644
index 0ef7a43..0000000
--- a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/jumpingjack/fragments/SettingsFragment.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.jumpingjack.fragments;
-
-import com.example.android.jumpingjack.MainActivity;
-import com.example.android.jumpingjack.R;
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-
-/**
- * A simple fragment that shows a button to reset the counter
- */
-public class SettingsFragment extends Fragment {
-
-    private Button mButton;
-    private MainActivity mMainActivity;
-
-    public SettingsFragment(MainActivity mainActivity) {
-        mMainActivity = mainActivity;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.setting_layout, container, false);
-        mButton = (Button) view.findViewById(R.id.btn);
-        mButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mMainActivity.resetCounter();
-            }
-        });
-        return view;
-    }
-
-}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/MainActivity.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/MainActivity.java
new file mode 100644
index 0000000..f1589c7
--- /dev/null
+++ b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/MainActivity.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.jumpingjack;
+
+import com.example.android.wearable.jumpingjack.fragments.CounterFragment;
+import com.example.android.wearable.jumpingjack.fragments.SettingsFragment;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.view.ViewPager;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * The main activity for the Jumping Jack application. This activity registers itself to receive
+ * sensor values. Since on wearable devices a full screen activity is very short-lived, we set the
+ * FLAG_KEEP_SCREEN_ON to give user adequate time for taking actions but since we don't want to
+ * keep screen on for an extended period of time, there is a SCREEN_ON_TIMEOUT_MS that is enforced
+ * if no interaction is discovered.
+ *
+ * This activity includes a {@link android.support.v4.view.ViewPager} with two pages, one that
+ * shows the current count and one that allows user to reset the counter. the current value of the
+ * counter is persisted so that upon re-launch, the counter picks up from the last value. At any
+ * stage, user can set this counter to 0.
+ */
+public class MainActivity extends Activity
+        implements SensorEventListener {
+
+    private static final String TAG = "JJMainActivity";
+
+    /** How long to keep the screen on when no activity is happening **/
+    private static final long SCREEN_ON_TIMEOUT_MS = 20000; // in milliseconds
+
+    /** an up-down movement that takes more than this will not be registered as such **/
+    private static final long TIME_THRESHOLD_NS = 2000000000; // in nanoseconds (= 2sec)
+
+    /**
+     * Earth gravity is around 9.8 m/s^2 but user may not completely direct his/her hand vertical
+     * during the exercise so we leave some room. Basically if the x-component of gravity, as
+     * measured by the Gravity sensor, changes with a variation (delta) > GRAVITY_THRESHOLD,
+     * we consider that a successful count.
+     */
+    private static final float GRAVITY_THRESHOLD = 7.0f;
+
+    private SensorManager mSensorManager;
+    private Sensor mSensor;
+    private long mLastTime = 0;
+    private boolean mUp = false;
+    private int mJumpCounter = 0;
+    private ViewPager mPager;
+    private CounterFragment mCounterPage;
+    private SettingsFragment mSettingPage;
+    private ImageView mSecondIndicator;
+    private ImageView mFirstIndicator;
+    private Timer mTimer;
+    private TimerTask mTimerTask;
+    private Handler mHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.jj_layout);
+        setupViews();
+        mHandler = new Handler();
+        mJumpCounter = Utils.getCounterFromPreference(this);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        renewTimer();
+        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
+    }
+
+    private void setupViews() {
+        mPager = (ViewPager) findViewById(R.id.pager);
+        mFirstIndicator = (ImageView) findViewById(R.id.indicator_0);
+        mSecondIndicator = (ImageView) findViewById(R.id.indicator_1);
+        final PagerAdapter adapter = new PagerAdapter(getFragmentManager());
+        mCounterPage = new CounterFragment();
+        mSettingPage = new SettingsFragment();
+        adapter.addFragment(mCounterPage);
+        adapter.addFragment(mSettingPage);
+        setIndicator(0);
+        mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+            @Override
+            public void onPageScrolled(int i, float v, int i2) {
+            }
+
+            @Override
+            public void onPageSelected(int i) {
+                setIndicator(i);
+                renewTimer();
+            }
+
+            @Override
+            public void onPageScrollStateChanged(int i) {
+            }
+        });
+
+        mPager.setAdapter(adapter);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mSensorManager.registerListener(this, mSensor,
+                SensorManager.SENSOR_DELAY_NORMAL)) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Successfully registered for the sensor updates");
+            }
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mSensorManager.unregisterListener(this);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Unregistered for sensor events");
+        }
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        detectJump(event.values[0], event.timestamp);
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+    }
+
+    /**
+     * A simple algorithm to detect a successful up-down movement of hand(s). The algorithm is
+     * based on the assumption that when a person is wearing the watch, the x-component of gravity
+     * as measured by the Gravity Sensor is +9.8 when the hand is downward and -9.8 when the hand
+     * is upward (signs are reversed if the watch is worn on the right hand). Since the upward or
+     * downward may not be completely accurate, we leave some room and instead of 9.8, we use
+     * GRAVITY_THRESHOLD. We also consider the up <-> down movement successful if it takes less than
+     * TIME_THRESHOLD_NS.
+     */
+    private void detectJump(float xValue, long timestamp) {
+        if ((Math.abs(xValue) > GRAVITY_THRESHOLD)) {
+            if(timestamp - mLastTime < TIME_THRESHOLD_NS && mUp != (xValue > 0)) {
+                onJumpDetected(!mUp);
+            }
+            mUp = xValue > 0;
+            mLastTime = timestamp;
+        }
+    }
+
+    /**
+     * Called on detection of a successful down -> up or up -> down movement of hand.
+     */
+    private void onJumpDetected(boolean up) {
+        // we only count a pair of up and down as one successful movement
+        if (up) {
+            return;
+        }
+        mJumpCounter++;
+        setCounter(mJumpCounter);
+        renewTimer();
+    }
+
+    /**
+     * Updates the counter on UI, saves it to preferences and vibrates the watch when counter
+     * reaches a multiple of 10.
+     */
+    private void setCounter(int i) {
+        mCounterPage.setCounter(i);
+        Utils.saveCounterToPreference(this, i);
+        if (i > 0 && i % 10 == 0) {
+            Utils.vibrate(this, 0);
+        }
+    }
+
+    public void resetCounter() {
+        setCounter(0);
+        renewTimer();
+    }
+
+    /**
+     * Starts a timer to clear the flag FLAG_KEEP_SCREEN_ON.
+     */
+    private void renewTimer() {
+        if (null != mTimer) {
+            mTimer.cancel();
+        }
+        mTimerTask = new TimerTask() {
+            @Override
+            public void run() {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG,
+                            "Removing the FLAG_KEEP_SCREEN_ON flag to allow going to background");
+                }
+                resetFlag();
+            }
+        };
+        mTimer = new Timer();
+        mTimer.schedule(mTimerTask, SCREEN_ON_TIMEOUT_MS);
+    }
+
+    /**
+     * Resets the FLAG_KEEP_SCREEN_ON flag so activity can go into background.
+     */
+    private void resetFlag() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Resetting FLAG_KEEP_SCREEN_ON flag to allow going to background");
+                }
+                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                finish();
+            }
+        });
+    }
+
+    /**
+     * Sets the page indicator for the ViewPager.
+     */
+    private void setIndicator(int i) {
+        switch (i) {
+            case 0:
+                mFirstIndicator.setImageResource(R.drawable.full_10);
+                mSecondIndicator.setImageResource(R.drawable.empty_10);
+                break;
+            case 1:
+                mFirstIndicator.setImageResource(R.drawable.empty_10);
+                mSecondIndicator.setImageResource(R.drawable.full_10);
+                break;
+        }
+    }
+
+
+}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/PagerAdapter.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/PagerAdapter.java
new file mode 100644
index 0000000..f7ac2b0
--- /dev/null
+++ b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/PagerAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.jumpingjack;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.support.v13.app.FragmentPagerAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A simple adapter for the {@link android.support.v4.view.ViewPager}
+ */
+public class PagerAdapter extends FragmentPagerAdapter {
+
+    List<Fragment> mFragments = null;
+
+    public PagerAdapter(FragmentManager fm) {
+        super(fm);
+        mFragments = new ArrayList<Fragment>();
+    }
+
+    @Override
+    public Fragment getItem(int position) {
+        return mFragments.get(position);
+    }
+
+    @Override
+    public int getCount() {
+        return mFragments.size();
+    }
+
+    public void addFragment(Fragment fragment) {
+        mFragments.add(fragment);
+        notifyDataSetChanged();
+    }
+}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/Utils.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/Utils.java
new file mode 100644
index 0000000..4db66c9
--- /dev/null
+++ b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/Utils.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.jumpingjack;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Vibrator;
+import android.preference.PreferenceManager;
+
+/**
+ * A utility class for some helper methods.
+ */
+public class Utils {
+
+    private static final int DEFAULT_VIBRATION_DURATION_MS = 200; // in millis
+    private static final String PREF_KEY_COUNTER = "counter";
+
+    /**
+     * Causes device to vibrate for the given duration (in millis). If duration is set to 0, then it
+     * will use the <code>DEFAULT_VIBRATION_DURATION_MS</code>.
+     */
+    public final static void vibrate(Context context, int duration) {
+        if (duration == 0) {
+            duration = DEFAULT_VIBRATION_DURATION_MS;
+        }
+        Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+        v.vibrate(duration);
+    }
+
+    /**
+     * Saves the counter value in the preference storage. If <code>value</code>
+     * is negative, then the value will be removed from the preferences.
+     */
+    public static void saveCounterToPreference(Context context, int value) {
+        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
+        if (value < 0) {
+            // we want to remove
+            pref.edit().remove(PREF_KEY_COUNTER).apply();
+        } else {
+            pref.edit().putInt(PREF_KEY_COUNTER, value).apply();
+        }
+    }
+
+    /**
+     * Retrieves the value of counter from preference manager. If no value exists, it will return
+     * <code>0</code>.
+     */
+    public static int getCounterFromPreference(Context context) {
+        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
+        return pref.getInt(PREF_KEY_COUNTER, 0);
+    }
+
+}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/fragments/CounterFragment.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/fragments/CounterFragment.java
new file mode 100644
index 0000000..c55eb9b
--- /dev/null
+++ b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/fragments/CounterFragment.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.jumpingjack.fragments;
+
+import com.example.android.wearable.jumpingjack.R;
+import com.example.android.wearable.jumpingjack.Utils;
+
+import android.app.Fragment;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * A simple fragment for showing the count
+ */
+public class CounterFragment extends Fragment {
+
+    private static final long ANIMATION_INTERVAL_MS = 500; // in milliseconds
+    private TextView mCounterText;
+    private Timer mAnimationTimer;
+    private Handler mHandler;
+    private TimerTask mAnimationTask;
+    private boolean up = false;
+    private Drawable mDownDrawable;
+    private Drawable mUpDrawable;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.counter_layout, container, false);
+        mDownDrawable = getResources().getDrawable(R.drawable.jump_down_50);
+        mUpDrawable = getResources().getDrawable(R.drawable.jump_up_50);
+        mCounterText = (TextView) view.findViewById(R.id.counter);
+        mCounterText.setCompoundDrawablesWithIntrinsicBounds(mUpDrawable, null, null, null);
+        setCounter(Utils.getCounterFromPreference(getActivity()));
+        mHandler = new Handler();
+        startAnimation();
+        return view;
+    }
+
+    private void startAnimation() {
+        mAnimationTask = new TimerTask() {
+            @Override
+            public void run() {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCounterText.setCompoundDrawablesWithIntrinsicBounds(
+                                up ? mUpDrawable : mDownDrawable, null, null, null);
+                        up = !up;
+                    }
+                });
+            }
+        };
+        mAnimationTimer = new Timer();
+        mAnimationTimer.scheduleAtFixedRate(mAnimationTask, ANIMATION_INTERVAL_MS,
+                ANIMATION_INTERVAL_MS);
+    }
+
+    public void setCounter(String text) {
+        mCounterText.setText(text);
+    }
+
+    public void setCounter(int i) {
+        setCounter(i < 0 ? "0" : String.valueOf(i));
+    }
+
+    @Override
+    public void onDetach() {
+        mAnimationTimer.cancel();
+        super.onDetach();
+    }
+}
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/fragments/SettingsFragment.java b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/fragments/SettingsFragment.java
new file mode 100644
index 0000000..771abd3
--- /dev/null
+++ b/wearable/wear/JumpingJack/Wearable/src/main/java/com/example/android/wearable/jumpingjack/fragments/SettingsFragment.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.jumpingjack.fragments;
+
+import com.example.android.wearable.jumpingjack.MainActivity;
+import com.example.android.wearable.jumpingjack.R;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+/**
+ * A simple fragment that shows a button to reset the counter
+ */
+public class SettingsFragment extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.setting_layout, container, false);
+        Button button = (Button) view.findViewById(R.id.btn);
+        button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                ((MainActivity) getActivity()).resetCounter();
+            }
+        });
+        return view;
+    }
+
+}
diff --git a/wearable/wear/JumpingJack/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/JumpingJack/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/JumpingJack/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/JumpingJack/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/JumpingJack/screenshots/jumping_jack.gif b/wearable/wear/JumpingJack/screenshots/jumping_jack.gif
new file mode 100644
index 0000000..86c4faa
--- /dev/null
+++ b/wearable/wear/JumpingJack/screenshots/jumping_jack.gif
Binary files differ
diff --git a/wearable/wear/JumpingJack/screenshots/web-icon.png b/wearable/wear/JumpingJack/screenshots/web-icon.png
new file mode 100644
index 0000000..da3c00a
--- /dev/null
+++ b/wearable/wear/JumpingJack/screenshots/web-icon.png
Binary files differ
diff --git a/wearable/wear/JumpingJack/settings.gradle b/wearable/wear/JumpingJack/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/JumpingJack/settings.gradle
+++ b/wearable/wear/JumpingJack/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/JumpingJack/template-params.xml b/wearable/wear/JumpingJack/template-params.xml
index 1674c64..7085d5c 100644
--- a/wearable/wear/JumpingJack/template-params.xml
+++ b/wearable/wear/JumpingJack/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>JumpingJack</name>
     <group>Wearable</group>
-    <package>com.example.android.jumpingjack</package>
+    <package>com.example.android.wearable.jumpingjack</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -41,4 +41,64 @@
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/web-icon.png</icon>
+        <screenshots>
+            <img>screenshots/jumping_jack.gif</img>
+        </screenshots>
+        <api_refs>
+            <android>android.hardware.SensorEvent</android>
+            <android>android.hardware.SensorEventManager</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic sample showing how to use the Gravity sensor on the wearable device
+by counting how many jumping jacks you have performed.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+[SensorEventListener][1] offers you methods used for receiving notifications from the
+[SensorManager][2] when sensor values have changed.
+
+This example counts how many times Jumping Jakcs are performed by detecting the value
+of the Gravity sensor by the following code:
+
+```java
+@Override
+public void onSensorChanged(SensorEvent event) {
+    detectJump(event.values[0], event.timestamp);
+}
+
+private void detectJump(float xValue, long timestamp) {
+    if ((Math.abs(xValue) > GRAVITY_THRESHOLD)) {
+        if(timestamp - mLastTime < TIME_THRESHOLD_NS && mUp != (xValue > 0)) {
+            onJumpDetected(!mUp);
+        }
+        mUp = xValue > 0;
+        mLastTime = timestamp;
+    }
+}
+```
+
+The detectJump method above assumes that when a person is wearing the watch, the x-component of gravity
+as measured by the Gravity Sensor is +9.8 when the hand is downward and -9.8 when the hand
+is upward (signs are reversed if the watch is worn on the right hand). Since the upward or
+downward may not be completely accurate, we leave some room and instead of 9.8, we use
+GRAVITY_THRESHOLD (7.0f). We also consider the up <-> down movement successful if it takes less than
+TIME_THRESHOLD_NS (2000000000 nanoseconds).
+
+[1]: http://developer.android.com/reference/android/hardware/SensorEventListener.html
+[2]: http://developer.android.com/reference/android/hardware/SensorManager.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml b/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml
index 005d745..3f1274d 100644
--- a/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml
@@ -15,11 +15,12 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.notifications" >
+        package="com.example.android.support.wearable.notifications" >
 
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-permission android:name="android.permission.VIBRATE" />
 
     <application
             android:allowBackup="true"
@@ -40,9 +41,9 @@
         <receiver android:name=".NotificationIntentReceiver"
                 android:exported="false">
             <intent-filter>
-                <action android:name="com.example.android.notifications.ACTION_EXAMPLE" />
-                <action android:name="com.example.android.notifications.ACTION_ENABLE_MESSAGES" />
-                <action android:name="com.example.android.notifications.ACTION_DISABLE_MESSAGES" />
+                <action android:name="com.example.android.support.wearable.notifications.ACTION_EXAMPLE" />
+                <action android:name="com.example.android.support.wearable.notifications.ACTION_ENABLE_MESSAGES" />
+                <action android:name="com.example.android.support.wearable.notifications.ACTION_DISABLE_MESSAGES" />
             </intent-filter>
         </receiver>
 
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/ActionsPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/ActionsPreset.java
deleted file mode 100644
index 32be1a6..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/ActionsPreset.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.content.Context;
-import android.support.v4.app.NotificationCompat;
-
-/**
- * Base class for notification actions presets.
- */
-public abstract class ActionsPreset extends NamedPreset {
-    public ActionsPreset(int nameResId) {
-        super(nameResId);
-    }
-
-    /** Apply the priority to a notification builder */
-    public abstract void apply(Context context, NotificationCompat.Builder builder,
-            NotificationCompat.WearableExtender wearableOptions);
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/ActionsPresets.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/ActionsPresets.java
deleted file mode 100644
index 2481ac8..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/ActionsPresets.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.content.Context;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.RemoteInput;
-
-/**
- * Collection of notification actions presets.
- */
-public class ActionsPresets {
-    public static final ActionsPreset NO_ACTIONS_PRESET = new NoActionsPreset();
-    public static final ActionsPreset SINGLE_ACTION_PRESET = new SingleActionPreset();
-
-    public static final ActionsPreset[] PRESETS = new ActionsPreset[] {
-            NO_ACTIONS_PRESET,
-            SINGLE_ACTION_PRESET,
-            new ReplyActionPreset(),
-            new ReplyWithChoicesActionPreset(),
-            new DifferentActionsOnPhoneAndWearable(),
-            new LongTitleActionPreset()
-    };
-
-    private static class NoActionsPreset extends ActionsPreset {
-        public NoActionsPreset() {
-            super(R.string.no_actions);
-        }
-
-        @Override
-        public void apply(Context context, NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-        }
-    }
-
-    private static class SingleActionPreset extends ActionsPreset {
-        public SingleActionPreset() {
-            super(R.string.single_action);
-        }
-
-        @Override
-        public void apply(Context context, NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-            builder.addAction(R.drawable.ic_full_action,
-                    context.getString(R.string.example_action),
-                    NotificationUtil.getExamplePendingIntent(context,
-                            R.string.example_action_clicked))
-                    .build();
-        }
-    }
-
-    private static class LongTitleActionPreset extends ActionsPreset {
-        public LongTitleActionPreset() {
-            super(R.string.long_title_action);
-        }
-
-        @Override
-        public void apply(Context context, NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-            builder.addAction(R.drawable.ic_full_action,
-                    context.getString(R.string.example_action_long_title),
-                    NotificationUtil.getExamplePendingIntent(context,
-                            R.string.example_action_clicked))
-                    .build();
-        }
-    }
-
-    private static class ReplyActionPreset extends ActionsPreset {
-        public ReplyActionPreset() {
-            super(R.string.reply_action);
-        }
-
-        @Override
-        public void apply(Context context, NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-            RemoteInput remoteInput = new RemoteInput.Builder(NotificationUtil.EXTRA_REPLY)
-                    .setLabel(context.getString(R.string.example_reply_label))
-                    .build();
-            NotificationCompat.Action action = new NotificationCompat.Action.Builder(
-                    R.drawable.ic_full_reply,
-                    context.getString(R.string.example_reply_action),
-                    NotificationUtil.getExamplePendingIntent(context,
-                            R.string.example_reply_action_clicked))
-                    .addRemoteInput(remoteInput)
-                    .build();
-            builder.addAction(action);
-        }
-    }
-
-    private static class ReplyWithChoicesActionPreset extends ActionsPreset {
-        public ReplyWithChoicesActionPreset() {
-            super(R.string.reply_action_with_choices);
-        }
-
-        @Override
-        public void apply(Context context, NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-            RemoteInput remoteInput = new RemoteInput.Builder(NotificationUtil.EXTRA_REPLY)
-                    .setLabel(context.getString(R.string.example_reply_answer_label))
-                    .setChoices(new String[] { context.getString(R.string.yes),
-                            context.getString(R.string.no), context.getString(R.string.maybe) })
-                    .build();
-            NotificationCompat.Action action = new NotificationCompat.Action.Builder(
-                    R.drawable.ic_full_reply,
-                    context.getString(R.string.example_reply_action),
-                    NotificationUtil.getExamplePendingIntent(context,
-                            R.string.example_reply_action_clicked))
-                    .addRemoteInput(remoteInput)
-                    .build();
-            wearableOptions.addAction(action);
-        }
-    }
-
-    private static class DifferentActionsOnPhoneAndWearable extends ActionsPreset {
-        public DifferentActionsOnPhoneAndWearable() {
-            super(R.string.different_actions_on_phone_and_wearable);
-        }
-
-        @Override
-        public void apply(Context context, NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-            NotificationCompat.Action phoneAction = new NotificationCompat.Action.Builder(
-                    R.drawable.ic_full_action,
-                    context.getString(R.string.phone_action),
-                    NotificationUtil.getExamplePendingIntent(context,
-                            R.string.phone_action_clicked))
-                    .build();
-            builder.addAction(phoneAction);
-
-            RemoteInput remoteInput = new RemoteInput.Builder(NotificationUtil.EXTRA_REPLY)
-                    .setLabel(context.getString(R.string.example_reply_label))
-                    .build();
-
-            NotificationCompat.Action wearableAction = new NotificationCompat.Action.Builder(
-                    R.drawable.ic_full_reply,
-                    context.getString(R.string.wearable_action),
-                    NotificationUtil.getExamplePendingIntent(context,
-                            R.string.wearable_action_clicked))
-                    .addRemoteInput(remoteInput)
-                    .build();
-            wearableOptions.addAction(wearableAction);
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/BackgroundPickers.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/BackgroundPickers.java
deleted file mode 100644
index 69263b2..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/BackgroundPickers.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manages the background image pickers.
- */
-public class BackgroundPickers {
-
-    public interface OnBackgroundPickersChangedListener {
-        public void onBackgroundPickersChanged(BackgroundPickers pickers);
-    }
-
-    private final ViewGroup mContainer;
-    private final OnPickedListener mOnPickedListener;
-    private final List<ViewGroup> mPickers;
-    private final OnBackgroundPickersChangedListener listener;
-
-    public BackgroundPickers(ViewGroup container, OnBackgroundPickersChangedListener listener) {
-        this.mContainer = container;
-        this.mOnPickedListener = new OnPickedListener();
-        this.mPickers = new ArrayList<ViewGroup>();
-        this.listener = listener;
-    }
-
-    /**
-     * Generates the pickers as necessary.
-     */
-    public void generatePickers(int count) {
-        // Clear existing containers.
-        clear();
-
-        // Fill in new pickers.
-        LayoutInflater inflater = LayoutInflater.from(mContainer.getContext());
-        Resources res = mContainer.getResources();
-        for (int i = 0; i < count; i++) {
-            View picker = inflater.inflate(R.layout.background_picker, mContainer, false);
-            TextView label = (TextView) picker.findViewById(R.id.bg_picker_label);
-            label.setText(String.format(res.getString(R.string.bg_picker_label), i+1));
-            ViewGroup pickerBox = (ViewGroup) picker.findViewById(R.id.bg_picker_container);
-            mPickers.add(pickerBox);
-            for (int j = 0; j < pickerBox.getChildCount(); j++) {
-                ImageView img = (ImageView) pickerBox.getChildAt(j);
-                img.setOnClickListener(mOnPickedListener);
-            }
-            mContainer.addView(picker);
-        }
-    }
-
-    /**
-     * Returns the background resource for the picker at the given index.
-     * @param position Index of the background picker.
-     * @return Id of the background image resource. null if no image is picked.
-     */
-    public Integer getRes(int position) {
-        String tag = (String) mPickers.get(position).getTag();
-        if (tag == null) {
-            return null;
-        }
-
-        Context context = mContainer.getContext();
-        return context.getResources().getIdentifier(tag, "drawable", context.getPackageName());
-    }
-
-    /**
-     * Returns the all the background resources for the pickers managed by this object. Returns null
-     * if no pickers exist.
-     */
-    public Integer[] getRes() {
-        if (mPickers.size() == 0) {
-            return null;
-        }
-
-        Integer[] res = new Integer[mPickers.size()];
-        for (int i = 0; i < mPickers.size(); i++) {
-            res[i] = getRes(i);
-        }
-        return res;
-    }
-
-    /**
-     * Clears the pickers.
-     */
-    public void clear() {
-        mContainer.removeAllViews();
-        mPickers.clear();
-    }
-
-    public int getCount() {
-        return mPickers.size();
-    }
-
-    private class OnPickedListener implements View.OnClickListener {
-
-        @Override
-        public void onClick(View view) {
-            ImageView pickedView = (ImageView) view;
-            ViewGroup pickerBox = (ViewGroup) view.getParent();
-
-            // Clear old selection.
-            for (int i = 0; i < pickerBox.getChildCount(); i++) {
-                ImageView childView = (ImageView) pickerBox.getChildAt(i);
-                childView.setBackgroundResource(R.drawable.unselected_background);
-            }
-
-            // Set new selection.
-            pickedView.setBackgroundResource(R.drawable.selected_background);
-            pickerBox.setTag(pickedView.getTag());
-
-            if (listener != null) {
-                listener.onBackgroundPickersChanged(BackgroundPickers.this);
-            }
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/MainActivity.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/MainActivity.java
deleted file mode 100644
index 22b1059..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/MainActivity.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.support.v4.app.NotificationManagerCompat;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import java.util.Arrays;
-
-/**
- * Main activity which posts a notification when resumed, and allows customization
- * of that notification via controls.
- */
-public class MainActivity extends Activity implements Handler.Callback {
-    private static final int MSG_POST_NOTIFICATIONS = 0;
-    private static final long POST_NOTIFICATIONS_DELAY_MS = 200;
-
-    private Handler mHandler;
-    private Spinner mPresetSpinner;
-    private EditText mTitleEditText;
-    private EditText mTextEditText;
-    private TextWatcher mTextChangedListener;
-    private Spinner mPrioritySpinner;
-    private Spinner mActionsSpinner;
-    private CheckBox mIncludeLargeIconCheckbox;
-    private CheckBox mLocalOnlyCheckbox;
-    private CheckBox mIncludeContentIntentCheckbox;
-    private CheckBox mVibrateCheckbox;
-    private BackgroundPickers mBackgroundPickers;
-    private int postedNotificationCount = 0;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.main);
-
-        mHandler = new Handler(this);
-        mTextChangedListener = new UpdateNotificationsOnTextChangeListener();
-
-        initPresetSpinner();
-        initTitleEditText();
-        initTextEditText();
-        initPrioritySpinner();
-        initActionsSpinner();
-        initIncludeLargeIconCheckbox();
-        initLocalOnlyCheckbox();
-        initIncludeContentIntentCheckbox();
-        initVibrateCheckbox();
-        initBackgroundPickers();
-
-        NotificationPreset preset = NotificationPresets.PRESETS[
-                mPresetSpinner.getSelectedItemPosition()];
-        updateTextEditors(preset);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        updateNotifications(false /* cancelExisting */);
-    }
-
-    private void initPresetSpinner() {
-        mPresetSpinner = (Spinner) findViewById(R.id.preset_spinner);
-        mPresetSpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this,
-                NotificationPresets.PRESETS));
-        mPresetSpinner.post(new Runnable() {
-            @Override
-            public void run() {
-                mPresetSpinner.setOnItemSelectedListener(new PresetSpinnerListener());
-            }
-        });
-    }
-
-    private void initTitleEditText() {
-        mTitleEditText = (EditText) findViewById(R.id.title_editor);
-    }
-
-    private void initTextEditText() {
-        mTextEditText = (EditText) findViewById(R.id.text_editor);
-    }
-
-    private void initPrioritySpinner() {
-        mPrioritySpinner = (Spinner) findViewById(R.id.priority_spinner);
-        mPrioritySpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this,
-                PriorityPresets.PRESETS));
-        mPrioritySpinner.setSelection(Arrays.asList(PriorityPresets.PRESETS)
-                .indexOf(PriorityPresets.DEFAULT));
-        mPrioritySpinner.post(new Runnable() {
-            @Override
-            public void run() {
-                mPrioritySpinner.setOnItemSelectedListener(
-                        new UpdateNotificationsOnItemSelectedListener(true /* cancelExisting */));
-            }
-        });
-    }
-
-    private void initActionsSpinner() {
-        mActionsSpinner = (Spinner) findViewById(R.id.actions_spinner);
-        mActionsSpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this,
-                ActionsPresets.PRESETS));
-        mActionsSpinner.post(new Runnable() {
-            @Override
-            public void run() {
-                mActionsSpinner.setOnItemSelectedListener(
-                        new UpdateNotificationsOnItemSelectedListener(false /* cancelExisting */));
-            }
-        });
-    }
-
-    private void initIncludeLargeIconCheckbox() {
-        mIncludeLargeIconCheckbox = (CheckBox) findViewById(R.id.include_large_icon_checkbox);
-        mIncludeLargeIconCheckbox.setOnCheckedChangeListener(
-                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
-    }
-
-    private void initLocalOnlyCheckbox() {
-        mLocalOnlyCheckbox = (CheckBox) findViewById(R.id.local_only_checkbox);
-        mLocalOnlyCheckbox.setOnCheckedChangeListener(
-                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
-    }
-
-    private void initIncludeContentIntentCheckbox() {
-        mIncludeContentIntentCheckbox = (CheckBox) findViewById(
-                R.id.include_content_intent_checkbox);
-        mIncludeContentIntentCheckbox.setOnCheckedChangeListener(
-                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
-    }
-
-    private void initVibrateCheckbox() {
-        mVibrateCheckbox = (CheckBox) findViewById(R.id.vibrate_checkbox);
-        mVibrateCheckbox.setOnCheckedChangeListener(
-                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
-    }
-
-    private void initBackgroundPickers() {
-        mBackgroundPickers = new BackgroundPickers(
-                (ViewGroup) findViewById(R.id.background_pickers),
-                new BackgroundPickerListener());
-    }
-
-    private void updateTextEditors(NotificationPreset preset) {
-        if (preset == NotificationPresets.BASIC) {
-            findViewById(R.id.title_edit_field).setVisibility(View.VISIBLE);
-            mTitleEditText.setText(getString(preset.titleResId));
-            mTitleEditText.addTextChangedListener(mTextChangedListener);
-            findViewById(R.id.text_edit_field).setVisibility(View.VISIBLE);
-            mTextEditText.setText(getString(preset.textResId));
-            mTextEditText.addTextChangedListener(mTextChangedListener);
-        } else {
-            findViewById(R.id.title_edit_field).setVisibility(View.GONE);
-            mTitleEditText.removeTextChangedListener(mTextChangedListener);
-            findViewById(R.id.text_edit_field).setVisibility(View.GONE);
-            mTextEditText.removeTextChangedListener(mTextChangedListener);
-        }
-    }
-
-    /**
-     * Begin to re-post the sample notification(s).
-     */
-    private void updateNotifications(boolean cancelExisting) {
-        // Disable messages to skip notification deleted messages during cancel.
-        sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_DISABLE_MESSAGES)
-                .setClass(this, NotificationIntentReceiver.class));
-
-        if (cancelExisting) {
-            // Cancel all existing notifications to trigger fresh-posting behavior: For example,
-            // switching from HIGH to LOW priority does not cause a reordering in Notification Shade.
-            NotificationManagerCompat.from(this).cancelAll();
-            postedNotificationCount = 0;
-
-            // Post the updated notifications on a delay to avoid a cancel+post race condition
-            // with notification manager.
-            mHandler.removeMessages(MSG_POST_NOTIFICATIONS);
-            mHandler.sendEmptyMessageDelayed(MSG_POST_NOTIFICATIONS, POST_NOTIFICATIONS_DELAY_MS);
-        } else {
-            postNotifications();
-        }
-    }
-
-    /**
-     * Post the sample notification(s) using current options.
-     */
-    private void postNotifications() {
-        sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_ENABLE_MESSAGES)
-                .setClass(this, NotificationIntentReceiver.class));
-
-        NotificationPreset preset = NotificationPresets.PRESETS[
-                mPresetSpinner.getSelectedItemPosition()];
-        CharSequence titlePreset = mTitleEditText.getText();
-        CharSequence textPreset = mTextEditText.getText();
-        PriorityPreset priorityPreset = PriorityPresets.PRESETS[
-                mPrioritySpinner.getSelectedItemPosition()];
-        ActionsPreset actionsPreset = ActionsPresets.PRESETS[
-                mActionsSpinner.getSelectedItemPosition()];
-        if (preset.actionsRequired() && actionsPreset == ActionsPresets.NO_ACTIONS_PRESET) {
-            // If actions are required, but the no-actions preset was selected, change presets.
-            actionsPreset = ActionsPresets.SINGLE_ACTION_PRESET;
-            mActionsSpinner.setSelection(Arrays.asList(ActionsPresets.PRESETS).indexOf(
-                    actionsPreset), true);
-        }
-        NotificationPreset.BuildOptions options = new NotificationPreset.BuildOptions(
-                titlePreset,
-                textPreset,
-                priorityPreset,
-                actionsPreset,
-                mIncludeLargeIconCheckbox.isChecked(),
-                mLocalOnlyCheckbox.isChecked(),
-                mIncludeContentIntentCheckbox.isChecked(),
-                mVibrateCheckbox.isChecked(),
-                mBackgroundPickers.getRes());
-        Notification[] notifications = preset.buildNotifications(this, options);
-
-        // Post new notifications
-        for (int i = 0; i < notifications.length; i++) {
-            NotificationManagerCompat.from(this).notify(i, notifications[i]);
-        }
-        // Cancel any that are beyond the current count.
-        for (int i = notifications.length; i < postedNotificationCount; i++) {
-            NotificationManagerCompat.from(this).cancel(i);
-        }
-        postedNotificationCount = notifications.length;
-    }
-
-    @Override
-    public boolean handleMessage(Message message) {
-        switch (message.what) {
-            case MSG_POST_NOTIFICATIONS:
-                postNotifications();
-                return true;
-        }
-        return false;
-    }
-
-    private class PresetSpinnerListener implements AdapterView.OnItemSelectedListener {
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            NotificationPreset preset = NotificationPresets.PRESETS[position];
-            mBackgroundPickers.generatePickers(preset.countBackgroundPickersRequired());
-            updateTextEditors(preset);
-            updateNotifications(false /* cancelExisting */);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> adapterView) {
-        }
-    }
-
-    private class UpdateNotificationsOnTextChangeListener implements TextWatcher {
-        @Override
-        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        }
-
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-        }
-
-        @Override
-        public void afterTextChanged(Editable s) {
-            updateNotifications(false /* cancelExisting */);
-        }
-    }
-
-    private class UpdateNotificationsOnItemSelectedListener
-            implements AdapterView.OnItemSelectedListener {
-        private final boolean mCancelExisting;
-
-        public UpdateNotificationsOnItemSelectedListener(boolean cancelExisting) {
-            mCancelExisting = cancelExisting;
-        }
-        @Override
-        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-            updateNotifications(mCancelExisting);
-        }
-
-        @Override
-        public void onNothingSelected(AdapterView<?> adapterView) {
-        }
-    }
-
-    private class UpdateNotificationsOnCheckedChangeListener
-            implements CompoundButton.OnCheckedChangeListener {
-        private final boolean mCancelExisting;
-
-        public UpdateNotificationsOnCheckedChangeListener(boolean cancelExisting) {
-            mCancelExisting = cancelExisting;
-        }
-
-        @Override
-        public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
-            updateNotifications(mCancelExisting);
-        }
-    }
-
-    private class BackgroundPickerListener
-            implements BackgroundPickers.OnBackgroundPickersChangedListener {
-        @Override
-        public void onBackgroundPickersChanged(BackgroundPickers pickers) {
-            updateNotifications(false /* cancelExisting */);
-        }
-    }
-
-    private class NamedPresetSpinnerArrayAdapter extends ArrayAdapter<NamedPreset> {
-        public NamedPresetSpinnerArrayAdapter(Context context, NamedPreset[] presets) {
-            super(context, R.layout.simple_spinner_item, presets);
-        }
-
-        @Override
-        public View getDropDownView(int position, View convertView, ViewGroup parent) {
-            TextView view = (TextView) super.getDropDownView(position, convertView, parent);
-            view.setText(getString(getItem(position).nameResId));
-            return view;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            TextView view = (TextView) getLayoutInflater().inflate(
-                    android.R.layout.simple_spinner_item, parent, false);
-            view.setText(getString(getItem(position).nameResId));
-            return view;
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NamedPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NamedPreset.java
deleted file mode 100644
index b97bcb9..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NamedPreset.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-/**
- * Base class for presets that have a simple name to display.
- */
-public abstract class NamedPreset {
-    public final int nameResId;
-
-    public NamedPreset(int nameResId) {
-        this.nameResId = nameResId;
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationIntentReceiver.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationIntentReceiver.java
deleted file mode 100644
index 8dabb34..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationIntentReceiver.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.RemoteInput;
-import android.widget.Toast;
-
-/**
- * Broadcast receiver to post toast messages in response to notification intents firing.
- */
-public class NotificationIntentReceiver extends BroadcastReceiver {
-    public static final String ACTION_EXAMPLE =
-            "com.example.android.notifications.ACTION_EXAMPLE";
-    public static final String ACTION_ENABLE_MESSAGES =
-            "com.example.android.notifications.ACTION_ENABLE_MESSAGES";
-    public static final String ACTION_DISABLE_MESSAGES =
-            "com.example.android.notifications.ACTION_DISABLE_MESSAGES";
-
-    private boolean mEnableMessages = true;
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (intent.getAction().equals(ACTION_EXAMPLE)) {
-            if (mEnableMessages) {
-                String message = intent.getStringExtra(NotificationUtil.EXTRA_MESSAGE);
-                Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
-                CharSequence replyMessage = null;
-                if (remoteInputResults != null) {
-                    replyMessage = remoteInputResults.getCharSequence(NotificationUtil.EXTRA_REPLY);
-                }
-                if (replyMessage != null) {
-                    message = message + ": \"" + replyMessage + "\"";
-                }
-                Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
-            }
-        } else if (intent.getAction().equals(ACTION_ENABLE_MESSAGES)) {
-            mEnableMessages = true;
-        } else if (intent.getAction().equals(ACTION_DISABLE_MESSAGES)) {
-            mEnableMessages = false;
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationPreset.java
deleted file mode 100644
index 3ab792b..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationPreset.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Notification;
-import android.content.Context;
-
-/**
- * Base class for notification preset generators.
- */
-public abstract class NotificationPreset extends NamedPreset {
-    public final int titleResId;
-    public final int textResId;
-
-    public NotificationPreset(int nameResId, int titleResId, int textResId) {
-        super(nameResId);
-        this.titleResId = titleResId;
-        this.textResId = textResId;
-    }
-
-    public static class BuildOptions {
-        public final CharSequence titlePreset;
-        public final CharSequence textPreset;
-        public final PriorityPreset priorityPreset;
-        public final ActionsPreset actionsPreset;
-        public final boolean includeLargeIcon;
-        public final boolean isLocalOnly;
-        public final boolean hasContentIntent;
-        public final boolean vibrate;
-        public final Integer[] backgroundIds;
-
-        public BuildOptions(CharSequence titlePreset, CharSequence textPreset,
-                PriorityPreset priorityPreset, ActionsPreset actionsPreset,
-                boolean includeLargeIcon, boolean isLocalOnly, boolean hasContentIntent,
-                boolean vibrate, Integer[] backgroundIds) {
-            this.titlePreset = titlePreset;
-            this.textPreset = textPreset;
-            this.priorityPreset = priorityPreset;
-            this.actionsPreset = actionsPreset;
-            this.includeLargeIcon = includeLargeIcon;
-            this.isLocalOnly = isLocalOnly;
-            this.hasContentIntent = hasContentIntent;
-            this.vibrate = vibrate;
-            this.backgroundIds = backgroundIds;
-        }
-    }
-
-    /** Build a notification with this preset and the provided options */
-    public abstract Notification[] buildNotifications(Context context, BuildOptions options);
-
-    /** Whether actions are required to use this preset. */
-    public boolean actionsRequired() {
-        return false;
-    }
-
-    /** Number of background pickers required */
-    public int countBackgroundPickersRequired() {
-        return 0;
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationPresets.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationPresets.java
deleted file mode 100644
index b6fe24a..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationPresets.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Notification;
-import android.content.Context;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.support.v4.app.NotificationCompat;
-import android.text.SpannableStringBuilder;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.RelativeSizeSpan;
-import android.text.style.StrikethroughSpan;
-import android.text.style.StyleSpan;
-import android.text.style.SubscriptSpan;
-import android.text.style.SuperscriptSpan;
-import android.text.style.TypefaceSpan;
-import android.text.style.UnderlineSpan;
-import android.view.Gravity;
-
-/**
- * Collection of notification builder presets.
- */
-public class NotificationPresets {
-    private static final String EXAMPLE_GROUP_KEY = "example";
-
-    public static final NotificationPreset BASIC = new BasicNotificationPreset();
-    public static final NotificationPreset STYLIZED_TEXT = new StylizedTextNotificationPreset();
-    public static final NotificationPreset INBOX = new InboxNotificationPreset();
-    public static final NotificationPreset BIG_PICTURE = new BigPictureNotificationPreset();
-    public static final NotificationPreset BIG_TEXT = new BigTextNotificationPreset();
-    public static final NotificationPreset BOTTOM_ALIGNED = new BottomAlignedNotificationPreset();
-    public static final NotificationPreset GRAVITY = new GravityNotificationPreset();
-    public static final NotificationPreset CONTENT_ACTION = new ContentActionNotificationPreset();
-    public static final NotificationPreset CONTENT_ICON = new ContentIconNotificationPreset();
-    public static final NotificationPreset MULTIPLE_PAGE = new MultiplePageNotificationPreset();
-    public static final NotificationPreset BUNDLE = new NotificationBundlePreset();
-
-    public static final NotificationPreset[] PRESETS = new NotificationPreset[] {
-            BASIC,
-            STYLIZED_TEXT,
-            INBOX,
-            BIG_PICTURE,
-            BIG_TEXT,
-            BOTTOM_ALIGNED,
-            GRAVITY,
-            CONTENT_ACTION,
-            CONTENT_ICON,
-            MULTIPLE_PAGE,
-            BUNDLE
-    };
-
-    private static NotificationCompat.Builder applyBasicOptions(Context context,
-            NotificationCompat.Builder builder, NotificationCompat.WearableExtender wearableOptions,
-            NotificationPreset.BuildOptions options) {
-        builder.setContentTitle(options.titlePreset)
-                .setContentText(options.textPreset)
-                .setSmallIcon(R.mipmap.ic_launcher)
-                .setDeleteIntent(NotificationUtil.getExamplePendingIntent(
-                        context, R.string.example_notification_deleted));
-        options.actionsPreset.apply(context, builder, wearableOptions);
-        options.priorityPreset.apply(builder, wearableOptions);
-        if (options.includeLargeIcon) {
-            builder.setLargeIcon(BitmapFactory.decodeResource(
-                    context.getResources(), R.drawable.example_large_icon));
-        }
-        if (options.isLocalOnly) {
-            builder.setLocalOnly(true);
-        }
-        if (options.hasContentIntent) {
-            builder.setContentIntent(NotificationUtil.getExamplePendingIntent(context,
-                    R.string.content_intent_clicked));
-        }
-        if (options.vibrate) {
-            builder.setVibrate(new long[] {0, 100, 50, 100} );
-        }
-        return builder;
-    }
-
-    private static class BasicNotificationPreset extends NotificationPreset {
-        public BasicNotificationPreset() {
-            super(R.string.basic_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class StylizedTextNotificationPreset extends NotificationPreset {
-        public StylizedTextNotificationPreset() {
-            super(R.string.stylized_text_example, R.string.example_content_title,
-                    R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle();
-
-            SpannableStringBuilder title = new SpannableStringBuilder();
-            appendStyled(title, "Stylized", new StyleSpan(Typeface.BOLD_ITALIC));
-            title.append(" title");
-            SpannableStringBuilder text = new SpannableStringBuilder("Stylized text: ");
-            appendStyled(text, "C", new ForegroundColorSpan(Color.RED));
-            appendStyled(text, "O", new ForegroundColorSpan(Color.GREEN));
-            appendStyled(text, "L", new ForegroundColorSpan(Color.BLUE));
-            appendStyled(text, "O", new ForegroundColorSpan(Color.YELLOW));
-            appendStyled(text, "R", new ForegroundColorSpan(Color.MAGENTA));
-            appendStyled(text, "S", new ForegroundColorSpan(Color.CYAN));
-            text.append("; ");
-            appendStyled(text, "1.25x size", new RelativeSizeSpan(1.25f));
-            text.append("; ");
-            appendStyled(text, "0.75x size", new RelativeSizeSpan(0.75f));
-            text.append("; ");
-            appendStyled(text, "underline", new UnderlineSpan());
-            text.append("; ");
-            appendStyled(text, "strikethrough", new StrikethroughSpan());
-            text.append("; ");
-            appendStyled(text, "bold", new StyleSpan(Typeface.BOLD));
-            text.append("; ");
-            appendStyled(text, "italic", new StyleSpan(Typeface.ITALIC));
-            text.append("; ");
-            appendStyled(text, "sans-serif-thin", new TypefaceSpan("sans-serif-thin"));
-            text.append("; ");
-            appendStyled(text, "monospace", new TypefaceSpan("monospace"));
-            text.append("; ");
-            appendStyled(text, "sub", new SubscriptSpan());
-            text.append("script");
-            appendStyled(text, "super", new SuperscriptSpan());
-
-            style.setBigContentTitle(title);
-            style.bigText(text);
-
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
-                    .setStyle(style);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-
-        private void appendStyled(SpannableStringBuilder builder, String str, Object... spans) {
-            builder.append(str);
-            for (Object span : spans) {
-                builder.setSpan(span, builder.length() - str.length(), builder.length(), 0);
-            }
-        }
-    }
-
-    private static class InboxNotificationPreset extends NotificationPreset {
-        public InboxNotificationPreset() {
-            super(R.string.inbox_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
-            style.addLine(context.getString(R.string.inbox_style_example_line1));
-            style.addLine(context.getString(R.string.inbox_style_example_line2));
-            style.addLine(context.getString(R.string.inbox_style_example_line3));
-            style.setBigContentTitle(context.getString(R.string.inbox_style_example_title));
-            style.setSummaryText(context.getString(R.string.inbox_style_example_summary_text));
-
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
-                    .setStyle(style);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class BigPictureNotificationPreset extends NotificationPreset {
-        public BigPictureNotificationPreset() {
-            super(R.string.big_picture_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
-            style.bigPicture(BitmapFactory.decodeResource(context.getResources(),
-                    R.drawable.example_big_picture));
-            style.setBigContentTitle(context.getString(R.string.big_picture_style_example_title));
-            style.setSummaryText(context.getString(
-                    R.string.big_picture_style_example_summary_text));
-
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
-                    .setStyle(style);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class BigTextNotificationPreset extends NotificationPreset {
-        public BigTextNotificationPreset() {
-            super(R.string.big_text_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle();
-            style.bigText(context.getString(R.string.big_text_example_big_text));
-            style.setBigContentTitle(context.getString(R.string.big_text_example_title));
-            style.setSummaryText(context.getString(R.string.big_text_example_summary_text));
-
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
-                    .setStyle(style);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class BottomAlignedNotificationPreset extends NotificationPreset {
-        public BottomAlignedNotificationPreset() {
-            super(R.string.bottom_aligned_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-
-            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context);
-            secondPageBuilder.setContentTitle(
-                    context.getString(R.string.second_page_content_title));
-            secondPageBuilder.setContentText(context.getString(R.string.big_text_example_big_text));
-            secondPageBuilder.extend(new NotificationCompat.WearableExtender()
-                            .setStartScrollBottom(true));
-
-            wearableOptions.addPage(secondPageBuilder.build());
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class GravityNotificationPreset extends NotificationPreset {
-        public GravityNotificationPreset() {
-            super(R.string.gravity_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, builder, wearableOptions, options);
-
-            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context)
-                    .setContentTitle(options.titlePreset)
-                    .setContentText(options.textPreset)
-                    .extend(new NotificationCompat.WearableExtender()
-                            .setGravity(Gravity.CENTER_VERTICAL));
-            wearableOptions.addPage(secondPageBuilder.build());
-
-            NotificationCompat.Builder thirdPageBuilder = new NotificationCompat.Builder(context)
-                    .setContentTitle(options.titlePreset)
-                    .setContentText(options.textPreset)
-                    .extend(new NotificationCompat.WearableExtender()
-                            .setGravity(Gravity.TOP));
-            wearableOptions.addPage(thirdPageBuilder.build());
-
-            wearableOptions.setGravity(Gravity.BOTTOM);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class ContentActionNotificationPreset extends NotificationPreset {
-        public ContentActionNotificationPreset() {
-            super(R.string.content_action_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            Notification secondPage = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.second_page_content_title))
-                    .setContentText(context.getString(R.string.second_page_content_text))
-                    .extend(new NotificationCompat.WearableExtender()
-                            .setContentAction(1))
-                    .build();
-
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-            NotificationCompat.Action action = new NotificationCompat.Action.Builder(
-                    R.drawable.ic_result_open, null, NotificationUtil.getExamplePendingIntent(
-                            context, R.string.example_content_action_clicked)).build();
-            NotificationCompat.Action action2 = new NotificationCompat.Action.Builder(
-                    R.drawable.ic_result_open, null, NotificationUtil.getExamplePendingIntent(
-                            context, R.string.example_content_action2_clicked)).build();
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender()
-                            .addAction(action)
-                            .addAction(action2)
-                            .addPage(secondPage)
-                            .setContentAction(0)
-                            .setHintHideIcon(true);
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-
-        @Override
-        public boolean actionsRequired() {
-            return true;
-        }
-    }
-
-    private static class ContentIconNotificationPreset extends NotificationPreset {
-        public ContentIconNotificationPreset() {
-            super(R.string.content_icon_example, R.string.example_content_title,
-                    R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            Notification secondPage = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.second_page_content_title))
-                    .setContentText(context.getString(R.string.second_page_content_text))
-                    .extend(new NotificationCompat.WearableExtender()
-                            .setContentIcon(R.drawable.content_icon_small)
-                            .setContentIconGravity(Gravity.START))
-                    .build();
-
-            Notification thirdPage = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.third_page_content_title))
-                    .setContentText(context.getString(R.string.third_page_content_text))
-                    .extend(new NotificationCompat.WearableExtender()
-                            .setContentIcon(R.drawable.content_icon_large))
-                    .build();
-
-            Notification fourthPage = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.fourth_page_content_title))
-                    .setContentText(context.getString(R.string.fourth_page_content_text))
-                    .extend(new NotificationCompat.WearableExtender()
-                            .setContentIcon(R.drawable.content_icon_large)
-                            .setContentIconGravity(Gravity.START))
-                    .build();
-
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-            NotificationCompat.WearableExtender wearableOptions =
-                    new NotificationCompat.WearableExtender()
-                            .setHintHideIcon(true)
-                            .setContentIcon(R.drawable.content_icon_small)
-                            .addPage(secondPage)
-                            .addPage(thirdPage)
-                            .addPage(fourthPage);
-            applyBasicOptions(context, builder, wearableOptions, options);
-            builder.extend(wearableOptions);
-            return new Notification[] { builder.build() };
-        }
-    }
-
-    private static class MultiplePageNotificationPreset extends NotificationPreset {
-        public MultiplePageNotificationPreset() {
-            super(R.string.multiple_page_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.second_page_content_title))
-                    .setContentText(context.getString(R.string.second_page_content_text));
-
-            NotificationCompat.Builder firstPageBuilder = new NotificationCompat.Builder(context);
-            NotificationCompat.WearableExtender firstPageWearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, firstPageBuilder, firstPageWearableOptions, options);
-
-            Integer firstBackground = options.backgroundIds == null
-                    ? null : options.backgroundIds[0];
-            if (firstBackground != null) {
-                NotificationCompat.BigPictureStyle style =
-                        new NotificationCompat.BigPictureStyle();
-                style.bigPicture(BitmapFactory.decodeResource(context.getResources(),
-                        firstBackground));
-                firstPageBuilder.setStyle(style);
-            }
-
-            Integer secondBackground = options.backgroundIds == null
-                    ? null : options.backgroundIds[1];
-            if (secondBackground != null) {
-                NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
-                style.bigPicture(BitmapFactory.decodeResource(context.getResources(),
-                        secondBackground));
-                secondPageBuilder.setStyle(style);
-            }
-
-            firstPageBuilder.extend(
-                    firstPageWearableOptions.addPage(secondPageBuilder.build()));
-
-            return new Notification[]{ firstPageBuilder.build() };
-        }
-
-        @Override
-        public int countBackgroundPickersRequired() {
-            return 2; // This sample does 2 pages notifications.
-        }
-    }
-
-    private static class NotificationBundlePreset extends NotificationPreset {
-        public NotificationBundlePreset() {
-            super(R.string.bundle_example, R.string.example_content_title,
-                R.string.example_content_text);
-        }
-
-        @Override
-        public Notification[] buildNotifications(Context context, BuildOptions options) {
-            NotificationCompat.Builder childBuilder1 = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.first_child_content_title))
-                    .setContentText(context.getString(R.string.first_child_content_text))
-                    .setSmallIcon(R.mipmap.ic_launcher)
-                    .setLocalOnly(options.isLocalOnly)
-                    .setGroup(EXAMPLE_GROUP_KEY)
-                    .setSortKey("0");
-
-            NotificationCompat.Builder childBuilder2 = new NotificationCompat.Builder(context)
-                    .setContentTitle(context.getString(R.string.second_child_content_title))
-                    .setContentText(context.getString(R.string.second_child_content_text))
-                    .setSmallIcon(R.mipmap.ic_launcher)
-                    .addAction(R.mipmap.ic_launcher,
-                            context.getString(R.string.second_child_action),
-                            NotificationUtil.getExamplePendingIntent(
-                                    context, R.string.second_child_action_clicked))
-                    .setLocalOnly(options.isLocalOnly)
-                    .setGroup(EXAMPLE_GROUP_KEY)
-                    .setSortKey("1");
-
-            NotificationCompat.Builder summaryBuilder = new NotificationCompat.Builder(context)
-                    .setGroup(EXAMPLE_GROUP_KEY)
-                    .setGroupSummary(true);
-
-            NotificationCompat.WearableExtender summaryWearableOptions =
-                    new NotificationCompat.WearableExtender();
-            applyBasicOptions(context, summaryBuilder, summaryWearableOptions, options);
-            summaryBuilder.extend(summaryWearableOptions);
-
-            return new Notification[] { summaryBuilder.build(), childBuilder1.build(),
-                    childBuilder2.build() };
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationUtil.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationUtil.java
deleted file mode 100644
index b5c43e5..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/NotificationUtil.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-
-public class NotificationUtil {
-    public static final String EXTRA_MESSAGE =
-            "com.example.android.notifications.MESSAGE";
-    public static final String EXTRA_REPLY =
-            "com.example.android.notifications.REPLY";
-
-    public static PendingIntent getExamplePendingIntent(Context context, int messageResId) {
-        Intent intent = new Intent(NotificationIntentReceiver.ACTION_EXAMPLE)
-                .setClass(context, NotificationIntentReceiver.class);
-        intent.putExtra(EXTRA_MESSAGE, context.getString(messageResId));
-        return PendingIntent.getBroadcast(context, messageResId /* requestCode */, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/PriorityPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/PriorityPreset.java
deleted file mode 100644
index eaddb83..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/PriorityPreset.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.support.v4.app.NotificationCompat;
-
-/**
- * Base class for notification priority presets.
- */
-public abstract class PriorityPreset extends NamedPreset {
-    public PriorityPreset(int nameResId) {
-        super(nameResId);
-    }
-
-    /** Apply the priority to a notification builder */
-    public abstract void apply(NotificationCompat.Builder builder,
-            NotificationCompat.WearableExtender wearableOptions);
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/PriorityPresets.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/PriorityPresets.java
deleted file mode 100644
index 7d1ed7c..0000000
--- a/wearable/wear/Notifications/Application/src/main/java/com/example/android/notifications/PriorityPresets.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Notification;
-import android.support.v4.app.NotificationCompat;
-
-/**
- * Collection of notification priority presets.
- */
-public class PriorityPresets {
-    public static final PriorityPreset DEFAULT = new SimplePriorityPreset(
-            R.string.default_priority, Notification.PRIORITY_DEFAULT);
-
-    public static final PriorityPreset[] PRESETS = new PriorityPreset[] {
-            new SimplePriorityPreset(R.string.min_priority, Notification.PRIORITY_MIN),
-            new SimplePriorityPreset(R.string.low_priority, Notification.PRIORITY_LOW),
-            DEFAULT,
-            new SimplePriorityPreset(R.string.high_priority, Notification.PRIORITY_HIGH),
-            new SimplePriorityPreset(R.string.max_priority, Notification.PRIORITY_MAX)
-    };
-
-    /**
-     * Simple notification priority preset that sets a priority using
-     * {@link android.support.v4.app.NotificationCompat.Builder#setPriority}
-     */
-    private static class SimplePriorityPreset extends PriorityPreset {
-        private final int mPriority;
-
-        public SimplePriorityPreset(int nameResId, int priority) {
-            super(nameResId);
-            mPriority = priority;
-        }
-
-        @Override
-        public void apply(NotificationCompat.Builder builder,
-                NotificationCompat.WearableExtender wearableOptions) {
-            builder.setPriority(mPriority);
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/ActionsPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/ActionsPreset.java
new file mode 100644
index 0000000..a550912
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/ActionsPreset.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.content.Context;
+import android.support.v4.app.NotificationCompat;
+
+/**
+ * Base class for notification actions presets.
+ */
+public abstract class ActionsPreset extends NamedPreset {
+    public ActionsPreset(int nameResId) {
+        super(nameResId);
+    }
+
+    /** Apply the priority to a notification builder */
+    public abstract void apply(Context context, NotificationCompat.Builder builder,
+            NotificationCompat.WearableExtender wearableOptions);
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/ActionsPresets.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/ActionsPresets.java
new file mode 100644
index 0000000..ff639dc
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/ActionsPresets.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.content.Context;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.RemoteInput;
+
+/**
+ * Collection of notification actions presets.
+ */
+public class ActionsPresets {
+    public static final ActionsPreset NO_ACTIONS_PRESET = new NoActionsPreset();
+    public static final ActionsPreset SINGLE_ACTION_PRESET = new SingleActionPreset();
+
+    public static final ActionsPreset[] PRESETS = new ActionsPreset[] {
+            NO_ACTIONS_PRESET,
+            SINGLE_ACTION_PRESET,
+            new ReplyActionPreset(),
+            new ReplyWithChoicesActionPreset(),
+            new DifferentActionsOnPhoneAndWearable(),
+            new LongTitleActionPreset()
+    };
+
+    private static class NoActionsPreset extends ActionsPreset {
+        public NoActionsPreset() {
+            super(R.string.no_actions);
+        }
+
+        @Override
+        public void apply(Context context, NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+        }
+    }
+
+    private static class SingleActionPreset extends ActionsPreset {
+        public SingleActionPreset() {
+            super(R.string.single_action);
+        }
+
+        @Override
+        public void apply(Context context, NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+            builder.addAction(R.drawable.ic_full_action,
+                    context.getString(R.string.example_action),
+                    NotificationUtil.getExamplePendingIntent(context,
+                            R.string.example_action_clicked))
+                    .build();
+        }
+    }
+
+    private static class LongTitleActionPreset extends ActionsPreset {
+        public LongTitleActionPreset() {
+            super(R.string.long_title_action);
+        }
+
+        @Override
+        public void apply(Context context, NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+            builder.addAction(R.drawable.ic_full_action,
+                    context.getString(R.string.example_action_long_title),
+                    NotificationUtil.getExamplePendingIntent(context,
+                            R.string.example_action_clicked))
+                    .build();
+        }
+    }
+
+    private static class ReplyActionPreset extends ActionsPreset {
+        public ReplyActionPreset() {
+            super(R.string.reply_action);
+        }
+
+        @Override
+        public void apply(Context context, NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+            RemoteInput remoteInput = new RemoteInput.Builder(NotificationUtil.EXTRA_REPLY)
+                    .setLabel(context.getString(R.string.example_reply_label))
+                    .build();
+            NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+                    R.drawable.ic_full_reply,
+                    context.getString(R.string.example_reply_action),
+                    NotificationUtil.getExamplePendingIntent(context,
+                            R.string.example_reply_action_clicked))
+                    .addRemoteInput(remoteInput)
+                    .build();
+            builder.addAction(action);
+        }
+    }
+
+    private static class ReplyWithChoicesActionPreset extends ActionsPreset {
+        public ReplyWithChoicesActionPreset() {
+            super(R.string.reply_action_with_choices);
+        }
+
+        @Override
+        public void apply(Context context, NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+            RemoteInput remoteInput = new RemoteInput.Builder(NotificationUtil.EXTRA_REPLY)
+                    .setLabel(context.getString(R.string.example_reply_answer_label))
+                    .setChoices(new String[] { context.getString(R.string.yes),
+                            context.getString(R.string.no), context.getString(R.string.maybe) })
+                    .build();
+            NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+                    R.drawable.ic_full_reply,
+                    context.getString(R.string.example_reply_action),
+                    NotificationUtil.getExamplePendingIntent(context,
+                            R.string.example_reply_action_clicked))
+                    .addRemoteInput(remoteInput)
+                    .build();
+            wearableOptions.addAction(action);
+        }
+    }
+
+    private static class DifferentActionsOnPhoneAndWearable extends ActionsPreset {
+        public DifferentActionsOnPhoneAndWearable() {
+            super(R.string.different_actions_on_phone_and_wearable);
+        }
+
+        @Override
+        public void apply(Context context, NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+            NotificationCompat.Action phoneAction = new NotificationCompat.Action.Builder(
+                    R.drawable.ic_full_action,
+                    context.getString(R.string.phone_action),
+                    NotificationUtil.getExamplePendingIntent(context,
+                            R.string.phone_action_clicked))
+                    .build();
+            builder.addAction(phoneAction);
+
+            RemoteInput remoteInput = new RemoteInput.Builder(NotificationUtil.EXTRA_REPLY)
+                    .setLabel(context.getString(R.string.example_reply_label))
+                    .build();
+
+            NotificationCompat.Action wearableAction = new NotificationCompat.Action.Builder(
+                    R.drawable.ic_full_reply,
+                    context.getString(R.string.wearable_action),
+                    NotificationUtil.getExamplePendingIntent(context,
+                            R.string.wearable_action_clicked))
+                    .addRemoteInput(remoteInput)
+                    .build();
+            wearableOptions.addAction(wearableAction);
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/BackgroundPickers.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/BackgroundPickers.java
new file mode 100644
index 0000000..aedd548
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/BackgroundPickers.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the background image pickers.
+ */
+public class BackgroundPickers {
+
+    public interface OnBackgroundPickersChangedListener {
+        public void onBackgroundPickersChanged(BackgroundPickers pickers);
+    }
+
+    private final ViewGroup mContainer;
+    private final OnPickedListener mOnPickedListener;
+    private final List<ViewGroup> mPickers;
+    private final OnBackgroundPickersChangedListener listener;
+
+    public BackgroundPickers(ViewGroup container, OnBackgroundPickersChangedListener listener) {
+        this.mContainer = container;
+        this.mOnPickedListener = new OnPickedListener();
+        this.mPickers = new ArrayList<ViewGroup>();
+        this.listener = listener;
+    }
+
+    /**
+     * Generates the pickers as necessary.
+     */
+    public void generatePickers(int count) {
+        // Clear existing containers.
+        clear();
+
+        // Fill in new pickers.
+        LayoutInflater inflater = LayoutInflater.from(mContainer.getContext());
+        Resources res = mContainer.getResources();
+        for (int i = 0; i < count; i++) {
+            View picker = inflater.inflate(R.layout.background_picker, mContainer, false);
+            TextView label = (TextView) picker.findViewById(R.id.bg_picker_label);
+            label.setText(String.format(res.getString(R.string.bg_picker_label), i+1));
+            ViewGroup pickerBox = (ViewGroup) picker.findViewById(R.id.bg_picker_container);
+            mPickers.add(pickerBox);
+            for (int j = 0; j < pickerBox.getChildCount(); j++) {
+                ImageView img = (ImageView) pickerBox.getChildAt(j);
+                img.setOnClickListener(mOnPickedListener);
+            }
+            mContainer.addView(picker);
+        }
+    }
+
+    /**
+     * Returns the background resource for the picker at the given index.
+     * @param position Index of the background picker.
+     * @return Id of the background image resource. null if no image is picked.
+     */
+    public Integer getRes(int position) {
+        String tag = (String) mPickers.get(position).getTag();
+        if (tag == null) {
+            return null;
+        }
+
+        Context context = mContainer.getContext();
+        return context.getResources().getIdentifier(tag, "drawable", context.getPackageName());
+    }
+
+    /**
+     * Returns the all the background resources for the pickers managed by this object. Returns null
+     * if no pickers exist.
+     */
+    public Integer[] getRes() {
+        if (mPickers.size() == 0) {
+            return null;
+        }
+
+        Integer[] res = new Integer[mPickers.size()];
+        for (int i = 0; i < mPickers.size(); i++) {
+            res[i] = getRes(i);
+        }
+        return res;
+    }
+
+    /**
+     * Clears the pickers.
+     */
+    public void clear() {
+        mContainer.removeAllViews();
+        mPickers.clear();
+    }
+
+    public int getCount() {
+        return mPickers.size();
+    }
+
+    private class OnPickedListener implements View.OnClickListener {
+
+        @Override
+        public void onClick(View view) {
+            ImageView pickedView = (ImageView) view;
+            ViewGroup pickerBox = (ViewGroup) view.getParent();
+
+            // Clear old selection.
+            for (int i = 0; i < pickerBox.getChildCount(); i++) {
+                ImageView childView = (ImageView) pickerBox.getChildAt(i);
+                childView.setBackgroundResource(R.drawable.unselected_background);
+            }
+
+            // Set new selection.
+            pickedView.setBackgroundResource(R.drawable.selected_background);
+            pickerBox.setTag(pickedView.getTag());
+
+            if (listener != null) {
+                listener.onBackgroundPickersChanged(BackgroundPickers.this);
+            }
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/MainActivity.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/MainActivity.java
new file mode 100644
index 0000000..4ade3ed
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/MainActivity.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.app.NotificationManagerCompat;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.Arrays;
+
+/**
+ * Main activity which posts a notification when resumed, and allows customization
+ * of that notification via controls.
+ */
+public class MainActivity extends Activity implements Handler.Callback {
+    private static final int MSG_POST_NOTIFICATIONS = 0;
+    private static final long POST_NOTIFICATIONS_DELAY_MS = 200;
+
+    private Handler mHandler;
+    private Spinner mPresetSpinner;
+    private EditText mTitleEditText;
+    private EditText mTextEditText;
+    private TextWatcher mTextChangedListener;
+    private Spinner mPrioritySpinner;
+    private Spinner mActionsSpinner;
+    private CheckBox mIncludeLargeIconCheckbox;
+    private CheckBox mLocalOnlyCheckbox;
+    private CheckBox mIncludeContentIntentCheckbox;
+    private CheckBox mVibrateCheckbox;
+    private BackgroundPickers mBackgroundPickers;
+    private int postedNotificationCount = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mHandler = new Handler(this);
+        mTextChangedListener = new UpdateNotificationsOnTextChangeListener();
+
+        initPresetSpinner();
+        initTitleEditText();
+        initTextEditText();
+        initPrioritySpinner();
+        initActionsSpinner();
+        initIncludeLargeIconCheckbox();
+        initLocalOnlyCheckbox();
+        initIncludeContentIntentCheckbox();
+        initVibrateCheckbox();
+        initBackgroundPickers();
+
+        NotificationPreset preset = NotificationPresets.PRESETS[
+                mPresetSpinner.getSelectedItemPosition()];
+        updateTextEditors(preset);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        updateNotifications(false /* cancelExisting */);
+    }
+
+    private void initPresetSpinner() {
+        mPresetSpinner = (Spinner) findViewById(R.id.preset_spinner);
+        mPresetSpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this,
+                NotificationPresets.PRESETS));
+        mPresetSpinner.post(new Runnable() {
+            @Override
+            public void run() {
+                mPresetSpinner.setOnItemSelectedListener(new PresetSpinnerListener());
+            }
+        });
+    }
+
+    private void initTitleEditText() {
+        mTitleEditText = (EditText) findViewById(R.id.title_editor);
+    }
+
+    private void initTextEditText() {
+        mTextEditText = (EditText) findViewById(R.id.text_editor);
+    }
+
+    private void initPrioritySpinner() {
+        mPrioritySpinner = (Spinner) findViewById(R.id.priority_spinner);
+        mPrioritySpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this,
+                PriorityPresets.PRESETS));
+        mPrioritySpinner.setSelection(Arrays.asList(PriorityPresets.PRESETS)
+                .indexOf(PriorityPresets.DEFAULT));
+        mPrioritySpinner.post(new Runnable() {
+            @Override
+            public void run() {
+                mPrioritySpinner.setOnItemSelectedListener(
+                        new UpdateNotificationsOnItemSelectedListener(true /* cancelExisting */));
+            }
+        });
+    }
+
+    private void initActionsSpinner() {
+        mActionsSpinner = (Spinner) findViewById(R.id.actions_spinner);
+        mActionsSpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this,
+                ActionsPresets.PRESETS));
+        mActionsSpinner.post(new Runnable() {
+            @Override
+            public void run() {
+                mActionsSpinner.setOnItemSelectedListener(
+                        new UpdateNotificationsOnItemSelectedListener(false /* cancelExisting */));
+            }
+        });
+    }
+
+    private void initIncludeLargeIconCheckbox() {
+        mIncludeLargeIconCheckbox = (CheckBox) findViewById(R.id.include_large_icon_checkbox);
+        mIncludeLargeIconCheckbox.setOnCheckedChangeListener(
+                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
+    }
+
+    private void initLocalOnlyCheckbox() {
+        mLocalOnlyCheckbox = (CheckBox) findViewById(R.id.local_only_checkbox);
+        mLocalOnlyCheckbox.setOnCheckedChangeListener(
+                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
+    }
+
+    private void initIncludeContentIntentCheckbox() {
+        mIncludeContentIntentCheckbox = (CheckBox) findViewById(
+                R.id.include_content_intent_checkbox);
+        mIncludeContentIntentCheckbox.setOnCheckedChangeListener(
+                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
+    }
+
+    private void initVibrateCheckbox() {
+        mVibrateCheckbox = (CheckBox) findViewById(R.id.vibrate_checkbox);
+        mVibrateCheckbox.setOnCheckedChangeListener(
+                new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */));
+    }
+
+    private void initBackgroundPickers() {
+        mBackgroundPickers = new BackgroundPickers(
+                (ViewGroup) findViewById(R.id.background_pickers),
+                new BackgroundPickerListener());
+    }
+
+    private void updateTextEditors(NotificationPreset preset) {
+        mTitleEditText.setText(getString(preset.titleResId));
+        mTextEditText.setText(getString(preset.textResId));
+        if (preset == NotificationPresets.BASIC) {
+            findViewById(R.id.title_edit_field).setVisibility(View.VISIBLE);
+            mTitleEditText.addTextChangedListener(mTextChangedListener);
+            findViewById(R.id.text_edit_field).setVisibility(View.VISIBLE);
+            mTextEditText.addTextChangedListener(mTextChangedListener);
+        } else {
+            findViewById(R.id.title_edit_field).setVisibility(View.GONE);
+            mTitleEditText.removeTextChangedListener(mTextChangedListener);
+            findViewById(R.id.text_edit_field).setVisibility(View.GONE);
+            mTextEditText.removeTextChangedListener(mTextChangedListener);
+        }
+    }
+
+    /**
+     * Begin to re-post the sample notification(s).
+     */
+    private void updateNotifications(boolean cancelExisting) {
+        // Disable messages to skip notification deleted messages during cancel.
+        sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_DISABLE_MESSAGES)
+                .setClass(this, NotificationIntentReceiver.class));
+
+        if (cancelExisting) {
+            // Cancel all existing notifications to trigger fresh-posting behavior: For example,
+            // switching from HIGH to LOW priority does not cause a reordering in Notification Shade.
+            NotificationManagerCompat.from(this).cancelAll();
+            postedNotificationCount = 0;
+
+            // Post the updated notifications on a delay to avoid a cancel+post race condition
+            // with notification manager.
+            mHandler.removeMessages(MSG_POST_NOTIFICATIONS);
+            mHandler.sendEmptyMessageDelayed(MSG_POST_NOTIFICATIONS, POST_NOTIFICATIONS_DELAY_MS);
+        } else {
+            postNotifications();
+        }
+    }
+
+    /**
+     * Post the sample notification(s) using current options.
+     */
+    private void postNotifications() {
+        sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_ENABLE_MESSAGES)
+                .setClass(this, NotificationIntentReceiver.class));
+
+        NotificationPreset preset = NotificationPresets.PRESETS[
+                mPresetSpinner.getSelectedItemPosition()];
+        CharSequence titlePreset = mTitleEditText.getText();
+        CharSequence textPreset = mTextEditText.getText();
+        PriorityPreset priorityPreset = PriorityPresets.PRESETS[
+                mPrioritySpinner.getSelectedItemPosition()];
+        ActionsPreset actionsPreset = ActionsPresets.PRESETS[
+                mActionsSpinner.getSelectedItemPosition()];
+        if (preset.actionsRequired() && actionsPreset == ActionsPresets.NO_ACTIONS_PRESET) {
+            // If actions are required, but the no-actions preset was selected, change presets.
+            actionsPreset = ActionsPresets.SINGLE_ACTION_PRESET;
+            mActionsSpinner.setSelection(Arrays.asList(ActionsPresets.PRESETS).indexOf(
+                    actionsPreset), true);
+        }
+        NotificationPreset.BuildOptions options = new NotificationPreset.BuildOptions(
+                titlePreset,
+                textPreset,
+                priorityPreset,
+                actionsPreset,
+                mIncludeLargeIconCheckbox.isChecked(),
+                mLocalOnlyCheckbox.isChecked(),
+                mIncludeContentIntentCheckbox.isChecked(),
+                mVibrateCheckbox.isChecked(),
+                mBackgroundPickers.getRes());
+        Notification[] notifications = preset.buildNotifications(this, options);
+
+        // Post new notifications
+        for (int i = 0; i < notifications.length; i++) {
+            NotificationManagerCompat.from(this).notify(i, notifications[i]);
+        }
+        // Cancel any that are beyond the current count.
+        for (int i = notifications.length; i < postedNotificationCount; i++) {
+            NotificationManagerCompat.from(this).cancel(i);
+        }
+        postedNotificationCount = notifications.length;
+    }
+
+    @Override
+    public boolean handleMessage(Message message) {
+        switch (message.what) {
+            case MSG_POST_NOTIFICATIONS:
+                postNotifications();
+                return true;
+        }
+        return false;
+    }
+
+    private class PresetSpinnerListener implements AdapterView.OnItemSelectedListener {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            NotificationPreset preset = NotificationPresets.PRESETS[position];
+            mBackgroundPickers.generatePickers(preset.countBackgroundPickersRequired());
+            updateTextEditors(preset);
+            updateNotifications(false /* cancelExisting */);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> adapterView) {
+        }
+    }
+
+    private class UpdateNotificationsOnTextChangeListener implements TextWatcher {
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        }
+
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            updateNotifications(false /* cancelExisting */);
+        }
+    }
+
+    private class UpdateNotificationsOnItemSelectedListener
+            implements AdapterView.OnItemSelectedListener {
+        private final boolean mCancelExisting;
+
+        public UpdateNotificationsOnItemSelectedListener(boolean cancelExisting) {
+            mCancelExisting = cancelExisting;
+        }
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            updateNotifications(mCancelExisting);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> adapterView) {
+        }
+    }
+
+    private class UpdateNotificationsOnCheckedChangeListener
+            implements CompoundButton.OnCheckedChangeListener {
+        private final boolean mCancelExisting;
+
+        public UpdateNotificationsOnCheckedChangeListener(boolean cancelExisting) {
+            mCancelExisting = cancelExisting;
+        }
+
+        @Override
+        public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+            updateNotifications(mCancelExisting);
+        }
+    }
+
+    private class BackgroundPickerListener
+            implements BackgroundPickers.OnBackgroundPickersChangedListener {
+        @Override
+        public void onBackgroundPickersChanged(BackgroundPickers pickers) {
+            updateNotifications(false /* cancelExisting */);
+        }
+    }
+
+    private class NamedPresetSpinnerArrayAdapter extends ArrayAdapter<NamedPreset> {
+        public NamedPresetSpinnerArrayAdapter(Context context, NamedPreset[] presets) {
+            super(context, R.layout.simple_spinner_item, presets);
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) super.getDropDownView(position, convertView, parent);
+            view.setText(getString(getItem(position).nameResId));
+            return view;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) getLayoutInflater().inflate(
+                    android.R.layout.simple_spinner_item, parent, false);
+            view.setText(getString(getItem(position).nameResId));
+            return view;
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NamedPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NamedPreset.java
new file mode 100644
index 0000000..afc5f16
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NamedPreset.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+/**
+ * Base class for presets that have a simple name to display.
+ */
+public abstract class NamedPreset {
+    public final int nameResId;
+
+    public NamedPreset(int nameResId) {
+        this.nameResId = nameResId;
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationIntentReceiver.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationIntentReceiver.java
new file mode 100644
index 0000000..39a1c78
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationIntentReceiver.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.RemoteInput;
+import android.widget.Toast;
+
+/**
+ * Broadcast receiver to post toast messages in response to notification intents firing.
+ */
+public class NotificationIntentReceiver extends BroadcastReceiver {
+    public static final String ACTION_EXAMPLE =
+            "com.example.android.support.wearable.notifications.ACTION_EXAMPLE";
+    public static final String ACTION_ENABLE_MESSAGES =
+            "com.example.android.support.wearable.notifications.ACTION_ENABLE_MESSAGES";
+    public static final String ACTION_DISABLE_MESSAGES =
+            "com.example.android.support.wearable.notifications.ACTION_DISABLE_MESSAGES";
+
+    private boolean mEnableMessages = true;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getAction().equals(ACTION_EXAMPLE)) {
+            if (mEnableMessages) {
+                String message = intent.getStringExtra(NotificationUtil.EXTRA_MESSAGE);
+                Bundle remoteInputResults = RemoteInput.getResultsFromIntent(intent);
+                CharSequence replyMessage = null;
+                if (remoteInputResults != null) {
+                    replyMessage = remoteInputResults.getCharSequence(NotificationUtil.EXTRA_REPLY);
+                }
+                if (replyMessage != null) {
+                    message = message + ": \"" + replyMessage + "\"";
+                }
+                Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+            }
+        } else if (intent.getAction().equals(ACTION_ENABLE_MESSAGES)) {
+            mEnableMessages = true;
+        } else if (intent.getAction().equals(ACTION_DISABLE_MESSAGES)) {
+            mEnableMessages = false;
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationPreset.java
new file mode 100644
index 0000000..a0dff18
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationPreset.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Notification;
+import android.content.Context;
+
+/**
+ * Base class for notification preset generators.
+ */
+public abstract class NotificationPreset extends NamedPreset {
+    public final int titleResId;
+    public final int textResId;
+
+    public NotificationPreset(int nameResId, int titleResId, int textResId) {
+        super(nameResId);
+        this.titleResId = titleResId;
+        this.textResId = textResId;
+    }
+
+    public static class BuildOptions {
+        public final CharSequence titlePreset;
+        public final CharSequence textPreset;
+        public final PriorityPreset priorityPreset;
+        public final ActionsPreset actionsPreset;
+        public final boolean includeLargeIcon;
+        public final boolean isLocalOnly;
+        public final boolean hasContentIntent;
+        public final boolean vibrate;
+        public final Integer[] backgroundIds;
+
+        public BuildOptions(CharSequence titlePreset, CharSequence textPreset,
+                PriorityPreset priorityPreset, ActionsPreset actionsPreset,
+                boolean includeLargeIcon, boolean isLocalOnly, boolean hasContentIntent,
+                boolean vibrate, Integer[] backgroundIds) {
+            this.titlePreset = titlePreset;
+            this.textPreset = textPreset;
+            this.priorityPreset = priorityPreset;
+            this.actionsPreset = actionsPreset;
+            this.includeLargeIcon = includeLargeIcon;
+            this.isLocalOnly = isLocalOnly;
+            this.hasContentIntent = hasContentIntent;
+            this.vibrate = vibrate;
+            this.backgroundIds = backgroundIds;
+        }
+    }
+
+    /** Build a notification with this preset and the provided options */
+    public abstract Notification[] buildNotifications(Context context, BuildOptions options);
+
+    /** Whether actions are required to use this preset. */
+    public boolean actionsRequired() {
+        return false;
+    }
+
+    /** Number of background pickers required */
+    public int countBackgroundPickersRequired() {
+        return 0;
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationPresets.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationPresets.java
new file mode 100644
index 0000000..641b861
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationPresets.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.support.v4.app.NotificationCompat;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.UnderlineSpan;
+import android.view.Gravity;
+
+/**
+ * Collection of notification builder presets.
+ */
+public class NotificationPresets {
+    private static final String EXAMPLE_GROUP_KEY = "example";
+
+    public static final NotificationPreset BASIC = new BasicNotificationPreset();
+    public static final NotificationPreset STYLIZED_TEXT = new StylizedTextNotificationPreset();
+    public static final NotificationPreset INBOX = new InboxNotificationPreset();
+    public static final NotificationPreset BIG_PICTURE = new BigPictureNotificationPreset();
+    public static final NotificationPreset BIG_TEXT = new BigTextNotificationPreset();
+    public static final NotificationPreset BOTTOM_ALIGNED = new BottomAlignedNotificationPreset();
+    public static final NotificationPreset GRAVITY = new GravityNotificationPreset();
+    public static final NotificationPreset CONTENT_ACTION = new ContentActionNotificationPreset();
+    public static final NotificationPreset CONTENT_ICON = new ContentIconNotificationPreset();
+    public static final NotificationPreset MULTIPLE_PAGE = new MultiplePageNotificationPreset();
+    public static final NotificationPreset BUNDLE = new NotificationBundlePreset();
+    public static final NotificationPreset BARCODE = new NotificationBarcodePreset();
+
+    public static final NotificationPreset[] PRESETS = new NotificationPreset[] {
+            BASIC,
+            STYLIZED_TEXT,
+            INBOX,
+            BIG_PICTURE,
+            BIG_TEXT,
+            BOTTOM_ALIGNED,
+            GRAVITY,
+            CONTENT_ACTION,
+            CONTENT_ICON,
+            MULTIPLE_PAGE,
+            BUNDLE,
+            BARCODE
+    };
+
+    private static NotificationCompat.Builder applyBasicOptions(Context context,
+            NotificationCompat.Builder builder, NotificationCompat.WearableExtender wearableOptions,
+            NotificationPreset.BuildOptions options) {
+        builder.setContentTitle(options.titlePreset)
+                .setContentText(options.textPreset)
+                .setSmallIcon(R.mipmap.ic_launcher)
+                .setDeleteIntent(NotificationUtil.getExamplePendingIntent(
+                        context, R.string.example_notification_deleted));
+        options.actionsPreset.apply(context, builder, wearableOptions);
+        options.priorityPreset.apply(builder, wearableOptions);
+        if (options.includeLargeIcon) {
+            builder.setLargeIcon(BitmapFactory.decodeResource(
+                    context.getResources(), R.drawable.example_large_icon));
+        }
+        if (options.isLocalOnly) {
+            builder.setLocalOnly(true);
+        }
+        if (options.hasContentIntent) {
+            builder.setContentIntent(NotificationUtil.getExamplePendingIntent(context,
+                    R.string.content_intent_clicked));
+        }
+        if (options.vibrate) {
+            builder.setVibrate(new long[] {0, 100, 50, 100} );
+        }
+        return builder;
+    }
+
+    private static class BasicNotificationPreset extends NotificationPreset {
+        public BasicNotificationPreset() {
+            super(R.string.basic_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class StylizedTextNotificationPreset extends NotificationPreset {
+        public StylizedTextNotificationPreset() {
+            super(R.string.stylized_text_example, R.string.example_content_title,
+                    R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle();
+
+            SpannableStringBuilder title = new SpannableStringBuilder();
+            appendStyled(title, "Stylized", new StyleSpan(Typeface.BOLD_ITALIC));
+            title.append(" title");
+            SpannableStringBuilder text = new SpannableStringBuilder("Stylized text: ");
+            appendStyled(text, "C", new ForegroundColorSpan(Color.RED));
+            appendStyled(text, "O", new ForegroundColorSpan(Color.GREEN));
+            appendStyled(text, "L", new ForegroundColorSpan(Color.BLUE));
+            appendStyled(text, "O", new ForegroundColorSpan(Color.YELLOW));
+            appendStyled(text, "R", new ForegroundColorSpan(Color.MAGENTA));
+            appendStyled(text, "S", new ForegroundColorSpan(Color.CYAN));
+            text.append("; ");
+            appendStyled(text, "1.25x size", new RelativeSizeSpan(1.25f));
+            text.append("; ");
+            appendStyled(text, "0.75x size", new RelativeSizeSpan(0.75f));
+            text.append("; ");
+            appendStyled(text, "underline", new UnderlineSpan());
+            text.append("; ");
+            appendStyled(text, "strikethrough", new StrikethroughSpan());
+            text.append("; ");
+            appendStyled(text, "bold", new StyleSpan(Typeface.BOLD));
+            text.append("; ");
+            appendStyled(text, "italic", new StyleSpan(Typeface.ITALIC));
+            text.append("; ");
+            appendStyled(text, "sans-serif-thin", new TypefaceSpan("sans-serif-thin"));
+            text.append("; ");
+            appendStyled(text, "monospace", new TypefaceSpan("monospace"));
+            text.append("; ");
+            appendStyled(text, "sub", new SubscriptSpan());
+            text.append("script");
+            appendStyled(text, "super", new SuperscriptSpan());
+
+            style.setBigContentTitle(title);
+            style.bigText(text);
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
+                    .setStyle(style);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+
+        private void appendStyled(SpannableStringBuilder builder, String str, Object... spans) {
+            builder.append(str);
+            for (Object span : spans) {
+                builder.setSpan(span, builder.length() - str.length(), builder.length(), 0);
+            }
+        }
+    }
+
+    private static class InboxNotificationPreset extends NotificationPreset {
+        public InboxNotificationPreset() {
+            super(R.string.inbox_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
+            style.addLine(context.getString(R.string.inbox_style_example_line1));
+            style.addLine(context.getString(R.string.inbox_style_example_line2));
+            style.addLine(context.getString(R.string.inbox_style_example_line3));
+            style.setBigContentTitle(context.getString(R.string.inbox_style_example_title));
+            style.setSummaryText(context.getString(R.string.inbox_style_example_summary_text));
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
+                    .setStyle(style);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class BigPictureNotificationPreset extends NotificationPreset {
+        public BigPictureNotificationPreset() {
+            super(R.string.big_picture_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
+            style.bigPicture(BitmapFactory.decodeResource(context.getResources(),
+                    R.drawable.example_big_picture));
+            style.setBigContentTitle(context.getString(R.string.big_picture_style_example_title));
+            style.setSummaryText(context.getString(
+                    R.string.big_picture_style_example_summary_text));
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
+                    .setStyle(style);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class BigTextNotificationPreset extends NotificationPreset {
+        public BigTextNotificationPreset() {
+            super(R.string.big_text_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle();
+            style.bigText(context.getString(R.string.big_text_example_big_text));
+            style.setBigContentTitle(context.getString(R.string.big_text_example_title));
+            style.setSummaryText(context.getString(R.string.big_text_example_summary_text));
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
+                    .setStyle(style);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class BottomAlignedNotificationPreset extends NotificationPreset {
+        public BottomAlignedNotificationPreset() {
+            super(R.string.bottom_aligned_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+
+            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context);
+            secondPageBuilder.setContentTitle(
+                    context.getString(R.string.second_page_content_title));
+            secondPageBuilder.setContentText(context.getString(R.string.big_text_example_big_text));
+            secondPageBuilder.extend(new NotificationCompat.WearableExtender()
+                            .setStartScrollBottom(true));
+
+            wearableOptions.addPage(secondPageBuilder.build());
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class GravityNotificationPreset extends NotificationPreset {
+        public GravityNotificationPreset() {
+            super(R.string.gravity_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, builder, wearableOptions, options);
+
+            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context)
+                    .setContentTitle(options.titlePreset)
+                    .setContentText(options.textPreset)
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setGravity(Gravity.CENTER_VERTICAL));
+            wearableOptions.addPage(secondPageBuilder.build());
+
+            NotificationCompat.Builder thirdPageBuilder = new NotificationCompat.Builder(context)
+                    .setContentTitle(options.titlePreset)
+                    .setContentText(options.textPreset)
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setGravity(Gravity.TOP));
+            wearableOptions.addPage(thirdPageBuilder.build());
+
+            wearableOptions.setGravity(Gravity.BOTTOM);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class ContentActionNotificationPreset extends NotificationPreset {
+        public ContentActionNotificationPreset() {
+            super(R.string.content_action_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            Notification secondPage = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.second_page_content_title))
+                    .setContentText(context.getString(R.string.second_page_content_text))
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setContentAction(1))
+                    .build();
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+            NotificationCompat.Action action = new NotificationCompat.Action.Builder(
+                    R.drawable.ic_result_open, null, NotificationUtil.getExamplePendingIntent(
+                            context, R.string.example_content_action_clicked)).build();
+            NotificationCompat.Action action2 = new NotificationCompat.Action.Builder(
+                    R.drawable.ic_result_open, null, NotificationUtil.getExamplePendingIntent(
+                            context, R.string.example_content_action2_clicked)).build();
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender()
+                            .addAction(action)
+                            .addAction(action2)
+                            .addPage(secondPage)
+                            .setContentAction(0)
+                            .setHintHideIcon(true);
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+
+        @Override
+        public boolean actionsRequired() {
+            return true;
+        }
+    }
+
+    private static class ContentIconNotificationPreset extends NotificationPreset {
+        public ContentIconNotificationPreset() {
+            super(R.string.content_icon_example, R.string.example_content_title,
+                    R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            Notification secondPage = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.second_page_content_title))
+                    .setContentText(context.getString(R.string.second_page_content_text))
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setContentIcon(R.drawable.content_icon_small)
+                            .setContentIconGravity(Gravity.START))
+                    .build();
+
+            Notification thirdPage = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.third_page_content_title))
+                    .setContentText(context.getString(R.string.third_page_content_text))
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setContentIcon(R.drawable.content_icon_large))
+                    .build();
+
+            Notification fourthPage = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.fourth_page_content_title))
+                    .setContentText(context.getString(R.string.fourth_page_content_text))
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setContentIcon(R.drawable.content_icon_large)
+                            .setContentIconGravity(Gravity.START))
+                    .build();
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+            NotificationCompat.WearableExtender wearableOptions =
+                    new NotificationCompat.WearableExtender()
+                            .setHintHideIcon(true)
+                            .setContentIcon(R.drawable.content_icon_small)
+                            .addPage(secondPage)
+                            .addPage(thirdPage)
+                            .addPage(fourthPage);
+            applyBasicOptions(context, builder, wearableOptions, options);
+            builder.extend(wearableOptions);
+            return new Notification[] { builder.build() };
+        }
+    }
+
+    private static class MultiplePageNotificationPreset extends NotificationPreset {
+        public MultiplePageNotificationPreset() {
+            super(R.string.multiple_page_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.second_page_content_title))
+                    .setContentText(context.getString(R.string.second_page_content_text));
+
+            NotificationCompat.Builder firstPageBuilder = new NotificationCompat.Builder(context);
+            NotificationCompat.WearableExtender firstPageWearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, firstPageBuilder, firstPageWearableOptions, options);
+
+            Integer firstBackground = options.backgroundIds == null
+                    ? null : options.backgroundIds[0];
+            if (firstBackground != null) {
+                NotificationCompat.BigPictureStyle style =
+                        new NotificationCompat.BigPictureStyle();
+                style.bigPicture(BitmapFactory.decodeResource(context.getResources(),
+                        firstBackground));
+                firstPageBuilder.setStyle(style);
+            }
+
+            Integer secondBackground = options.backgroundIds == null
+                    ? null : options.backgroundIds[1];
+            if (secondBackground != null) {
+                NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle();
+                style.bigPicture(BitmapFactory.decodeResource(context.getResources(),
+                        secondBackground));
+                secondPageBuilder.setStyle(style);
+            }
+
+            firstPageBuilder.extend(
+                    firstPageWearableOptions.addPage(secondPageBuilder.build()));
+
+            return new Notification[]{ firstPageBuilder.build() };
+        }
+
+        @Override
+        public int countBackgroundPickersRequired() {
+            return 2; // This sample does 2 pages notifications.
+        }
+    }
+
+    private static class NotificationBundlePreset extends NotificationPreset {
+        public NotificationBundlePreset() {
+            super(R.string.bundle_example, R.string.example_content_title,
+                R.string.example_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.Builder childBuilder1 = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.first_child_content_title))
+                    .setContentText(context.getString(R.string.first_child_content_text))
+                    .setSmallIcon(R.mipmap.ic_launcher)
+                    .setLocalOnly(options.isLocalOnly)
+                    .setGroup(EXAMPLE_GROUP_KEY)
+                    .setSortKey("0");
+
+            NotificationCompat.Builder childBuilder2 = new NotificationCompat.Builder(context)
+                    .setContentTitle(context.getString(R.string.second_child_content_title))
+                    .setContentText(context.getString(R.string.second_child_content_text))
+                    .setSmallIcon(R.mipmap.ic_launcher)
+                    .addAction(R.mipmap.ic_launcher,
+                            context.getString(R.string.second_child_action),
+                            NotificationUtil.getExamplePendingIntent(
+                                    context, R.string.second_child_action_clicked))
+                    .setLocalOnly(options.isLocalOnly)
+                    .setGroup(EXAMPLE_GROUP_KEY)
+                    .setSortKey("1");
+
+            NotificationCompat.Builder summaryBuilder = new NotificationCompat.Builder(context)
+                    .setGroup(EXAMPLE_GROUP_KEY)
+                    .setGroupSummary(true);
+
+            NotificationCompat.WearableExtender summaryWearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, summaryBuilder, summaryWearableOptions, options);
+            summaryBuilder.extend(summaryWearableOptions);
+
+            return new Notification[] { summaryBuilder.build(), childBuilder1.build(),
+                    childBuilder2.build() };
+        }
+    }
+
+    private static class NotificationBarcodePreset extends NotificationPreset {
+        public NotificationBarcodePreset() {
+            super(R.string.barcode_example, R.string.barcode_content_title,
+                    R.string.barcode_content_text);
+        }
+
+        @Override
+        public Notification[] buildNotifications(Context context, BuildOptions options) {
+            NotificationCompat.Builder secondPageBuilder = new NotificationCompat.Builder(context)
+                    .extend(new NotificationCompat.WearableExtender()
+                            .setHintShowBackgroundOnly(true)
+                            .setBackground(BitmapFactory.decodeResource(context.getResources(),
+                                    R.drawable.qr_code))
+                            .setHintAvoidBackgroundClipping(true)
+                            .setHintScreenTimeout(
+                                    NotificationCompat.WearableExtender.SCREEN_TIMEOUT_LONG));
+
+            NotificationCompat.Builder firstPageBuilder = new NotificationCompat.Builder(context);
+            NotificationCompat.WearableExtender firstPageWearableOptions =
+                    new NotificationCompat.WearableExtender();
+            applyBasicOptions(context, firstPageBuilder, firstPageWearableOptions, options);
+
+            firstPageBuilder.extend(
+                    firstPageWearableOptions.addPage(secondPageBuilder.build()));
+
+            return new Notification[]{ firstPageBuilder.build() };
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationUtil.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationUtil.java
new file mode 100644
index 0000000..389b90a
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/NotificationUtil.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+public class NotificationUtil {
+    public static final String EXTRA_MESSAGE =
+            "com.example.android.support.wearable.notifications.MESSAGE";
+    public static final String EXTRA_REPLY =
+            "com.example.android.support.wearable.notifications.REPLY";
+
+    public static PendingIntent getExamplePendingIntent(Context context, int messageResId) {
+        Intent intent = new Intent(NotificationIntentReceiver.ACTION_EXAMPLE)
+                .setClass(context, NotificationIntentReceiver.class);
+        intent.putExtra(EXTRA_MESSAGE, context.getString(messageResId));
+        return PendingIntent.getBroadcast(context, messageResId /* requestCode */, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/PriorityPreset.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/PriorityPreset.java
new file mode 100644
index 0000000..b4298fd
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/PriorityPreset.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.support.v4.app.NotificationCompat;
+
+/**
+ * Base class for notification priority presets.
+ */
+public abstract class PriorityPreset extends NamedPreset {
+    public PriorityPreset(int nameResId) {
+        super(nameResId);
+    }
+
+    /** Apply the priority to a notification builder */
+    public abstract void apply(NotificationCompat.Builder builder,
+            NotificationCompat.WearableExtender wearableOptions);
+}
diff --git a/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/PriorityPresets.java b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/PriorityPresets.java
new file mode 100644
index 0000000..c33b59d
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/java/com/example/android/support/wearable/notifications/PriorityPresets.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Notification;
+import android.support.v4.app.NotificationCompat;
+
+/**
+ * Collection of notification priority presets.
+ */
+public class PriorityPresets {
+    public static final PriorityPreset DEFAULT = new SimplePriorityPreset(
+            R.string.default_priority, Notification.PRIORITY_DEFAULT);
+
+    public static final PriorityPreset[] PRESETS = new PriorityPreset[] {
+            new SimplePriorityPreset(R.string.min_priority, Notification.PRIORITY_MIN),
+            new SimplePriorityPreset(R.string.low_priority, Notification.PRIORITY_LOW),
+            DEFAULT,
+            new SimplePriorityPreset(R.string.high_priority, Notification.PRIORITY_HIGH),
+            new SimplePriorityPreset(R.string.max_priority, Notification.PRIORITY_MAX)
+    };
+
+    /**
+     * Simple notification priority preset that sets a priority using
+     * {@link android.support.v4.app.NotificationCompat.Builder#setPriority}
+     */
+    private static class SimplePriorityPreset extends PriorityPreset {
+        private final int mPriority;
+
+        public SimplePriorityPreset(int nameResId, int priority) {
+            super(nameResId);
+            mPriority = priority;
+        }
+
+        @Override
+        public void apply(NotificationCompat.Builder builder,
+                NotificationCompat.WearableExtender wearableOptions) {
+            builder.setPriority(mPriority);
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Application/src/main/res/drawable-nodpi/qr_code.png b/wearable/wear/Notifications/Application/src/main/res/drawable-nodpi/qr_code.png
new file mode 100644
index 0000000..0dcd7b7
--- /dev/null
+++ b/wearable/wear/Notifications/Application/src/main/res/drawable-nodpi/qr_code.png
Binary files differ
diff --git a/wearable/wear/Notifications/Application/src/main/res/values/colors.xml b/wearable/wear/Notifications/Application/src/main/res/values/colors.xml
index 9464085..fbcf956 100644
--- a/wearable/wear/Notifications/Application/src/main/res/values/colors.xml
+++ b/wearable/wear/Notifications/Application/src/main/res/values/colors.xml
@@ -15,5 +15,5 @@
 -->
 
 <resources>
-    <color name="divider_text">@android:color/holo_blue_bright</color>
+    <color name="divider_text">@android:color/holo_blue_dark</color>
 </resources>
diff --git a/wearable/wear/Notifications/Application/src/main/res/values/strings.xml b/wearable/wear/Notifications/Application/src/main/res/values/strings.xml
index 52f0f10..ffcb9f3 100644
--- a/wearable/wear/Notifications/Application/src/main/res/values/strings.xml
+++ b/wearable/wear/Notifications/Application/src/main/res/values/strings.xml
@@ -38,6 +38,7 @@
     <string name="content_icon_example">Content icon example</string>
     <string name="multiple_page_example">Multiple page example</string>
     <string name="bundle_example">Bundle example</string>
+    <string name="barcode_example">Barcode example</string>
 
     <string name="min_priority">Min priority</string>
     <string name="low_priority">Low priority</string>
@@ -161,6 +162,9 @@
     <string name="big_picture_style_example_title">Big picture style example title</string>
     <string name="big_picture_style_example_summary_text">Big picture style example summary</string>
 
+    <string name="barcode_content_title">Barcode example</string>
+    <string name="barcode_content_text">Swipe to view</string>
+
     <string name="second_page_content_title">Second page title</string>
     <string name="second_page_content_text">Second page text</string>
 
diff --git a/wearable/wear/Notifications/Shared/.gitignore b/wearable/wear/Notifications/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/Notifications/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/Notifications/Shared/src/main/AndroidManifest.xml b/wearable/wear/Notifications/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 0054143..0000000
--- a/wearable/wear/Notifications/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.notifications.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/Notifications/Shared/src/main/res/values/strings.xml b/wearable/wear/Notifications/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/Notifications/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml
index 99640f5..34a29ff 100644
--- a/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.notifications" >
+        package="com.example.android.support.wearable.notifications" >
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="20" />
+        android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/AnimatedNotificationDisplayActivity.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/AnimatedNotificationDisplayActivity.java
deleted file mode 100644
index f14acef..0000000
--- a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/AnimatedNotificationDisplayActivity.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.Random;
-
-/**
- * Custom display activity for an animated sample notification.
- */
-public class AnimatedNotificationDisplayActivity extends Activity {
-    public static final String EXTRA_TITLE = "title";
-
-    private static final int BASE_ANIMATION_DURATION_MS = 2000;
-
-    private Random mRandom;
-    private int mAnimationRange;
-    private ImageView mImageView;
-    private Animator mAnimation;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_animated_notification_display);
-
-        mRandom = new Random(System.currentTimeMillis());
-        mAnimationRange = getResources().getDimensionPixelSize(R.dimen.animation_range);
-
-        String title = getIntent().getStringExtra(EXTRA_TITLE);
-        ((TextView) findViewById(R.id.title)).setText(title);
-
-        mImageView = new ImageView(this);
-        mImageView.setImageResource(R.drawable.example_big_picture);
-
-        ImageZoomView zoomView = new ImageZoomView(this, mImageView, mAnimationRange);
-        zoomView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-
-        ((FrameLayout) findViewById(R.id.container)).addView(zoomView, 0);
-
-        createNextAnimation(false);
-    }
-
-    private void createNextAnimation(boolean start) {
-        float startX = mImageView.getTranslationX();
-        float startY = mImageView.getTranslationY();
-        float endX = -mRandom.nextInt(mAnimationRange);
-        float endY = -mRandom.nextInt(mAnimationRange);
-        float distance = (float) Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
-
-        mAnimation = ObjectAnimator.ofPropertyValuesHolder(mImageView,
-                PropertyValuesHolder.ofFloat("translationX", startX, endX),
-                PropertyValuesHolder.ofFloat("translationY", startY, endY));
-        mAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
-
-        mAnimation.setDuration(Math.max(BASE_ANIMATION_DURATION_MS / 10,
-                (int) (distance * BASE_ANIMATION_DURATION_MS / mAnimationRange)));
-
-        mAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                createNextAnimation(true);
-            }
-        });
-        if (start) {
-            mAnimation.start();
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mAnimation.start();
-    }
-
-    @Override
-    protected void onPause() {
-        mAnimation.pause();
-        super.onPause();
-    }
-
-    /** Helper view that zooms in on a child image view */
-    private static class ImageZoomView extends ViewGroup {
-        private final int mZoomLength;
-
-        public ImageZoomView(Context context, ImageView imageView, int zoomLength) {
-            super(context);
-            addView(imageView);
-            mZoomLength = zoomLength;
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-            ImageView imageView = (ImageView) getChildAt(0);
-
-            // Resize the image view to be at least mZoomLength pixels larger in both
-            // dimensions than the containing view.
-            int imageWidth = imageView.getDrawable().getIntrinsicWidth();
-            int imageHeight = imageView.getDrawable().getIntrinsicHeight();
-            int minSize = Math.max(right - left, bottom - top) + mZoomLength;
-            if (imageWidth > imageHeight) {
-                imageWidth = minSize * imageWidth / imageHeight;
-                imageHeight = minSize;
-            } else {
-                imageHeight = minSize * imageHeight / imageWidth;
-                imageWidth = minSize;
-            }
-            imageView.layout(left, top, left + imageWidth, top + imageHeight);
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/BasicNotificationDisplayActivity.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/BasicNotificationDisplayActivity.java
deleted file mode 100644
index a6cc5fb..0000000
--- a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/BasicNotificationDisplayActivity.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.widget.TextView;
-
-/**
- * Custom display activity for a sample notification.
- */
-public class BasicNotificationDisplayActivity extends Activity {
-    public static final String EXTRA_TITLE = "title";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_notification_display);
-
-        String title = getIntent().getStringExtra(EXTRA_TITLE);
-
-        ((TextView) findViewById(R.id.title)).setText(title);
-    }
-}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/MainActivity.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/MainActivity.java
deleted file mode 100644
index fe7e134..0000000
--- a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/MainActivity.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.wearable.view.WearableListView;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class MainActivity extends Activity implements WearableListView.ClickListener {
-    private static final int SAMPLE_NOTIFICATION_ID = 0;
-    public static final String KEY_REPLY = "reply";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-
-        WearableListView listView = (WearableListView) findViewById(R.id.list);
-        listView.setAdapter(new Adapter(this));
-        listView.setClickListener(this);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        if (getIntent() != null) {
-            Bundle inputResults = RemoteInput.getResultsFromIntent(getIntent());
-            if (inputResults != null) {
-                CharSequence replyText = inputResults.getCharSequence(KEY_REPLY);
-                if (replyText != null) {
-                    Toast.makeText(this, TextUtils.concat(getString(R.string.reply_was), replyText),
-                            Toast.LENGTH_LONG).show();
-                }
-            }
-        }
-    }
-
-    /** Post a new or updated notification using the selected notification options. */
-    private void updateNotification(int presetIndex) {
-        NotificationPreset preset = NotificationPresets.PRESETS[presetIndex];
-        Notification notif = preset.buildNotification(this);
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .notify(SAMPLE_NOTIFICATION_ID, notif);
-        finish();
-    }
-
-    @Override
-    public void onClick(WearableListView.ViewHolder v) {
-        updateNotification((Integer) v.itemView.getTag());
-    }
-
-    @Override
-    public void onTopEmptyRegionClick() {
-    }
-
-    private static final class Adapter extends WearableListView.Adapter {
-        private final Context mContext;
-        private final LayoutInflater mInflater;
-
-        private Adapter(Context context) {
-            mContext = context;
-            mInflater = LayoutInflater.from(context);
-        }
-
-        @Override
-        public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            return new WearableListView.ViewHolder(
-                    mInflater.inflate(R.layout.notif_preset_list_item, null));
-        }
-
-        @Override
-        public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
-            TextView view = (TextView) holder.itemView.findViewById(R.id.name);
-            view.setText(mContext.getString(NotificationPresets.PRESETS[position].nameResId));
-            holder.itemView.setTag(position);
-        }
-
-        @Override
-        public int getItemCount() {
-            return NotificationPresets.PRESETS.length;
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/NotificationPreset.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/NotificationPreset.java
deleted file mode 100644
index db15467..0000000
--- a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/NotificationPreset.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Notification;
-import android.content.Context;
-
-/**
- * Base class for notification preset generators.
- */
-public abstract class NotificationPreset {
-    public final int nameResId;
-
-    public NotificationPreset(int nameResId) {
-        this.nameResId = nameResId;
-    }
-
-    /** Start building a notification with this preset */
-    public abstract Notification buildNotification(Context context);
-}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/NotificationPresets.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/NotificationPresets.java
deleted file mode 100644
index 04a4d15..0000000
--- a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/NotificationPresets.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.net.Uri;
-import android.text.SpannableStringBuilder;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.RelativeSizeSpan;
-import android.text.style.StrikethroughSpan;
-import android.text.style.StyleSpan;
-import android.text.style.SubscriptSpan;
-import android.text.style.SuperscriptSpan;
-import android.text.style.TypefaceSpan;
-import android.text.style.UnderlineSpan;
-import android.util.TypedValue;
-import android.view.Gravity;
-
-/**
- * Collection of notification builder presets.
- */
-public class NotificationPresets {
-    public static final NotificationPreset[] PRESETS = new NotificationPreset[] {
-            new BasicPreset(),
-            new StylizedTextPreset(),
-            new DisplayIntentPreset(),
-            new MultiSizeDisplayIntentPreset(),
-            new AnimatedDisplayIntentPreset(),
-            new ContentIconPreset()
-    };
-
-    private static Notification.Builder buildBasicNotification(Context context) {
-        return new Notification.Builder(context)
-                .setContentTitle(context.getString(R.string.example_content_title))
-                .setContentText(context.getString(R.string.example_content_text))
-                // Set a content intent to return to this sample
-                .setContentIntent(PendingIntent.getActivity(context, 0,
-                        new Intent(context, MainActivity.class), 0))
-                .setSmallIcon(R.mipmap.ic_launcher);
-    }
-
-    private static class BasicPreset extends NotificationPreset {
-        public BasicPreset() {
-            super(R.string.basic_example);
-        }
-
-        @Override
-        public Notification buildNotification(Context context) {
-            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
-                    new Intent(context, MainActivity.class), 0);
-
-            Notification page2 = buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setHintShowBackgroundOnly(true)
-                            .setBackground(BitmapFactory.decodeResource(context.getResources(),
-                                    R.drawable.example_big_picture)))
-                    .build();
-
-            Notification page3 = buildBasicNotification(context)
-                    .setContentTitle(context.getString(R.string.third_page))
-                    .setContentText(null)
-                    .extend(new Notification.WearableExtender()
-                            .setContentAction(0 /* action A */))
-                    .build();
-
-            SpannableStringBuilder choice2 = new SpannableStringBuilder(
-                    "This choice is best");
-            choice2.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 5, 11, 0);
-
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
-                                    context.getString(R.string.action_a), pendingIntent))
-                            .addAction(new Notification.Action.Builder(R.mipmap.ic_launcher,
-                                    context.getString(R.string.reply), pendingIntent)
-                                    .addRemoteInput(new RemoteInput.Builder(MainActivity.KEY_REPLY)
-                                            .setChoices(new CharSequence[] {
-                                                    context.getString(R.string.choice_1),
-                                                    choice2 })
-                                            .build())
-                                    .build())
-                            .addPage(page2)
-                            .addPage(page3))
-                    .build();
-        }
-    }
-
-    private static class StylizedTextPreset extends NotificationPreset {
-        public StylizedTextPreset() {
-            super(R.string.stylized_text_example);
-        }
-
-        @Override
-        public Notification buildNotification(Context context) {
-            Notification.Builder builder = buildBasicNotification(context);
-
-            Notification.BigTextStyle style = new Notification.BigTextStyle();
-
-            SpannableStringBuilder title = new SpannableStringBuilder();
-            appendStyled(title, "Stylized", new StyleSpan(Typeface.BOLD_ITALIC));
-            title.append(" title");
-            SpannableStringBuilder text = new SpannableStringBuilder("Stylized text: ");
-            appendStyled(text, "C", new ForegroundColorSpan(Color.RED));
-            appendStyled(text, "O", new ForegroundColorSpan(Color.GREEN));
-            appendStyled(text, "L", new ForegroundColorSpan(Color.BLUE));
-            appendStyled(text, "O", new ForegroundColorSpan(Color.YELLOW));
-            appendStyled(text, "R", new ForegroundColorSpan(Color.MAGENTA));
-            appendStyled(text, "S", new ForegroundColorSpan(Color.CYAN));
-            text.append("; ");
-            appendStyled(text, "1.25x size", new RelativeSizeSpan(1.25f));
-            text.append("; ");
-            appendStyled(text, "0.75x size", new RelativeSizeSpan(0.75f));
-            text.append("; ");
-            appendStyled(text, "underline", new UnderlineSpan());
-            text.append("; ");
-            appendStyled(text, "strikethrough", new StrikethroughSpan());
-            text.append("; ");
-            appendStyled(text, "bold", new StyleSpan(Typeface.BOLD));
-            text.append("; ");
-            appendStyled(text, "italic", new StyleSpan(Typeface.ITALIC));
-            text.append("; ");
-            appendStyled(text, "sans-serif-thin", new TypefaceSpan("sans-serif-thin"));
-            text.append("; ");
-            appendStyled(text, "monospace", new TypefaceSpan("monospace"));
-            text.append("; ");
-            appendStyled(text, "sub", new SubscriptSpan());
-            text.append("script");
-            appendStyled(text, "super", new SuperscriptSpan());
-
-            style.setBigContentTitle(title);
-            style.bigText(text);
-
-            builder.setStyle(style);
-            return builder.build();
-        }
-
-        private void appendStyled(SpannableStringBuilder builder, String str, Object... spans) {
-            builder.append(str);
-            for (Object span : spans) {
-                builder.setSpan(span, builder.length() - str.length(), builder.length(), 0);
-            }
-        }
-    }
-
-    private static class DisplayIntentPreset extends NotificationPreset {
-        public DisplayIntentPreset() {
-            super(R.string.display_intent_example);
-        }
-
-        @Override
-        public Notification buildNotification(Context context) {
-            Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class);
-            displayIntent.putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
-                    context.getString(nameResId));
-            PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
-                    0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setDisplayIntent(displayPendingIntent))
-                    .build();
-        }
-    }
-
-    private static class MultiSizeDisplayIntentPreset extends NotificationPreset {
-        public MultiSizeDisplayIntentPreset() {
-            super(R.string.multisize_display_intent_example);
-        }
-
-        @Override
-        public Notification buildNotification(Context context) {
-           PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
-                                   new Intent(context, MainActivity.class), 0);
-             Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class)
-                    .putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
-                            context.getString(R.string.xsmall_sized_display));
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
-                                    PendingIntent.FLAG_UPDATE_CURRENT))
-                            .addPage(createPageForSizePreset(context,
-                                    Notification.WearableExtender.SIZE_SMALL,
-                                    R.string.small_sized_display, 0))
-                            .addPage(createPageForSizePreset(context,
-                                    Notification.WearableExtender.SIZE_MEDIUM,
-                                    R.string.medium_sized_display, 1))
-                            .addPage(createPageForSizePreset(context,
-                                    Notification.WearableExtender.SIZE_LARGE,
-                                    R.string.large_sized_display, 2))
-                            .addPage(createPageForSizePreset(context,
-                                    Notification.WearableExtender.SIZE_FULL_SCREEN,
-                                    R.string.full_screen_display, 3))
-                             .addPage(createPageForCustomHeight(context, 256,
-                                    R.string.dp256_height_display))
-                            .addPage(createPageForCustomHeight(context, 512,
-                                    R.string.dp512_height_display))
-                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
-                                    context.getString(R.string.action_a), pendingIntent))
-                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
-                                    context.getString(R.string.action_b), pendingIntent))
-                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
-                                    context.getString(R.string.action_c), pendingIntent))
-                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
-                                    context.getString(R.string.action_d), pendingIntent))
-                            .setCustomSizePreset(Notification.WearableExtender.SIZE_XSMALL))
-                    .build();
-        }
-
-        private Notification createPageForCustomHeight(Context context, int heightDisplayDp,
-                int pageNameResId) {
-            int contentHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                    heightDisplayDp, context.getResources().getDisplayMetrics());
-            Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class)
-                    .setData(Uri.fromParts("example", "height/" + heightDisplayDp, null))
-                    .putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
-                            context.getString(pageNameResId));
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
-                                    PendingIntent.FLAG_UPDATE_CURRENT))
-                            .setCustomContentHeight(contentHeight))
-                    .build();
-        }
-
-        private Notification createPageForSizePreset(Context context, int sizePreset,
-                int pageNameResId, int contentAction) {
-           Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class)
-                    .setData(Uri.fromParts("example", "size/" + sizePreset, null))
-                    .putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
-                            context.getString(pageNameResId));
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
-                                    PendingIntent.FLAG_UPDATE_CURRENT))
-                            .setCustomSizePreset(sizePreset)
-                            .setContentAction(contentAction))
-                    .build();
-        }
-    }
-
-    private static class AnimatedDisplayIntentPreset extends NotificationPreset {
-        public AnimatedDisplayIntentPreset() {
-            super(R.string.animated_display_intent_example);
-        }
-
-        @Override
-        public Notification buildNotification(Context context) {
-            Intent displayIntent = new Intent(context, AnimatedNotificationDisplayActivity.class);
-            displayIntent.putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
-                    context.getString(nameResId));
-            PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
-                    0, displayIntent, 0);
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setDisplayIntent(displayPendingIntent))
-                    .build();
-        }
-    }
-
-    private static class ContentIconPreset extends NotificationPreset {
-        public ContentIconPreset() {
-            super(R.string.content_icon_example);
-        }
-
-        @Override
-        public Notification buildNotification(Context context) {
-            Notification page2 = buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setContentIcon(R.drawable.content_icon_small)
-                            .setContentIconGravity(Gravity.START))
-                    .build();
-
-            Notification page3 = buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setContentIcon(R.drawable.content_icon_large))
-                    .build();
-
-            Notification page4 = buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setContentIcon(R.drawable.content_icon_large)
-                            .setContentIconGravity(Gravity.START))
-                    .build();
-
-            return buildBasicNotification(context)
-                    .extend(new Notification.WearableExtender()
-                            .setHintHideIcon(true)
-                            .setContentIcon(R.drawable.content_icon_small)
-                            .addPage(page2)
-                            .addPage(page3)
-                            .addPage(page4))
-                    .build();
-        }
-    }
-}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/WearableListItemLayout.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/WearableListItemLayout.java
deleted file mode 100644
index cb08a9c..0000000
--- a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/notifications/WearableListItemLayout.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.notifications;
-
-import android.content.Context;
-import android.graphics.drawable.GradientDrawable;
-import android.support.wearable.view.WearableListView;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class WearableListItemLayout extends LinearLayout implements WearableListView.Item {
-
-    private final float mFadedTextAlpha;
-    private final int mFadedCircleColor;
-    private final int mChosenCircleColor;
-    private ImageView mCircle;
-    private float mScale;
-    private TextView mName;
-
-    public WearableListItemLayout(Context context) {
-        this(context, null);
-    }
-
-    public WearableListItemLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public WearableListItemLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mFadedTextAlpha = getResources().getInteger(R.integer.action_text_faded_alpha) / 100f;
-        mFadedCircleColor = getResources().getColor(R.color.wl_gray);
-        mChosenCircleColor = getResources().getColor(R.color.wl_blue);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mCircle = (ImageView) findViewById(R.id.circle);
-        mName = (TextView) findViewById(R.id.name);
-    }
-
-    @Override
-    public float getProximityMinValue() {
-        return 1f;
-    }
-
-    @Override
-    public float getProximityMaxValue() {
-        return 1.6f;
-    }
-
-    @Override
-    public float getCurrentProximityValue() {
-        return mScale;
-    }
-
-    @Override
-    public void setScalingAnimatorValue(float scale) {
-        mScale = scale;
-        mCircle.setScaleX(scale);
-        mCircle.setScaleY(scale);
-    }
-
-    @Override
-    public void onScaleUpStart() {
-        mName.setAlpha(1f);
-        ((GradientDrawable) mCircle.getDrawable()).setColor(mChosenCircleColor);
-    }
-
-    @Override
-    public void onScaleDownStart() {
-        ((GradientDrawable) mCircle.getDrawable()).setColor(mFadedCircleColor);
-        mName.setAlpha(mFadedTextAlpha);
-    }
-}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/AnimatedNotificationDisplayActivity.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/AnimatedNotificationDisplayActivity.java
new file mode 100644
index 0000000..2e0b2ee
--- /dev/null
+++ b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/AnimatedNotificationDisplayActivity.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.Random;
+
+/**
+ * Custom display activity for an animated sample notification.
+ */
+public class AnimatedNotificationDisplayActivity extends Activity {
+    public static final String EXTRA_TITLE = "title";
+
+    private static final int BASE_ANIMATION_DURATION_MS = 2000;
+
+    private Random mRandom;
+    private int mAnimationRange;
+    private ImageView mImageView;
+    private Animator mAnimation;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_animated_notification_display);
+
+        mRandom = new Random(System.currentTimeMillis());
+        mAnimationRange = getResources().getDimensionPixelSize(R.dimen.animation_range);
+
+        String title = getIntent().getStringExtra(EXTRA_TITLE);
+        ((TextView) findViewById(R.id.title)).setText(title);
+
+        mImageView = new ImageView(this);
+        mImageView.setImageResource(R.drawable.example_big_picture);
+
+        ImageZoomView zoomView = new ImageZoomView(this, mImageView, mAnimationRange);
+        zoomView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+
+        ((FrameLayout) findViewById(R.id.container)).addView(zoomView, 0);
+
+        createNextAnimation(false);
+    }
+
+    private void createNextAnimation(boolean start) {
+        float startX = mImageView.getTranslationX();
+        float startY = mImageView.getTranslationY();
+        float endX = -mRandom.nextInt(mAnimationRange);
+        float endY = -mRandom.nextInt(mAnimationRange);
+        float distance = (float) Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
+
+        mAnimation = ObjectAnimator.ofPropertyValuesHolder(mImageView,
+                PropertyValuesHolder.ofFloat("translationX", startX, endX),
+                PropertyValuesHolder.ofFloat("translationY", startY, endY));
+        mAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
+
+        mAnimation.setDuration(Math.max(BASE_ANIMATION_DURATION_MS / 10,
+                (int) (distance * BASE_ANIMATION_DURATION_MS / mAnimationRange)));
+
+        mAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                createNextAnimation(true);
+            }
+        });
+        if (start) {
+            mAnimation.start();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAnimation.start();
+    }
+
+    @Override
+    protected void onPause() {
+        mAnimation.pause();
+        super.onPause();
+    }
+
+    /** Helper view that zooms in on a child image view */
+    private static class ImageZoomView extends ViewGroup {
+        private final int mZoomLength;
+
+        public ImageZoomView(Context context, ImageView imageView, int zoomLength) {
+            super(context);
+            addView(imageView);
+            mZoomLength = zoomLength;
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            ImageView imageView = (ImageView) getChildAt(0);
+
+            // Resize the image view to be at least mZoomLength pixels larger in both
+            // dimensions than the containing view.
+            int imageWidth = imageView.getDrawable().getIntrinsicWidth();
+            int imageHeight = imageView.getDrawable().getIntrinsicHeight();
+            int minSize = Math.max(right - left, bottom - top) + mZoomLength;
+            if (imageWidth > imageHeight) {
+                imageWidth = minSize * imageWidth / imageHeight;
+                imageHeight = minSize;
+            } else {
+                imageHeight = minSize * imageHeight / imageWidth;
+                imageWidth = minSize;
+            }
+            imageView.layout(left, top, left + imageWidth, top + imageHeight);
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/BasicNotificationDisplayActivity.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/BasicNotificationDisplayActivity.java
new file mode 100644
index 0000000..d029f43
--- /dev/null
+++ b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/BasicNotificationDisplayActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Custom display activity for a sample notification.
+ */
+public class BasicNotificationDisplayActivity extends Activity {
+    public static final String EXTRA_TITLE = "title";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_notification_display);
+
+        String title = getIntent().getStringExtra(EXTRA_TITLE);
+
+        ((TextView) findViewById(R.id.title)).setText(title);
+    }
+}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/MainActivity.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/MainActivity.java
new file mode 100644
index 0000000..d564e13
--- /dev/null
+++ b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/MainActivity.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.wearable.view.WearableListView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class MainActivity extends Activity implements WearableListView.ClickListener {
+    private static final int SAMPLE_NOTIFICATION_ID = 0;
+    public static final String KEY_REPLY = "reply";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        WearableListView listView = (WearableListView) findViewById(R.id.list);
+        listView.setAdapter(new Adapter(this));
+        listView.setClickListener(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (getIntent() != null) {
+            Bundle inputResults = RemoteInput.getResultsFromIntent(getIntent());
+            if (inputResults != null) {
+                CharSequence replyText = inputResults.getCharSequence(KEY_REPLY);
+                if (replyText != null) {
+                    Toast.makeText(this, TextUtils.concat(getString(R.string.reply_was), replyText),
+                            Toast.LENGTH_LONG).show();
+                }
+            }
+        }
+    }
+
+    /** Post a new or updated notification using the selected notification options. */
+    private void updateNotification(int presetIndex) {
+        NotificationPreset preset = NotificationPresets.PRESETS[presetIndex];
+        Notification notif = preset.buildNotification(this);
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .notify(SAMPLE_NOTIFICATION_ID, notif);
+        finish();
+    }
+
+    @Override
+    public void onClick(WearableListView.ViewHolder v) {
+        updateNotification((Integer) v.itemView.getTag());
+    }
+
+    @Override
+    public void onTopEmptyRegionClick() {
+    }
+
+    private static final class Adapter extends WearableListView.Adapter {
+        private final Context mContext;
+        private final LayoutInflater mInflater;
+
+        private Adapter(Context context) {
+            mContext = context;
+            mInflater = LayoutInflater.from(context);
+        }
+
+        @Override
+        public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new WearableListView.ViewHolder(
+                    mInflater.inflate(R.layout.notif_preset_list_item, null));
+        }
+
+        @Override
+        public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
+            TextView view = (TextView) holder.itemView.findViewById(R.id.name);
+            view.setText(mContext.getString(NotificationPresets.PRESETS[position].nameResId));
+            holder.itemView.setTag(position);
+        }
+
+        @Override
+        public int getItemCount() {
+            return NotificationPresets.PRESETS.length;
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/NotificationPreset.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/NotificationPreset.java
new file mode 100644
index 0000000..d6570d5
--- /dev/null
+++ b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/NotificationPreset.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Notification;
+import android.content.Context;
+
+/**
+ * Base class for notification preset generators.
+ */
+public abstract class NotificationPreset {
+    public final int nameResId;
+
+    public NotificationPreset(int nameResId) {
+        this.nameResId = nameResId;
+    }
+
+    /** Start building a notification with this preset */
+    public abstract Notification buildNotification(Context context);
+}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/NotificationPresets.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/NotificationPresets.java
new file mode 100644
index 0000000..5578659
--- /dev/null
+++ b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/NotificationPresets.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.UnderlineSpan;
+import android.util.TypedValue;
+import android.view.Gravity;
+
+/**
+ * Collection of notification builder presets.
+ */
+public class NotificationPresets {
+    public static final NotificationPreset[] PRESETS = new NotificationPreset[] {
+            new BasicPreset(),
+            new StylizedTextPreset(),
+            new DisplayIntentPreset(),
+            new MultiSizeDisplayIntentPreset(),
+            new AnimatedDisplayIntentPreset(),
+            new ContentIconPreset()
+    };
+
+    private static Notification.Builder buildBasicNotification(Context context) {
+        return new Notification.Builder(context)
+                .setContentTitle(context.getString(R.string.example_content_title))
+                .setContentText(context.getString(R.string.example_content_text))
+                // Set a content intent to return to this sample
+                .setContentIntent(PendingIntent.getActivity(context, 0,
+                        new Intent(context, MainActivity.class), 0))
+                .setSmallIcon(R.mipmap.ic_launcher);
+    }
+
+    private static class BasicPreset extends NotificationPreset {
+        public BasicPreset() {
+            super(R.string.basic_example);
+        }
+
+        @Override
+        public Notification buildNotification(Context context) {
+            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
+                    new Intent(context, MainActivity.class), 0);
+
+            Notification page2 = buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setHintShowBackgroundOnly(true)
+                            .setBackground(BitmapFactory.decodeResource(context.getResources(),
+                                    R.drawable.example_big_picture)))
+                    .build();
+
+            Notification page3 = buildBasicNotification(context)
+                    .setContentTitle(context.getString(R.string.third_page))
+                    .setContentText(null)
+                    .extend(new Notification.WearableExtender()
+                            .setContentAction(0 /* action A */))
+                    .build();
+
+            SpannableStringBuilder choice2 = new SpannableStringBuilder(
+                    "This choice is best");
+            choice2.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 5, 11, 0);
+
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
+                                    context.getString(R.string.action_a), pendingIntent))
+                            .addAction(new Notification.Action.Builder(R.mipmap.ic_launcher,
+                                    context.getString(R.string.reply), pendingIntent)
+                                    .addRemoteInput(new RemoteInput.Builder(MainActivity.KEY_REPLY)
+                                            .setChoices(new CharSequence[] {
+                                                    context.getString(R.string.choice_1),
+                                                    choice2 })
+                                            .build())
+                                    .build())
+                            .addPage(page2)
+                            .addPage(page3))
+                    .build();
+        }
+    }
+
+    private static class StylizedTextPreset extends NotificationPreset {
+        public StylizedTextPreset() {
+            super(R.string.stylized_text_example);
+        }
+
+        @Override
+        public Notification buildNotification(Context context) {
+            Notification.Builder builder = buildBasicNotification(context);
+
+            Notification.BigTextStyle style = new Notification.BigTextStyle();
+
+            SpannableStringBuilder title = new SpannableStringBuilder();
+            appendStyled(title, "Stylized", new StyleSpan(Typeface.BOLD_ITALIC));
+            title.append(" title");
+            SpannableStringBuilder text = new SpannableStringBuilder("Stylized text: ");
+            appendStyled(text, "C", new ForegroundColorSpan(Color.RED));
+            appendStyled(text, "O", new ForegroundColorSpan(Color.GREEN));
+            appendStyled(text, "L", new ForegroundColorSpan(Color.BLUE));
+            appendStyled(text, "O", new ForegroundColorSpan(Color.YELLOW));
+            appendStyled(text, "R", new ForegroundColorSpan(Color.MAGENTA));
+            appendStyled(text, "S", new ForegroundColorSpan(Color.CYAN));
+            text.append("; ");
+            appendStyled(text, "1.25x size", new RelativeSizeSpan(1.25f));
+            text.append("; ");
+            appendStyled(text, "0.75x size", new RelativeSizeSpan(0.75f));
+            text.append("; ");
+            appendStyled(text, "underline", new UnderlineSpan());
+            text.append("; ");
+            appendStyled(text, "strikethrough", new StrikethroughSpan());
+            text.append("; ");
+            appendStyled(text, "bold", new StyleSpan(Typeface.BOLD));
+            text.append("; ");
+            appendStyled(text, "italic", new StyleSpan(Typeface.ITALIC));
+            text.append("; ");
+            appendStyled(text, "sans-serif-thin", new TypefaceSpan("sans-serif-thin"));
+            text.append("; ");
+            appendStyled(text, "monospace", new TypefaceSpan("monospace"));
+            text.append("; ");
+            appendStyled(text, "sub", new SubscriptSpan());
+            text.append("script");
+            appendStyled(text, "super", new SuperscriptSpan());
+
+            style.setBigContentTitle(title);
+            style.bigText(text);
+
+            builder.setStyle(style);
+            return builder.build();
+        }
+
+        private void appendStyled(SpannableStringBuilder builder, String str, Object... spans) {
+            builder.append(str);
+            for (Object span : spans) {
+                builder.setSpan(span, builder.length() - str.length(), builder.length(), 0);
+            }
+        }
+    }
+
+    private static class DisplayIntentPreset extends NotificationPreset {
+        public DisplayIntentPreset() {
+            super(R.string.display_intent_example);
+        }
+
+        @Override
+        public Notification buildNotification(Context context) {
+            Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class);
+            displayIntent.putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
+                    context.getString(nameResId));
+            PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
+                    0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setDisplayIntent(displayPendingIntent))
+                    .build();
+        }
+    }
+
+    private static class MultiSizeDisplayIntentPreset extends NotificationPreset {
+        public MultiSizeDisplayIntentPreset() {
+            super(R.string.multisize_display_intent_example);
+        }
+
+        @Override
+        public Notification buildNotification(Context context) {
+           PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
+                                   new Intent(context, MainActivity.class), 0);
+             Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class)
+                    .putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
+                            context.getString(R.string.xsmall_sized_display));
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
+                                    PendingIntent.FLAG_UPDATE_CURRENT))
+                            .addPage(createPageForSizePreset(context,
+                                    Notification.WearableExtender.SIZE_SMALL,
+                                    R.string.small_sized_display, 0))
+                            .addPage(createPageForSizePreset(context,
+                                    Notification.WearableExtender.SIZE_MEDIUM,
+                                    R.string.medium_sized_display, 1))
+                            .addPage(createPageForSizePreset(context,
+                                    Notification.WearableExtender.SIZE_LARGE,
+                                    R.string.large_sized_display, 2))
+                            .addPage(createPageForSizePreset(context,
+                                    Notification.WearableExtender.SIZE_FULL_SCREEN,
+                                    R.string.full_screen_display, 3))
+                             .addPage(createPageForCustomHeight(context, 256,
+                                    R.string.dp256_height_display))
+                            .addPage(createPageForCustomHeight(context, 512,
+                                    R.string.dp512_height_display))
+                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
+                                    context.getString(R.string.action_a), pendingIntent))
+                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
+                                    context.getString(R.string.action_b), pendingIntent))
+                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
+                                    context.getString(R.string.action_c), pendingIntent))
+                            .addAction(new Notification.Action(R.mipmap.ic_launcher,
+                                    context.getString(R.string.action_d), pendingIntent))
+                            .setCustomSizePreset(Notification.WearableExtender.SIZE_XSMALL))
+                    .build();
+        }
+
+        private Notification createPageForCustomHeight(Context context, int heightDisplayDp,
+                int pageNameResId) {
+            int contentHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    heightDisplayDp, context.getResources().getDisplayMetrics());
+            Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class)
+                    .setData(Uri.fromParts("example", "height/" + heightDisplayDp, null))
+                    .putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
+                            context.getString(pageNameResId));
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
+                                    PendingIntent.FLAG_UPDATE_CURRENT))
+                            .setCustomContentHeight(contentHeight))
+                    .build();
+        }
+
+        private Notification createPageForSizePreset(Context context, int sizePreset,
+                int pageNameResId, int contentAction) {
+           Intent displayIntent = new Intent(context, BasicNotificationDisplayActivity.class)
+                    .setData(Uri.fromParts("example", "size/" + sizePreset, null))
+                    .putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
+                            context.getString(pageNameResId));
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
+                                    PendingIntent.FLAG_UPDATE_CURRENT))
+                            .setCustomSizePreset(sizePreset)
+                            .setContentAction(contentAction))
+                    .build();
+        }
+    }
+
+    private static class AnimatedDisplayIntentPreset extends NotificationPreset {
+        public AnimatedDisplayIntentPreset() {
+            super(R.string.animated_display_intent_example);
+        }
+
+        @Override
+        public Notification buildNotification(Context context) {
+            Intent displayIntent = new Intent(context, AnimatedNotificationDisplayActivity.class);
+            displayIntent.putExtra(BasicNotificationDisplayActivity.EXTRA_TITLE,
+                    context.getString(nameResId));
+            PendingIntent displayPendingIntent = PendingIntent.getActivity(context,
+                    0, displayIntent, 0);
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setDisplayIntent(displayPendingIntent))
+                    .build();
+        }
+    }
+
+    private static class ContentIconPreset extends NotificationPreset {
+        public ContentIconPreset() {
+            super(R.string.content_icon_example);
+        }
+
+        @Override
+        public Notification buildNotification(Context context) {
+            Notification page2 = buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setContentIcon(R.drawable.content_icon_small)
+                            .setContentIconGravity(Gravity.START))
+                    .build();
+
+            Notification page3 = buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setContentIcon(R.drawable.content_icon_large))
+                    .build();
+
+            Notification page4 = buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setContentIcon(R.drawable.content_icon_large)
+                            .setContentIconGravity(Gravity.START))
+                    .build();
+
+            return buildBasicNotification(context)
+                    .extend(new Notification.WearableExtender()
+                            .setHintHideIcon(true)
+                            .setContentIcon(R.drawable.content_icon_small)
+                            .addPage(page2)
+                            .addPage(page3)
+                            .addPage(page4))
+                    .build();
+        }
+    }
+}
diff --git a/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/WearableListItemLayout.java b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/WearableListItemLayout.java
new file mode 100644
index 0000000..4ec554c
--- /dev/null
+++ b/wearable/wear/Notifications/Wearable/src/main/java/com/example/android/support/wearable/notifications/WearableListItemLayout.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.support.wearable.notifications;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.support.wearable.view.WearableListView;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class WearableListItemLayout extends LinearLayout
+        implements WearableListView.OnCenterProximityListener {
+
+    private final float mFadedTextAlpha;
+    private final int mFadedCircleColor;
+    private final int mChosenCircleColor;
+    private ImageView mCircle;
+    private float mScale;
+    private TextView mName;
+
+    public WearableListItemLayout(Context context) {
+        this(context, null);
+    }
+
+    public WearableListItemLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableListItemLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mFadedTextAlpha = getResources().getInteger(R.integer.action_text_faded_alpha) / 100f;
+        mFadedCircleColor = getResources().getColor(R.color.wl_gray);
+        mChosenCircleColor = getResources().getColor(R.color.wl_blue);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mCircle = (ImageView) findViewById(R.id.circle);
+        mName = (TextView) findViewById(R.id.name);
+    }
+
+    @Override
+    public void onCenterPosition(boolean animate) {
+        mName.setAlpha(1f);
+        ((GradientDrawable) mCircle.getDrawable()).setColor(mChosenCircleColor);
+    }
+
+    @Override
+    public void onNonCenterPosition(boolean animate) {
+        ((GradientDrawable) mCircle.getDrawable()).setColor(mFadedCircleColor);
+        mName.setAlpha(mFadedTextAlpha);
+    }
+}
diff --git a/wearable/wear/Notifications/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Notifications/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/Notifications/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/Notifications/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/Notifications/screenshots/basic-menu.png b/wearable/wear/Notifications/screenshots/basic-menu.png
new file mode 100644
index 0000000..75c29cc
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/basic-menu.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/basic.png b/wearable/wear/Notifications/screenshots/basic.png
new file mode 100644
index 0000000..9e13b09
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/basic.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/bottom-aligned.png b/wearable/wear/Notifications/screenshots/bottom-aligned.png
new file mode 100644
index 0000000..88c7ffa
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/bottom-aligned.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/bundle.png b/wearable/wear/Notifications/screenshots/bundle.png
new file mode 100644
index 0000000..a6e1ad5
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/bundle.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/companion-bottom-aligned.png b/wearable/wear/Notifications/screenshots/companion-bottom-aligned.png
new file mode 100644
index 0000000..2bda56c
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/companion-bottom-aligned.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/companion-bundle.png b/wearable/wear/Notifications/screenshots/companion-bundle.png
new file mode 100644
index 0000000..90e0e08
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/companion-bundle.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/companion-content-action.png b/wearable/wear/Notifications/screenshots/companion-content-action.png
new file mode 100644
index 0000000..07e6806
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/companion-content-action.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/companion-multiple-page.png b/wearable/wear/Notifications/screenshots/companion-multiple-page.png
new file mode 100644
index 0000000..7f11521
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/companion-multiple-page.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-action.png b/wearable/wear/Notifications/screenshots/content-action.png
new file mode 100644
index 0000000..415dd9f
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-action.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-action2.png b/wearable/wear/Notifications/screenshots/content-action2.png
new file mode 100644
index 0000000..7f01994
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-action2.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-icon-menu.png b/wearable/wear/Notifications/screenshots/content-icon-menu.png
new file mode 100644
index 0000000..6c810ce
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-icon-menu.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-icon1.png b/wearable/wear/Notifications/screenshots/content-icon1.png
new file mode 100644
index 0000000..f9d12da
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-icon1.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-icon2.png b/wearable/wear/Notifications/screenshots/content-icon2.png
new file mode 100644
index 0000000..a36b734
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-icon2.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-icon3.png b/wearable/wear/Notifications/screenshots/content-icon3.png
new file mode 100644
index 0000000..e2c757e
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-icon3.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/content-icon4.png b/wearable/wear/Notifications/screenshots/content-icon4.png
new file mode 100644
index 0000000..f5d0523
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/content-icon4.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/display-intent-menu.png b/wearable/wear/Notifications/screenshots/display-intent-menu.png
new file mode 100644
index 0000000..567673f
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/display-intent-menu.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/display-intent.png b/wearable/wear/Notifications/screenshots/display-intent.png
new file mode 100644
index 0000000..f3b333d
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/display-intent.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/ic_launcher.png b/wearable/wear/Notifications/screenshots/ic_launcher.png
new file mode 100644
index 0000000..163f1f0
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/multiple-page1.png b/wearable/wear/Notifications/screenshots/multiple-page1.png
new file mode 100644
index 0000000..d6591d9
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/multiple-page1.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/multiple-page2.png b/wearable/wear/Notifications/screenshots/multiple-page2.png
new file mode 100644
index 0000000..730cb30
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/multiple-page2.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/stylized-menu.png b/wearable/wear/Notifications/screenshots/stylized-menu.png
new file mode 100644
index 0000000..2e94c93
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/stylized-menu.png
Binary files differ
diff --git a/wearable/wear/Notifications/screenshots/stylized.png b/wearable/wear/Notifications/screenshots/stylized.png
new file mode 100644
index 0000000..338c9cc
--- /dev/null
+++ b/wearable/wear/Notifications/screenshots/stylized.png
Binary files differ
diff --git a/wearable/wear/Notifications/settings.gradle b/wearable/wear/Notifications/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/Notifications/settings.gradle
+++ b/wearable/wear/Notifications/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/Notifications/template-params.xml b/wearable/wear/Notifications/template-params.xml
index cda9fdd..939c45e 100644
--- a/wearable/wear/Notifications/template-params.xml
+++ b/wearable/wear/Notifications/template-params.xml
@@ -18,14 +18,14 @@
 
 
 <sample>
-    <name>Notifications</name>
+    <name>Wearable Notifications</name>
     <group>Wearable</group>
-    <package>com.example.android.notifications</package>
+    <package>com.example.android.support.wearable.notifications</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -41,7 +41,42 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/companion-content-action.png</img>
+            <img>screenshots/content-action</img>
+            <img>screenshots/content-icon-menu</img>
+        </screenshots>
+        <api_refs>
+            <android>android.app.Notification</android>
+            <android>android.support.v4.app.NotificationCompat</android>
+            <android>android.support.v4.app.NotificationManagerCompat</android>
+            <android>android.support.v4.app.RemoteInput</android>
+            <android>android.support.wearable.view.WearableListView</android>
+        </api_refs>
+        <description>
+This sample showcases the available notification styles on a device and wearable.
+        </description>
+        <intro>
+This sample app enables the user to choose a notification type (the selection is done on the phone),
+which it then displays on the phone and wearable. The notification is built using
+[NotificationCompat.Builder][1] and [NotificationCompat.WearableExtender][2].
 
+See [Creating a Notification][3] for all the details on how to create a notification with wearable features.
+
+On the wearable side, the sample also shows how to create a custom layout using [WearableListView][4].
+
+[1]: http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html
+[2]: https://developer.android.com/reference/android/support/v4/app/NotificationCompat.WearableExtender.html
+[3]: https://developer.android.com/training/wearables/notifications/creating.html
+[4]: https://developer.android.com/training/wearables/apps/layouts.html#UiLibrary
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml b/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml
index 40e3602..55b666f 100644
--- a/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.quiz" >
+        package="com.example.android.wearable.quiz" >
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application
             android:allowBackup="true"
@@ -29,7 +29,7 @@
                    android:value="@integer/google_play_services_version" />
 
         <activity
-                android:name="com.example.android.quiz.MainActivity"
+                android:name="com.example.android.wearable.quiz.MainActivity"
                 android:label="@string/app_name"
                 android:windowSoftInputMode="stateHidden"
                 android:configChanges="keyboardHidden|orientation|screenSize"  >
diff --git a/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/Constants.java b/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/Constants.java
deleted file mode 100644
index ea5c56b..0000000
--- a/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/Constants.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-/** Constants used in the companion app. */
-public final class Constants {
-    private Constants() {
-    }
-
-    public static final String ANSWERS = "answers";
-    public static final String CHOSEN_ANSWER_CORRECT = "chosen_answer_correct";
-    public static final String CORRECT_ANSWER_INDEX = "correct_answer_index";
-    public static final String QUESTION = "question";
-    public static final String QUESTION_INDEX = "question_index";
-    public static final String QUESTION_WAS_ANSWERED = "question_was_answered";
-    public static final String QUESTION_WAS_DELETED = "question_was_deleted";
-
-    public static final String NUM_CORRECT = "num_correct";
-    public static final String NUM_INCORRECT = "num_incorrect";
-    public static final String NUM_SKIPPED = "num_skipped";
-
-    public static final String QUIZ_ENDED_PATH = "/quiz_ended";
-    public static final String QUIZ_EXITED_PATH = "/quiz_exited";
-    public static final String RESET_QUIZ_PATH = "/reset_quiz";
-}
diff --git a/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/JsonUtils.java b/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/JsonUtils.java
deleted file mode 100644
index a0f98c1..0000000
--- a/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/JsonUtils.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-import android.content.Context;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-final class JsonUtils {
-    public static final String JSON_FIELD_QUESTIONS = "questions";
-    public static final String JSON_FIELD_QUESTION = "question";
-    public static final String JSON_FIELD_ANSWERS = "answers";
-    public static final String JSON_FIELD_CORRECT_INDEX = "correctIndex";
-    public static final int NUM_ANSWER_CHOICES = 4;
-
-    private JsonUtils() {
-    }
-
-    public static JSONObject loadJsonFile(Context context, String fileName) throws IOException,
-            JSONException {
-        InputStream is = context.getAssets().open(fileName);
-        int size = is.available();
-        byte[] buffer = new byte[size];
-        is.read(buffer);
-        is.close();
-        String jsonString = new String(buffer);
-        return new JSONObject(jsonString);
-    }
-}
diff --git a/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/MainActivity.java b/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/MainActivity.java
deleted file mode 100644
index ab8e3b4..0000000
--- a/wearable/wear/Quiz/Application/src/main/java/com/example/android/quiz/MainActivity.java
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-import static com.example.android.quiz.Constants.ANSWERS;
-import static com.example.android.quiz.Constants.CHOSEN_ANSWER_CORRECT;
-import static com.example.android.quiz.Constants.CORRECT_ANSWER_INDEX;
-import static com.example.android.quiz.Constants.NUM_CORRECT;
-import static com.example.android.quiz.Constants.NUM_INCORRECT;
-import static com.example.android.quiz.Constants.NUM_SKIPPED;
-import static com.example.android.quiz.Constants.QUESTION;
-import static com.example.android.quiz.Constants.QUESTION_INDEX;
-import static com.example.android.quiz.Constants.QUESTION_WAS_ANSWERED;
-import static com.example.android.quiz.Constants.QUESTION_WAS_DELETED;
-import static com.example.android.quiz.Constants.QUIZ_ENDED_PATH;
-import static com.example.android.quiz.Constants.QUIZ_EXITED_PATH;
-import static com.example.android.quiz.Constants.RESET_QUIZ_PATH;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.RadioGroup;
-import android.widget.TextView;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
-import com.google.android.gms.common.api.ResultCallback;
-import com.google.android.gms.common.data.FreezableUtils;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataItem;
-import com.google.android.gms.wearable.DataItemBuffer;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.MessageApi;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.PutDataRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.PriorityQueue;
-
-/**
- * Allows the user to create questions, which will be put as notifications on the watch's stream.
- * The status of questions will be updated on the phone when the user answers them.
- */
-public class MainActivity extends Activity implements DataApi.DataListener,
-        MessageApi.MessageListener, ConnectionCallbacks,
-        GoogleApiClient.OnConnectionFailedListener {
-
-    private static final String TAG = "ExampleQuizApp";
-    private static final String QUIZ_JSON_FILE = "Quiz.json";
-
-    // Various UI components.
-    private EditText questionEditText;
-    private EditText choiceAEditText;
-    private EditText choiceBEditText;
-    private EditText choiceCEditText;
-    private EditText choiceDEditText;
-    private RadioGroup choicesRadioGroup;
-    private TextView quizStatus;
-    private LinearLayout quizButtons;
-    private LinearLayout questionsContainer;
-    private Button readQuizFromFileButton;
-    private Button resetQuizButton;
-
-    private GoogleApiClient mGoogleApiClient;
-    private PriorityQueue<Question> mFutureQuestions;
-    private int mQuestionIndex = 0;
-    private boolean mHasQuestionBeenAsked = false;
-
-    // Data to display in end report.
-    private int mNumCorrect = 0;
-    private int mNumIncorrect = 0;
-    private int mNumSkipped = 0;
-
-    private static final Map<Integer, Integer> radioIdToIndex;
-
-    static {
-        Map<Integer, Integer> temp = new HashMap<Integer, Integer>(4);
-        temp.put(R.id.choice_a_radio, 0);
-        temp.put(R.id.choice_b_radio, 1);
-        temp.put(R.id.choice_c_radio, 2);
-        temp.put(R.id.choice_d_radio, 3);
-        radioIdToIndex = Collections.unmodifiableMap(temp);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.main);
-
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-        mFutureQuestions = new PriorityQueue<Question>(10);
-
-        // Find UI components to be used later.
-        questionEditText = (EditText) findViewById(R.id.question_text);
-        choiceAEditText = (EditText) findViewById(R.id.choice_a_text);
-        choiceBEditText = (EditText) findViewById(R.id.choice_b_text);
-        choiceCEditText = (EditText) findViewById(R.id.choice_c_text);
-        choiceDEditText = (EditText) findViewById(R.id.choice_d_text);
-        choicesRadioGroup = (RadioGroup) findViewById(R.id.choices_radio_group);
-        quizStatus = (TextView) findViewById(R.id.quiz_status);
-        quizButtons = (LinearLayout) findViewById(R.id.quiz_buttons);
-        questionsContainer = (LinearLayout) findViewById(R.id.questions_container);
-        readQuizFromFileButton = (Button) findViewById(R.id.read_quiz_from_file_button);
-        resetQuizButton = (Button) findViewById(R.id.reset_quiz_button);
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        if (!mGoogleApiClient.isConnected()) {
-            mGoogleApiClient.connect();
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        Wearable.DataApi.removeListener(mGoogleApiClient, this);
-        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
-
-        // Tell the wearable to end the quiz (counting unanswered questions as skipped), and then
-        // disconnect mGoogleApiClient.
-        DataMap dataMap = new DataMap();
-        dataMap.putInt(NUM_CORRECT, mNumCorrect);
-        dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
-        if (mHasQuestionBeenAsked) {
-            mNumSkipped += 1;
-        }
-        mNumSkipped += mFutureQuestions.size();
-        dataMap.putInt(NUM_SKIPPED, mNumSkipped);
-        if (mNumCorrect + mNumIncorrect + mNumSkipped > 0) {
-            sendMessageToWearable(QUIZ_EXITED_PATH, dataMap.toByteArray());
-        }
-
-        clearQuizStatus();
-        super.onStop();
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-        Wearable.DataApi.addListener(mGoogleApiClient, this);
-        Wearable.MessageApi.addListener(mGoogleApiClient, this);
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-        // Ignore
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-        Log.e(TAG, "Failed to connect to Google Play Services");
-    }
-
-    @Override
-    public void onMessageReceived(MessageEvent messageEvent) {
-        if (messageEvent.getPath().equals(RESET_QUIZ_PATH)) {
-            runOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    resetQuiz(null);
-                }
-            });
-        }
-    }
-
-    /**
-     * Used to ensure questions with smaller indexes come before questions with larger
-     * indexes. For example, question0 should come before question1.
-     */
-    private static class Question implements Comparable<Question> {
-        private String question;
-        private int questionIndex;
-        private String[] answers;
-        private int correctAnswerIndex;
-
-        public Question(String question, int questionIndex, String[] answers,
-                int correctAnswerIndex) {
-            this.question = question;
-            this.questionIndex = questionIndex;
-            this.answers = answers;
-            this.correctAnswerIndex = correctAnswerIndex;
-        }
-
-        public static Question fromJson(JSONObject questionObject, int questionIndex)
-                throws JSONException {
-            String question = questionObject.getString(JsonUtils.JSON_FIELD_QUESTION);
-            JSONArray answersJsonArray = questionObject.getJSONArray(JsonUtils.JSON_FIELD_ANSWERS);
-            String[] answers = new String[JsonUtils.NUM_ANSWER_CHOICES];
-            for (int j = 0; j < answersJsonArray.length(); j++) {
-                answers[j] = answersJsonArray.getString(j);
-            }
-            int correctIndex = questionObject.getInt(JsonUtils.JSON_FIELD_CORRECT_INDEX);
-            return new Question(question, questionIndex, answers, correctIndex);
-        }
-
-        @Override
-        public int compareTo(Question that) {
-            return this.questionIndex - that.questionIndex;
-        }
-
-        public PutDataRequest toPutDataRequest() {
-            PutDataMapRequest request = PutDataMapRequest.create("/question/" + questionIndex);
-            DataMap dataMap = request.getDataMap();
-            dataMap.putString(QUESTION, question);
-            dataMap.putInt(QUESTION_INDEX, questionIndex);
-            dataMap.putStringArray(ANSWERS, answers);
-            dataMap.putInt(CORRECT_ANSWER_INDEX, correctAnswerIndex);
-            return request.asPutDataRequest();
-        }
-    }
-
-    /**
-     * Create a quiz, as defined in Quiz.json, when the user clicks on "Read quiz from file."
-     * @throws IOException
-     */
-    public void readQuizFromFile(View view) throws IOException, JSONException {
-        clearQuizStatus();
-        JSONObject jsonObject = JsonUtils.loadJsonFile(this, QUIZ_JSON_FILE);
-        JSONArray jsonArray = jsonObject.getJSONArray(JsonUtils.JSON_FIELD_QUESTIONS);
-        for (int i = 0; i < jsonArray.length(); i++) {
-            JSONObject questionObject = jsonArray.getJSONObject(i);
-            Question question = Question.fromJson(questionObject, mQuestionIndex++);
-            addQuestionDataItem(question);
-            setNewQuestionStatus(question.question);
-        }
-    }
-
-    /**
-     * Adds a question (with answer choices) when user clicks on "Add Question."
-     */
-    public void addQuestion(View view) {
-        // Retrieve the question and answers supplied by the user.
-        String question = questionEditText.getText().toString();
-        String[] answers = new String[4];
-        answers[0] = choiceAEditText.getText().toString();
-        answers[1] = choiceBEditText.getText().toString();
-        answers[2] = choiceCEditText.getText().toString();
-        answers[3] = choiceDEditText.getText().toString();
-        int correctAnswerIndex = radioIdToIndex.get(choicesRadioGroup.getCheckedRadioButtonId());
-
-        addQuestionDataItem(new Question(question, mQuestionIndex++, answers, correctAnswerIndex));
-        setNewQuestionStatus(question);
-
-        // Clear the edit boxes to let the user input a new question.
-        questionEditText.setText("");
-        choiceAEditText.setText("");
-        choiceBEditText.setText("");
-        choiceCEditText.setText("");
-        choiceDEditText.setText("");
-    }
-
-    /**
-     * Adds the questions (and answers) to the wearable's stream by creating a Data Item
-     * that will be received on the wearable, which will create corresponding notifications.
-     */
-    private void addQuestionDataItem(Question question) {
-        if (!mHasQuestionBeenAsked) {
-            // Ask the question now.
-            Wearable.DataApi.putDataItem(mGoogleApiClient, question.toPutDataRequest());
-            setHasQuestionBeenAsked(true);
-        } else {
-            // Enqueue the question to be asked in the future.
-            mFutureQuestions.add(question);
-        }
-    }
-
-    /**
-     * Sets the question's status to be the default "unanswered." This will be updated when the
-     * user chooses an answer for the question on the wearable.
-     */
-    private void setNewQuestionStatus(String question) {
-        quizStatus.setVisibility(View.VISIBLE);
-        quizButtons.setVisibility(View.VISIBLE);
-        LayoutInflater inflater = LayoutInflater.from(this);
-        View questionStatusElem = inflater.inflate(R.layout.question_status_element, null, false);
-        ((TextView) questionStatusElem.findViewById(R.id.question)).setText(question);
-        ((TextView) questionStatusElem.findViewById(R.id.status))
-                .setText(R.string.question_unanswered);
-        questionsContainer.addView(questionStatusElem);
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
-        dataEvents.close();
-        runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                for (DataEvent event : events) {
-                    if (event.getType() == DataEvent.TYPE_CHANGED) {
-                        DataMap dataMap = DataMapItem.fromDataItem(event.getDataItem())
-                                .getDataMap();
-                        boolean questionWasAnswered = dataMap.getBoolean(QUESTION_WAS_ANSWERED);
-                        boolean questionWasDeleted = dataMap.getBoolean(QUESTION_WAS_DELETED);
-                        if (questionWasAnswered) {
-                            // Update the answered question's status.
-                            int questionIndex = dataMap.getInt(QUESTION_INDEX);
-                            boolean questionCorrect = dataMap.getBoolean(CHOSEN_ANSWER_CORRECT);
-                            updateQuestionStatus(questionIndex, questionCorrect);
-                            askNextQuestionIfExists();
-                        } else if (questionWasDeleted) {
-                            // Update the deleted question's status by marking it as left blank.
-                            int questionIndex = dataMap.getInt(QUESTION_INDEX);
-                            markQuestionLeftBlank(questionIndex);
-                            askNextQuestionIfExists();
-                        }
-                    }
-                }
-            }
-        });
-    }
-
-    /**
-     * Updates the given question based on whether it was answered correctly or not.
-     * This involves changing the question's text color and changing the status text for it.
-     */
-    public void updateQuestionStatus(int questionIndex, boolean questionCorrect) {
-        LinearLayout questionStatusElement = (LinearLayout)
-                questionsContainer.getChildAt(questionIndex);
-        TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
-        TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
-        if (questionCorrect) {
-            questionText.setTextColor(Color.GREEN);
-            questionStatus.setText(R.string.question_correct);
-            mNumCorrect++;
-        } else {
-            questionText.setTextColor(Color.RED);
-            questionStatus.setText(R.string.question_incorrect);
-            mNumIncorrect++;
-        }
-    }
-
-    /**
-     * Marks a question as "left blank" when its corresponding question notification is deleted.
-     */
-    private void markQuestionLeftBlank(int index) {
-        LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(index);
-        if (questionStatusElement != null) {
-            TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
-            TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
-            if (questionStatus.getText().equals(getString(R.string.question_unanswered))) {
-                questionText.setTextColor(Color.YELLOW);
-                questionStatus.setText(R.string.question_left_blank);
-                mNumSkipped++;
-            }
-        }
-    }
-
-    /**
-     * Asks the next enqueued question if it exists, otherwise ends the quiz.
-     */
-    private void askNextQuestionIfExists() {
-        if (mFutureQuestions.isEmpty()) {
-            // Quiz has been completed - send message to wearable to display end report.
-            DataMap dataMap = new DataMap();
-            dataMap.putInt(NUM_CORRECT, mNumCorrect);
-            dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
-            dataMap.putInt(NUM_SKIPPED, mNumSkipped);
-            sendMessageToWearable(QUIZ_ENDED_PATH, dataMap.toByteArray());
-            setHasQuestionBeenAsked(false);
-        } else {
-            // Ask next question by putting a DataItem that will be received on the wearable.
-            Wearable.DataApi.putDataItem(mGoogleApiClient,
-                    mFutureQuestions.remove().toPutDataRequest());
-            setHasQuestionBeenAsked(true);
-        }
-    }
-
-    private void sendMessageToWearable(final String path, final byte[] data) {
-        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
-                new ResultCallback<NodeApi.GetConnectedNodesResult>() {
-            @Override
-            public void onResult(NodeApi.GetConnectedNodesResult nodes) {
-                for (Node node : nodes.getNodes()) {
-                    Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, data);
-                }
-
-                if (path.equals(QUIZ_EXITED_PATH) && mGoogleApiClient.isConnected()) {
-                    mGoogleApiClient.disconnect();
-                }
-            }
-        });
-    }
-
-    /**
-     * Resets the current quiz when Reset Quiz is pressed.
-     */
-    public void resetQuiz(View view) {
-        // Reset quiz status in phone layout.
-        for(int i = 0; i < questionsContainer.getChildCount(); i++) {
-            LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(i);
-            TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
-            TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
-            questionText.setTextColor(Color.WHITE);
-            questionStatus.setText(R.string.question_unanswered);
-        }
-        // Reset data items and notifications on wearable.
-        if (mGoogleApiClient.isConnected()) {
-            Wearable.DataApi.getDataItems(mGoogleApiClient)
-                    .setResultCallback(new ResultCallback<DataItemBuffer>() {
-                        @Override
-                        public void onResult(DataItemBuffer result) {
-                            if (result.getStatus().isSuccess()) {
-                                List<DataItem> dataItemList = FreezableUtils.freezeIterable(result);
-                                result.close();
-                                resetDataItems(dataItemList);
-                            } else {
-                                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                                    Log.d(TAG, "Reset quiz: failed to get Data Items to reset");
-                                }
-                            }
-                            result.close();
-                        }
-                    });
-        } else {
-            Log.e(TAG, "Failed to reset data items because client is disconnected from "
-                    + "Google Play Services");
-        }
-        setHasQuestionBeenAsked(false);
-        mNumCorrect = 0;
-        mNumIncorrect = 0;
-        mNumSkipped = 0;
-    }
-
-    private void resetDataItems(List<DataItem> dataItemList) {
-        if (mGoogleApiClient.isConnected()) {
-            for (final DataItem dataItem : dataItemList) {
-                final Uri dataItemUri = dataItem.getUri();
-                Wearable.DataApi.getDataItem(mGoogleApiClient, dataItemUri)
-                        .setResultCallback(new ResetDataItemCallback());
-            }
-        } else {
-            Log.e(TAG, "Failed to reset data items because client is disconnected from "
-                    + "Google Play Services");
-        }
-    }
-
-    /**
-     * Callback that marks a DataItem, which represents a question, as unanswered and not deleted.
-     */
-    private class ResetDataItemCallback implements ResultCallback<DataApi.DataItemResult> {
-        @Override
-        public void onResult(DataApi.DataItemResult dataItemResult) {
-            if (dataItemResult.getStatus().isSuccess()) {
-                PutDataMapRequest request = PutDataMapRequest.createFromDataMapItem(
-                                DataMapItem.fromDataItem(dataItemResult.getDataItem()));
-                DataMap dataMap = request.getDataMap();
-                dataMap.putBoolean(QUESTION_WAS_ANSWERED, false);
-                dataMap.putBoolean(QUESTION_WAS_DELETED, false);
-                if (!mHasQuestionBeenAsked && dataMap.getInt(QUESTION_INDEX) == 0) {
-                    // Ask the first question now.
-                    Wearable.DataApi.putDataItem(mGoogleApiClient, request.asPutDataRequest());
-                    setHasQuestionBeenAsked(true);
-                } else {
-                    // Enqueue future questions.
-                    mFutureQuestions.add(new Question(dataMap.getString(QUESTION),
-                            dataMap.getInt(QUESTION_INDEX), dataMap.getStringArray(ANSWERS),
-                            dataMap.getInt(CORRECT_ANSWER_INDEX)));
-                }
-            } else {
-                Log.e(TAG, "Failed to reset data item " + dataItemResult.getDataItem().getUri());
-            }
-        }
-    }
-
-    /**
-     * Clears the current quiz when user clicks on "New Quiz."
-     * On this end, this involves clearing the quiz status layout and deleting all DataItems. The
-     * wearable will then remove any outstanding question notifications upon receiving this change.
-     */
-    public void newQuiz(View view) {
-        clearQuizStatus();
-        if (mGoogleApiClient.isConnected()) {
-            Wearable.DataApi.getDataItems(mGoogleApiClient)
-                    .setResultCallback(new ResultCallback<DataItemBuffer>() {
-                        @Override
-                        public void onResult(DataItemBuffer result) {
-                            if (result.getStatus().isSuccess()) {
-                                List<Uri> dataItemUriList = new ArrayList<Uri>();
-                                for (final DataItem dataItem : result) {
-                                    dataItemUriList.add(dataItem.getUri());
-                                }
-                                result.close();
-                                deleteDataItems(dataItemUriList);
-                            } else {
-                                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                                    Log.d(TAG, "Clear quiz: failed to get Data Items for deletion");
-                                }
-                            }
-                            result.close();
-                        }
-                    });
-        } else {
-            Log.e(TAG, "Failed to delete data items because client is disconnected from "
-                    + "Google Play Services");
-        }
-    }
-
-    /**
-     * Removes quiz status views (i.e. the views describing the status of each question).
-     */
-    private void clearQuizStatus() {
-        questionsContainer.removeAllViews();
-        quizStatus.setVisibility(View.INVISIBLE);
-        quizButtons.setVisibility(View.INVISIBLE);
-        setHasQuestionBeenAsked(false);
-        mFutureQuestions.clear();
-        mQuestionIndex = 0;
-        mNumCorrect = 0;
-        mNumIncorrect = 0;
-        mNumSkipped = 0;
-    }
-
-    private void deleteDataItems(List<Uri> dataItemUriList) {
-        if (mGoogleApiClient.isConnected()) {
-            for (final Uri dataItemUri : dataItemUriList) {
-                Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
-                        .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
-                            @Override
-                            public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
-                                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                                    if (deleteResult.getStatus().isSuccess()) {
-                                        Log.d(TAG, "Successfully deleted data item " + dataItemUri);
-                                    } else {
-                                        Log.d(TAG, "Failed to delete data item " + dataItemUri);
-                                    }
-                                }
-                            }
-                        });
-            }
-        } else {
-            Log.e(TAG, "Failed to delete data items because client is disconnected from "
-                    + "Google Play Services");
-        }
-    }
-
-    private void setHasQuestionBeenAsked(boolean b) {
-        mHasQuestionBeenAsked = b;
-        // Only let user click on Reset or Read from file if they have answered all the questions.
-        readQuizFromFileButton.setEnabled(!mHasQuestionBeenAsked);
-        resetQuizButton.setEnabled(!mHasQuestionBeenAsked);
-    }
-}
diff --git a/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/Constants.java b/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/Constants.java
new file mode 100644
index 0000000..2bcfe5f
--- /dev/null
+++ b/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/Constants.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+/** Constants used in the companion app. */
+public final class Constants {
+    private Constants() {
+    }
+
+    public static final String ANSWERS = "answers";
+    public static final String CHOSEN_ANSWER_CORRECT = "chosen_answer_correct";
+    public static final String CORRECT_ANSWER_INDEX = "correct_answer_index";
+    public static final String QUESTION = "question";
+    public static final String QUESTION_INDEX = "question_index";
+    public static final String QUESTION_WAS_ANSWERED = "question_was_answered";
+    public static final String QUESTION_WAS_DELETED = "question_was_deleted";
+
+    public static final String NUM_CORRECT = "num_correct";
+    public static final String NUM_INCORRECT = "num_incorrect";
+    public static final String NUM_SKIPPED = "num_skipped";
+
+    public static final String QUIZ_ENDED_PATH = "/quiz_ended";
+    public static final String QUIZ_EXITED_PATH = "/quiz_exited";
+    public static final String RESET_QUIZ_PATH = "/reset_quiz";
+}
diff --git a/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/JsonUtils.java b/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/JsonUtils.java
new file mode 100644
index 0000000..df8c899
--- /dev/null
+++ b/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/JsonUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+import android.content.Context;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+final class JsonUtils {
+    public static final String JSON_FIELD_QUESTIONS = "questions";
+    public static final String JSON_FIELD_QUESTION = "question";
+    public static final String JSON_FIELD_ANSWERS = "answers";
+    public static final String JSON_FIELD_CORRECT_INDEX = "correctIndex";
+    public static final int NUM_ANSWER_CHOICES = 4;
+
+    private JsonUtils() {
+    }
+
+    public static JSONObject loadJsonFile(Context context, String fileName) throws IOException,
+            JSONException {
+        InputStream is = context.getAssets().open(fileName);
+        int size = is.available();
+        byte[] buffer = new byte[size];
+        is.read(buffer);
+        is.close();
+        String jsonString = new String(buffer);
+        return new JSONObject(jsonString);
+    }
+}
diff --git a/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/MainActivity.java b/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/MainActivity.java
new file mode 100644
index 0000000..0ff41f4
--- /dev/null
+++ b/wearable/wear/Quiz/Application/src/main/java/com/example/android/wearable/quiz/MainActivity.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+import static com.example.android.wearable.quiz.Constants.ANSWERS;
+import static com.example.android.wearable.quiz.Constants.CHOSEN_ANSWER_CORRECT;
+import static com.example.android.wearable.quiz.Constants.CORRECT_ANSWER_INDEX;
+import static com.example.android.wearable.quiz.Constants.NUM_CORRECT;
+import static com.example.android.wearable.quiz.Constants.NUM_INCORRECT;
+import static com.example.android.wearable.quiz.Constants.NUM_SKIPPED;
+import static com.example.android.wearable.quiz.Constants.QUESTION;
+import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
+import static com.example.android.wearable.quiz.Constants.QUIZ_ENDED_PATH;
+import static com.example.android.wearable.quiz.Constants.QUIZ_EXITED_PATH;
+import static com.example.android.wearable.quiz.Constants.RESET_QUIZ_PATH;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataItemBuffer;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.MessageApi;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+
+/**
+ * Allows the user to create questions, which will be put as notifications on the watch's stream.
+ * The status of questions will be updated on the phone when the user answers them.
+ */
+public class MainActivity extends Activity implements DataApi.DataListener,
+        MessageApi.MessageListener, ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "ExampleQuizApp";
+    private static final String QUIZ_JSON_FILE = "Quiz.json";
+
+    // Various UI components.
+    private EditText questionEditText;
+    private EditText choiceAEditText;
+    private EditText choiceBEditText;
+    private EditText choiceCEditText;
+    private EditText choiceDEditText;
+    private RadioGroup choicesRadioGroup;
+    private TextView quizStatus;
+    private LinearLayout quizButtons;
+    private LinearLayout questionsContainer;
+    private Button readQuizFromFileButton;
+    private Button resetQuizButton;
+
+    private GoogleApiClient mGoogleApiClient;
+    private PriorityQueue<Question> mFutureQuestions;
+    private int mQuestionIndex = 0;
+    private boolean mHasQuestionBeenAsked = false;
+
+    // Data to display in end report.
+    private int mNumCorrect = 0;
+    private int mNumIncorrect = 0;
+    private int mNumSkipped = 0;
+
+    private static final Map<Integer, Integer> radioIdToIndex;
+
+    static {
+        Map<Integer, Integer> temp = new HashMap<Integer, Integer>(4);
+        temp.put(R.id.choice_a_radio, 0);
+        temp.put(R.id.choice_b_radio, 1);
+        temp.put(R.id.choice_c_radio, 2);
+        temp.put(R.id.choice_d_radio, 3);
+        radioIdToIndex = Collections.unmodifiableMap(temp);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+        mFutureQuestions = new PriorityQueue<Question>(10);
+
+        // Find UI components to be used later.
+        questionEditText = (EditText) findViewById(R.id.question_text);
+        choiceAEditText = (EditText) findViewById(R.id.choice_a_text);
+        choiceBEditText = (EditText) findViewById(R.id.choice_b_text);
+        choiceCEditText = (EditText) findViewById(R.id.choice_c_text);
+        choiceDEditText = (EditText) findViewById(R.id.choice_d_text);
+        choicesRadioGroup = (RadioGroup) findViewById(R.id.choices_radio_group);
+        quizStatus = (TextView) findViewById(R.id.quiz_status);
+        quizButtons = (LinearLayout) findViewById(R.id.quiz_buttons);
+        questionsContainer = (LinearLayout) findViewById(R.id.questions_container);
+        readQuizFromFileButton = (Button) findViewById(R.id.read_quiz_from_file_button);
+        resetQuizButton = (Button) findViewById(R.id.reset_quiz_button);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        if (!mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        Wearable.DataApi.removeListener(mGoogleApiClient, this);
+        Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+
+        // Tell the wearable to end the quiz (counting unanswered questions as skipped), and then
+        // disconnect mGoogleApiClient.
+        DataMap dataMap = new DataMap();
+        dataMap.putInt(NUM_CORRECT, mNumCorrect);
+        dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
+        if (mHasQuestionBeenAsked) {
+            mNumSkipped += 1;
+        }
+        mNumSkipped += mFutureQuestions.size();
+        dataMap.putInt(NUM_SKIPPED, mNumSkipped);
+        if (mNumCorrect + mNumIncorrect + mNumSkipped > 0) {
+            sendMessageToWearable(QUIZ_EXITED_PATH, dataMap.toByteArray());
+        }
+
+        clearQuizStatus();
+        super.onStop();
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+        Wearable.DataApi.addListener(mGoogleApiClient, this);
+        Wearable.MessageApi.addListener(mGoogleApiClient, this);
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+        // Ignore
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+        Log.e(TAG, "Failed to connect to Google Play Services");
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent messageEvent) {
+        if (messageEvent.getPath().equals(RESET_QUIZ_PATH)) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    resetQuiz(null);
+                }
+            });
+        }
+    }
+
+    /**
+     * Used to ensure questions with smaller indexes come before questions with larger
+     * indexes. For example, question0 should come before question1.
+     */
+    private static class Question implements Comparable<Question> {
+        private String question;
+        private int questionIndex;
+        private String[] answers;
+        private int correctAnswerIndex;
+
+        public Question(String question, int questionIndex, String[] answers,
+                int correctAnswerIndex) {
+            this.question = question;
+            this.questionIndex = questionIndex;
+            this.answers = answers;
+            this.correctAnswerIndex = correctAnswerIndex;
+        }
+
+        public static Question fromJson(JSONObject questionObject, int questionIndex)
+                throws JSONException {
+            String question = questionObject.getString(JsonUtils.JSON_FIELD_QUESTION);
+            JSONArray answersJsonArray = questionObject.getJSONArray(JsonUtils.JSON_FIELD_ANSWERS);
+            String[] answers = new String[JsonUtils.NUM_ANSWER_CHOICES];
+            for (int j = 0; j < answersJsonArray.length(); j++) {
+                answers[j] = answersJsonArray.getString(j);
+            }
+            int correctIndex = questionObject.getInt(JsonUtils.JSON_FIELD_CORRECT_INDEX);
+            return new Question(question, questionIndex, answers, correctIndex);
+        }
+
+        @Override
+        public int compareTo(Question that) {
+            return this.questionIndex - that.questionIndex;
+        }
+
+        public PutDataRequest toPutDataRequest() {
+            PutDataMapRequest request = PutDataMapRequest.create("/question/" + questionIndex);
+            DataMap dataMap = request.getDataMap();
+            dataMap.putString(QUESTION, question);
+            dataMap.putInt(QUESTION_INDEX, questionIndex);
+            dataMap.putStringArray(ANSWERS, answers);
+            dataMap.putInt(CORRECT_ANSWER_INDEX, correctAnswerIndex);
+            return request.asPutDataRequest();
+        }
+    }
+
+    /**
+     * Create a quiz, as defined in Quiz.json, when the user clicks on "Read quiz from file."
+     * @throws IOException
+     */
+    public void readQuizFromFile(View view) throws IOException, JSONException {
+        clearQuizStatus();
+        JSONObject jsonObject = JsonUtils.loadJsonFile(this, QUIZ_JSON_FILE);
+        JSONArray jsonArray = jsonObject.getJSONArray(JsonUtils.JSON_FIELD_QUESTIONS);
+        for (int i = 0; i < jsonArray.length(); i++) {
+            JSONObject questionObject = jsonArray.getJSONObject(i);
+            Question question = Question.fromJson(questionObject, mQuestionIndex++);
+            addQuestionDataItem(question);
+            setNewQuestionStatus(question.question);
+        }
+    }
+
+    /**
+     * Adds a question (with answer choices) when user clicks on "Add Question."
+     */
+    public void addQuestion(View view) {
+        // Retrieve the question and answers supplied by the user.
+        String question = questionEditText.getText().toString();
+        String[] answers = new String[4];
+        answers[0] = choiceAEditText.getText().toString();
+        answers[1] = choiceBEditText.getText().toString();
+        answers[2] = choiceCEditText.getText().toString();
+        answers[3] = choiceDEditText.getText().toString();
+        int correctAnswerIndex = radioIdToIndex.get(choicesRadioGroup.getCheckedRadioButtonId());
+
+        addQuestionDataItem(new Question(question, mQuestionIndex++, answers, correctAnswerIndex));
+        setNewQuestionStatus(question);
+
+        // Clear the edit boxes to let the user input a new question.
+        questionEditText.setText("");
+        choiceAEditText.setText("");
+        choiceBEditText.setText("");
+        choiceCEditText.setText("");
+        choiceDEditText.setText("");
+    }
+
+    /**
+     * Adds the questions (and answers) to the wearable's stream by creating a Data Item
+     * that will be received on the wearable, which will create corresponding notifications.
+     */
+    private void addQuestionDataItem(Question question) {
+        if (!mHasQuestionBeenAsked) {
+            // Ask the question now.
+            Wearable.DataApi.putDataItem(mGoogleApiClient, question.toPutDataRequest());
+            setHasQuestionBeenAsked(true);
+        } else {
+            // Enqueue the question to be asked in the future.
+            mFutureQuestions.add(question);
+        }
+    }
+
+    /**
+     * Sets the question's status to be the default "unanswered." This will be updated when the
+     * user chooses an answer for the question on the wearable.
+     */
+    private void setNewQuestionStatus(String question) {
+        quizStatus.setVisibility(View.VISIBLE);
+        quizButtons.setVisibility(View.VISIBLE);
+        LayoutInflater inflater = LayoutInflater.from(this);
+        View questionStatusElem = inflater.inflate(R.layout.question_status_element, null, false);
+        ((TextView) questionStatusElem.findViewById(R.id.question)).setText(question);
+        ((TextView) questionStatusElem.findViewById(R.id.status))
+                .setText(R.string.question_unanswered);
+        questionsContainer.addView(questionStatusElem);
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
+        dataEvents.close();
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                for (DataEvent event : events) {
+                    if (event.getType() == DataEvent.TYPE_CHANGED) {
+                        DataMap dataMap = DataMapItem.fromDataItem(event.getDataItem())
+                                .getDataMap();
+                        boolean questionWasAnswered = dataMap.getBoolean(QUESTION_WAS_ANSWERED);
+                        boolean questionWasDeleted = dataMap.getBoolean(QUESTION_WAS_DELETED);
+                        if (questionWasAnswered) {
+                            // Update the answered question's status.
+                            int questionIndex = dataMap.getInt(QUESTION_INDEX);
+                            boolean questionCorrect = dataMap.getBoolean(CHOSEN_ANSWER_CORRECT);
+                            updateQuestionStatus(questionIndex, questionCorrect);
+                            askNextQuestionIfExists();
+                        } else if (questionWasDeleted) {
+                            // Update the deleted question's status by marking it as left blank.
+                            int questionIndex = dataMap.getInt(QUESTION_INDEX);
+                            markQuestionLeftBlank(questionIndex);
+                            askNextQuestionIfExists();
+                        }
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Updates the given question based on whether it was answered correctly or not.
+     * This involves changing the question's text color and changing the status text for it.
+     */
+    public void updateQuestionStatus(int questionIndex, boolean questionCorrect) {
+        LinearLayout questionStatusElement = (LinearLayout)
+                questionsContainer.getChildAt(questionIndex);
+        TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
+        TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
+        if (questionCorrect) {
+            questionText.setTextColor(Color.GREEN);
+            questionStatus.setText(R.string.question_correct);
+            mNumCorrect++;
+        } else {
+            questionText.setTextColor(Color.RED);
+            questionStatus.setText(R.string.question_incorrect);
+            mNumIncorrect++;
+        }
+    }
+
+    /**
+     * Marks a question as "left blank" when its corresponding question notification is deleted.
+     */
+    private void markQuestionLeftBlank(int index) {
+        LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(index);
+        if (questionStatusElement != null) {
+            TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
+            TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
+            if (questionStatus.getText().equals(getString(R.string.question_unanswered))) {
+                questionText.setTextColor(Color.YELLOW);
+                questionStatus.setText(R.string.question_left_blank);
+                mNumSkipped++;
+            }
+        }
+    }
+
+    /**
+     * Asks the next enqueued question if it exists, otherwise ends the quiz.
+     */
+    private void askNextQuestionIfExists() {
+        if (mFutureQuestions.isEmpty()) {
+            // Quiz has been completed - send message to wearable to display end report.
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(NUM_CORRECT, mNumCorrect);
+            dataMap.putInt(NUM_INCORRECT, mNumIncorrect);
+            dataMap.putInt(NUM_SKIPPED, mNumSkipped);
+            sendMessageToWearable(QUIZ_ENDED_PATH, dataMap.toByteArray());
+            setHasQuestionBeenAsked(false);
+        } else {
+            // Ask next question by putting a DataItem that will be received on the wearable.
+            Wearable.DataApi.putDataItem(mGoogleApiClient,
+                    mFutureQuestions.remove().toPutDataRequest());
+            setHasQuestionBeenAsked(true);
+        }
+    }
+
+    private void sendMessageToWearable(final String path, final byte[] data) {
+        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
+                new ResultCallback<NodeApi.GetConnectedNodesResult>() {
+            @Override
+            public void onResult(NodeApi.GetConnectedNodesResult nodes) {
+                for (Node node : nodes.getNodes()) {
+                    Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, data);
+                }
+
+                if (path.equals(QUIZ_EXITED_PATH) && mGoogleApiClient.isConnected()) {
+                    mGoogleApiClient.disconnect();
+                }
+            }
+        });
+    }
+
+    /**
+     * Resets the current quiz when Reset Quiz is pressed.
+     */
+    public void resetQuiz(View view) {
+        // Reset quiz status in phone layout.
+        for(int i = 0; i < questionsContainer.getChildCount(); i++) {
+            LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(i);
+            TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
+            TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
+            questionText.setTextColor(Color.WHITE);
+            questionStatus.setText(R.string.question_unanswered);
+        }
+        // Reset data items and notifications on wearable.
+        if (mGoogleApiClient.isConnected()) {
+            Wearable.DataApi.getDataItems(mGoogleApiClient)
+                    .setResultCallback(new ResultCallback<DataItemBuffer>() {
+                        @Override
+                        public void onResult(DataItemBuffer result) {
+                            if (result.getStatus().isSuccess()) {
+                                List<DataItem> dataItemList = FreezableUtils.freezeIterable(result);
+                                result.close();
+                                resetDataItems(dataItemList);
+                            } else {
+                                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                    Log.d(TAG, "Reset quiz: failed to get Data Items to reset");
+                                }
+                            }
+                            result.close();
+                        }
+                    });
+        } else {
+            Log.e(TAG, "Failed to reset data items because client is disconnected from "
+                    + "Google Play Services");
+        }
+        setHasQuestionBeenAsked(false);
+        mNumCorrect = 0;
+        mNumIncorrect = 0;
+        mNumSkipped = 0;
+    }
+
+    private void resetDataItems(List<DataItem> dataItemList) {
+        if (mGoogleApiClient.isConnected()) {
+            for (final DataItem dataItem : dataItemList) {
+                final Uri dataItemUri = dataItem.getUri();
+                Wearable.DataApi.getDataItem(mGoogleApiClient, dataItemUri)
+                        .setResultCallback(new ResetDataItemCallback());
+            }
+        } else {
+            Log.e(TAG, "Failed to reset data items because client is disconnected from "
+                    + "Google Play Services");
+        }
+    }
+
+    /**
+     * Callback that marks a DataItem, which represents a question, as unanswered and not deleted.
+     */
+    private class ResetDataItemCallback implements ResultCallback<DataApi.DataItemResult> {
+        @Override
+        public void onResult(DataApi.DataItemResult dataItemResult) {
+            if (dataItemResult.getStatus().isSuccess()) {
+                PutDataMapRequest request = PutDataMapRequest.createFromDataMapItem(
+                                DataMapItem.fromDataItem(dataItemResult.getDataItem()));
+                DataMap dataMap = request.getDataMap();
+                dataMap.putBoolean(QUESTION_WAS_ANSWERED, false);
+                dataMap.putBoolean(QUESTION_WAS_DELETED, false);
+                if (!mHasQuestionBeenAsked && dataMap.getInt(QUESTION_INDEX) == 0) {
+                    // Ask the first question now.
+                    Wearable.DataApi.putDataItem(mGoogleApiClient, request.asPutDataRequest());
+                    setHasQuestionBeenAsked(true);
+                } else {
+                    // Enqueue future questions.
+                    mFutureQuestions.add(new Question(dataMap.getString(QUESTION),
+                            dataMap.getInt(QUESTION_INDEX), dataMap.getStringArray(ANSWERS),
+                            dataMap.getInt(CORRECT_ANSWER_INDEX)));
+                }
+            } else {
+                Log.e(TAG, "Failed to reset data item " + dataItemResult.getDataItem().getUri());
+            }
+        }
+    }
+
+    /**
+     * Clears the current quiz when user clicks on "New Quiz."
+     * On this end, this involves clearing the quiz status layout and deleting all DataItems. The
+     * wearable will then remove any outstanding question notifications upon receiving this change.
+     */
+    public void newQuiz(View view) {
+        clearQuizStatus();
+        if (mGoogleApiClient.isConnected()) {
+            Wearable.DataApi.getDataItems(mGoogleApiClient)
+                    .setResultCallback(new ResultCallback<DataItemBuffer>() {
+                        @Override
+                        public void onResult(DataItemBuffer result) {
+                            if (result.getStatus().isSuccess()) {
+                                List<Uri> dataItemUriList = new ArrayList<Uri>();
+                                for (final DataItem dataItem : result) {
+                                    dataItemUriList.add(dataItem.getUri());
+                                }
+                                result.close();
+                                deleteDataItems(dataItemUriList);
+                            } else {
+                                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                    Log.d(TAG, "Clear quiz: failed to get Data Items for deletion");
+                                }
+                            }
+                            result.close();
+                        }
+                    });
+        } else {
+            Log.e(TAG, "Failed to delete data items because client is disconnected from "
+                    + "Google Play Services");
+        }
+    }
+
+    /**
+     * Removes quiz status views (i.e. the views describing the status of each question).
+     */
+    private void clearQuizStatus() {
+        questionsContainer.removeAllViews();
+        quizStatus.setVisibility(View.INVISIBLE);
+        quizButtons.setVisibility(View.INVISIBLE);
+        setHasQuestionBeenAsked(false);
+        mFutureQuestions.clear();
+        mQuestionIndex = 0;
+        mNumCorrect = 0;
+        mNumIncorrect = 0;
+        mNumSkipped = 0;
+    }
+
+    private void deleteDataItems(List<Uri> dataItemUriList) {
+        if (mGoogleApiClient.isConnected()) {
+            for (final Uri dataItemUri : dataItemUriList) {
+                Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
+                        .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
+                            @Override
+                            public void onResult(DataApi.DeleteDataItemsResult deleteResult) {
+                                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                    if (deleteResult.getStatus().isSuccess()) {
+                                        Log.d(TAG, "Successfully deleted data item " + dataItemUri);
+                                    } else {
+                                        Log.d(TAG, "Failed to delete data item " + dataItemUri);
+                                    }
+                                }
+                            }
+                        });
+            }
+        } else {
+            Log.e(TAG, "Failed to delete data items because client is disconnected from "
+                    + "Google Play Services");
+        }
+    }
+
+    private void setHasQuestionBeenAsked(boolean b) {
+        mHasQuestionBeenAsked = b;
+        // Only let user click on Reset or Read from file if they have answered all the questions.
+        readQuizFromFileButton.setEnabled(!mHasQuestionBeenAsked);
+        resetQuizButton.setEnabled(!mHasQuestionBeenAsked);
+    }
+}
diff --git a/wearable/wear/Quiz/Shared/.gitignore b/wearable/wear/Quiz/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/Quiz/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/Quiz/Shared/src/main/AndroidManifest.xml b/wearable/wear/Quiz/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 0d7b8a6..0000000
--- a/wearable/wear/Quiz/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.quiz.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/Quiz/Shared/src/main/res/values/strings.xml b/wearable/wear/Quiz/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/Quiz/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/Quiz/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Quiz/Wearable/src/main/AndroidManifest.xml
index 7954e32..fbc7492 100644
--- a/wearable/wear/Quiz/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Quiz/Wearable/src/main/AndroidManifest.xml
@@ -15,11 +15,11 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.quiz" >
+    package="com.example.android.wearable.quiz" >
 
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/Constants.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/Constants.java
deleted file mode 100644
index 8218ad9..0000000
--- a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/Constants.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-/** Constants used in the wearable app. */
-public final class Constants {
-    private Constants() {
-    }
-
-    public static final String ANSWERS = "answers";
-    public static final String CHOSEN_ANSWER_CORRECT = "chosen_answer_correct";
-    public static final String CORRECT_ANSWER_INDEX = "correct_answer_index";
-    public static final String QUESTION = "question";
-    public static final String QUESTION_INDEX = "question_index";
-    public static final String QUESTION_WAS_ANSWERED = "question_was_answered";
-    public static final String QUESTION_WAS_DELETED = "question_was_deleted";
-
-    public static final String NUM_CORRECT = "num_correct";
-    public static final String NUM_INCORRECT = "num_incorrect";
-    public static final String NUM_SKIPPED = "num_skipped";
-
-    public static final String QUIZ_ENDED_PATH = "/quiz_ended";
-    public static final String QUIZ_EXITED_PATH = "/quiz_exited";
-    public static final String RESET_QUIZ_PATH = "/reset_quiz";
-
-    public static final int CONNECT_TIMEOUT_MS = 100;
-}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/DeleteQuestionService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/DeleteQuestionService.java
deleted file mode 100644
index 38b5e4a..0000000
--- a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/DeleteQuestionService.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.PutDataRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.concurrent.TimeUnit;
-
-import static com.example.android.quiz.Constants.CONNECT_TIMEOUT_MS;
-import static com.example.android.quiz.Constants.QUESTION_WAS_DELETED;
-
-/**
- * Used to update quiz status on the phone when user dismisses a question on the watch.
- */
-public class DeleteQuestionService extends IntentService
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
-
-    private static final String TAG = "DeleteQuestionReceiver";
-
-    private GoogleApiClient mGoogleApiClient;
-
-    public DeleteQuestionService() {
-        super(DeleteQuestionService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    public void onHandleIntent(Intent intent) {
-        mGoogleApiClient.blockingConnect(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        Uri dataItemUri = intent.getData();
-        if (!mGoogleApiClient.isConnected()) {
-            Log.e(TAG, "Failed to update data item " + dataItemUri
-                    + " because client is disconnected from Google Play Services");
-            return;
-        }
-        DataApi.DataItemResult dataItemResult = Wearable.DataApi.getDataItem(
-                mGoogleApiClient, dataItemUri).await();
-        PutDataMapRequest putDataMapRequest = PutDataMapRequest
-                .createFromDataMapItem(DataMapItem.fromDataItem(dataItemResult.getDataItem()));
-        DataMap dataMap = putDataMapRequest.getDataMap();
-        dataMap.putBoolean(QUESTION_WAS_DELETED, true);
-        PutDataRequest request = putDataMapRequest.asPutDataRequest();
-        Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
-        mGoogleApiClient.disconnect();
-    }
-
-    @Override
-    public void onConnected(Bundle bundle) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int i) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-    }
-}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/QuizListenerService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/QuizListenerService.java
deleted file mode 100644
index 3226f9b..0000000
--- a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/QuizListenerService.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-import static com.example.android.quiz.Constants.ANSWERS;
-import static com.example.android.quiz.Constants.CONNECT_TIMEOUT_MS;
-import static com.example.android.quiz.Constants.CORRECT_ANSWER_INDEX;
-import static com.example.android.quiz.Constants.NUM_CORRECT;
-import static com.example.android.quiz.Constants.NUM_INCORRECT;
-import static com.example.android.quiz.Constants.NUM_SKIPPED;
-import static com.example.android.quiz.Constants.QUESTION;
-import static com.example.android.quiz.Constants.QUESTION_INDEX;
-import static com.example.android.quiz.Constants.QUESTION_WAS_ANSWERED;
-import static com.example.android.quiz.Constants.QUESTION_WAS_DELETED;
-import static com.example.android.quiz.Constants.QUIZ_ENDED_PATH;
-import static com.example.android.quiz.Constants.QUIZ_EXITED_PATH;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.net.Uri;
-import android.text.SpannableStringBuilder;
-import android.text.style.ForegroundColorSpan;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.common.data.FreezableUtils;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataItem;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.MessageEvent;
-import com.google.android.gms.wearable.Wearable;
-import com.google.android.gms.wearable.WearableListenerService;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Listens to changes in DataItems, which represent quiz questions.
- * If a new question is created, this builds a new notification for it.
- * Otherwise, if a question is deleted, this cancels the corresponding notification.
- *
- * When the quiz ends, this listener receives a message telling it to create an end-of-quiz report.
- */
-public class QuizListenerService extends WearableListenerService {
-    private static final String TAG = "QuizSample";
-    private static final int QUIZ_REPORT_NOTIF_ID = -1; // Never used by question notifications.
-    private static final Map<Integer, Integer> questionNumToDrawableId;
-
-    static {
-        Map<Integer, Integer> temp = new HashMap<Integer, Integer>(4);
-        temp.put(0, R.drawable.ic_choice_a);
-        temp.put(1, R.drawable.ic_choice_b);
-        temp.put(2, R.drawable.ic_choice_c);
-        temp.put(3, R.drawable.ic_choice_d);
-        questionNumToDrawableId = Collections.unmodifiableMap(temp);
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
-        dataEvents.close();
-
-        GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .build();
-
-        ConnectionResult connectionResult = googleApiClient.blockingConnect(CONNECT_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
-        if (!connectionResult.isSuccess()) {
-            Log.e(TAG, "QuizListenerService failed to connect to GoogleApiClient.");
-            return;
-        }
-
-        for (DataEvent event : events) {
-            if (event.getType() == DataEvent.TYPE_CHANGED) {
-                DataItem dataItem = event.getDataItem();
-                DataMap dataMap = DataMapItem.fromDataItem(dataItem).getDataMap();
-                if (dataMap.getBoolean(QUESTION_WAS_ANSWERED)
-                        || dataMap.getBoolean(QUESTION_WAS_DELETED)) {
-                    // Ignore the change in data; it is used in MainActivity to update
-                    // the question's status (i.e. was the answer right or wrong or left blank).
-                    continue;
-                }
-                String question = dataMap.getString(QUESTION);
-                int questionIndex = dataMap.getInt(QUESTION_INDEX);
-                int questionNum = questionIndex + 1;
-                String[] answers = dataMap.getStringArray(ANSWERS);
-                int correctAnswerIndex = dataMap.getInt(CORRECT_ANSWER_INDEX);
-                Intent deleteOperation = new Intent(this, DeleteQuestionService.class);
-                deleteOperation.setData(dataItem.getUri());
-                PendingIntent deleteIntent = PendingIntent.getService(this, 0,
-                        deleteOperation, PendingIntent.FLAG_UPDATE_CURRENT);
-                // First page of notification contains question as Big Text.
-                Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle()
-                        .setBigContentTitle(getString(R.string.question, questionNum))
-                        .bigText(question);
-                Notification.Builder builder = new Notification.Builder(this)
-                        .setStyle(bigTextStyle)
-                        .setSmallIcon(R.drawable.ic_launcher)
-                        .setLocalOnly(true)
-                        .setDeleteIntent(deleteIntent);
-
-                // Add answers as actions.
-                Notification.WearableExtender wearableOptions = new Notification.WearableExtender();
-                for (int i = 0; i < answers.length; i++) {
-                    Notification answerPage = new Notification.Builder(this)
-                            .setContentTitle(question)
-                            .setContentText(answers[i])
-                            .extend(new Notification.WearableExtender()
-                                    .setContentAction(i))
-                            .build();
-
-                    boolean correct = (i == correctAnswerIndex);
-                    Intent updateOperation = new Intent(this, UpdateQuestionService.class);
-                    // Give each intent a unique action.
-                    updateOperation.setAction("question_" + questionIndex + "_answer_" + i);
-                    updateOperation.setData(dataItem.getUri());
-                    updateOperation.putExtra(UpdateQuestionService.EXTRA_QUESTION_INDEX,
-                            questionIndex);
-                    updateOperation.putExtra(UpdateQuestionService.EXTRA_QUESTION_CORRECT, correct);
-                    PendingIntent updateIntent = PendingIntent.getService(this, 0, updateOperation,
-                            PendingIntent.FLAG_UPDATE_CURRENT);
-                    Notification.Action action = new Notification.Action.Builder(
-                            questionNumToDrawableId.get(i), null, updateIntent)
-                            .build();
-                    wearableOptions.addAction(action).addPage(answerPage);
-                }
-                builder.extend(wearableOptions);
-                Notification notification = builder.build();
-                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                        .notify(questionIndex, notification);
-            } else if (event.getType() == DataEvent.TYPE_DELETED) {
-                Uri uri = event.getDataItem().getUri();
-                // URI's are of the form "/question/0", "/question/1" etc.
-                // We use the question index as the notification id.
-                int notificationId = Integer.parseInt(uri.getLastPathSegment());
-                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                        .cancel(notificationId);
-            }
-            // Delete the quiz report, if it exists.
-            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                    .cancel(QUIZ_REPORT_NOTIF_ID);
-        }
-        googleApiClient.disconnect();
-    }
-
-    @Override
-    public void onMessageReceived(MessageEvent messageEvent) {
-        String path = messageEvent.getPath();
-        if (path.equals(QUIZ_EXITED_PATH)) {
-            // Remove any lingering question notifications.
-            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancelAll();
-        }
-        if (path.equals(QUIZ_ENDED_PATH) || path.equals(QUIZ_EXITED_PATH)) {
-            // Quiz ended - display overall results.
-            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
-            int numCorrect = dataMap.getInt(NUM_CORRECT);
-            int numIncorrect = dataMap.getInt(NUM_INCORRECT);
-            int numSkipped = dataMap.getInt(NUM_SKIPPED);
-
-            Notification.Builder builder = new Notification.Builder(this)
-                    .setContentTitle(getString(R.string.quiz_report))
-                    .setSmallIcon(R.drawable.ic_launcher)
-                    .setLocalOnly(true);
-            SpannableStringBuilder quizReportText = new SpannableStringBuilder();
-            appendColored(quizReportText, String.valueOf(numCorrect), R.color.dark_green);
-            quizReportText.append(" " + getString(R.string.correct) + "\n");
-            appendColored(quizReportText, String.valueOf(numIncorrect), R.color.dark_red);
-            quizReportText.append(" " + getString(R.string.incorrect) + "\n");
-            appendColored(quizReportText, String.valueOf(numSkipped), R.color.dark_yellow);
-            quizReportText.append(" " + getString(R.string.skipped) + "\n");
-
-            builder.setContentText(quizReportText);
-            if (!path.equals(QUIZ_EXITED_PATH)) {
-                // Don't add reset option if user exited quiz (there might not be a quiz to reset!).
-                builder.addAction(R.drawable.ic_launcher,
-                        getString(R.string.reset_quiz), getResetQuizPendingIntent());
-            }
-            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                    .notify(QUIZ_REPORT_NOTIF_ID, builder.build());
-        }
-    }
-
-    private void appendColored(SpannableStringBuilder builder, String text, int colorResId) {
-        builder.append(text).setSpan(new ForegroundColorSpan(getResources().getColor(colorResId)),
-                builder.length() - text.length(), builder.length(), 0);
-    }
-
-    /**
-     * Returns a PendingIntent that will send a message to the phone to reset the quiz when fired.
-     */
-    private PendingIntent getResetQuizPendingIntent() {
-        Intent intent = new Intent(QuizReportActionService.ACTION_RESET_QUIZ)
-                .setClass(this, QuizReportActionService.class);
-        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-    }
-}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/QuizReportActionService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/QuizReportActionService.java
deleted file mode 100644
index 4ca55be..0000000
--- a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/QuizReportActionService.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.concurrent.TimeUnit;
-
-import static com.example.android.quiz.Constants.CONNECT_TIMEOUT_MS;
-import static com.example.android.quiz.Constants.RESET_QUIZ_PATH;
-
-/**
- * Service to reset the quiz (by sending a message to the phone) when the Reset Quiz
- * action on the Quiz Report is selected.
- */
-public class QuizReportActionService extends IntentService {
-    public static final String ACTION_RESET_QUIZ = "com.example.android.quiz.RESET_QUIZ";
-
-    private static final String TAG = "QuizReportActionReceiver";
-
-    public QuizReportActionService() {
-        super(QuizReportActionService.class.getSimpleName());
-    }
-
-    @Override
-    public void onHandleIntent(Intent intent) {
-        if (intent.getAction().equals(ACTION_RESET_QUIZ)) {
-            GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
-                    .addApi(Wearable.API)
-                    .build();
-            ConnectionResult result = googleApiClient.blockingConnect(CONNECT_TIMEOUT_MS,
-                    TimeUnit.MILLISECONDS);
-            if (!result.isSuccess()) {
-                Log.e(TAG, "QuizListenerService failed to connect to GoogleApiClient.");
-                return;
-            }
-            NodeApi.GetConnectedNodesResult nodes =
-                    Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
-            for (Node node : nodes.getNodes()) {
-                Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), RESET_QUIZ_PATH,
-                        new byte[0]);
-            }
-        }
-    }
-}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/UpdateQuestionService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/UpdateQuestionService.java
deleted file mode 100644
index 671ecad..0000000
--- a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/quiz/UpdateQuestionService.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.quiz;
-
-import android.app.IntentService;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.GoogleApiClient;
-import com.google.android.gms.wearable.DataApi;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.PutDataRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.util.concurrent.TimeUnit;
-
-import static com.example.android.quiz.Constants.CHOSEN_ANSWER_CORRECT;
-import static com.example.android.quiz.Constants.QUESTION_INDEX;
-import static com.example.android.quiz.Constants.QUESTION_WAS_ANSWERED;
-
-/**
- * Updates quiz status on the phone when user selects an answer to a question on the watch.
- */
-public class UpdateQuestionService extends IntentService
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
-    public static final String EXTRA_QUESTION_CORRECT = "extra_question_correct";
-    public static final String EXTRA_QUESTION_INDEX = "extra_question_index";
-
-    private static final long TIME_OUT_MS = 100;
-    private static final String TAG = "UpdateQuestionService";
-
-    private GoogleApiClient mGoogleApiClient;
-
-    public UpdateQuestionService() {
-        super(UpdateQuestionService.class.getSimpleName());
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        mGoogleApiClient.blockingConnect(TIME_OUT_MS, TimeUnit.MILLISECONDS);
-        Uri dataItemUri = intent.getData();
-        if (!mGoogleApiClient.isConnected()) {
-            Log.e(TAG, "Failed to update data item " + dataItemUri
-                    + " because client is disconnected from Google Play Services");
-            return;
-        }
-        DataApi.DataItemResult dataItemResult = Wearable.DataApi.getDataItem(
-                mGoogleApiClient, dataItemUri).await();
-        PutDataMapRequest putDataMapRequest = PutDataMapRequest
-                .createFromDataMapItem(DataMapItem.fromDataItem(dataItemResult.getDataItem()));
-        DataMap dataMap = putDataMapRequest.getDataMap();
-
-        // Update quiz status variables, which will be reflected on the phone.
-        int questionIndex = intent.getIntExtra(EXTRA_QUESTION_INDEX, -1);
-        boolean chosenAnswerCorrect = intent.getBooleanExtra(EXTRA_QUESTION_CORRECT, false);
-        dataMap.putInt(QUESTION_INDEX, questionIndex);
-        dataMap.putBoolean(CHOSEN_ANSWER_CORRECT, chosenAnswerCorrect);
-        dataMap.putBoolean(QUESTION_WAS_ANSWERED, true);
-        PutDataRequest request = putDataMapRequest.asPutDataRequest();
-        Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
-
-        // Remove this question notification.
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(questionIndex);
-        mGoogleApiClient.disconnect();
-    }
-
-    @Override
-    public void onConnected(Bundle connectionHint) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int cause) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult result) {
-    }
-}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/Constants.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/Constants.java
new file mode 100644
index 0000000..8b81f59
--- /dev/null
+++ b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/Constants.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+/** Constants used in the wearable app. */
+public final class Constants {
+    private Constants() {
+    }
+
+    public static final String ANSWERS = "answers";
+    public static final String CHOSEN_ANSWER_CORRECT = "chosen_answer_correct";
+    public static final String CORRECT_ANSWER_INDEX = "correct_answer_index";
+    public static final String QUESTION = "question";
+    public static final String QUESTION_INDEX = "question_index";
+    public static final String QUESTION_WAS_ANSWERED = "question_was_answered";
+    public static final String QUESTION_WAS_DELETED = "question_was_deleted";
+
+    public static final String NUM_CORRECT = "num_correct";
+    public static final String NUM_INCORRECT = "num_incorrect";
+    public static final String NUM_SKIPPED = "num_skipped";
+
+    public static final String QUIZ_ENDED_PATH = "/quiz_ended";
+    public static final String QUIZ_EXITED_PATH = "/quiz_exited";
+    public static final String RESET_QUIZ_PATH = "/reset_quiz";
+
+    public static final int CONNECT_TIMEOUT_MS = 100;
+}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/DeleteQuestionService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/DeleteQuestionService.java
new file mode 100644
index 0000000..78e81ca
--- /dev/null
+++ b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/DeleteQuestionService.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
+
+/**
+ * Used to update quiz status on the phone when user dismisses a question on the watch.
+ */
+public class DeleteQuestionService extends IntentService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "DeleteQuestionReceiver";
+
+    private GoogleApiClient mGoogleApiClient;
+
+    public DeleteQuestionService() {
+        super(DeleteQuestionService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    public void onHandleIntent(Intent intent) {
+        mGoogleApiClient.blockingConnect(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        Uri dataItemUri = intent.getData();
+        if (!mGoogleApiClient.isConnected()) {
+            Log.e(TAG, "Failed to update data item " + dataItemUri
+                    + " because client is disconnected from Google Play Services");
+            return;
+        }
+        DataApi.DataItemResult dataItemResult = Wearable.DataApi.getDataItem(
+                mGoogleApiClient, dataItemUri).await();
+        PutDataMapRequest putDataMapRequest = PutDataMapRequest
+                .createFromDataMapItem(DataMapItem.fromDataItem(dataItemResult.getDataItem()));
+        DataMap dataMap = putDataMapRequest.getDataMap();
+        dataMap.putBoolean(QUESTION_WAS_DELETED, true);
+        PutDataRequest request = putDataMapRequest.asPutDataRequest();
+        Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
+        mGoogleApiClient.disconnect();
+    }
+
+    @Override
+    public void onConnected(Bundle bundle) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+    }
+}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/QuizListenerService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/QuizListenerService.java
new file mode 100644
index 0000000..020a37b
--- /dev/null
+++ b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/QuizListenerService.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+import static com.example.android.wearable.quiz.Constants.ANSWERS;
+import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
+import static com.example.android.wearable.quiz.Constants.CORRECT_ANSWER_INDEX;
+import static com.example.android.wearable.quiz.Constants.NUM_CORRECT;
+import static com.example.android.wearable.quiz.Constants.NUM_INCORRECT;
+import static com.example.android.wearable.quiz.Constants.NUM_SKIPPED;
+import static com.example.android.wearable.quiz.Constants.QUESTION;
+import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
+import static com.example.android.wearable.quiz.Constants.QUIZ_ENDED_PATH;
+import static com.example.android.wearable.quiz.Constants.QUIZ_EXITED_PATH;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Listens to changes in DataItems, which represent quiz questions.
+ * If a new question is created, this builds a new notification for it.
+ * Otherwise, if a question is deleted, this cancels the corresponding notification.
+ *
+ * When the quiz ends, this listener receives a message telling it to create an end-of-quiz report.
+ */
+public class QuizListenerService extends WearableListenerService {
+    private static final String TAG = "QuizSample";
+    private static final int QUIZ_REPORT_NOTIF_ID = -1; // Never used by question notifications.
+    private static final Map<Integer, Integer> questionNumToDrawableId;
+
+    static {
+        Map<Integer, Integer> temp = new HashMap<Integer, Integer>(4);
+        temp.put(0, R.drawable.ic_choice_a);
+        temp.put(1, R.drawable.ic_choice_b);
+        temp.put(2, R.drawable.ic_choice_c);
+        temp.put(3, R.drawable.ic_choice_d);
+        questionNumToDrawableId = Collections.unmodifiableMap(temp);
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
+        dataEvents.close();
+
+        GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .build();
+
+        ConnectionResult connectionResult = googleApiClient.blockingConnect(CONNECT_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS);
+        if (!connectionResult.isSuccess()) {
+            Log.e(TAG, "QuizListenerService failed to connect to GoogleApiClient.");
+            return;
+        }
+
+        for (DataEvent event : events) {
+            if (event.getType() == DataEvent.TYPE_CHANGED) {
+                DataItem dataItem = event.getDataItem();
+                DataMap dataMap = DataMapItem.fromDataItem(dataItem).getDataMap();
+                if (dataMap.getBoolean(QUESTION_WAS_ANSWERED)
+                        || dataMap.getBoolean(QUESTION_WAS_DELETED)) {
+                    // Ignore the change in data; it is used in MainActivity to update
+                    // the question's status (i.e. was the answer right or wrong or left blank).
+                    continue;
+                }
+                String question = dataMap.getString(QUESTION);
+                int questionIndex = dataMap.getInt(QUESTION_INDEX);
+                int questionNum = questionIndex + 1;
+                String[] answers = dataMap.getStringArray(ANSWERS);
+                int correctAnswerIndex = dataMap.getInt(CORRECT_ANSWER_INDEX);
+                Intent deleteOperation = new Intent(this, DeleteQuestionService.class);
+                deleteOperation.setData(dataItem.getUri());
+                PendingIntent deleteIntent = PendingIntent.getService(this, 0,
+                        deleteOperation, PendingIntent.FLAG_UPDATE_CURRENT);
+                // First page of notification contains question as Big Text.
+                Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle()
+                        .setBigContentTitle(getString(R.string.question, questionNum))
+                        .bigText(question);
+                Notification.Builder builder = new Notification.Builder(this)
+                        .setStyle(bigTextStyle)
+                        .setSmallIcon(R.drawable.ic_launcher)
+                        .setLocalOnly(true)
+                        .setDeleteIntent(deleteIntent);
+
+                // Add answers as actions.
+                Notification.WearableExtender wearableOptions = new Notification.WearableExtender();
+                for (int i = 0; i < answers.length; i++) {
+                    Notification answerPage = new Notification.Builder(this)
+                            .setContentTitle(question)
+                            .setContentText(answers[i])
+                            .extend(new Notification.WearableExtender()
+                                    .setContentAction(i))
+                            .build();
+
+                    boolean correct = (i == correctAnswerIndex);
+                    Intent updateOperation = new Intent(this, UpdateQuestionService.class);
+                    // Give each intent a unique action.
+                    updateOperation.setAction("question_" + questionIndex + "_answer_" + i);
+                    updateOperation.setData(dataItem.getUri());
+                    updateOperation.putExtra(UpdateQuestionService.EXTRA_QUESTION_INDEX,
+                            questionIndex);
+                    updateOperation.putExtra(UpdateQuestionService.EXTRA_QUESTION_CORRECT, correct);
+                    PendingIntent updateIntent = PendingIntent.getService(this, 0, updateOperation,
+                            PendingIntent.FLAG_UPDATE_CURRENT);
+                    Notification.Action action = new Notification.Action.Builder(
+                            questionNumToDrawableId.get(i), null, updateIntent)
+                            .build();
+                    wearableOptions.addAction(action).addPage(answerPage);
+                }
+                builder.extend(wearableOptions);
+                Notification notification = builder.build();
+                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                        .notify(questionIndex, notification);
+            } else if (event.getType() == DataEvent.TYPE_DELETED) {
+                Uri uri = event.getDataItem().getUri();
+                // URI's are of the form "/question/0", "/question/1" etc.
+                // We use the question index as the notification id.
+                int notificationId = Integer.parseInt(uri.getLastPathSegment());
+                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                        .cancel(notificationId);
+            }
+            // Delete the quiz report, if it exists.
+            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                    .cancel(QUIZ_REPORT_NOTIF_ID);
+        }
+        googleApiClient.disconnect();
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent messageEvent) {
+        String path = messageEvent.getPath();
+        if (path.equals(QUIZ_EXITED_PATH)) {
+            // Remove any lingering question notifications.
+            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancelAll();
+        }
+        if (path.equals(QUIZ_ENDED_PATH) || path.equals(QUIZ_EXITED_PATH)) {
+            // Quiz ended - display overall results.
+            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
+            int numCorrect = dataMap.getInt(NUM_CORRECT);
+            int numIncorrect = dataMap.getInt(NUM_INCORRECT);
+            int numSkipped = dataMap.getInt(NUM_SKIPPED);
+
+            Notification.Builder builder = new Notification.Builder(this)
+                    .setContentTitle(getString(R.string.quiz_report))
+                    .setSmallIcon(R.drawable.ic_launcher)
+                    .setLocalOnly(true);
+            SpannableStringBuilder quizReportText = new SpannableStringBuilder();
+            appendColored(quizReportText, String.valueOf(numCorrect), R.color.dark_green);
+            quizReportText.append(" " + getString(R.string.correct) + "\n");
+            appendColored(quizReportText, String.valueOf(numIncorrect), R.color.dark_red);
+            quizReportText.append(" " + getString(R.string.incorrect) + "\n");
+            appendColored(quizReportText, String.valueOf(numSkipped), R.color.dark_yellow);
+            quizReportText.append(" " + getString(R.string.skipped) + "\n");
+
+            builder.setContentText(quizReportText);
+            if (!path.equals(QUIZ_EXITED_PATH)) {
+                // Don't add reset option if user exited quiz (there might not be a quiz to reset!).
+                builder.addAction(R.drawable.ic_launcher,
+                        getString(R.string.reset_quiz), getResetQuizPendingIntent());
+            }
+            ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                    .notify(QUIZ_REPORT_NOTIF_ID, builder.build());
+        }
+    }
+
+    private void appendColored(SpannableStringBuilder builder, String text, int colorResId) {
+        builder.append(text).setSpan(new ForegroundColorSpan(getResources().getColor(colorResId)),
+                builder.length() - text.length(), builder.length(), 0);
+    }
+
+    /**
+     * Returns a PendingIntent that will send a message to the phone to reset the quiz when fired.
+     */
+    private PendingIntent getResetQuizPendingIntent() {
+        Intent intent = new Intent(QuizReportActionService.ACTION_RESET_QUIZ)
+                .setClass(this, QuizReportActionService.class);
+        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/QuizReportActionService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/QuizReportActionService.java
new file mode 100644
index 0000000..956bdaf
--- /dev/null
+++ b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/QuizReportActionService.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
+import static com.example.android.wearable.quiz.Constants.RESET_QUIZ_PATH;
+
+/**
+ * Service to reset the quiz (by sending a message to the phone) when the Reset Quiz
+ * action on the Quiz Report is selected.
+ */
+public class QuizReportActionService extends IntentService {
+    public static final String ACTION_RESET_QUIZ = "com.example.android.wearable.quiz.RESET_QUIZ";
+
+    private static final String TAG = "QuizReportActionReceiver";
+
+    public QuizReportActionService() {
+        super(QuizReportActionService.class.getSimpleName());
+    }
+
+    @Override
+    public void onHandleIntent(Intent intent) {
+        if (intent.getAction().equals(ACTION_RESET_QUIZ)) {
+            GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+                    .addApi(Wearable.API)
+                    .build();
+            ConnectionResult result = googleApiClient.blockingConnect(CONNECT_TIMEOUT_MS,
+                    TimeUnit.MILLISECONDS);
+            if (!result.isSuccess()) {
+                Log.e(TAG, "QuizListenerService failed to connect to GoogleApiClient.");
+                return;
+            }
+            NodeApi.GetConnectedNodesResult nodes =
+                    Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
+            for (Node node : nodes.getNodes()) {
+                Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), RESET_QUIZ_PATH,
+                        new byte[0]);
+            }
+        }
+    }
+}
diff --git a/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/UpdateQuestionService.java b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/UpdateQuestionService.java
new file mode 100644
index 0000000..e06c303
--- /dev/null
+++ b/wearable/wear/Quiz/Wearable/src/main/java/com/example/android/wearable/quiz/UpdateQuestionService.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.quiz;
+
+import android.app.IntentService;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+import static com.example.android.wearable.quiz.Constants.CHOSEN_ANSWER_CORRECT;
+import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
+
+/**
+ * Updates quiz status on the phone when user selects an answer to a question on the watch.
+ */
+public class UpdateQuestionService extends IntentService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+    public static final String EXTRA_QUESTION_CORRECT = "extra_question_correct";
+    public static final String EXTRA_QUESTION_INDEX = "extra_question_index";
+
+    private static final long TIME_OUT_MS = 100;
+    private static final String TAG = "UpdateQuestionService";
+
+    private GoogleApiClient mGoogleApiClient;
+
+    public UpdateQuestionService() {
+        super(UpdateQuestionService.class.getSimpleName());
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        mGoogleApiClient.blockingConnect(TIME_OUT_MS, TimeUnit.MILLISECONDS);
+        Uri dataItemUri = intent.getData();
+        if (!mGoogleApiClient.isConnected()) {
+            Log.e(TAG, "Failed to update data item " + dataItemUri
+                    + " because client is disconnected from Google Play Services");
+            return;
+        }
+        DataApi.DataItemResult dataItemResult = Wearable.DataApi.getDataItem(
+                mGoogleApiClient, dataItemUri).await();
+        PutDataMapRequest putDataMapRequest = PutDataMapRequest
+                .createFromDataMapItem(DataMapItem.fromDataItem(dataItemResult.getDataItem()));
+        DataMap dataMap = putDataMapRequest.getDataMap();
+
+        // Update quiz status variables, which will be reflected on the phone.
+        int questionIndex = intent.getIntExtra(EXTRA_QUESTION_INDEX, -1);
+        boolean chosenAnswerCorrect = intent.getBooleanExtra(EXTRA_QUESTION_CORRECT, false);
+        dataMap.putInt(QUESTION_INDEX, questionIndex);
+        dataMap.putBoolean(CHOSEN_ANSWER_CORRECT, chosenAnswerCorrect);
+        dataMap.putBoolean(QUESTION_WAS_ANSWERED, true);
+        PutDataRequest request = putDataMapRequest.asPutDataRequest();
+        Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
+
+        // Remove this question notification.
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(questionIndex);
+        mGoogleApiClient.disconnect();
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+    }
+}
diff --git a/wearable/wear/Quiz/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Quiz/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/Quiz/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/Quiz/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/Quiz/screenshots/companion_quiz.png b/wearable/wear/Quiz/screenshots/companion_quiz.png
new file mode 100644
index 0000000..15f7e80
--- /dev/null
+++ b/wearable/wear/Quiz/screenshots/companion_quiz.png
Binary files differ
diff --git a/wearable/wear/Quiz/screenshots/companion_quiz_correct_status.png b/wearable/wear/Quiz/screenshots/companion_quiz_correct_status.png
new file mode 100644
index 0000000..c0e25ca
--- /dev/null
+++ b/wearable/wear/Quiz/screenshots/companion_quiz_correct_status.png
Binary files differ
diff --git a/wearable/wear/Quiz/screenshots/wearable_quiz.png b/wearable/wear/Quiz/screenshots/wearable_quiz.png
new file mode 100644
index 0000000..0c63139
--- /dev/null
+++ b/wearable/wear/Quiz/screenshots/wearable_quiz.png
Binary files differ
diff --git a/wearable/wear/Quiz/screenshots/wearable_quiz_answer.png b/wearable/wear/Quiz/screenshots/wearable_quiz_answer.png
new file mode 100644
index 0000000..eb47516
--- /dev/null
+++ b/wearable/wear/Quiz/screenshots/wearable_quiz_answer.png
Binary files differ
diff --git a/wearable/wear/Quiz/screenshots/wearable_quiz_report.png b/wearable/wear/Quiz/screenshots/wearable_quiz_report.png
new file mode 100644
index 0000000..196d045
--- /dev/null
+++ b/wearable/wear/Quiz/screenshots/wearable_quiz_report.png
Binary files differ
diff --git a/wearable/wear/Quiz/settings.gradle b/wearable/wear/Quiz/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/Quiz/settings.gradle
+++ b/wearable/wear/Quiz/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/Quiz/template-params.xml b/wearable/wear/Quiz/template-params.xml
index 88dca63..383a313 100644
--- a/wearable/wear/Quiz/template-params.xml
+++ b/wearable/wear/Quiz/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>Quiz</name>
     <group>Wearable</group>
-    <package>com.example.android.quiz</package>
+    <package>com.example.android.wearable.quiz</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -44,7 +44,5 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
 </sample>
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml b/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml
index db13ed9..1786d27 100644
--- a/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.recipeassistant" >
+    package="com.example.android.wearable.recipeassistant" >
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application
         android:allowBackup="true"
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/AssetUtils.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/AssetUtils.java
deleted file mode 100644
index b9ad8d5..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/AssetUtils.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.util.Log;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-final class AssetUtils {
-    private static final String TAG = "RecipeAssistant";
-
-    public static byte[] loadAsset(Context context, String asset) {
-        byte[] buffer = null;
-        try {
-            InputStream is = context.getAssets().open(asset);
-            int size = is.available();
-            buffer = new byte[size];
-            is.read(buffer);
-            is.close();
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to load asset " + asset + ": " + e);
-        }
-        return buffer;
-    }
-
-    public static JSONObject loadJSONAsset(Context context, String asset) {
-        String jsonString = new String(loadAsset(context, asset));
-        JSONObject jsonObject = null;
-        try {
-            jsonObject = new JSONObject(jsonString);
-        } catch (JSONException e) {
-            Log.e(TAG, "Failed to parse JSON asset " + asset + ": " + e);
-        }
-        return jsonObject;
-    }
-
-    public static Bitmap loadBitmapAsset(Context context, String asset) {
-        InputStream is = null;
-        Bitmap bitmap = null;
-        try {
-            is = context.getAssets().open(asset);
-            if (is != null) {
-                bitmap = BitmapFactory.decodeStream(is);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, e.toString());
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "Cannot close InputStream: ", e);
-                }
-            }
-        }
-        return bitmap;
-    }
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/Constants.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/Constants.java
deleted file mode 100644
index e6d367d..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/Constants.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-public final class Constants {
-    private Constants() {
-    }
-    public static final String RECIPE_LIST_FILE = "recipelist.json";
-    public static final String RECIPE_NAME_TO_LOAD = "recipe_name";
-
-    public static final String RECIPE_FIELD_LIST = "recipe_list";
-    public static final String RECIPE_FIELD_IMAGE = "img";
-    public static final String RECIPE_FIELD_INGREDIENTS = "ingredients";
-    public static final String RECIPE_FIELD_NAME = "name";
-    public static final String RECIPE_FIELD_SUMMARY = "summary";
-    public static final String RECIPE_FIELD_STEPS = "steps";
-    public static final String RECIPE_FIELD_TEXT = "text";
-    public static final String RECIPE_FIELD_TITLE = "title";
-    public static final String RECIPE_FIELD_STEP_TEXT = "step_text";
-    public static final String RECIPE_FIELD_STEP_IMAGE = "step_image";
-
-    static final String ACTION_START_COOKING =
-            "com.example.android.recipeassistant.START_COOKING";
-    public static final String EXTRA_RECIPE = "recipe";
-
-    public static final int NOTIFICATION_ID = 0;
-    public static final int NOTIFICATION_IMAGE_WIDTH = 280;
-    public static final int NOTIFICATION_IMAGE_HEIGHT = 280;
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/MainActivity.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/MainActivity.java
deleted file mode 100644
index 5738e2a..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/MainActivity.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-import android.app.ListActivity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.ListView;
-
-public class MainActivity extends ListActivity {
-
-    private static final String TAG = "RecipeAssistant";
-    private RecipeListAdapter mAdapter;
-
-    @Override
-    protected void onListItemClick(ListView l, View v, int position, long id) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG , "onListItemClick " + position);
-        }
-        String itemName = mAdapter.getItemName(position);
-        Intent intent = new Intent(getApplicationContext(), RecipeActivity.class);
-        intent.putExtra(Constants.RECIPE_NAME_TO_LOAD, itemName);
-        startActivity(intent);
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(android.R.layout.list_content);
-
-        mAdapter = new RecipeListAdapter(this);
-        setListAdapter(mAdapter);
-    }
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/Recipe.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/Recipe.java
deleted file mode 100644
index 3551907..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/Recipe.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-
-public class Recipe {
-    private static final String TAG = "RecipeAssistant";
-
-    public String titleText;
-    public String summaryText;
-    public String recipeImage;
-    public String ingredientsText;
-
-    public static class RecipeStep {
-        RecipeStep() { }
-        public String stepImage;
-        public String stepText;
-
-        public Bundle toBundle() {
-            Bundle bundle = new Bundle();
-            bundle.putString(Constants.RECIPE_FIELD_STEP_TEXT, stepText);
-            bundle.putString(Constants.RECIPE_FIELD_STEP_IMAGE, stepImage);
-            return bundle;
-        }
-
-        public static RecipeStep fromBundle(Bundle bundle) {
-            RecipeStep recipeStep = new RecipeStep();
-            recipeStep.stepText = bundle.getString(Constants.RECIPE_FIELD_STEP_TEXT);
-            recipeStep.stepImage = bundle.getString(Constants.RECIPE_FIELD_STEP_IMAGE);
-            return recipeStep;
-        }
-    }
-    ArrayList<RecipeStep> recipeSteps;
-
-    public Recipe() {
-        recipeSteps = new ArrayList<RecipeStep>();
-    }
-
-    public static Recipe fromJson(Context context, JSONObject json) {
-        Recipe recipe = new Recipe();
-        try {
-            recipe.titleText = json.getString(Constants.RECIPE_FIELD_TITLE);
-            recipe.summaryText = json.getString(Constants.RECIPE_FIELD_SUMMARY);
-            if (json.has(Constants.RECIPE_FIELD_IMAGE)) {
-                recipe.recipeImage = json.getString(Constants.RECIPE_FIELD_IMAGE);
-            }
-            JSONArray ingredients = json.getJSONArray(Constants.RECIPE_FIELD_INGREDIENTS);
-            recipe.ingredientsText = "";
-            for (int i = 0; i < ingredients.length(); i++) {
-                recipe.ingredientsText += " - "
-                        + ingredients.getJSONObject(i).getString(Constants.RECIPE_FIELD_TEXT) + "\n";
-            }
-
-            JSONArray steps = json.getJSONArray(Constants.RECIPE_FIELD_STEPS);
-            for (int i = 0; i < steps.length(); i++) {
-                JSONObject step = steps.getJSONObject(i);
-                RecipeStep recipeStep = new RecipeStep();
-                recipeStep.stepText = step.getString(Constants.RECIPE_FIELD_TEXT);
-                if (step.has(Constants.RECIPE_FIELD_IMAGE)) {
-                    recipeStep.stepImage = step.getString(Constants.RECIPE_FIELD_IMAGE);
-                }
-                recipe.recipeSteps.add(recipeStep);
-            }
-        } catch (JSONException e) {
-            Log.e(TAG, "Error loading recipe: " + e);
-            return null;
-        }
-        return recipe;
-    }
-
-    public Bundle toBundle() {
-        Bundle bundle = new Bundle();
-        bundle.putString(Constants.RECIPE_FIELD_TITLE, titleText);
-        bundle.putString(Constants.RECIPE_FIELD_SUMMARY, summaryText);
-        bundle.putString(Constants.RECIPE_FIELD_IMAGE, recipeImage);
-        bundle.putString(Constants.RECIPE_FIELD_INGREDIENTS, ingredientsText);
-        if (recipeSteps != null) {
-            ArrayList<Parcelable> stepBundles = new ArrayList<Parcelable>(recipeSteps.size());
-            for (RecipeStep recipeStep : recipeSteps) {
-                stepBundles.add(recipeStep.toBundle());
-            }
-            bundle.putParcelableArrayList(Constants.RECIPE_FIELD_STEPS, stepBundles);
-        }
-        return bundle;
-    }
-
-    public static Recipe fromBundle(Bundle bundle) {
-        Recipe recipe = new Recipe();
-        recipe.titleText = bundle.getString(Constants.RECIPE_FIELD_TITLE);
-        recipe.summaryText = bundle.getString(Constants.RECIPE_FIELD_SUMMARY);
-        recipe.recipeImage = bundle.getString(Constants.RECIPE_FIELD_IMAGE);
-        recipe.ingredientsText = bundle.getString(Constants.RECIPE_FIELD_INGREDIENTS);
-        ArrayList<Parcelable> stepBundles =
-                bundle.getParcelableArrayList(Constants.RECIPE_FIELD_STEPS);
-        if (stepBundles != null) {
-            for (Parcelable stepBundle : stepBundles) {
-                recipe.recipeSteps.add(RecipeStep.fromBundle((Bundle) stepBundle));
-            }
-        }
-        return recipe;
-    }
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeActivity.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeActivity.java
deleted file mode 100644
index 4b9d72d..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeActivity.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.json.JSONObject;
-
-public class RecipeActivity extends Activity {
-    private static final String TAG = "RecipeAssistant";
-    private String mRecipeName;
-    private Recipe mRecipe;
-    private ImageView mImageView;
-    private TextView mTitleTextView;
-    private TextView mSummaryTextView;
-    private TextView mIngredientsTextView;
-    private LinearLayout mStepsLayout;
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        Intent intent = getIntent();
-        mRecipeName = intent.getStringExtra(Constants.RECIPE_NAME_TO_LOAD);
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Intent: " + intent.toString() + " " + mRecipeName);
-        }
-        loadRecipe();
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.recipe);
-        mTitleTextView = (TextView) findViewById(R.id.recipeTextTitle);
-        mSummaryTextView = (TextView) findViewById(R.id.recipeTextSummary);
-        mImageView = (ImageView) findViewById(R.id.recipeImageView);
-        mIngredientsTextView = (TextView) findViewById(R.id.textIngredients);
-        mStepsLayout = (LinearLayout) findViewById(R.id.layoutSteps);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // Inflate the menu; this adds items to the action bar if it is present.
-        getMenuInflater().inflate(R.menu.main, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch(item.getItemId()) {
-            case R.id.action_cook:
-                startCooking();
-                return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private void loadRecipe() {
-        JSONObject jsonObject = AssetUtils.loadJSONAsset(this, mRecipeName);
-        if (jsonObject != null) {
-            mRecipe = Recipe.fromJson(this, jsonObject);
-            if (mRecipe != null) {
-                displayRecipe(mRecipe);
-            }
-        }
-    }
-
-    private void displayRecipe(Recipe recipe) {
-        Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
-        mTitleTextView.setAnimation(fadeIn);
-        mTitleTextView.setText(recipe.titleText);
-        mSummaryTextView.setText(recipe.summaryText);
-        if (recipe.recipeImage != null) {
-            mImageView.setAnimation(fadeIn);
-            Bitmap recipeImage = AssetUtils.loadBitmapAsset(this, recipe.recipeImage);
-            mImageView.setImageBitmap(recipeImage);
-        }
-        mIngredientsTextView.setText(recipe.ingredientsText);
-
-        findViewById(R.id.ingredientsHeader).setAnimation(fadeIn);
-        findViewById(R.id.ingredientsHeader).setVisibility(View.VISIBLE);
-        findViewById(R.id.stepsHeader).setAnimation(fadeIn);
-
-        findViewById(R.id.stepsHeader).setVisibility(View.VISIBLE);
-
-        LayoutInflater inf = LayoutInflater.from(this);
-        mStepsLayout.removeAllViews();
-        int stepNumber = 1;
-        for (Recipe.RecipeStep step : recipe.recipeSteps) {
-            View view = inf.inflate(R.layout.step_item, null);
-            ImageView iv = (ImageView) view.findViewById(R.id.stepImageView);
-            if (step.stepImage == null) {
-                iv.setVisibility(View.GONE);
-            } else {
-                Bitmap stepImage = AssetUtils.loadBitmapAsset(this, step.stepImage);
-                iv.setImageBitmap(stepImage);
-            }
-            ((TextView) view.findViewById(R.id.textStep)).setText(
-                    (stepNumber++) + ". " + step.stepText);
-            mStepsLayout.addView(view);
-        }
-    }
-
-    private void startCooking() {
-        Intent intent = new Intent(this, RecipeService.class);
-        intent.setAction(Constants.ACTION_START_COOKING);
-        intent.putExtra(Constants.EXTRA_RECIPE, mRecipe.toBundle());
-        startService(intent);
-    }
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeListAdapter.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeListAdapter.java
deleted file mode 100644
index bc602a1..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeListAdapter.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-import android.content.Context;
-import android.database.DataSetObserver;
-import android.graphics.Bitmap;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class RecipeListAdapter implements ListAdapter {
-    private String TAG = "RecipeListAdapter";
-
-    private class Item {
-        String title;
-        String name;
-        String summary;
-        Bitmap image;
-    }
-
-    private List<Item> mItems = new ArrayList<Item>();
-    private Context mContext;
-    private DataSetObserver mObserver;
-
-    public RecipeListAdapter(Context context) {
-        mContext = context;
-        loadRecipeList();
-    }
-
-    private void loadRecipeList() {
-        JSONObject jsonObject = AssetUtils.loadJSONAsset(mContext, Constants.RECIPE_LIST_FILE);
-        if (jsonObject != null) {
-            List<Item> items = parseJson(jsonObject);
-            appendItemsToList(items);
-        }
-    }
-
-    private List<Item> parseJson(JSONObject json) {
-        List<Item> result = new ArrayList<Item>();
-        try {
-            JSONArray items = json.getJSONArray(Constants.RECIPE_FIELD_LIST);
-            for (int i = 0; i < items.length(); i++) {
-                JSONObject item = items.getJSONObject(i);
-                Item parsed = new Item();
-                parsed.name = item.getString(Constants.RECIPE_FIELD_NAME);
-                parsed.title = item.getString(Constants.RECIPE_FIELD_TITLE);
-                if (item.has(Constants.RECIPE_FIELD_IMAGE)) {
-                    String imageFile = item.getString(Constants.RECIPE_FIELD_IMAGE);
-                    parsed.image = AssetUtils.loadBitmapAsset(mContext, imageFile);
-                }
-                parsed.summary = item.getString(Constants.RECIPE_FIELD_SUMMARY);
-                result.add(parsed);
-            }
-        } catch (JSONException e) {
-            Log.e(TAG, "Failed to parse recipe list: " + e);
-        }
-        return result;
-    }
-
-    private void appendItemsToList(List<Item> items) {
-        mItems.addAll(items);
-        if (mObserver != null) {
-            mObserver.onChanged();
-        }
-    }
-
-    @Override
-    public int getCount() {
-        return mItems.size();
-    }
-
-    @Override
-    public Object getItem(int position) {
-        return mItems.get(position);
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return 0;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return 0;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View view = convertView;
-        if (view == null) {
-            LayoutInflater inf = LayoutInflater.from(mContext);
-            view = inf.inflate(R.layout.list_item, null);
-        }
-        Item item = (Item) getItem(position);
-        TextView titleView = (TextView) view.findViewById(R.id.textTitle);
-        TextView summaryView = (TextView) view.findViewById(R.id.textSummary);
-        ImageView iv = (ImageView) view.findViewById(R.id.imageView);
-
-        titleView.setText(item.title);
-        summaryView.setText(item.summary);
-        if (item.image != null) {
-            iv.setImageBitmap(item.image);
-        } else {
-            iv.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_noimage));
-        }
-        return view;
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return 1;
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return false;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return mItems.isEmpty();
-    }
-
-    @Override
-    public void registerDataSetObserver(DataSetObserver observer) {
-        mObserver = observer;
-    }
-
-    @Override
-    public void unregisterDataSetObserver(DataSetObserver observer) {
-        mObserver = null;
-    }
-
-    @Override
-    public boolean areAllItemsEnabled() {
-        return true;
-    }
-
-    @Override
-    public boolean isEnabled(int position) {
-        return true;
-    }
-
-    public String getItemName(int position) {
-        return mItems.get(position).name;
-    }
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeService.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeService.java
deleted file mode 100644
index 74bbfda..0000000
--- a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/recipeassistant/RecipeService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.recipeassistant;
-
-import android.app.Notification;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Binder;
-import android.os.IBinder;
-import android.support.v4.app.NotificationManagerCompat;
-import android.support.v4.app.NotificationCompat;
-
-import java.util.ArrayList;
-
-public class RecipeService extends Service {
-    private NotificationManagerCompat mNotificationManager;
-    private Binder mBinder = new LocalBinder();
-    private Recipe mRecipe;
-
-    public class LocalBinder extends Binder {
-        RecipeService getService() {
-            return RecipeService.this;
-        }
-    }
-
-    @Override
-    public void onCreate() {
-        mNotificationManager = NotificationManagerCompat.from(this);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (intent.getAction().equals(Constants.ACTION_START_COOKING)) {
-            createNotification(intent);
-            return START_STICKY;
-        }
-        return START_NOT_STICKY;
-    }
-
-    private void createNotification(Intent intent) {
-        mRecipe = Recipe.fromBundle(intent.getBundleExtra(Constants.EXTRA_RECIPE));
-        ArrayList<Notification> notificationPages = new ArrayList<Notification>();
-
-        int stepCount = mRecipe.recipeSteps.size();
-
-        for (int i = 0; i < stepCount; ++i) {
-            Recipe.RecipeStep recipeStep = mRecipe.recipeSteps.get(i);
-            NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle();
-            style.bigText(recipeStep.stepText);
-            style.setBigContentTitle(String.format(
-                    getResources().getString(R.string.step_count), i + 1, stepCount));
-            style.setSummaryText("");
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
-            builder.setStyle(style);
-            notificationPages.add(builder.build());
-        }
-
-        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
-
-        if (mRecipe.recipeImage != null) {
-            Bitmap recipeImage = Bitmap.createScaledBitmap(
-                    AssetUtils.loadBitmapAsset(this, mRecipe.recipeImage),
-                    Constants.NOTIFICATION_IMAGE_WIDTH, Constants.NOTIFICATION_IMAGE_HEIGHT, false);
-            builder.setLargeIcon(recipeImage);
-        }
-        builder.setContentTitle(mRecipe.titleText);
-        builder.setContentText(mRecipe.summaryText);
-        builder.setSmallIcon(R.mipmap.ic_notification_recipe);
-
-        Notification notification = builder
-                .extend(new NotificationCompat.WearableExtender()
-                        .addPages(notificationPages))
-                .build();
-        mNotificationManager.notify(Constants.NOTIFICATION_ID, notification);
-    }
-}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/AssetUtils.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/AssetUtils.java
new file mode 100644
index 0000000..8753582
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/AssetUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+final class AssetUtils {
+    private static final String TAG = "RecipeAssistant";
+
+    public static byte[] loadAsset(Context context, String asset) {
+        byte[] buffer = null;
+        try {
+            InputStream is = context.getAssets().open(asset);
+            int size = is.available();
+            buffer = new byte[size];
+            is.read(buffer);
+            is.close();
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to load asset " + asset + ": " + e);
+        }
+        return buffer;
+    }
+
+    public static JSONObject loadJSONAsset(Context context, String asset) {
+        String jsonString = new String(loadAsset(context, asset));
+        JSONObject jsonObject = null;
+        try {
+            jsonObject = new JSONObject(jsonString);
+        } catch (JSONException e) {
+            Log.e(TAG, "Failed to parse JSON asset " + asset + ": " + e);
+        }
+        return jsonObject;
+    }
+
+    public static Bitmap loadBitmapAsset(Context context, String asset) {
+        InputStream is = null;
+        Bitmap bitmap = null;
+        try {
+            is = context.getAssets().open(asset);
+            if (is != null) {
+                bitmap = BitmapFactory.decodeStream(is);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Cannot close InputStream: ", e);
+                }
+            }
+        }
+        return bitmap;
+    }
+}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/Constants.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/Constants.java
new file mode 100644
index 0000000..fae49ee
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/Constants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+public final class Constants {
+    private Constants() {
+    }
+    public static final String RECIPE_LIST_FILE = "recipelist.json";
+    public static final String RECIPE_NAME_TO_LOAD = "recipe_name";
+
+    public static final String RECIPE_FIELD_LIST = "recipe_list";
+    public static final String RECIPE_FIELD_IMAGE = "img";
+    public static final String RECIPE_FIELD_INGREDIENTS = "ingredients";
+    public static final String RECIPE_FIELD_NAME = "name";
+    public static final String RECIPE_FIELD_SUMMARY = "summary";
+    public static final String RECIPE_FIELD_STEPS = "steps";
+    public static final String RECIPE_FIELD_TEXT = "text";
+    public static final String RECIPE_FIELD_TITLE = "title";
+    public static final String RECIPE_FIELD_STEP_TEXT = "step_text";
+    public static final String RECIPE_FIELD_STEP_IMAGE = "step_image";
+
+    static final String ACTION_START_COOKING =
+            "com.example.android.wearable.recipeassistant.START_COOKING";
+    public static final String EXTRA_RECIPE = "recipe";
+
+    public static final int NOTIFICATION_ID = 0;
+    public static final int NOTIFICATION_IMAGE_WIDTH = 280;
+    public static final int NOTIFICATION_IMAGE_HEIGHT = 280;
+}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/MainActivity.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/MainActivity.java
new file mode 100644
index 0000000..4cc860f
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/MainActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
+
+public class MainActivity extends ListActivity {
+
+    private static final String TAG = "RecipeAssistant";
+    private RecipeListAdapter mAdapter;
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG , "onListItemClick " + position);
+        }
+        String itemName = mAdapter.getItemName(position);
+        Intent intent = new Intent(getApplicationContext(), RecipeActivity.class);
+        intent.putExtra(Constants.RECIPE_NAME_TO_LOAD, itemName);
+        startActivity(intent);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(android.R.layout.list_content);
+
+        mAdapter = new RecipeListAdapter(this);
+        setListAdapter(mAdapter);
+    }
+}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/Recipe.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/Recipe.java
new file mode 100644
index 0000000..e470c0f
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/Recipe.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+public class Recipe {
+    private static final String TAG = "RecipeAssistant";
+
+    public String titleText;
+    public String summaryText;
+    public String recipeImage;
+    public String ingredientsText;
+
+    public static class RecipeStep {
+        RecipeStep() { }
+        public String stepImage;
+        public String stepText;
+
+        public Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putString(Constants.RECIPE_FIELD_STEP_TEXT, stepText);
+            bundle.putString(Constants.RECIPE_FIELD_STEP_IMAGE, stepImage);
+            return bundle;
+        }
+
+        public static RecipeStep fromBundle(Bundle bundle) {
+            RecipeStep recipeStep = new RecipeStep();
+            recipeStep.stepText = bundle.getString(Constants.RECIPE_FIELD_STEP_TEXT);
+            recipeStep.stepImage = bundle.getString(Constants.RECIPE_FIELD_STEP_IMAGE);
+            return recipeStep;
+        }
+    }
+    ArrayList<RecipeStep> recipeSteps;
+
+    public Recipe() {
+        recipeSteps = new ArrayList<RecipeStep>();
+    }
+
+    public static Recipe fromJson(Context context, JSONObject json) {
+        Recipe recipe = new Recipe();
+        try {
+            recipe.titleText = json.getString(Constants.RECIPE_FIELD_TITLE);
+            recipe.summaryText = json.getString(Constants.RECIPE_FIELD_SUMMARY);
+            if (json.has(Constants.RECIPE_FIELD_IMAGE)) {
+                recipe.recipeImage = json.getString(Constants.RECIPE_FIELD_IMAGE);
+            }
+            JSONArray ingredients = json.getJSONArray(Constants.RECIPE_FIELD_INGREDIENTS);
+            recipe.ingredientsText = "";
+            for (int i = 0; i < ingredients.length(); i++) {
+                recipe.ingredientsText += " - "
+                        + ingredients.getJSONObject(i).getString(Constants.RECIPE_FIELD_TEXT) + "\n";
+            }
+
+            JSONArray steps = json.getJSONArray(Constants.RECIPE_FIELD_STEPS);
+            for (int i = 0; i < steps.length(); i++) {
+                JSONObject step = steps.getJSONObject(i);
+                RecipeStep recipeStep = new RecipeStep();
+                recipeStep.stepText = step.getString(Constants.RECIPE_FIELD_TEXT);
+                if (step.has(Constants.RECIPE_FIELD_IMAGE)) {
+                    recipeStep.stepImage = step.getString(Constants.RECIPE_FIELD_IMAGE);
+                }
+                recipe.recipeSteps.add(recipeStep);
+            }
+        } catch (JSONException e) {
+            Log.e(TAG, "Error loading recipe: " + e);
+            return null;
+        }
+        return recipe;
+    }
+
+    public Bundle toBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putString(Constants.RECIPE_FIELD_TITLE, titleText);
+        bundle.putString(Constants.RECIPE_FIELD_SUMMARY, summaryText);
+        bundle.putString(Constants.RECIPE_FIELD_IMAGE, recipeImage);
+        bundle.putString(Constants.RECIPE_FIELD_INGREDIENTS, ingredientsText);
+        if (recipeSteps != null) {
+            ArrayList<Parcelable> stepBundles = new ArrayList<Parcelable>(recipeSteps.size());
+            for (RecipeStep recipeStep : recipeSteps) {
+                stepBundles.add(recipeStep.toBundle());
+            }
+            bundle.putParcelableArrayList(Constants.RECIPE_FIELD_STEPS, stepBundles);
+        }
+        return bundle;
+    }
+
+    public static Recipe fromBundle(Bundle bundle) {
+        Recipe recipe = new Recipe();
+        recipe.titleText = bundle.getString(Constants.RECIPE_FIELD_TITLE);
+        recipe.summaryText = bundle.getString(Constants.RECIPE_FIELD_SUMMARY);
+        recipe.recipeImage = bundle.getString(Constants.RECIPE_FIELD_IMAGE);
+        recipe.ingredientsText = bundle.getString(Constants.RECIPE_FIELD_INGREDIENTS);
+        ArrayList<Parcelable> stepBundles =
+                bundle.getParcelableArrayList(Constants.RECIPE_FIELD_STEPS);
+        if (stepBundles != null) {
+            for (Parcelable stepBundle : stepBundles) {
+                recipe.recipeSteps.add(RecipeStep.fromBundle((Bundle) stepBundle));
+            }
+        }
+        return recipe;
+    }
+}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeActivity.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeActivity.java
new file mode 100644
index 0000000..cc7f034
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeActivity.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.json.JSONObject;
+
+public class RecipeActivity extends Activity {
+    private static final String TAG = "RecipeAssistant";
+    private String mRecipeName;
+    private Recipe mRecipe;
+    private ImageView mImageView;
+    private TextView mTitleTextView;
+    private TextView mSummaryTextView;
+    private TextView mIngredientsTextView;
+    private LinearLayout mStepsLayout;
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Intent intent = getIntent();
+        mRecipeName = intent.getStringExtra(Constants.RECIPE_NAME_TO_LOAD);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Intent: " + intent.toString() + " " + mRecipeName);
+        }
+        loadRecipe();
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.recipe);
+        mTitleTextView = (TextView) findViewById(R.id.recipeTextTitle);
+        mSummaryTextView = (TextView) findViewById(R.id.recipeTextSummary);
+        mImageView = (ImageView) findViewById(R.id.recipeImageView);
+        mIngredientsTextView = (TextView) findViewById(R.id.textIngredients);
+        mStepsLayout = (LinearLayout) findViewById(R.id.layoutSteps);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId()) {
+            case R.id.action_cook:
+                startCooking();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void loadRecipe() {
+        JSONObject jsonObject = AssetUtils.loadJSONAsset(this, mRecipeName);
+        if (jsonObject != null) {
+            mRecipe = Recipe.fromJson(this, jsonObject);
+            if (mRecipe != null) {
+                displayRecipe(mRecipe);
+            }
+        }
+    }
+
+    private void displayRecipe(Recipe recipe) {
+        Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
+        mTitleTextView.setAnimation(fadeIn);
+        mTitleTextView.setText(recipe.titleText);
+        mSummaryTextView.setText(recipe.summaryText);
+        if (recipe.recipeImage != null) {
+            mImageView.setAnimation(fadeIn);
+            Bitmap recipeImage = AssetUtils.loadBitmapAsset(this, recipe.recipeImage);
+            mImageView.setImageBitmap(recipeImage);
+        }
+        mIngredientsTextView.setText(recipe.ingredientsText);
+
+        findViewById(R.id.ingredientsHeader).setAnimation(fadeIn);
+        findViewById(R.id.ingredientsHeader).setVisibility(View.VISIBLE);
+        findViewById(R.id.stepsHeader).setAnimation(fadeIn);
+
+        findViewById(R.id.stepsHeader).setVisibility(View.VISIBLE);
+
+        LayoutInflater inf = LayoutInflater.from(this);
+        mStepsLayout.removeAllViews();
+        int stepNumber = 1;
+        for (Recipe.RecipeStep step : recipe.recipeSteps) {
+            View view = inf.inflate(R.layout.step_item, null);
+            ImageView iv = (ImageView) view.findViewById(R.id.stepImageView);
+            if (step.stepImage == null) {
+                iv.setVisibility(View.GONE);
+            } else {
+                Bitmap stepImage = AssetUtils.loadBitmapAsset(this, step.stepImage);
+                iv.setImageBitmap(stepImage);
+            }
+            ((TextView) view.findViewById(R.id.textStep)).setText(
+                    (stepNumber++) + ". " + step.stepText);
+            mStepsLayout.addView(view);
+        }
+    }
+
+    private void startCooking() {
+        Intent intent = new Intent(this, RecipeService.class);
+        intent.setAction(Constants.ACTION_START_COOKING);
+        intent.putExtra(Constants.EXTRA_RECIPE, mRecipe.toBundle());
+        startService(intent);
+    }
+}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeListAdapter.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeListAdapter.java
new file mode 100644
index 0000000..746603a
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeListAdapter.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RecipeListAdapter implements ListAdapter {
+    private String TAG = "RecipeListAdapter";
+
+    private class Item {
+        String title;
+        String name;
+        String summary;
+        Bitmap image;
+    }
+
+    private List<Item> mItems = new ArrayList<Item>();
+    private Context mContext;
+    private DataSetObserver mObserver;
+
+    public RecipeListAdapter(Context context) {
+        mContext = context;
+        loadRecipeList();
+    }
+
+    private void loadRecipeList() {
+        JSONObject jsonObject = AssetUtils.loadJSONAsset(mContext, Constants.RECIPE_LIST_FILE);
+        if (jsonObject != null) {
+            List<Item> items = parseJson(jsonObject);
+            appendItemsToList(items);
+        }
+    }
+
+    private List<Item> parseJson(JSONObject json) {
+        List<Item> result = new ArrayList<Item>();
+        try {
+            JSONArray items = json.getJSONArray(Constants.RECIPE_FIELD_LIST);
+            for (int i = 0; i < items.length(); i++) {
+                JSONObject item = items.getJSONObject(i);
+                Item parsed = new Item();
+                parsed.name = item.getString(Constants.RECIPE_FIELD_NAME);
+                parsed.title = item.getString(Constants.RECIPE_FIELD_TITLE);
+                if (item.has(Constants.RECIPE_FIELD_IMAGE)) {
+                    String imageFile = item.getString(Constants.RECIPE_FIELD_IMAGE);
+                    parsed.image = AssetUtils.loadBitmapAsset(mContext, imageFile);
+                }
+                parsed.summary = item.getString(Constants.RECIPE_FIELD_SUMMARY);
+                result.add(parsed);
+            }
+        } catch (JSONException e) {
+            Log.e(TAG, "Failed to parse recipe list: " + e);
+        }
+        return result;
+    }
+
+    private void appendItemsToList(List<Item> items) {
+        mItems.addAll(items);
+        if (mObserver != null) {
+            mObserver.onChanged();
+        }
+    }
+
+    @Override
+    public int getCount() {
+        return mItems.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return mItems.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return 0;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return 0;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        View view = convertView;
+        if (view == null) {
+            LayoutInflater inf = LayoutInflater.from(mContext);
+            view = inf.inflate(R.layout.list_item, null);
+        }
+        Item item = (Item) getItem(position);
+        TextView titleView = (TextView) view.findViewById(R.id.textTitle);
+        TextView summaryView = (TextView) view.findViewById(R.id.textSummary);
+        ImageView iv = (ImageView) view.findViewById(R.id.imageView);
+
+        titleView.setText(item.title);
+        summaryView.setText(item.summary);
+        if (item.image != null) {
+            iv.setImageBitmap(item.image);
+        } else {
+            iv.setImageDrawable(mContext.getResources().getDrawable(R.drawable.ic_noimage));
+        }
+        return view;
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return 1;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return mItems.isEmpty();
+    }
+
+    @Override
+    public void registerDataSetObserver(DataSetObserver observer) {
+        mObserver = observer;
+    }
+
+    @Override
+    public void unregisterDataSetObserver(DataSetObserver observer) {
+        mObserver = null;
+    }
+
+    @Override
+    public boolean areAllItemsEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        return true;
+    }
+
+    public String getItemName(int position) {
+        return mItems.get(position).name;
+    }
+}
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeService.java b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeService.java
new file mode 100644
index 0000000..4abdf97
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/Application/src/main/java/com/example/android/wearable/recipeassistant/RecipeService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.recipeassistant;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Binder;
+import android.os.IBinder;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.NotificationCompat;
+
+import java.util.ArrayList;
+
+public class RecipeService extends Service {
+    private NotificationManagerCompat mNotificationManager;
+    private Binder mBinder = new LocalBinder();
+    private Recipe mRecipe;
+
+    public class LocalBinder extends Binder {
+        RecipeService getService() {
+            return RecipeService.this;
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        mNotificationManager = NotificationManagerCompat.from(this);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent.getAction().equals(Constants.ACTION_START_COOKING)) {
+            createNotification(intent);
+            return START_STICKY;
+        }
+        return START_NOT_STICKY;
+    }
+
+    private void createNotification(Intent intent) {
+        mRecipe = Recipe.fromBundle(intent.getBundleExtra(Constants.EXTRA_RECIPE));
+        ArrayList<Notification> notificationPages = new ArrayList<Notification>();
+
+        int stepCount = mRecipe.recipeSteps.size();
+
+        for (int i = 0; i < stepCount; ++i) {
+            Recipe.RecipeStep recipeStep = mRecipe.recipeSteps.get(i);
+            NotificationCompat.BigTextStyle style = new NotificationCompat.BigTextStyle();
+            style.bigText(recipeStep.stepText);
+            style.setBigContentTitle(String.format(
+                    getResources().getString(R.string.step_count), i + 1, stepCount));
+            style.setSummaryText("");
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+            builder.setStyle(style);
+            notificationPages.add(builder.build());
+        }
+
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+
+        if (mRecipe.recipeImage != null) {
+            Bitmap recipeImage = Bitmap.createScaledBitmap(
+                    AssetUtils.loadBitmapAsset(this, mRecipe.recipeImage),
+                    Constants.NOTIFICATION_IMAGE_WIDTH, Constants.NOTIFICATION_IMAGE_HEIGHT, false);
+            builder.setLargeIcon(recipeImage);
+        }
+        builder.setContentTitle(mRecipe.titleText);
+        builder.setContentText(mRecipe.summaryText);
+        builder.setSmallIcon(R.mipmap.ic_notification_recipe);
+
+        Notification notification = builder
+                .extend(new NotificationCompat.WearableExtender()
+                        .addPages(notificationPages))
+                .build();
+        mNotificationManager.notify(Constants.NOTIFICATION_ID, notification);
+    }
+}
diff --git a/wearable/wear/RecipeAssistant/Shared/.gitignore b/wearable/wear/RecipeAssistant/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/RecipeAssistant/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/RecipeAssistant/Shared/src/main/AndroidManifest.xml b/wearable/wear/RecipeAssistant/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 1951e26..0000000
--- a/wearable/wear/RecipeAssistant/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.recipeassistant.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/RecipeAssistant/Shared/src/main/res/values/strings.xml b/wearable/wear/RecipeAssistant/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/RecipeAssistant/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/RecipeAssistant/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/RecipeAssistant/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/RecipeAssistant/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/RecipeAssistant/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/RecipeAssistant/screenshots/guac_notification.png b/wearable/wear/RecipeAssistant/screenshots/guac_notification.png
new file mode 100644
index 0000000..e041b11
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/screenshots/guac_notification.png
Binary files differ
diff --git a/wearable/wear/RecipeAssistant/screenshots/guac_notification_step1.png b/wearable/wear/RecipeAssistant/screenshots/guac_notification_step1.png
new file mode 100644
index 0000000..e0157ea
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/screenshots/guac_notification_step1.png
Binary files differ
diff --git a/wearable/wear/RecipeAssistant/screenshots/recipe_assistant_guac.png b/wearable/wear/RecipeAssistant/screenshots/recipe_assistant_guac.png
new file mode 100644
index 0000000..a34da6e
--- /dev/null
+++ b/wearable/wear/RecipeAssistant/screenshots/recipe_assistant_guac.png
Binary files differ
diff --git a/wearable/wear/RecipeAssistant/template-params.xml b/wearable/wear/RecipeAssistant/template-params.xml
index e24a2c5..5fe7af7 100644
--- a/wearable/wear/RecipeAssistant/template-params.xml
+++ b/wearable/wear/RecipeAssistant/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>RecipeAssistant</name>
     <group>Wearable</group>
-    <package>com.example.android.recipeassistant</package>
+    <package>com.example.android.wearable.recipeassistant</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
diff --git a/wearable/wear/SkeletonWearableApp/Application/src/main/AndroidManifest.xml b/wearable/wear/SkeletonWearableApp/Application/src/main/AndroidManifest.xml
index 7e19680..23497ca 100644
--- a/wearable/wear/SkeletonWearableApp/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/SkeletonWearableApp/Application/src/main/AndroidManifest.xml
@@ -16,12 +16,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.skeletonwearableapp">
+    package="com.example.android.google.wearable.app">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
         android:label="@string/app_name">
     </application>
 
diff --git a/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/Shared/.gitignore b/wearable/wear/SkeletonWearableApp/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/SkeletonWearableApp/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/SkeletonWearableApp/Shared/src/main/AndroidManifest.xml b/wearable/wear/SkeletonWearableApp/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 5e29d53..0000000
--- a/wearable/wear/SkeletonWearableApp/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.skeletonwearableapp.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/SkeletonWearableApp/Shared/src/main/res/values/strings.xml b/wearable/wear/SkeletonWearableApp/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/SkeletonWearableApp/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml b/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml
index 3e9310e..f99d785 100644
--- a/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.skeletonwearableapp" >
+        package="com.example.android.google.wearable.app" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -42,7 +42,7 @@
         <activity
             android:name=".GridExampleActivity">
             <intent-filter>
-                <action android:name="com.example.android.skeletonwearableapp.GRID_ACTIVITY"/>
+                <action android:name="com.example.android.google.wearable.app.GRID_ACTIVITY"/>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
diff --git a/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/google/wearable/app/GridExampleActivity.java b/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/google/wearable/app/GridExampleActivity.java
new file mode 100644
index 0000000..80af2a6
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/google/wearable/app/GridExampleActivity.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.google.wearable.app;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.wearable.view.CardFragment;
+import android.support.wearable.view.FragmentGridPagerAdapter;
+import android.support.wearable.view.GridViewPager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GridExampleActivity extends Activity {
+    private static final int NUM_ROWS = 10;
+    private static final int NUM_COLS = 3;
+
+    MainAdapter mAdapter;
+    GridViewPager mPager;
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.grid_activity);
+        mPager = (GridViewPager) findViewById(R.id.fragment_container);
+        mAdapter = new MainAdapter(this, getFragmentManager());
+        mPager.setAdapter(mAdapter);
+
+    }
+
+    private static class MainAdapter extends FragmentGridPagerAdapter{
+        Map<Point, Drawable> mBackgrounds = new HashMap<Point, Drawable>();
+        private Context mContext;
+
+        public MainAdapter(Context ctx, FragmentManager fm) {
+            super(fm);
+            mContext = ctx;
+        }
+
+        @Override
+        public int getRowCount() {
+            return NUM_ROWS;
+        }
+
+        @Override
+        public int getColumnCount(int rowNum) {
+            return NUM_COLS;
+        }
+
+        @Override
+        public Fragment getFragment(int rowNum, int colNum) {
+            return MainFragment.newInstance(rowNum, colNum);
+        }
+
+        @Override
+        public Drawable getBackgroundForPage(int row, int column) {
+            Point pt = new Point(column, row);
+            Drawable drawable = mBackgrounds.get(pt);
+            if (drawable == null) {
+                Bitmap bm = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(bm);
+                Paint p = new Paint();
+                // Clear previous image.
+                c.drawRect(0, 0, 200, 200, p);
+                p.setAntiAlias(true);
+                p.setTypeface(Typeface.DEFAULT);
+                p.setTextSize(64);
+                p.setColor(Color.LTGRAY);
+                p.setTextAlign(Align.CENTER);
+                c.drawText(column+ "-" + row, 100, 100, p);
+                drawable = new BitmapDrawable(mContext.getResources(), bm);
+                mBackgrounds.put(pt, drawable);
+            }
+            return drawable;
+        }
+    }
+
+    public static class MainFragment extends CardFragment {
+        private static MainFragment newInstance(int rowNum, int colNum) {
+            Bundle args = new Bundle();
+            args.putString(CardFragment.KEY_TITLE, "Row:" + rowNum);
+            args.putString(CardFragment.KEY_TEXT, "Col:" + colNum);
+            MainFragment f = new MainFragment();
+            f.setArguments(args);
+            return f;
+        }
+    }
+}
diff --git a/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/google/wearable/app/MainActivity.java b/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/google/wearable/app/MainActivity.java
new file mode 100644
index 0000000..817e195
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/google/wearable/app/MainActivity.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.google.wearable.app;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.view.GestureDetectorCompat;
+import android.support.wearable.view.DelayedConfirmationView;
+import android.support.wearable.view.DismissOverlayView;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ScrollView;
+
+public class MainActivity extends Activity
+        implements DelayedConfirmationView.DelayedConfirmationListener {
+    private static final String TAG = "MainActivity";
+
+    private static final int NOTIFICATION_ID = 1;
+    private static final int NOTIFICATION_REQUEST_CODE = 1;
+    private static final int NUM_SECONDS = 5;
+
+    private GestureDetectorCompat mGestureDetector;
+    private DismissOverlayView mDismissOverlayView;
+
+    @Override
+    public void onCreate(Bundle b) {
+        super.onCreate(b);
+        setContentView(R.layout.main_activity);
+
+        mDismissOverlayView = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
+        mDismissOverlayView.setIntroText(R.string.intro_text);
+        mDismissOverlayView.showIntroIfNecessary();
+        mGestureDetector = new GestureDetectorCompat(this, new LongPressListener());
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
+    }
+
+    private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
+        @Override
+        public void onLongPress(MotionEvent event) {
+            mDismissOverlayView.show();
+        }
+    }
+
+    /**
+     * Handles the button to launch a notification.
+     */
+    public void showNotification(View view) {
+        Notification notification = new NotificationCompat.Builder(this)
+                .setContentTitle(getString(R.string.notification_title))
+                .setContentText(getString(R.string.notification_title))
+                .setSmallIcon(R.drawable.ic_launcher)
+                .addAction(R.drawable.ic_launcher,
+                        getText(R.string.action_launch_activity),
+                        PendingIntent.getActivity(this, NOTIFICATION_REQUEST_CODE,
+                                new Intent(this, GridExampleActivity.class),
+                                PendingIntent.FLAG_UPDATE_CURRENT))
+                .build();
+        NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification);
+        finish();
+    }
+
+
+    /**
+     * Handles the button press to finish this activity and take the user back to the Home.
+     */
+    public void onFinishActivity(View view) {
+        setResult(RESULT_OK);
+        finish();
+    }
+
+    /**
+     * Handles the button to start a DelayedConfirmationView timer.
+     */
+    public void onStartTimer(View view) {
+        DelayedConfirmationView delayedConfirmationView = (DelayedConfirmationView)
+                findViewById(R.id.timer);
+        delayedConfirmationView.setTotalTimeMs(NUM_SECONDS * 1000);
+        delayedConfirmationView.setListener(this);
+        delayedConfirmationView.start();
+        scroll(View.FOCUS_DOWN);
+    }
+
+    @Override
+    public void onTimerFinished(View v) {
+        Log.d(TAG, "onTimerFinished is called.");
+        scroll(View.FOCUS_UP);
+    }
+
+    @Override
+    public void onTimerSelected(View v) {
+        Log.d(TAG, "onTimerSelected is called.");
+        scroll(View.FOCUS_UP);
+    }
+
+    private void scroll(final int scrollDirection) {
+        final ScrollView scrollView = (ScrollView) findViewById(R.id.scroll);
+        scrollView.post(new Runnable() {
+            @Override
+            public void run() {
+                scrollView.fullScroll(scrollDirection);
+            }
+        });
+    }
+}
diff --git a/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/skeletonwearableapp/GridExampleActivity.java b/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/skeletonwearableapp/GridExampleActivity.java
deleted file mode 100644
index c0d0b2b..0000000
--- a/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/skeletonwearableapp/GridExampleActivity.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.skeletonwearableapp;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Typeface;
-import android.os.Bundle;
-import android.support.wearable.view.CardFragment;
-import android.support.wearable.view.FragmentGridPagerAdapter;
-import android.support.wearable.view.GridViewPager;
-import android.support.wearable.view.ImageReference;
-import java.util.HashMap;
-import java.util.Map;
-
-public class GridExampleActivity extends Activity {
-    private static final int NUM_ROWS = 10;
-    private static final int NUM_COLS = 3;
-
-    MainAdapter mAdapter;
-    GridViewPager mPager;
-
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.grid_activity);
-        mPager = (GridViewPager) findViewById(R.id.fragment_container);
-        mAdapter = new MainAdapter(getFragmentManager());
-        mPager.setAdapter(mAdapter);
-
-    }
-
-    private static class MainAdapter extends FragmentGridPagerAdapter{
-        Map<Point, ImageReference> mBackgrounds = new HashMap<Point, ImageReference>();
-
-        public MainAdapter(FragmentManager fm) {
-            super(fm);
-        }
-
-        @Override
-        public int getRowCount() {
-            return NUM_ROWS;
-        }
-
-        @Override
-        public int getColumnCount(int rowNum) {
-            return NUM_COLS;
-        }
-
-        @Override
-        public Fragment getFragment(int rowNum, int colNum) {
-            return MainFragment.newInstance(rowNum, colNum);
-        }
-
-        @Override
-        public ImageReference getBackground(int row, int column) {
-            Point pt = new Point(column, row);
-            ImageReference ref = mBackgrounds.get(pt);
-            if (ref == null) {
-                Bitmap bm = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
-                Canvas c = new Canvas(bm);
-                Paint p = new Paint();
-                // Clear previous image.
-                c.drawRect(0, 0, 200, 200, p);
-                p.setAntiAlias(true);
-                p.setTypeface(Typeface.DEFAULT);
-                p.setTextSize(64);
-                p.setColor(Color.LTGRAY);
-                c.drawText(column+ "-" + row, 20, 100, p);
-                ref = ImageReference.forBitmap(bm);
-                mBackgrounds.put(pt, ref);
-            }
-            return ref;
-        }
-    }
-
-    public static class MainFragment extends CardFragment {
-        private static MainFragment newInstance(int rowNum, int colNum) {
-            Bundle args = new Bundle();
-            args.putString(CardFragment.KEY_TITLE, "Row:" + rowNum);
-            args.putString(CardFragment.KEY_TEXT, "Col:" + colNum);
-            MainFragment f = new MainFragment();
-            f.setArguments(args);
-            return f;
-        }
-    }
-}
diff --git a/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/skeletonwearableapp/MainActivity.java b/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/skeletonwearableapp/MainActivity.java
deleted file mode 100644
index b7a91e6..0000000
--- a/wearable/wear/SkeletonWearableApp/Wearable/src/main/java/com/example/android/skeletonwearableapp/MainActivity.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.skeletonwearableapp;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationManagerCompat;
-import android.support.v4.view.GestureDetectorCompat;
-import android.support.wearable.view.DelayedConfirmationView;
-import android.support.wearable.view.DismissOverlayView;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ScrollView;
-
-public class MainActivity extends Activity
-        implements DelayedConfirmationView.DelayedConfirmationListener {
-    private static final String TAG = "MainActivity";
-
-    private static final int NOTIFICATION_ID = 1;
-    private static final int NOTIFICATION_REQUEST_CODE = 1;
-    private static final int NUM_SECONDS = 5;
-
-    private GestureDetectorCompat mGestureDetector;
-    private DismissOverlayView mDismissOverlayView;
-
-    @Override
-    public void onCreate(Bundle b) {
-        super.onCreate(b);
-        setContentView(R.layout.main_activity);
-
-        mDismissOverlayView = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
-        mDismissOverlayView.setIntroText(R.string.intro_text);
-        mDismissOverlayView.showIntroIfNecessary();
-        mGestureDetector = new GestureDetectorCompat(this, new LongPressListener());
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
-    }
-
-    private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
-        @Override
-        public void onLongPress(MotionEvent event) {
-            mDismissOverlayView.show();
-        }
-    }
-
-    /**
-     * Handles the button to launch a notification.
-     */
-    public void showNotification(View view) {
-        Notification notification = new NotificationCompat.Builder(this)
-                .setContentTitle(getString(R.string.notification_title))
-                .setContentText(getString(R.string.notification_title))
-                .setSmallIcon(R.drawable.ic_launcher)
-                .addAction(R.drawable.ic_launcher,
-                        getText(R.string.action_launch_activity),
-                        PendingIntent.getActivity(this, NOTIFICATION_REQUEST_CODE,
-                                new Intent(this, GridExampleActivity.class),
-                                PendingIntent.FLAG_UPDATE_CURRENT))
-                .build();
-        NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification);
-        finish();
-    }
-
-
-    /**
-     * Handles the button press to finish this activity and take the user back to the Home.
-     */
-    public void onFinishActivity(View view) {
-        setResult(RESULT_OK);
-        finish();
-    }
-
-    /**
-     * Handles the button to start a DelayedConfirmationView timer.
-     */
-    public void onStartTimer(View view) {
-        DelayedConfirmationView delayedConfirmationView = (DelayedConfirmationView)
-                findViewById(R.id.timer);
-        delayedConfirmationView.setTotalTimeMs(NUM_SECONDS * 1000);
-        delayedConfirmationView.setListener(this);
-        delayedConfirmationView.start();
-        scroll(View.FOCUS_DOWN);
-    }
-
-    @Override
-    public void onTimerFinished(View v) {
-        Log.d(TAG, "onTimerFinished is called.");
-        scroll(View.FOCUS_UP);
-    }
-
-    @Override
-    public void onTimerSelected(View v) {
-        Log.d(TAG, "onTimerSelected is called.");
-        scroll(View.FOCUS_UP);
-    }
-
-    private void scroll(final int scrollDirection) {
-        final ScrollView scrollView = (ScrollView) findViewById(R.id.scroll);
-        scrollView.post(new Runnable() {
-            @Override
-            public void run() {
-                scrollView.fullScroll(scrollDirection);
-            }
-        });
-    }
-}
diff --git a/wearable/wear/SkeletonWearableApp/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/SkeletonWearableApp/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/SkeletonWearableApp/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/SkeletonWearableApp/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/SkeletonWearableApp/screenshots/delayed_confirmation.png b/wearable/wear/SkeletonWearableApp/screenshots/delayed_confirmation.png
new file mode 100644
index 0000000..2994361
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/screenshots/delayed_confirmation.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/screenshots/dismiss_overlay.png b/wearable/wear/SkeletonWearableApp/screenshots/dismiss_overlay.png
new file mode 100644
index 0000000..142d7e4
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/screenshots/dismiss_overlay.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/screenshots/grid_view_pager.png b/wearable/wear/SkeletonWearableApp/screenshots/grid_view_pager.png
new file mode 100644
index 0000000..f59c13d
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/screenshots/grid_view_pager.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/screenshots/skeleton_wearable_app.png b/wearable/wear/SkeletonWearableApp/screenshots/skeleton_wearable_app.png
new file mode 100644
index 0000000..4f17274
--- /dev/null
+++ b/wearable/wear/SkeletonWearableApp/screenshots/skeleton_wearable_app.png
Binary files differ
diff --git a/wearable/wear/SkeletonWearableApp/settings.gradle b/wearable/wear/SkeletonWearableApp/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/SkeletonWearableApp/settings.gradle
+++ b/wearable/wear/SkeletonWearableApp/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/SkeletonWearableApp/template-params.xml b/wearable/wear/SkeletonWearableApp/template-params.xml
index 0729757..ea98d4a 100644
--- a/wearable/wear/SkeletonWearableApp/template-params.xml
+++ b/wearable/wear/SkeletonWearableApp/template-params.xml
@@ -14,20 +14,12 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
-
-
 <sample>
     <name>SkeletonWearableApp</name>
     <group>Wearable</group>
-    <package>com.example.android.skeletonwearableapp</package>
-
-
-
+    <package>com.example.android.google.wearable.app</package>
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
-
-
+    <minSdk>18</minSdk>
     <strings>
         <intro>
             <![CDATA[
@@ -38,10 +30,44 @@
             ]]>
         </intro>
     </strings>
-
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Getting Started, Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>BEGINNER</level>
+        <icon>Wearable/src/main/res/drawable-xhdpi/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/skeleton_wearable_app.png</img>
+            <img>screenshots/grid_view_pager.png</img>
+            <img>screenshots/dismiss_overlay.png</img>
+            <img>screenshots/delayed_confirmation.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.wearable.view.CardFragment</android>
+            <android>android.support.wearable.view.GridViewPager</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample is a basic skeleton app which can be used as a starting point for wear development.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+This sample is a mostly empty wearable app that implements a fullscreen activity
+conforming to Android Wear best practices. Included in the sample are examples of [GridViewPager][1],
+[DelayedConfirmationView][2], and [DismissOverlayView][3].
 
+Developers who require a fullscreen activity for
+their wearable app can use this sample as a starting point.
+
+[1]: https://developer.android.com/reference/com/google/android/support/wearable/view/GridViewPager.html
+[2]: https://developer.android.com/reference/com/google/android/support/wearable/view/DelayedConfirmationView.html
+[3]: https://developer.android.com/reference/com/google/android/support/wearable/view/DismissOverlayView.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/AgendaData/Shared/.gitignore b/wearable/wear/SpeedTracker/Application/.gitignore
similarity index 100%
copy from wearable/wear/AgendaData/Shared/.gitignore
copy to wearable/wear/SpeedTracker/Application/.gitignore
diff --git a/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml b/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml
index 3361a44..44284d4 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml
@@ -7,12 +7,13 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
-
+    
+    <uses-feature android:name="android.hardware.location.gps" android:required="true" />
     <uses-feature
         android:glEsVersion="0x00020000" android:required="true"/>
     <uses-sdk
         android:minSdkVersion="18"
-        android:targetSdkVersion="19" />
+        android:targetSdkVersion="21" />
 
     <application
         android:name=".PhoneApplication"
diff --git a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-hdpi/ic_launcher.png
index 2275b45..2476b11 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-mdpi/ic_launcher.png
index 25015af..a102b79 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xhdpi/ic_launcher.png
index 8aee870..8ad19e8 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
index 7008a2f..9c5429f 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png
index c31ab73..b2a49f8 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Application/src/main/res/drawable-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/DataLayer/Shared/.gitignore b/wearable/wear/SpeedTracker/Shared/.gitignore
similarity index 100%
rename from wearable/wear/DataLayer/Shared/.gitignore
rename to wearable/wear/SpeedTracker/Shared/.gitignore
diff --git a/wearable/wear/AgendaData/Shared/.gitignore b/wearable/wear/SpeedTracker/Wearable/.gitignore
similarity index 100%
rename from wearable/wear/AgendaData/Shared/.gitignore
rename to wearable/wear/SpeedTracker/Wearable/.gitignore
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml b/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml
index 750f052..ab19d5e 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml
@@ -24,7 +24,7 @@
 
     <uses-sdk
         android:minSdkVersion="20"
-        android:targetSdkVersion="20" />
+        android:targetSdkVersion="21" />
 
     <application
         android:allowBackup="true"
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/WearableMainActivity.java b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/WearableMainActivity.java
index 9610f22..f3015bf 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/WearableMainActivity.java
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/WearableMainActivity.java
@@ -336,8 +336,8 @@
 
     private void updateRecordingIcon() {
         mSaveGpsLocation = LocationSettingActivity.getGpsRecordingStatusFromPreferences(this);
-        mSaveImageView.setImageResource(mSaveGpsLocation ? R.drawable.ic_file_download_googblue_24dp
-                : R.drawable.ic_file_download_grey600_24dp);
+        mSaveImageView.setImageResource(mSaveGpsLocation ? R.drawable.ic_gps_saving_grey600_96dp
+                : R.drawable.ic_gps_not_saving_grey600_96dp);
     }
 
     /**
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerLayout.java b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerLayout.java
index 9fd882d..5796c13 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerLayout.java
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerLayout.java
@@ -30,13 +30,13 @@
  * A simple extension of the {@link android.widget.LinearLayout} to represent a single item in a
  * {@link android.support.wearable.view.WearableListView}.
  */
-public class SpeedPickerLayout extends LinearLayout implements WearableListView.Item {
+public class SpeedPickerLayout extends LinearLayout
+        implements WearableListView.OnCenterProximityListener {
 
     private final float mFadedTextAlpha;
     private final int mFadedCircleColor;
     private final int mChosenCircleColor;
     private ImageView mCircle;
-    private float mScale;
     private TextView mName;
 
     public SpeedPickerLayout(Context context) {
@@ -64,41 +64,16 @@
         mName = (TextView) findViewById(R.id.name);
     }
 
-    // Provide scaling values for WearableListView animations
     @Override
-    public float getProximityMinValue() {
-        return 1f;
-    }
-
-    @Override
-    public float getProximityMaxValue() {
-        return 1.6f;
-    }
-
-    @Override
-    public float getCurrentProximityValue() {
-        return mScale;
-    }
-
-    // Scale the icon for WearableListView animations
-    @Override
-    public void setScalingAnimatorValue(float scale) {
-        mScale = scale;
-        mCircle.setScaleX(scale);
-        mCircle.setScaleY(scale);
-    }
-
-    // Change color of the icon, remove fading from the text
-    @Override
-    public void onScaleUpStart() {
+    public void onCenterPosition(boolean animate) {
         mName.setAlpha(1f);
         ((GradientDrawable) mCircle.getDrawable()).setColor(mChosenCircleColor);
     }
 
-    // Change the color of the icon, fade the text
     @Override
-    public void onScaleDownStart() {
+    public void onNonCenterPosition(boolean animate) {
         ((GradientDrawable) mCircle.getDrawable()).setColor(mFadedCircleColor);
         mName.setAlpha(mFadedTextAlpha);
+
     }
 }
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-hdpi/ic_launcher.png
index 2275b45..2476b11 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-hdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-mdpi/ic_launcher.png
index 25015af..a102b79 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-mdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png
index 8aee870..8ad19e8 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_black_24dp.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_black_24dp.png
deleted file mode 100644
index 0ec94b2..0000000
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_black_24dp.png
+++ /dev/null
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_googblue_24dp.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_googblue_24dp.png
deleted file mode 100644
index 00b3e05..0000000
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_googblue_24dp.png
+++ /dev/null
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_grey600_24dp.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_grey600_24dp.png
deleted file mode 100644
index c7e86f9..0000000
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_grey600_24dp.png
+++ /dev/null
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_white_24dp.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_white_24dp.png
deleted file mode 100644
index a57e72f..0000000
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_file_download_white_24dp.png
+++ /dev/null
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_gps_not_saving_grey600_96dp.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_gps_not_saving_grey600_96dp.png
new file mode 100644
index 0000000..0eff434
--- /dev/null
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_gps_not_saving_grey600_96dp.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_gps_saving_grey600_96dp.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_gps_saving_grey600_96dp.png
new file mode 100644
index 0000000..7b3506b
--- /dev/null
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_gps_saving_grey600_96dp.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png
index 7008a2f..9c5429f 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxxhdpi/ic_launcher.png b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxxhdpi/ic_launcher.png
index c31ab73..b2a49f8 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxxhdpi/ic_launcher.png
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/drawable-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/main_activity.xml b/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/main_activity.xml
index 21f809e..a1b9081 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/main_activity.xml
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/main_activity.xml
@@ -83,7 +83,7 @@
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_file_download_grey600_24dp"
+        android:src="@drawable/ic_gps_not_saving_grey600_96dp"
         android:id="@+id/saving"
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
@@ -98,4 +98,4 @@
         android:layout_alignParentRight="true"
         android:layout_alignBottom="@+id/saving"
         android:layout_marginRight="60dp"/>
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/wearable/wear/SpeedTracker/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/SpeedTracker/gradle/wrapper/gradle-wrapper.properties
index 1e61d1f..0c71e76 100644
--- a/wearable/wear/SpeedTracker/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/SpeedTracker/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/SpeedTracker/template-params.xml b/wearable/wear/SpeedTracker/template-params.xml
index 704583d..b780173 100644
--- a/wearable/wear/SpeedTracker/template-params.xml
+++ b/wearable/wear/SpeedTracker/template-params.xml
@@ -25,20 +25,23 @@
 `
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
+
+    <dependency>com.google.android.gms:play-services-location:6.5.+</dependency>
+    <dependency_wearable>com.google.android.gms:play-services-location:6.5.+</dependency_wearable>
 
 
     <strings>
         <intro>
             <![CDATA[
-This sample uses the FusedLocation APIs of GMS on those
-devices that have a hardware GPS built in. In those cases,
-this sample provides a simple screen that shows the current
-speed of the device on the watch. User can set a speed limit
-and if the speed approaches that limit, it changes the color
-to yellow and if it exceeds the limit, it turns red. User
-can also enable recording of coordinates and when it pairs
-back with the phone, this data will be synced with the phone
+This sample uses the FusedLocation APIs of Google Play Services
+on those devices that have a hardware GPS built in. In those
+cases, this sample provides a simple screen that shows the
+current speed of the device on the watch. User can set a speed
+limit and if the speed approaches that limit, it changes the
+color to yellow and if it exceeds the limit, it turns red. User
+can also enable recording of coordinates and when it pairs back
+with the phone, this data will be synced with the phone
 component of the app and user can see a track made of those
 coordinates on a map on the phone.
             ]]>
@@ -46,8 +49,6 @@
     </strings>
 
     <template src="base"/>
-    <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
+    <template src="WearPlusShared"/>
 
 </sample>
diff --git a/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml b/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml
index c46f772..1737c7d 100644
--- a/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml
@@ -18,12 +18,12 @@
 
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.synchronizednotifications"
+    package="com.example.android.wearable.synchronizednotifications"
     android:versionCode="1"
     android:versionName="1.0">
 
-    <uses-sdk android:minSdkVersion="19"
-        android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+        android:targetSdkVersion="21" />
 
     <application android:allowBackup="true"
         android:label="@string/app_name"
@@ -47,7 +47,7 @@
             </intent-filter>
             <intent-filter>
                 <action
-                    android:name="com.example.android.wearable.synchronizednotifications.DISMISS" />
+                    android:name="com.example.android.wearable.wearable.synchronizednotifications.DISMISS" />
             </intent-filter>
         </service>
     </application>
diff --git a/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/synchronizednotifications/DismissListener.java b/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/synchronizednotifications/DismissListener.java
deleted file mode 100644
index 8648148..0000000
--- a/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/synchronizednotifications/DismissListener.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.synchronizednotifications;
-
-import static com.google.android.gms.wearable.PutDataRequest.WEAR_URI_SCHEME;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.NotificationManagerCompat;
-import android.util.Log;
-
-import com.example.android.synchronizednotifications.common.Constants;
-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.wearable.DataApi;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.Wearable;
-import com.google.android.gms.wearable.WearableListenerService;
-
-/**
- * A {@link com.google.android.gms.wearable.WearableListenerService} that is invoked when certain
- * notifications are dismissed from either the phone or watch.
- */
-public class DismissListener extends WearableListenerService
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
-        ResultCallback<DataApi.DeleteDataItemsResult> {
-
-    private static final String TAG = "DismissListener";
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        for (DataEvent dataEvent : dataEvents) {
-            if (dataEvent.getType() == DataEvent.TYPE_DELETED) {
-                if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
-                    // notification on the phone should be dismissed
-                    NotificationManagerCompat.from(this).cancel(Constants.BOTH_ID);
-                }
-            }
-        }
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (null != intent) {
-            String action = intent.getAction();
-            if (Constants.ACTION_DISMISS.equals(action)) {
-                // We need to dismiss the wearable notification. We delete the DataItem that
-                // created the notification to inform the wearable.
-                int notificationId = intent.getIntExtra(Constants.KEY_NOTIFICATION_ID, -1);
-                if (notificationId == Constants.BOTH_ID) {
-                    dismissWearableNotification(notificationId);
-                }
-            }
-        }
-        return super.onStartCommand(intent, flags, startId);
-    }
-
-    /**
-     * Removes the DataItem that was used to create a notification on the watch. By deleting the
-     * data item, a {@link com.google.android.gms.wearable.WearableListenerService} on the watch
-     * will be notified and the notification on the watch will be removed. To
-     * access the Wearable DataApi, we first need to ensure the GoogleApiClient is ready,
-     * which will then run the onConnected callback were the data removal is
-     * defined.
-     *
-     * @param id The ID of the notification that should be removed
-     */
-    private void dismissWearableNotification(final int id) {
-        mGoogleApiClient.connect();
-    }
-
-    @Override // ConnectionCallbacks
-    public void onConnected(Bundle bundle) {
-        final Uri dataItemUri =
-                new Uri.Builder().scheme(WEAR_URI_SCHEME).path(Constants.BOTH_PATH).build();
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Deleting Uri: " + dataItemUri.toString());
-        }
-        Wearable.DataApi.deleteDataItems(
-                mGoogleApiClient, dataItemUri).setResultCallback(this);
-    }
-
-    @Override // ConnectionCallbacks
-    public void onConnectionSuspended(int i) {
-    }
-
-    @Override // OnConnectionFailedListener
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-        Log.e(TAG, "Failed to connect to the Google API client");
-    }
-
-    @Override // ResultCallback<DataApi.DeleteDataItemsResult>
-    public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
-        if (!deleteDataItemsResult.getStatus().isSuccess()) {
-            Log.e(TAG, "dismissWearableNotification(): failed to delete DataItem");
-        }
-        mGoogleApiClient.disconnect();
-    }
-}
diff --git a/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/synchronizednotifications/SynchronizedNotificationsFragment.java b/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/synchronizednotifications/SynchronizedNotificationsFragment.java
deleted file mode 100644
index 2273ab1..0000000
--- a/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/synchronizednotifications/SynchronizedNotificationsFragment.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.synchronizednotifications;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.support.v4.app.Fragment;
-import android.app.Activity;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationManagerCompat;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Toast;
-
-import com.example.android.synchronizednotifications.common.Constants;
-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.wearable.DataApi;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.PutDataRequest;
-import com.google.android.gms.wearable.Wearable;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-
-/**
- * A simple fragment that presents three buttons that would trigger three different combinations of
- * notifications on the handset and the watch:
- * <ul>
- * <li>The first button builds a simple local-only notification on the handset.</li>
- * <li>The second one creates a wearable-only notification by putting a data item in the shared data
- * store and having a {@link com.google.android.gms.wearable.WearableListenerService} listen for
- * that on the wearable</li>
- * <li>The third one creates a local notification and a wearable notification by combining the above
- * two. It, however, demonstrates how one can set things up so that the dismissal of one
- * notification results in the dismissal of the other one.</li>
- * </ul>
- */
-public class SynchronizedNotificationsFragment extends Fragment
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
-
-    private static final String TAG = "SynchronizedNotificationsFragment";
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mGoogleApiClient = new GoogleApiClient.Builder(this.getActivity())
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-        setHasOptionsMenu(true);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.btn_phone_only:
-                buildLocalOnlyNotification(getString(R.string.phone_only), now(),
-                        Constants.PHONE_ONLY_ID, false);
-                return true;
-            case R.id.btn_wear_only:
-                buildWearableOnlyNotification(getString(R.string.wear_only), now(),
-                        Constants.WATCH_ONLY_PATH);
-                return true;
-            case R.id.btn_different:
-                buildMirroredNotifications(getString(R.string.phone_both), getString(R.string.watch_both), now());
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Builds a local-only notification for the handset. This is achieved by using
-     * <code>setLocalOnly(true)</code>. If <code>withDismissal</code> is set to <code>true</code>, a
-     * {@link android.app.PendingIntent} will be added to handle the dismissal of notification to
-     * be able to remove the mirrored notification on the wearable.
-     */
-    private void buildLocalOnlyNotification(String title, String content, int notificationId,
-                                            boolean withDismissal) {
-        NotificationCompat.Builder builder = new NotificationCompat.Builder(this.getActivity());
-        builder.setContentTitle(title)
-                .setContentText(content)
-                .setLocalOnly(true)
-                .setSmallIcon(R.drawable.ic_launcher);
-
-        if (withDismissal) {
-            Intent dismissIntent = new Intent(Constants.ACTION_DISMISS);
-            dismissIntent.putExtra(Constants.KEY_NOTIFICATION_ID, Constants.BOTH_ID);
-            PendingIntent pendingIntent = PendingIntent
-                    .getService(this.getActivity(), 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            builder.setDeleteIntent(pendingIntent);
-        }
-        NotificationManagerCompat.from(this.getActivity()).notify(notificationId, builder.build());
-    }
-
-    /**
-     * Builds a DataItem that on the wearable will be interpreted as a request to show a
-     * notification. The result will be a notification that only shows up on the wearable.
-     */
-    private void buildWearableOnlyNotification(String title, String content, String path) {
-        if (mGoogleApiClient.isConnected()) {
-            PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path);
-            putDataMapRequest.getDataMap().putString(Constants.KEY_CONTENT, content);
-            putDataMapRequest.getDataMap().putString(Constants.KEY_TITLE, title);
-            PutDataRequest request = putDataMapRequest.asPutDataRequest();
-            Wearable.DataApi.putDataItem(mGoogleApiClient, request)
-                    .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
-                        @Override
-                        public void onResult(DataApi.DataItemResult dataItemResult) {
-                            if (!dataItemResult.getStatus().isSuccess()) {
-                                Log.e(TAG, "buildWatchOnlyNotification(): Failed to set the data, "
-                                        + "status: " + dataItemResult.getStatus().getStatusCode());
-                            }
-                        }
-                    });
-        } else {
-            Log.e(TAG, "buildWearableOnlyNotification(): no Google API Client connection");
-        }
-    }
-
-    /**
-     * Builds a local notification and sets a DataItem that will be interpreted by the wearable as
-     * a request to build a notification on the wearable as as well. The two notifications show
-     * different messages.
-     * Dismissing either of the notifications will result in dismissal of the other; this is
-     * achieved by creating a {@link android.app.PendingIntent} that results in removal of
-     * the DataItem that created the watch notification. The deletion of the DataItem is observed on
-     * both sides, using WearableListenerService callbacks, and is interpreted on each side as a
-     * request to dismiss the corresponding notification.
-     */
-    private void buildMirroredNotifications(String phoneTitle, String watchTitle, String content) {
-        if (mGoogleApiClient.isConnected()) {
-            // Wearable notification
-            buildWearableOnlyNotification(watchTitle, content, Constants.BOTH_PATH);
-
-            // Local notification, with a pending intent for dismissal
-            buildLocalOnlyNotification(phoneTitle, content, Constants.BOTH_ID, true);
-        }
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        mGoogleApiClient.connect();
-    }
-
-    @Override
-    public void onStop() {
-        mGoogleApiClient.disconnect();
-        super.onStop();
-    }
-
-    @Override
-    public void onConnected(Bundle bundle) {
-    }
-
-    @Override
-    public void onConnectionSuspended(int i) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-        Log.e(TAG, "Failed to connect to Google API Client");
-    }
-
-    /**
-     * Returns a string built from the current time
-     */
-    private String now() {
-        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
-        return sdf.format(new Date())
-    }
-
-}
diff --git a/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/wearable/synchronizednotifications/DismissListener.java b/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/wearable/synchronizednotifications/DismissListener.java
new file mode 100644
index 0000000..8d5cca4
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/wearable/synchronizednotifications/DismissListener.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.synchronizednotifications;
+
+import static com.google.android.gms.wearable.PutDataRequest.WEAR_URI_SCHEME;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+import com.example.android.wearable.synchronizednotifications.common.Constants;
+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.wearable.DataApi;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+/**
+ * A {@link com.google.android.gms.wearable.WearableListenerService} that is invoked when certain
+ * notifications are dismissed from either the phone or watch.
+ */
+public class DismissListener extends WearableListenerService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
+        ResultCallback<DataApi.DeleteDataItemsResult> {
+
+    private static final String TAG = "DismissListener";
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        for (DataEvent dataEvent : dataEvents) {
+            if (dataEvent.getType() == DataEvent.TYPE_DELETED) {
+                if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
+                    // notification on the phone should be dismissed
+                    NotificationManagerCompat.from(this).cancel(Constants.BOTH_ID);
+                }
+            }
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (null != intent) {
+            String action = intent.getAction();
+            if (Constants.ACTION_DISMISS.equals(action)) {
+                // We need to dismiss the wearable notification. We delete the DataItem that
+                // created the notification to inform the wearable.
+                int notificationId = intent.getIntExtra(Constants.KEY_NOTIFICATION_ID, -1);
+                if (notificationId == Constants.BOTH_ID) {
+                    dismissWearableNotification(notificationId);
+                }
+            }
+        }
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    /**
+     * Removes the DataItem that was used to create a notification on the watch. By deleting the
+     * data item, a {@link com.google.android.gms.wearable.WearableListenerService} on the watch
+     * will be notified and the notification on the watch will be removed. To
+     * access the Wearable DataApi, we first need to ensure the GoogleApiClient is ready,
+     * which will then run the onConnected callback were the data removal is
+     * defined.
+     *
+     * @param id The ID of the notification that should be removed
+     */
+    private void dismissWearableNotification(final int id) {
+        mGoogleApiClient.connect();
+    }
+
+    @Override // ConnectionCallbacks
+    public void onConnected(Bundle bundle) {
+        final Uri dataItemUri =
+                new Uri.Builder().scheme(WEAR_URI_SCHEME).path(Constants.BOTH_PATH).build();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Deleting Uri: " + dataItemUri.toString());
+        }
+        Wearable.DataApi.deleteDataItems(
+                mGoogleApiClient, dataItemUri).setResultCallback(this);
+    }
+
+    @Override // ConnectionCallbacks
+    public void onConnectionSuspended(int i) {
+    }
+
+    @Override // OnConnectionFailedListener
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        Log.e(TAG, "Failed to connect to the Google API client");
+    }
+
+    @Override // ResultCallback<DataApi.DeleteDataItemsResult>
+    public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
+        if (!deleteDataItemsResult.getStatus().isSuccess()) {
+            Log.e(TAG, "dismissWearableNotification(): failed to delete DataItem");
+        }
+        mGoogleApiClient.disconnect();
+    }
+}
diff --git a/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/wearable/synchronizednotifications/SynchronizedNotificationsFragment.java b/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/wearable/synchronizednotifications/SynchronizedNotificationsFragment.java
new file mode 100644
index 0000000..240af9b
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/Application/src/main/java/com/example/android/wearable/synchronizednotifications/SynchronizedNotificationsFragment.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.synchronizednotifications;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import com.example.android.wearable.synchronizednotifications.common.Constants;
+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.wearable.DataApi;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+
+/**
+ * A simple fragment that presents three buttons that would trigger three different combinations of
+ * notifications on the handset and the watch:
+ * <ul>
+ * <li>The first button builds a simple local-only notification on the handset.</li>
+ * <li>The second one creates a wearable-only notification by putting a data item in the shared data
+ * store and having a {@link com.google.android.gms.wearable.WearableListenerService} listen for
+ * that on the wearable</li>
+ * <li>The third one creates a local notification and a wearable notification by combining the above
+ * two. It, however, demonstrates how one can set things up so that the dismissal of one
+ * notification results in the dismissal of the other one.</li>
+ * </ul>
+ */
+public class SynchronizedNotificationsFragment extends Fragment
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "SynchronizedNotificationsFragment";
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGoogleApiClient = new GoogleApiClient.Builder(this.getActivity())
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.btn_phone_only:
+                buildLocalOnlyNotification(getString(R.string.phone_only), now(),
+                        Constants.PHONE_ONLY_ID, false);
+                return true;
+            case R.id.btn_wear_only:
+                buildWearableOnlyNotification(getString(R.string.wear_only), now(),
+                        Constants.WATCH_ONLY_PATH);
+                return true;
+            case R.id.btn_different:
+                buildMirroredNotifications(getString(R.string.phone_both), getString(R.string.watch_both), now());
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Builds a local-only notification for the handset. This is achieved by using
+     * <code>setLocalOnly(true)</code>. If <code>withDismissal</code> is set to <code>true</code>, a
+     * {@link android.app.PendingIntent} will be added to handle the dismissal of notification to
+     * be able to remove the mirrored notification on the wearable.
+     */
+    private void buildLocalOnlyNotification(String title, String content, int notificationId,
+                                            boolean withDismissal) {
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(this.getActivity());
+        builder.setContentTitle(title)
+                .setContentText(content)
+                .setLocalOnly(true)
+                .setSmallIcon(R.drawable.ic_launcher);
+
+        if (withDismissal) {
+            Intent dismissIntent = new Intent(Constants.ACTION_DISMISS);
+            dismissIntent.putExtra(Constants.KEY_NOTIFICATION_ID, Constants.BOTH_ID);
+            PendingIntent pendingIntent = PendingIntent
+                    .getService(this.getActivity(), 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            builder.setDeleteIntent(pendingIntent);
+        }
+        NotificationManagerCompat.from(this.getActivity()).notify(notificationId, builder.build());
+    }
+
+    /**
+     * Builds a DataItem that on the wearable will be interpreted as a request to show a
+     * notification. The result will be a notification that only shows up on the wearable.
+     */
+    private void buildWearableOnlyNotification(String title, String content, String path) {
+        if (mGoogleApiClient.isConnected()) {
+            PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path);
+            putDataMapRequest.getDataMap().putString(Constants.KEY_CONTENT, content);
+            putDataMapRequest.getDataMap().putString(Constants.KEY_TITLE, title);
+            PutDataRequest request = putDataMapRequest.asPutDataRequest();
+            Wearable.DataApi.putDataItem(mGoogleApiClient, request)
+                    .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
+                        @Override
+                        public void onResult(DataApi.DataItemResult dataItemResult) {
+                            if (!dataItemResult.getStatus().isSuccess()) {
+                                Log.e(TAG, "buildWatchOnlyNotification(): Failed to set the data, "
+                                        + "status: " + dataItemResult.getStatus().getStatusCode());
+                            }
+                        }
+                    });
+        } else {
+            Log.e(TAG, "buildWearableOnlyNotification(): no Google API Client connection");
+        }
+    }
+
+    /**
+     * Builds a local notification and sets a DataItem that will be interpreted by the wearable as
+     * a request to build a notification on the wearable as as well. The two notifications show
+     * different messages.
+     * Dismissing either of the notifications will result in dismissal of the other; this is
+     * achieved by creating a {@link android.app.PendingIntent} that results in removal of
+     * the DataItem that created the watch notification. The deletion of the DataItem is observed on
+     * both sides, using WearableListenerService callbacks, and is interpreted on each side as a
+     * request to dismiss the corresponding notification.
+     */
+    private void buildMirroredNotifications(String phoneTitle, String watchTitle, String content) {
+        if (mGoogleApiClient.isConnected()) {
+            // Wearable notification
+            buildWearableOnlyNotification(watchTitle, content, Constants.BOTH_PATH);
+
+            // Local notification, with a pending intent for dismissal
+            buildLocalOnlyNotification(phoneTitle, content, Constants.BOTH_ID, true);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mGoogleApiClient.connect();
+    }
+
+    @Override
+    public void onStop() {
+        mGoogleApiClient.disconnect();
+        super.onStop();
+    }
+
+    @Override
+    public void onConnected(Bundle bundle) {
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        Log.e(TAG, "Failed to connect to Google API Client");
+    }
+
+    /**
+     * Returns a string built from the current time
+     */
+    private String now() {
+        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
+        return sdf.format(new Date());
+    }
+
+}
diff --git a/wearable/wear/SynchronizedNotifications/Application/tests/AndroidManifest.xml b/wearable/wear/SynchronizedNotifications/Application/tests/AndroidManifest.xml
deleted file mode 100644
index 5f8d223..0000000
--- a/wearable/wear/SynchronizedNotifications/Application/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2013 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.example.android.synchronizednotifications.tests"
-          android:versionCode="1"
-          android:versionName="1.0">
-
-    <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
-
-    <!-- We add an application tag here just so that we can indicate that
-         this package needs to link against the android.test library,
-         which is needed when building test cases. -->
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <!--
-    Specifies the instrumentation test runner used to run the tests.
-    -->
-    <instrumentation
-            android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="com.example.android.synchronizednotifications"
-            android:label="Tests for com.example.android.synchronizednotifications" />
-
-</manifest>
diff --git a/wearable/wear/SynchronizedNotifications/Application/tests/src/com/example/android/synchronizednotifications/tests/SampleTests.java b/wearable/wear/SynchronizedNotifications/Application/tests/src/com/example/android/synchronizednotifications/tests/SampleTests.java
deleted file mode 100644
index 7f8df42..0000000
--- a/wearable/wear/SynchronizedNotifications/Application/tests/src/com/example/android/synchronizednotifications/tests/SampleTests.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package com.example.android.synchronizednotifications.tests;
-
-import com.example.android.synchronizednotifications.*;
-
-import android.test.ActivityInstrumentationTestCase2;
-
-/**
-* Tests for SynchronizedNotifications sample.
-*/
-public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
-
-    private MainActivity mTestActivity;
-    private SynchronizedNotificationsFragment mTestFragment;
-
-    public SampleTests() {
-        super(MainActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        // Starts the activity under test using the default Intent with:
-        // action = {@link Intent#ACTION_MAIN}
-        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
-        // All other fields are null or empty.
-        mTestActivity = getActivity();
-        mTestFragment = (SynchronizedNotificationsFragment)
-            mTestActivity.getSupportFragmentManager().getFragments().get(1);
-    }
-
-    /**
-    * Test if the test fixture has been set up correctly.
-    */
-    public void testPreconditions() {
-        //Try to add a message to add context to your assertions. These messages will be shown if
-        //a tests fails and make it easy to understand why a test failed
-        assertNotNull("mTestActivity is null", mTestActivity);
-        assertNotNull("mTestFragment is null", mTestFragment);
-    }
-
-}
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/wearable/wear/SynchronizedNotifications/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to wearable/wear/SynchronizedNotifications/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/wearable/wear/SynchronizedNotifications/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to wearable/wear/SynchronizedNotifications/LICENSE
diff --git a/wearable/wear/SynchronizedNotifications/Shared/build.gradle b/wearable/wear/SynchronizedNotifications/Shared/build.gradle
deleted file mode 100644
index dda6f07..0000000
--- a/wearable/wear/SynchronizedNotifications/Shared/build.gradle
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-buildscript {
-    repositories {
-        mavenCentral()
-    }
-
-    dependencies {
-        classpath 'com.android.tools.build:gradle:0.12.+'
-    }
-}
-
-apply plugin: 'android-library'
-
-// The sample build uses multiple directories to
-// keep boilerplate and common code separate from
-// the main sample code.
-List<String> dirs = [
-    'main',     // main sample code; look here for the interesting stuff.
-    'common',   // components that are reused by multiple samples
-    'template'] // boilerplate code that is generated by the sample template process
-
-android {
-    compileSdkVersion "android-L"
-
-    buildToolsVersion '20'
-
-    sourceSets {
-        main {
-            dirs.each { dir ->
-                java.srcDirs "src/${dir}/java"
-                res.srcDirs "src/${dir}/res"
-            }
-        }
-
-    }
-}
diff --git a/wearable/wear/SynchronizedNotifications/Shared/src/main/AndroidManifest.xml b/wearable/wear/SynchronizedNotifications/Shared/src/main/AndroidManifest.xml
index 7235ace..5362fc7 100644
--- a/wearable/wear/SynchronizedNotifications/Shared/src/main/AndroidManifest.xml
+++ b/wearable/wear/SynchronizedNotifications/Shared/src/main/AndroidManifest.xml
@@ -16,7 +16,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.synchronizednotifications.common">
+    package="com.example.android.wearable.synchronizednotifications.common">
 
     <application android:allowBackup="true"
         android:label="@string/app_name">
diff --git a/wearable/wear/SynchronizedNotifications/Shared/src/main/java/com/example/android/synchronizednotifications/common/Constants.java b/wearable/wear/SynchronizedNotifications/Shared/src/main/java/com/example/android/synchronizednotifications/common/Constants.java
deleted file mode 100644
index 5a45583..0000000
--- a/wearable/wear/SynchronizedNotifications/Shared/src/main/java/com/example/android/synchronizednotifications/common/Constants.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.android.synchronizednotifications.common;
-
-/**
- * Constants that are used in both the Application and the Wearable modules.
- */
-public final class Constants {
-
-    private Constants() {};
-
-    public static final int WATCH_ONLY_ID = 2;
-    public static final int PHONE_ONLY_ID = 3;
-    public static final int BOTH_ID = 4;
-
-    public static final String BOTH_PATH = "/both";
-    public static final String WATCH_ONLY_PATH = "/watch-only";
-    public static final String KEY_NOTIFICATION_ID = "notification-id";
-    public static final String KEY_TITLE = "title";
-    public static final String KEY_CONTENT = "content";
-
-    public static final String ACTION_DISMISS
-            = "com.example.android.synchronizednotifications.DISMISS";
-}
diff --git a/wearable/wear/SynchronizedNotifications/Shared/src/main/java/com/example/android/wearable/synchronizednotifications/common/Constants.java b/wearable/wear/SynchronizedNotifications/Shared/src/main/java/com/example/android/wearable/synchronizednotifications/common/Constants.java
new file mode 100644
index 0000000..2e1d46c
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/Shared/src/main/java/com/example/android/wearable/synchronizednotifications/common/Constants.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.wearable.synchronizednotifications.common;
+
+/**
+ * Constants that are used in both the Application and the Wearable modules.
+ */
+public final class Constants {
+
+    private Constants() {};
+
+    public static final int WATCH_ONLY_ID = 2;
+    public static final int PHONE_ONLY_ID = 3;
+    public static final int BOTH_ID = 4;
+
+    public static final String BOTH_PATH = "/both";
+    public static final String WATCH_ONLY_PATH = "/watch-only";
+    public static final String KEY_NOTIFICATION_ID = "notification-id";
+    public static final String KEY_TITLE = "title";
+    public static final String KEY_CONTENT = "content";
+
+    public static final String ACTION_DISMISS
+            = "com.example.android.wearable.synchronizednotifications.DISMISS";
+}
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/build.gradle b/wearable/wear/SynchronizedNotifications/Wearable/build.gradle
deleted file mode 100644
index 529a093..0000000
--- a/wearable/wear/SynchronizedNotifications/Wearable/build.gradle
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-buildscript {
-    repositories {
-        mavenCentral()
-    }
-
-    dependencies {
-        classpath 'com.android.tools.build:gradle:0.12.+'
-    }
-}
-
-apply plugin: 'com.android.application'
-
-
-
-dependencies {
-    compile 'com.google.android.gms:play-services:5.0.+@aar'
-    compile 'com.android.support:support-v13:20.0.+'
-    compile 'com.google.android.support:wearable:1.0.+'
-    compile project(':Shared')
-}
-
-// The sample build uses multiple directories to
-// keep boilerplate and common code separate from
-// the main sample code.
-List<String> dirs = [
-    'main',     // main sample code; look here for the interesting stuff.
-    'common',   // components that are reused by multiple samples
-    'template'] // boilerplate code that is generated by the sample template process
-
-android {
-    compileSdkVersion 'android-L'
-
-    buildToolsVersion '20'
-
-    buildTypes {
-        release {
-            runProguard false
-            proguardFiles getDefaultProguardFile('proguard-android.txt')
-        }
-    }
-    lintOptions {
-        abortOnError false
-    }
-    sourceSets {
-        main {
-            dirs.each { dir ->
-                java.srcDirs "src/${dir}/java"
-                res.srcDirs "src/${dir}/res"
-            }
-        }
-        androidTest.setRoot('tests')
-        androidTest.java.srcDirs = ['tests/src']
-
-    }
-}
-
-// BEGIN_EXCLUDE
-// Tasks below this line will be hidden from release output
-
-task preflight (dependsOn: parent.preflight) {
-    project.afterEvaluate {
-        // Inject a preflight task into each variant so we have a place to hook tasks
-        // that need to run before any of the android build tasks.
-        //
-        android.applicationVariants.each { variant ->
-            tasks.getByPath("prepare${variant.name.capitalize()}Dependencies").dependsOn preflight
-        }
-    }
-}
-
-// END_EXCLUDE
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml b/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml
index 97702ce..f9b0d9c 100644
--- a/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml
@@ -17,10 +17,10 @@
 
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.synchronizednotifications">
+    package="com.example.android.wearable.synchronizednotifications">
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="20" />
+        android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -46,7 +46,7 @@
             </intent-filter>
             <intent-filter>
                 <action
-                    android:name="com.example.android.synchronizednotifications.DISMISS" />
+                    android:name="com.example.android.wearable.synchronizednotifications.DISMISS" />
             </intent-filter>
         </service>
 
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/synchronizednotifications/NotificationUpdateService.java b/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/synchronizednotifications/NotificationUpdateService.java
deleted file mode 100644
index b8963cd..0000000
--- a/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/synchronizednotifications/NotificationUpdateService.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.synchronizednotifications;
-
-import static com.google.android.gms.wearable.PutDataRequest.WEAR_URI_SCHEME;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.example.android.synchronizednotifications.common.Constants;
-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.wearable.DataApi;
-import com.google.android.gms.wearable.DataEvent;
-import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.DataMap;
-import com.google.android.gms.wearable.DataMapItem;
-import com.google.android.gms.wearable.PutDataMapRequest;
-import com.google.android.gms.wearable.Wearable;
-import com.google.android.gms.wearable.WearableListenerService;
-
-/**
- * A {@link com.google.android.gms.wearable.WearableListenerService} that will be invoked when a
- * DataItem is added or deleted. The creation of a new DataItem will be interpreted as a request to
- * create a new notification and the removal of that DataItem is interpreted as a request to
- * dismiss that notification.
- */
-public class NotificationUpdateService extends WearableListenerService
-        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
-        ResultCallback<DataApi.DeleteDataItemsResult> {
-
-    private static final String TAG = "NotificationUpdate";
-    private GoogleApiClient mGoogleApiClient;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mGoogleApiClient = new GoogleApiClient.Builder(this)
-                .addApi(Wearable.API)
-                .addConnectionCallbacks(this)
-                .addOnConnectionFailedListener(this)
-                .build();
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        if (null != intent) {
-            String action = intent.getAction();
-            if (Constants.ACTION_DISMISS.equals(action)) {
-                // We need to dismiss the wearable notification. We delete the data item that
-                // created the notification and that is how we inform the phone
-                int notificationId = intent.getIntExtra(Constants.KEY_NOTIFICATION_ID, -1);
-                if (notificationId == Constants.BOTH_ID) {
-                    dismissPhoneNotification(notificationId);
-                }
-            }
-        }
-        return super.onStartCommand(intent, flags, startId);
-    }
-
-    /**
-     * Dismisses the phone notification, via a {@link android.app.PendingIntent} that is triggered
-     * when the user dismisses the local notification. Deleting the corresponding data item notifies
-     * the {@link com.google.android.gms.wearable.WearableListenerService} on the phone that the
-     * matching notification on the phone side should be removed.
-     */
-    private void dismissPhoneNotification(int id) {
-        mGoogleApiClient.connect();
-    }
-
-    @Override
-    public void onDataChanged(DataEventBuffer dataEvents) {
-        for (DataEvent dataEvent : dataEvents) {
-            if (dataEvent.getType() == DataEvent.TYPE_CHANGED) {
-                DataMap dataMap = DataMapItem.fromDataItem(dataEvent.getDataItem()).getDataMap();
-                String content = dataMap.getString(Constants.KEY_CONTENT);
-                String title = dataMap.getString(Constants.KEY_TITLE);
-                if (Constants.WATCH_ONLY_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
-                    buildWearableOnlyNotification(title, content, false);
-                } else if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
-                    buildWearableOnlyNotification(title, content, true);
-                }
-            } else if (dataEvent.getType() == DataEvent.TYPE_DELETED) {
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "DataItem deleted: " + dataEvent.getDataItem().getUri().getPath());
-                }
-                if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
-                    // Dismiss the corresponding notification
-                    ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                            .cancel(Constants.WATCH_ONLY_ID);
-                }
-            }
-        }
-    }
-
-    /**
-     * Builds a simple notification on the wearable.
-     */
-    private void buildWearableOnlyNotification(String title, String content,
-            boolean withDismissal) {
-        Notification.Builder builder = new Notification.Builder(this)
-                .setSmallIcon(R.drawable.ic_launcher)
-                .setContentTitle(title)
-                .setContentText(content);
-
-        if (withDismissal) {
-            Intent dismissIntent = new Intent(Constants.ACTION_DISMISS);
-            dismissIntent.putExtra(Constants.KEY_NOTIFICATION_ID, Constants.BOTH_ID);
-            PendingIntent pendingIntent = PendingIntent
-                    .getService(this, 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-            builder.setDeleteIntent(pendingIntent);
-        }
-
-        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
-                .notify(Constants.WATCH_ONLY_ID, builder.build());
-    }
-
-    @Override
-    public void onConnected(Bundle bundle) {
-        final Uri dataItemUri =
-                new Uri.Builder().scheme(WEAR_URI_SCHEME).path(Constants.BOTH_PATH).build();
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Deleting Uri: " + dataItemUri.toString());
-        }
-        Wearable.DataApi.deleteDataItems(
-                mGoogleApiClient, dataItemUri).setResultCallback(this);
-    }
-
-    @Override
-    public void onConnectionSuspended(int i) {
-    }
-
-    @Override
-    public void onConnectionFailed(ConnectionResult connectionResult) {
-    }
-
-    @Override
-    public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
-        if (!deleteDataItemsResult.getStatus().isSuccess()) {
-            Log.e(TAG, "dismissWearableNotification(): failed to delete DataItem");
-        }
-        mGoogleApiClient.disconnect();
-    }
-}
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/synchronizednotifications/WearableActivity.java b/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/synchronizednotifications/WearableActivity.java
deleted file mode 100644
index 28736ad..0000000
--- a/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/synchronizednotifications/WearableActivity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.example.android.synchronizednotifications;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class WearableActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_wearable);
-    }
-}
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/wearable/synchronizednotifications/NotificationUpdateService.java b/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/wearable/synchronizednotifications/NotificationUpdateService.java
new file mode 100644
index 0000000..8b46bf3
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/wearable/synchronizednotifications/NotificationUpdateService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.synchronizednotifications;
+
+import static com.google.android.gms.wearable.PutDataRequest.WEAR_URI_SCHEME;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.example.android.wearable.synchronizednotifications.common.Constants;
+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.wearable.DataApi;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+/**
+ * A {@link com.google.android.gms.wearable.WearableListenerService} that will be invoked when a
+ * DataItem is added or deleted. The creation of a new DataItem will be interpreted as a request to
+ * create a new notification and the removal of that DataItem is interpreted as a request to
+ * dismiss that notification.
+ */
+public class NotificationUpdateService extends WearableListenerService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
+        ResultCallback<DataApi.DeleteDataItemsResult> {
+
+    private static final String TAG = "NotificationUpdate";
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (null != intent) {
+            String action = intent.getAction();
+            if (Constants.ACTION_DISMISS.equals(action)) {
+                // We need to dismiss the wearable notification. We delete the data item that
+                // created the notification and that is how we inform the phone
+                int notificationId = intent.getIntExtra(Constants.KEY_NOTIFICATION_ID, -1);
+                if (notificationId == Constants.BOTH_ID) {
+                    dismissPhoneNotification(notificationId);
+                }
+            }
+        }
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    /**
+     * Dismisses the phone notification, via a {@link android.app.PendingIntent} that is triggered
+     * when the user dismisses the local notification. Deleting the corresponding data item notifies
+     * the {@link com.google.android.gms.wearable.WearableListenerService} on the phone that the
+     * matching notification on the phone side should be removed.
+     */
+    private void dismissPhoneNotification(int id) {
+        mGoogleApiClient.connect();
+    }
+
+    @Override
+    public void onDataChanged(DataEventBuffer dataEvents) {
+        for (DataEvent dataEvent : dataEvents) {
+            if (dataEvent.getType() == DataEvent.TYPE_CHANGED) {
+                DataMap dataMap = DataMapItem.fromDataItem(dataEvent.getDataItem()).getDataMap();
+                String content = dataMap.getString(Constants.KEY_CONTENT);
+                String title = dataMap.getString(Constants.KEY_TITLE);
+                if (Constants.WATCH_ONLY_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
+                    buildWearableOnlyNotification(title, content, false);
+                } else if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
+                    buildWearableOnlyNotification(title, content, true);
+                }
+            } else if (dataEvent.getType() == DataEvent.TYPE_DELETED) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "DataItem deleted: " + dataEvent.getDataItem().getUri().getPath());
+                }
+                if (Constants.BOTH_PATH.equals(dataEvent.getDataItem().getUri().getPath())) {
+                    // Dismiss the corresponding notification
+                    ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                            .cancel(Constants.WATCH_ONLY_ID);
+                }
+            }
+        }
+    }
+
+    /**
+     * Builds a simple notification on the wearable.
+     */
+    private void buildWearableOnlyNotification(String title, String content,
+            boolean withDismissal) {
+        Notification.Builder builder = new Notification.Builder(this)
+                .setSmallIcon(R.drawable.ic_launcher)
+                .setContentTitle(title)
+                .setContentText(content);
+
+        if (withDismissal) {
+            Intent dismissIntent = new Intent(Constants.ACTION_DISMISS);
+            dismissIntent.putExtra(Constants.KEY_NOTIFICATION_ID, Constants.BOTH_ID);
+            PendingIntent pendingIntent = PendingIntent
+                    .getService(this, 0, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            builder.setDeleteIntent(pendingIntent);
+        }
+
+        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+                .notify(Constants.WATCH_ONLY_ID, builder.build());
+    }
+
+    @Override
+    public void onConnected(Bundle bundle) {
+        final Uri dataItemUri =
+                new Uri.Builder().scheme(WEAR_URI_SCHEME).path(Constants.BOTH_PATH).build();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Deleting Uri: " + dataItemUri.toString());
+        }
+        Wearable.DataApi.deleteDataItems(
+                mGoogleApiClient, dataItemUri).setResultCallback(this);
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+    }
+
+    @Override
+    public void onResult(DataApi.DeleteDataItemsResult deleteDataItemsResult) {
+        if (!deleteDataItemsResult.getStatus().isSuccess()) {
+            Log.e(TAG, "dismissWearableNotification(): failed to delete DataItem");
+        }
+        mGoogleApiClient.disconnect();
+    }
+}
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/wearable/synchronizednotifications/WearableActivity.java b/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/wearable/synchronizednotifications/WearableActivity.java
new file mode 100644
index 0000000..6ef2f1b
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/Wearable/src/main/java/com/example/android/wearable/synchronizednotifications/WearableActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.wearable.synchronizednotifications;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class WearableActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_wearable);
+    }
+}
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/res/values/strings.xml b/wearable/wear/SynchronizedNotifications/Wearable/src/main/res/values/strings.xml
index 4240ff5..aa51a71 100644
--- a/wearable/wear/SynchronizedNotifications/Wearable/src/main/res/values/strings.xml
+++ b/wearable/wear/SynchronizedNotifications/Wearable/src/main/res/values/strings.xml
@@ -15,6 +15,6 @@
 -->
 
 <resources>
-    <string name="app_name">Sample Wearable app</string>
+    <string name="app_name">Synchronized Notifications</string>
 </resources>
 
diff --git a/wearable/wear/SynchronizedNotifications/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/SynchronizedNotifications/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/SynchronizedNotifications/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/SynchronizedNotifications/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/SynchronizedNotifications/screenshots/different_notifications_phone.png b/wearable/wear/SynchronizedNotifications/screenshots/different_notifications_phone.png
new file mode 100644
index 0000000..cab31e0
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/screenshots/different_notifications_phone.png
Binary files differ
diff --git a/wearable/wear/SynchronizedNotifications/screenshots/different_notifications_wearable.png b/wearable/wear/SynchronizedNotifications/screenshots/different_notifications_wearable.png
new file mode 100644
index 0000000..97da715
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/screenshots/different_notifications_wearable.png
Binary files differ
diff --git a/wearable/wear/SynchronizedNotifications/screenshots/notification_options.png b/wearable/wear/SynchronizedNotifications/screenshots/notification_options.png
new file mode 100644
index 0000000..8cd0095
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/screenshots/notification_options.png
Binary files differ
diff --git a/wearable/wear/SynchronizedNotifications/screenshots/watch_only_notification.png b/wearable/wear/SynchronizedNotifications/screenshots/watch_only_notification.png
new file mode 100644
index 0000000..34e950f
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/screenshots/watch_only_notification.png
Binary files differ
diff --git a/wearable/wear/SynchronizedNotifications/screenshots/web-icon.png b/wearable/wear/SynchronizedNotifications/screenshots/web-icon.png
new file mode 100755
index 0000000..2f5f709
--- /dev/null
+++ b/wearable/wear/SynchronizedNotifications/screenshots/web-icon.png
Binary files differ
diff --git a/wearable/wear/SynchronizedNotifications/settings.gradle b/wearable/wear/SynchronizedNotifications/settings.gradle
index fc20be4..8522c57 100644
--- a/wearable/wear/SynchronizedNotifications/settings.gradle
+++ b/wearable/wear/SynchronizedNotifications/settings.gradle
@@ -1,4 +1 @@
-
-
-
 include ':Application', ':Wearable', ':Shared'
diff --git a/wearable/wear/SynchronizedNotifications/template-params.xml b/wearable/wear/SynchronizedNotifications/template-params.xml
index 3c49b5a..972ab82 100644
--- a/wearable/wear/SynchronizedNotifications/template-params.xml
+++ b/wearable/wear/SynchronizedNotifications/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>SynchronizedNotifications</name>
     <group>Wearable</group>
-    <package>com.example.android.synchronizednotifications</package>
+    <package>com.example.android.wearable.synchronizednotifications</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -39,8 +39,79 @@
 
     <template src="base"/>
     <template src="SimpleView"/>
-    <template src="Wear"/>
+    <template src="WearPlusShared"/>
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/web-icon.png</icon>
+        <screenshots>
+            <img>screenshots/different_notifications_phone.png</img>
+            <img>screenshots/different_notifications_wearable.png</img>
+            <img>screenshots/notification_options.png</img>
+            <img>screenshots/watch_only_notification.png</img>
+        </screenshots>
+        <api_refs>
+            <android>com.google.android.gms.wearable.DataApi</android>
+            <android>com.google.android.gms.wearable.Wearable</android>
+            <android>com.google.android.gms.wearable.WearableListenerService</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A basic sample showing how to use simple or synchronized notifications.
+This allows users to dismiss events from either their phone or wearable device simultaneously.
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+The [DataAPI][1] exposes an API for components to read or write data items and assets between
+the handhelds and wearables. A [DataItem][2] is synchronized across all devices in an Android Wear network.
+It is possible to set data items while not connected to any nodes. Those data items will be synchronized
+when the nodes eventually come online.
+
+This example presents three buttons that would trigger three different combinations of
+notifications on the handset and the watch:
+
+1. The first button builds a simple local-only notification on the handset.
+2. The second one creates a wearable-only notification by putting a data item in the shared data
+store and having a [com.google.android.gms.wearable.WearableListenerService][3] listen for
+that on the wearable.
+3. The third one creates a local notification and a wearable notification by combining the above
+two. It, however, demonstrates how one can set things up so that the dismissal of one
+notification results in the dismissal of the other one.
+
+In the #2 and #3 items, the following code is used to synchronize the data between the handheld
+and the wearable devices using DataAPI.
+
+```java
+PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path);
+putDataMapRequest.getDataMap().putString(Constants.KEY_CONTENT, content);
+putDataMapRequest.getDataMap().putString(Constants.KEY_TITLE, title);
+PutDataRequest request = putDataMapRequest.asPutDataRequest();
+Wearable.DataApi.putDataItem(mGoogleApiClient, request)
+        .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
+            @Override
+            public void onResult(DataApi.DataItemResult dataItemResult) {
+                if (!dataItemResult.getStatus().isSuccess()) {
+                    Log.e(TAG, "buildWatchOnlyNotification(): Failed to set the data, "
+                            + "status: " + dataItemResult.getStatus().getStatusCode());
+                }
+            }
+        });
+```
+
+[1]: http://developer.android.com/reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient%2C%20com.google.android.gms.wearable.PutDataRequest)
+[2]: http://developer.android.com/reference/com/google/android/gms/wearable/DataItem.html
+[3]: https://developer.android.com/reference/com/google/android/gms/wearable/WearableListenerService.html
+]]>
+        </intro>
+    </metadata>
 </sample>
diff --git a/wearable/wear/Timer/Application/src/main/AndroidManifest.xml b/wearable/wear/Timer/Application/src/main/AndroidManifest.xml
index b8ebd74..2c60ab9 100644
--- a/wearable/wear/Timer/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Timer/Application/src/main/AndroidManifest.xml
@@ -16,12 +16,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.timer">
+    package="com.example.android.wearable.timer">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
         android:label="@string/app_name">
     </application>
 
diff --git a/wearable/wear/Timer/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/Timer/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/Timer/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Timer/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/Timer/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/Timer/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Timer/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/Timer/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/Timer/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Timer/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/Timer/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/Timer/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/Timer/Shared/.gitignore b/wearable/wear/Timer/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/Timer/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/Timer/Shared/src/main/AndroidManifest.xml b/wearable/wear/Timer/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index c332a84..0000000
--- a/wearable/wear/Timer/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.timer.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/Timer/Shared/src/main/res/values/strings.xml b/wearable/wear/Timer/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/Timer/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml
index 3dabadc..364fb5a 100644
--- a/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.timer" >
+        package="com.example.android.wearable.timer" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/SetTimerActivity.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/SetTimerActivity.java
deleted file mode 100644
index 5285589..0000000
--- a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/SetTimerActivity.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.timer;
-
-import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.provider.AlarmClock;
-import android.support.wearable.view.WearableListView;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.example.android.timer.util.Constants;
-import com.example.android.timer.util.TimerFormat;
-
-/** This class sets a timer. */
-public class SetTimerActivity extends Activity implements WearableListView.ClickListener {
-
-    public static final int NUMBER_OF_TIMES = 10;
-    public static final String TAG = "SetTimerActivity";
-
-    private ListViewItem[] mTimeOptions = new ListViewItem[NUMBER_OF_TIMES];
-    private WearableListView mWearableListView;
-
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        int paramLength = getIntent().getIntExtra(AlarmClock.EXTRA_LENGTH, 0);
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "SetTimerActivity:onCreate=" + paramLength);
-        }
-        if (paramLength > 0 && paramLength <= 86400) {
-            long durationMillis = paramLength * 1000;
-            setupTimer(durationMillis);
-            finish();
-            return;
-        }
-
-        Resources res = getResources();
-        for (int i = 0; i < NUMBER_OF_TIMES; i++) {
-            mTimeOptions[i] = new ListViewItem(
-                    res.getQuantityString(R.plurals.timer_minutes, i + 1, i + 1),
-                    (i + 1) * 60 * 1000);
-        }
-
-        setContentView(R.layout.timer_set_timer);
-
-        // Initialize a simple list of countdown time options.
-        mWearableListView = (WearableListView) findViewById(R.id.times_list_view);
-        mWearableListView.setAdapter(new TimerWearableListViewAdapter(this));
-        mWearableListView.setClickListener(this);
-    }
-
-    /**
-     * Sets up an alarm (and an associated notification) to go off after <code>duration</code>
-     * milliseconds.
-     */
-    private void setupTimer(long duration) {
-        NotificationManager notifyMgr =
-                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
-
-        // Delete dataItem and cancel a potential old countdown.
-        cancelCountdown(notifyMgr);
-
-        // Build notification and set it.
-        notifyMgr.notify(Constants.NOTIFICATION_TIMER_COUNTDOWN, buildNotification(duration));
-
-        // Register with the alarm manager to display a notification when the timer is done.
-        registerWithAlarmManager(duration);
-
-        finish();
-    }
-
-    @Override
-    public void onClick(WearableListView.ViewHolder holder) {
-        long duration = mTimeOptions[holder.getPosition()].duration;
-        setupTimer(duration);
-    }
-
-    @Override
-    public void onTopEmptyRegionClick() {
-    }
-
-    private void registerWithAlarmManager(long duration) {
-        // Get the alarm manager.
-        AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-
-        // Create intent that gets fired when timer expires.
-        Intent intent = new Intent(Constants.ACTION_SHOW_ALARM, null, this,
-                TimerNotificationService.class);
-        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
-
-        // Calculate the time when it expires.
-        long wakeupTime = System.currentTimeMillis() + duration;
-
-        // Schedule an alarm.
-        alarm.setExact(AlarmManager.RTC_WAKEUP, wakeupTime, pendingIntent);
-    }
-
-    /**
-     * Build a notification including different actions and other various setup and return it.
-     *
-     * @param duration the duration of the timer.
-     * @return the notification to display.
-     */
-
-    private Notification buildNotification(long duration) {
-        // Intent to restart a timer.
-        Intent restartIntent = new Intent(Constants.ACTION_RESTART_ALARM, null, this,
-                TimerNotificationService.class);
-        PendingIntent pendingIntentRestart = PendingIntent
-                .getService(this, 0, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        // Intent to delete a timer.
-        Intent deleteIntent = new Intent(Constants.ACTION_DELETE_ALARM, null, this,
-                TimerNotificationService.class);
-        PendingIntent pendingIntentDelete = PendingIntent
-                .getService(this, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        // Create countdown notification using a chronometer style.
-        return new Notification.Builder(this)
-                .setSmallIcon(R.drawable.ic_cc_alarm)
-                .setContentTitle(getString(R.string.timer_time_left))
-                .setContentText(TimerFormat.getTimeString(duration))
-                .setUsesChronometer(true)
-                .setWhen(System.currentTimeMillis() + duration)
-                .addAction(R.drawable.ic_cc_alarm, getString(R.string.timer_restart),
-                        pendingIntentRestart)
-                .addAction(R.drawable.ic_cc_alarm, getString(R.string.timer_delete),
-                        pendingIntentDelete)
-                .setDeleteIntent(pendingIntentDelete)
-                .setLocalOnly(true)
-                .build();
-    }
-
-    /**
-     * Cancels an old countdown and deletes the dataItem.
-     *
-     * @param notifyMgr the notification manager.
-     */
-    private void cancelCountdown(NotificationManager notifyMgr) {
-        notifyMgr.cancel(Constants.NOTIFICATION_TIMER_EXPIRED);
-    }
-
-    /** Model class for the listview. */
-    private static class ListViewItem {
-
-        // Duration in milliseconds.
-        long duration;
-        // Label to display.
-        private String label;
-
-        public ListViewItem(String label, long duration) {
-            this.label = label;
-            this.duration = duration;
-        }
-
-        @Override
-        public String toString() {
-            return label;
-        }
-    }
-
-    private final class TimerWearableListViewAdapter extends WearableListView.Adapter {
-        private final Context mContext;
-        private final LayoutInflater mInflater;
-
-        private TimerWearableListViewAdapter(Context context) {
-            mContext = context;
-            mInflater = LayoutInflater.from(context);
-        }
-
-        @Override
-        public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-            return new WearableListView.ViewHolder(
-                    mInflater.inflate(R.layout.timer_list_item, null));
-        }
-
-        @Override
-        public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
-            TextView view = (TextView) holder.itemView.findViewById(R.id.time_text);
-            view.setText(mTimeOptions[position].label);
-            holder.itemView.setTag(position);
-        }
-
-        @Override
-        public int getItemCount() {
-            return NUMBER_OF_TIMES;
-        }
-    }
-
-}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/TimerNotificationService.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/TimerNotificationService.java
deleted file mode 100644
index 709b949..0000000
--- a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/TimerNotificationService.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.timer;
-
-import android.app.AlarmManager;
-import android.app.IntentService;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-import com.example.android.timer.util.Constants;
-
-/**
- * Service class that manages notifications of the timer.
- */
-public class TimerNotificationService extends IntentService {
-
-    public static final String TAG = "TimerNotificationSvc";
-
-    public TimerNotificationService() {
-        super(TAG);
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "onHandleIntent called with intent: " + intent);
-        }
-        String action = intent.getAction();
-        if (Constants.ACTION_SHOW_ALARM.equals(action)) {
-            showTimerDoneNotification();
-        } else if (Constants.ACTION_DELETE_ALARM.equals(action)) {
-            deleteTimer();
-        } else if (Constants.ACTION_RESTART_ALARM.equals(action)) {
-            restartAlarm();
-        } else {
-            throw new IllegalStateException("Undefined constant used: " + action);
-        }
-    }
-
-    private void restartAlarm() {
-        Intent dialogIntent = new Intent(this, SetTimerActivity.class);
-        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        startActivity(dialogIntent);
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Timer restarted.");
-        }
-    }
-
-    private void deleteTimer() {
-        cancelCountdownNotification();
-
-        AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-        Intent intent = new Intent(Constants.ACTION_SHOW_ALARM, null, this,
-                TimerNotificationService.class);
-        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
-        alarm.cancel(pendingIntent);
-
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Timer deleted.");
-        }
-    }
-
-    private void cancelCountdownNotification() {
-        NotificationManager notifyMgr =
-                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
-        notifyMgr.cancel(Constants.NOTIFICATION_TIMER_COUNTDOWN);
-    }
-
-    private void showTimerDoneNotification() {
-        // Cancel the countdown notification to show the "timer done" notification.
-        cancelCountdownNotification();
-
-        // Create an intent to restart a timer.
-        Intent restartIntent = new Intent(Constants.ACTION_RESTART_ALARM, null, this,
-                TimerNotificationService.class);
-        PendingIntent pendingIntentRestart = PendingIntent
-                .getService(this, 0, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        // Create notification that timer has expired.
-        NotificationManager notifyMgr =
-                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
-        Notification notif = new Notification.Builder(this)
-                .setSmallIcon(R.drawable.ic_cc_alarm)
-                .setContentTitle(getString(R.string.timer_done))
-                .setContentText(getString(R.string.timer_done))
-                .setUsesChronometer(true)
-                .setWhen(System.currentTimeMillis())
-                .addAction(R.drawable.ic_cc_alarm, getString(R.string.timer_restart),
-                        pendingIntentRestart)
-                .setLocalOnly(true)
-                .build();
-        notifyMgr.notify(Constants.NOTIFICATION_TIMER_EXPIRED, notif);
-    }
-
-}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/WearableListItemLayout.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/WearableListItemLayout.java
deleted file mode 100644
index 739213d..0000000
--- a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/WearableListItemLayout.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.timer;
-
-import android.content.Context;
-import android.graphics.drawable.GradientDrawable;
-import android.support.wearable.view.WearableListView;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class WearableListItemLayout extends LinearLayout implements WearableListView.Item {
-    private final float mFadedTextAlpha;
-    private final int mFadedCircleColor;
-    private final int mChosenCircleColor;
-    private ImageView mCircle;
-    private float mScale;
-    private TextView mName;
-
-    public WearableListItemLayout(Context context) {
-        this(context, null);
-    }
-
-    public WearableListItemLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public WearableListItemLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        mFadedTextAlpha = getResources().getInteger(R.integer.action_text_faded_alpha) / 100f;
-        mFadedCircleColor = getResources().getColor(R.color.wl_gray);
-        mChosenCircleColor = getResources().getColor(R.color.wl_blue);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mCircle = (ImageView) findViewById(R.id.circle);
-        mName = (TextView) findViewById(R.id.time_text);
-    }
-
-    @Override
-    public float getProximityMinValue() {
-        return 1f;
-    }
-
-    @Override
-    public float getProximityMaxValue() {
-        return 1.6f;
-    }
-
-    @Override
-    public float getCurrentProximityValue() {
-        return mScale;
-    }
-
-    @Override
-    public void setScalingAnimatorValue(float scale) {
-        mScale = scale;
-        mCircle.setScaleX(scale);
-        mCircle.setScaleY(scale);
-    }
-
-    @Override
-    public void onScaleUpStart() {
-        mName.setAlpha(1f);
-        ((GradientDrawable) mCircle.getDrawable()).setColor(mChosenCircleColor);
-    }
-
-    @Override
-    public void onScaleDownStart() {
-        ((GradientDrawable) mCircle.getDrawable()).setColor(mFadedCircleColor);
-        mName.setAlpha(mFadedTextAlpha);
-    }
-}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/Constants.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/Constants.java
deleted file mode 100644
index 18254f1..0000000
--- a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/Constants.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.timer.util;
-
-import android.net.Uri;
-
-/** Used to hold constants. */
-public final class Constants {
-
-    public static final String START_TIME = "timer_start_time";
-    public static final String ORIGINAL_TIME = "timer_original_time";
-    public static final String DATA_ITEM_PATH = "/timer";
-    public static final Uri URI_PATTERN_DATA_ITEMS =
-            Uri.fromParts("wear", DATA_ITEM_PATH, null);
-
-    public static final int NOTIFICATION_TIMER_COUNTDOWN = 1;
-    public static final int NOTIFICATION_TIMER_EXPIRED = 2;
-
-    public static final String ACTION_SHOW_ALARM
-            = "com.android.example.clockwork.timer.ACTION_SHOW";
-    public static final String ACTION_DELETE_ALARM
-            = "com.android.example.clockwork.timer.ACTION_DELETE";
-    public static final String ACTION_RESTART_ALARM
-            = "com.android.example.clockwork.timer.ACTION_RESTART";
-
-    private Constants() {
-    }
-
-}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/TimerFormat.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/TimerFormat.java
deleted file mode 100644
index b1c1dab..0000000
--- a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/TimerFormat.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.timer.util;
-
-/** Helper class to format the timer. Based on com.android.deskclock.timer.CountingTimerView. */
-public final class TimerFormat {
-
-    private static final String TWO_DIGITS = "%02d";
-
-    private static final String ONE_DIGIT = "%01d";
-
-    private static final String NEG_TWO_DIGITS = "-%02d";
-
-    private static final String NEG_ONE_DIGIT = "-%01d";
-
-    private static String mHours;
-
-    private static String mMinutes;
-
-    private static String mSeconds;
-
-    private TimerFormat() {
-
-    }
-
-    /**
-     * Update the time to display. Separates that time into the hours, minutes, seconds.
-     * Copied and shortened from com.android.deskclock.timer.CountingTimerView.
-     *
-     * @param time new time to display - in milliseconds
-     */
-    private static void setTime(long time) {
-        boolean neg = false;
-        boolean showNeg = false;
-        String format;
-        if (time < 0) {
-            time = -time;
-            neg = showNeg = true;
-        }
-        long seconds = time / 1000;
-        long hundreds = (time - seconds * 1000) / 10;
-        long minutes = seconds / 60;
-        seconds = seconds - minutes * 60;
-        long hours = minutes / 60;
-        minutes = minutes - hours * 60;
-        if (hours > 999) {
-            hours = 0;
-        }
-        // The time  can be between 0 and -1 seconds, but the "truncated" equivalent time of hours
-        // and minutes and seconds could be zero, so since we do not show fractions of seconds
-        // when counting down, do not show the minus sign.
-        if (hours == 0 && minutes == 0 && seconds == 0) {
-            showNeg = false;
-        }
-
-        // Normalize and check if it is 'time' to invalidate
-        if (!neg && hundreds != 0) {
-            seconds++;
-            if (seconds == 60) {
-                seconds = 0;
-                minutes++;
-                if (minutes == 60) {
-                    minutes = 0;
-                    hours++;
-                }
-            }
-        }
-
-        // Hours may be empty
-        if (hours >= 10) {
-            format = showNeg ? NEG_TWO_DIGITS : TWO_DIGITS;
-            mHours = String.format(format, hours);
-        } else if (hours > 0) {
-            format = showNeg ? NEG_ONE_DIGIT : ONE_DIGIT;
-            mHours = String.format(format, hours);
-        } else {
-            mHours = null;
-        }
-
-        // Minutes are never empty and when hours are non-empty, must be two digits
-        if (minutes >= 10 || hours > 0) {
-            format = (showNeg && hours == 0) ? NEG_TWO_DIGITS : TWO_DIGITS;
-            mMinutes = String.format(format, minutes);
-        } else {
-            format = (showNeg && hours == 0) ? NEG_ONE_DIGIT : ONE_DIGIT;
-            mMinutes = String.format(format, minutes);
-        }
-
-        // Seconds are always two digits
-        mSeconds = String.format(TWO_DIGITS, seconds);
-    }
-
-    /**
-     * Based on com.android.deskclock.timer.CountingTimerView.
-     *
-     * @param time the time to format.
-     * @return nicely formatted time.
-     */
-    public static String getTimeString(long time) {
-        setTime(time);
-        if (mHours == null) {
-            return String.format("%s:%s", mMinutes, mSeconds);
-        }
-        return String.format("%s:%s:%s", mHours, mMinutes, mSeconds);
-
-    }
-}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/TimerObj.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/TimerObj.java
deleted file mode 100644
index 47f019f..0000000
--- a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/timer/util/TimerObj.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.timer.util;
-
-import android.os.SystemClock;
-
-/** This class represents a timer. */
-public class TimerObj {
-
-    // Start time in milliseconds.
-    public long startTime;
-
-    // Length of the timer in milliseconds.
-    public long originalLength;
-
-    /**
-     * Construct a timer with a specific start time and length.
-     *
-     * @param startTime the start time of the timer.
-     * @param timerLength the length of the timer.
-     */
-    public TimerObj(long startTime, long timerLength) {
-        this.startTime = startTime;
-        this.originalLength = timerLength;
-    }
-
-    /**
-     * Calculate the time left of this timer.
-     * @return the time left for this timer.
-     */
-    public long timeLeft() {
-        long millis = SystemClock.elapsedRealtime();
-        return originalLength - (millis - startTime);
-    }
-}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/SetTimerActivity.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/SetTimerActivity.java
new file mode 100644
index 0000000..f660f9f
--- /dev/null
+++ b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/SetTimerActivity.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.timer;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.provider.AlarmClock;
+import android.support.wearable.view.WearableListView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.wearable.timer.util.Constants;
+import com.example.android.wearable.timer.util.TimerFormat;
+
+/** This class sets a timer. */
+public class SetTimerActivity extends Activity implements WearableListView.ClickListener {
+
+    public static final int NUMBER_OF_TIMES = 10;
+    public static final String TAG = "SetTimerActivity";
+
+    private ListViewItem[] mTimeOptions = new ListViewItem[NUMBER_OF_TIMES];
+    private WearableListView mWearableListView;
+
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        int paramLength = getIntent().getIntExtra(AlarmClock.EXTRA_LENGTH, 0);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "SetTimerActivity:onCreate=" + paramLength);
+        }
+        if (paramLength > 0 && paramLength <= 86400) {
+            long durationMillis = paramLength * 1000;
+            setupTimer(durationMillis);
+            finish();
+            return;
+        }
+
+        Resources res = getResources();
+        for (int i = 0; i < NUMBER_OF_TIMES; i++) {
+            mTimeOptions[i] = new ListViewItem(
+                    res.getQuantityString(R.plurals.timer_minutes, i + 1, i + 1),
+                    (i + 1) * 60 * 1000);
+        }
+
+        setContentView(R.layout.timer_set_timer);
+
+        // Initialize a simple list of countdown time options.
+        mWearableListView = (WearableListView) findViewById(R.id.times_list_view);
+        mWearableListView.setAdapter(new TimerWearableListViewAdapter(this));
+        mWearableListView.setClickListener(this);
+    }
+
+    /**
+     * Sets up an alarm (and an associated notification) to go off after <code>duration</code>
+     * milliseconds.
+     */
+    private void setupTimer(long duration) {
+        NotificationManager notifyMgr =
+                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
+
+        // Delete dataItem and cancel a potential old countdown.
+        cancelCountdown(notifyMgr);
+
+        // Build notification and set it.
+        notifyMgr.notify(Constants.NOTIFICATION_TIMER_COUNTDOWN, buildNotification(duration));
+
+        // Register with the alarm manager to display a notification when the timer is done.
+        registerWithAlarmManager(duration);
+
+        finish();
+    }
+
+    @Override
+    public void onClick(WearableListView.ViewHolder holder) {
+        long duration = mTimeOptions[holder.getPosition()].duration;
+        setupTimer(duration);
+    }
+
+    @Override
+    public void onTopEmptyRegionClick() {
+    }
+
+    private void registerWithAlarmManager(long duration) {
+        // Get the alarm manager.
+        AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+
+        // Create intent that gets fired when timer expires.
+        Intent intent = new Intent(Constants.ACTION_SHOW_ALARM, null, this,
+                TimerNotificationService.class);
+        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Calculate the time when it expires.
+        long wakeupTime = System.currentTimeMillis() + duration;
+
+        // Schedule an alarm.
+        alarm.setExact(AlarmManager.RTC_WAKEUP, wakeupTime, pendingIntent);
+    }
+
+    /**
+     * Build a notification including different actions and other various setup and return it.
+     *
+     * @param duration the duration of the timer.
+     * @return the notification to display.
+     */
+
+    private Notification buildNotification(long duration) {
+        // Intent to restart a timer.
+        Intent restartIntent = new Intent(Constants.ACTION_RESTART_ALARM, null, this,
+                TimerNotificationService.class);
+        PendingIntent pendingIntentRestart = PendingIntent
+                .getService(this, 0, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Intent to delete a timer.
+        Intent deleteIntent = new Intent(Constants.ACTION_DELETE_ALARM, null, this,
+                TimerNotificationService.class);
+        PendingIntent pendingIntentDelete = PendingIntent
+                .getService(this, 0, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Create countdown notification using a chronometer style.
+        return new Notification.Builder(this)
+                .setSmallIcon(R.drawable.ic_cc_alarm)
+                .setContentTitle(getString(R.string.timer_time_left))
+                .setContentText(TimerFormat.getTimeString(duration))
+                .setUsesChronometer(true)
+                .setWhen(System.currentTimeMillis() + duration)
+                .addAction(R.drawable.ic_cc_alarm, getString(R.string.timer_restart),
+                        pendingIntentRestart)
+                .addAction(R.drawable.ic_cc_alarm, getString(R.string.timer_delete),
+                        pendingIntentDelete)
+                .setDeleteIntent(pendingIntentDelete)
+                .setLocalOnly(true)
+                .build();
+    }
+
+    /**
+     * Cancels an old countdown and deletes the dataItem.
+     *
+     * @param notifyMgr the notification manager.
+     */
+    private void cancelCountdown(NotificationManager notifyMgr) {
+        notifyMgr.cancel(Constants.NOTIFICATION_TIMER_EXPIRED);
+    }
+
+    /** Model class for the listview. */
+    private static class ListViewItem {
+
+        // Duration in milliseconds.
+        long duration;
+        // Label to display.
+        private String label;
+
+        public ListViewItem(String label, long duration) {
+            this.label = label;
+            this.duration = duration;
+        }
+
+        @Override
+        public String toString() {
+            return label;
+        }
+    }
+
+    private final class TimerWearableListViewAdapter extends WearableListView.Adapter {
+        private final Context mContext;
+        private final LayoutInflater mInflater;
+
+        private TimerWearableListViewAdapter(Context context) {
+            mContext = context;
+            mInflater = LayoutInflater.from(context);
+        }
+
+        @Override
+        public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new WearableListView.ViewHolder(
+                    mInflater.inflate(R.layout.timer_list_item, null));
+        }
+
+        @Override
+        public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
+            TextView view = (TextView) holder.itemView.findViewById(R.id.time_text);
+            view.setText(mTimeOptions[position].label);
+            holder.itemView.setTag(position);
+        }
+
+        @Override
+        public int getItemCount() {
+            return NUMBER_OF_TIMES;
+        }
+    }
+
+}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/TimerNotificationService.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/TimerNotificationService.java
new file mode 100644
index 0000000..5822fe4
--- /dev/null
+++ b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/TimerNotificationService.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.timer;
+
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.example.android.wearable.timer.util.Constants;
+
+/**
+ * Service class that manages notifications of the timer.
+ */
+public class TimerNotificationService extends IntentService {
+
+    public static final String TAG = "TimerNotificationSvc";
+
+    public TimerNotificationService() {
+        super(TAG);
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onHandleIntent called with intent: " + intent);
+        }
+        String action = intent.getAction();
+        if (Constants.ACTION_SHOW_ALARM.equals(action)) {
+            showTimerDoneNotification();
+        } else if (Constants.ACTION_DELETE_ALARM.equals(action)) {
+            deleteTimer();
+        } else if (Constants.ACTION_RESTART_ALARM.equals(action)) {
+            restartAlarm();
+        } else {
+            throw new IllegalStateException("Undefined constant used: " + action);
+        }
+    }
+
+    private void restartAlarm() {
+        Intent dialogIntent = new Intent(this, SetTimerActivity.class);
+        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(dialogIntent);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Timer restarted.");
+        }
+    }
+
+    private void deleteTimer() {
+        cancelCountdownNotification();
+
+        AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+        Intent intent = new Intent(Constants.ACTION_SHOW_ALARM, null, this,
+                TimerNotificationService.class);
+        PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        alarm.cancel(pendingIntent);
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Timer deleted.");
+        }
+    }
+
+    private void cancelCountdownNotification() {
+        NotificationManager notifyMgr =
+                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
+        notifyMgr.cancel(Constants.NOTIFICATION_TIMER_COUNTDOWN);
+    }
+
+    private void showTimerDoneNotification() {
+        // Cancel the countdown notification to show the "timer done" notification.
+        cancelCountdownNotification();
+
+        // Create an intent to restart a timer.
+        Intent restartIntent = new Intent(Constants.ACTION_RESTART_ALARM, null, this,
+                TimerNotificationService.class);
+        PendingIntent pendingIntentRestart = PendingIntent
+                .getService(this, 0, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Create notification that timer has expired.
+        NotificationManager notifyMgr =
+                ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
+        Notification notif = new Notification.Builder(this)
+                .setSmallIcon(R.drawable.ic_cc_alarm)
+                .setContentTitle(getString(R.string.timer_done))
+                .setContentText(getString(R.string.timer_done))
+                .setUsesChronometer(true)
+                .setWhen(System.currentTimeMillis())
+                .addAction(R.drawable.ic_cc_alarm, getString(R.string.timer_restart),
+                        pendingIntentRestart)
+                .setLocalOnly(true)
+                .build();
+        notifyMgr.notify(Constants.NOTIFICATION_TIMER_EXPIRED, notif);
+    }
+
+}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/WearableListItemLayout.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/WearableListItemLayout.java
new file mode 100644
index 0000000..09591bf
--- /dev/null
+++ b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/WearableListItemLayout.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.timer;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.support.wearable.view.WearableListView;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class WearableListItemLayout extends LinearLayout
+        implements WearableListView.OnCenterProximityListener {
+    private final float mFadedTextAlpha;
+    private final int mFadedCircleColor;
+    private final int mChosenCircleColor;
+    private ImageView mCircle;
+    private TextView mName;
+
+    public WearableListItemLayout(Context context) {
+        this(context, null);
+    }
+
+    public WearableListItemLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WearableListItemLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mFadedTextAlpha = getResources().getInteger(R.integer.action_text_faded_alpha) / 100f;
+        mFadedCircleColor = getResources().getColor(R.color.wl_gray);
+        mChosenCircleColor = getResources().getColor(R.color.wl_blue);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mCircle = (ImageView) findViewById(R.id.circle);
+        mName = (TextView) findViewById(R.id.time_text);
+    }
+
+    @Override
+    public void onCenterPosition(boolean animate) {
+        mName.setAlpha(1f);
+        ((GradientDrawable) mCircle.getDrawable()).setColor(mChosenCircleColor);
+    }
+
+    @Override
+    public void onNonCenterPosition(boolean animate) {
+        ((GradientDrawable) mCircle.getDrawable()).setColor(mFadedCircleColor);
+        mName.setAlpha(mFadedTextAlpha);
+    }
+}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/Constants.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/Constants.java
new file mode 100644
index 0000000..85885e4
--- /dev/null
+++ b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/Constants.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.timer.util;
+
+import android.net.Uri;
+
+/** Used to hold constants. */
+public final class Constants {
+
+    public static final String START_TIME = "timer_start_time";
+    public static final String ORIGINAL_TIME = "timer_original_time";
+    public static final String DATA_ITEM_PATH = "/timer";
+    public static final Uri URI_PATTERN_DATA_ITEMS =
+            Uri.fromParts("wear", DATA_ITEM_PATH, null);
+
+    public static final int NOTIFICATION_TIMER_COUNTDOWN = 1;
+    public static final int NOTIFICATION_TIMER_EXPIRED = 2;
+
+    public static final String ACTION_SHOW_ALARM
+            = "com.android.example.clockwork.timer.ACTION_SHOW";
+    public static final String ACTION_DELETE_ALARM
+            = "com.android.example.clockwork.timer.ACTION_DELETE";
+    public static final String ACTION_RESTART_ALARM
+            = "com.android.example.clockwork.timer.ACTION_RESTART";
+
+    private Constants() {
+    }
+
+}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/TimerFormat.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/TimerFormat.java
new file mode 100644
index 0000000..b40351b
--- /dev/null
+++ b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/TimerFormat.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.timer.util;
+
+/** Helper class to format the timer. Based on com.android.deskclock.timer.CountingTimerView. */
+public final class TimerFormat {
+
+    private static final String TWO_DIGITS = "%02d";
+
+    private static final String ONE_DIGIT = "%01d";
+
+    private static final String NEG_TWO_DIGITS = "-%02d";
+
+    private static final String NEG_ONE_DIGIT = "-%01d";
+
+    private static String mHours;
+
+    private static String mMinutes;
+
+    private static String mSeconds;
+
+    private TimerFormat() {
+
+    }
+
+    /**
+     * Update the time to display. Separates that time into the hours, minutes, seconds.
+     * Copied and shortened from com.android.deskclock.timer.CountingTimerView.
+     *
+     * @param time new time to display - in milliseconds
+     */
+    private static void setTime(long time) {
+        boolean neg = false;
+        boolean showNeg = false;
+        String format;
+        if (time < 0) {
+            time = -time;
+            neg = showNeg = true;
+        }
+        long seconds = time / 1000;
+        long hundreds = (time - seconds * 1000) / 10;
+        long minutes = seconds / 60;
+        seconds = seconds - minutes * 60;
+        long hours = minutes / 60;
+        minutes = minutes - hours * 60;
+        if (hours > 999) {
+            hours = 0;
+        }
+        // The time  can be between 0 and -1 seconds, but the "truncated" equivalent time of hours
+        // and minutes and seconds could be zero, so since we do not show fractions of seconds
+        // when counting down, do not show the minus sign.
+        if (hours == 0 && minutes == 0 && seconds == 0) {
+            showNeg = false;
+        }
+
+        // Normalize and check if it is 'time' to invalidate
+        if (!neg && hundreds != 0) {
+            seconds++;
+            if (seconds == 60) {
+                seconds = 0;
+                minutes++;
+                if (minutes == 60) {
+                    minutes = 0;
+                    hours++;
+                }
+            }
+        }
+
+        // Hours may be empty
+        if (hours >= 10) {
+            format = showNeg ? NEG_TWO_DIGITS : TWO_DIGITS;
+            mHours = String.format(format, hours);
+        } else if (hours > 0) {
+            format = showNeg ? NEG_ONE_DIGIT : ONE_DIGIT;
+            mHours = String.format(format, hours);
+        } else {
+            mHours = null;
+        }
+
+        // Minutes are never empty and when hours are non-empty, must be two digits
+        if (minutes >= 10 || hours > 0) {
+            format = (showNeg && hours == 0) ? NEG_TWO_DIGITS : TWO_DIGITS;
+            mMinutes = String.format(format, minutes);
+        } else {
+            format = (showNeg && hours == 0) ? NEG_ONE_DIGIT : ONE_DIGIT;
+            mMinutes = String.format(format, minutes);
+        }
+
+        // Seconds are always two digits
+        mSeconds = String.format(TWO_DIGITS, seconds);
+    }
+
+    /**
+     * Based on com.android.deskclock.timer.CountingTimerView.
+     *
+     * @param time the time to format.
+     * @return nicely formatted time.
+     */
+    public static String getTimeString(long time) {
+        setTime(time);
+        if (mHours == null) {
+            return String.format("%s:%s", mMinutes, mSeconds);
+        }
+        return String.format("%s:%s:%s", mHours, mMinutes, mSeconds);
+
+    }
+}
diff --git a/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/TimerObj.java b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/TimerObj.java
new file mode 100644
index 0000000..52f7244
--- /dev/null
+++ b/wearable/wear/Timer/Wearable/src/main/java/com/example/android/wearable/timer/util/TimerObj.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.timer.util;
+
+import android.os.SystemClock;
+
+/** This class represents a timer. */
+public class TimerObj {
+
+    // Start time in milliseconds.
+    public long startTime;
+
+    // Length of the timer in milliseconds.
+    public long originalLength;
+
+    /**
+     * Construct a timer with a specific start time and length.
+     *
+     * @param startTime the start time of the timer.
+     * @param timerLength the length of the timer.
+     */
+    public TimerObj(long startTime, long timerLength) {
+        this.startTime = startTime;
+        this.originalLength = timerLength;
+    }
+
+    /**
+     * Calculate the time left of this timer.
+     * @return the time left for this timer.
+     */
+    public long timeLeft() {
+        long millis = SystemClock.elapsedRealtime();
+        return originalLength - (millis - startTime);
+    }
+}
diff --git a/wearable/wear/Timer/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Timer/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/Timer/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/Timer/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/Timer/screenshots/timer.png b/wearable/wear/Timer/screenshots/timer.png
new file mode 100644
index 0000000..6ef8f86
--- /dev/null
+++ b/wearable/wear/Timer/screenshots/timer.png
Binary files differ
diff --git a/wearable/wear/Timer/screenshots/timer_countdown.png b/wearable/wear/Timer/screenshots/timer_countdown.png
new file mode 100644
index 0000000..505733d
--- /dev/null
+++ b/wearable/wear/Timer/screenshots/timer_countdown.png
Binary files differ
diff --git a/wearable/wear/Timer/settings.gradle b/wearable/wear/Timer/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/Timer/settings.gradle
+++ b/wearable/wear/Timer/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/Timer/template-params.xml b/wearable/wear/Timer/template-params.xml
index b8a6d4a..43284b4 100644
--- a/wearable/wear/Timer/template-params.xml
+++ b/wearable/wear/Timer/template-params.xml
@@ -20,12 +20,12 @@
 <sample>
     <name>Timer</name>
     <group>Wearable</group>
-    <package>com.example.android.timer</package>
+    <package>com.example.android.wearable.timer</package>
 
 
 
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
+    <minSdk>18</minSdk>
 
 
     <strings>
@@ -39,7 +39,5 @@
 
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
 
 </sample>
diff --git a/wearable/wear/WatchFace/Application/.gitignore b/wearable/wear/WatchFace/Application/.gitignore
new file mode 100644
index 0000000..8fd2e1c
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/.gitignore
@@ -0,0 +1,16 @@
+# Copyright 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+src/template/
+src/common/
+build.gradle
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/wearable/wear/WatchFace/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to wearable/wear/WatchFace/Application/proguard-project.txt
diff --git a/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml b/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..732e306
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.example.android.wearable.watchface" >
+
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
+
+    <!-- Permissions required by the wearable app -->
+    <uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_CALENDAR" />
+
+    <!-- All intent-filters for config actions must include the categories
+        com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION and
+        android.intent.category.DEFAULT. -->
+    <application
+            android:allowBackup="true"
+            android:icon="@drawable/ic_launcher"
+            android:label="@string/app_name"
+            android:theme="@style/AppTheme" >
+
+        <activity
+                android:name=".AnalogAndCardBoundsWatchFaceConfigActivity"
+                android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="com.example.android.wearable.watchface.CONFIG_ANALOG" />
+                <action android:name="com.example.android.wearable.watchface.CONFIG_CARD_BOUNDS" />
+                <category android:name="com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name=".DigitalWatchFaceCompanionConfigActivity"
+                android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="com.example.android.wearable.watchface.CONFIG_DIGITAL" />
+                <category android:name="com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name=".TiltWatchFaceConfigActivity"
+                android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="com.example.android.wearable.watchface.CONFIG_TILT" />
+                <category android:name="com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <meta-data
+                android:name="com.google.android.gms.version"
+                android:value="@integer/google_play_services_version" />
+
+    </application>
+
+</manifest>
diff --git a/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/AnalogAndCardBoundsWatchFaceConfigActivity.java b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/AnalogAndCardBoundsWatchFaceConfigActivity.java
new file mode 100644
index 0000000..5943e6b
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/AnalogAndCardBoundsWatchFaceConfigActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.support.wearable.companion.WatchFaceCompanion;
+import android.widget.TextView;
+
+public class AnalogAndCardBoundsWatchFaceConfigActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_analog_watch_face_config);
+
+        ComponentName name =
+                getIntent().getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
+        TextView label = (TextView) findViewById(R.id.label);
+        label.setText(label.getText() + " (" + name.getClassName() + ")");
+    }
+}
diff --git a/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceCompanionConfigActivity.java b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceCompanionConfigActivity.java
new file mode 100644
index 0000000..b04f11e
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceCompanionConfigActivity.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.wearable.companion.WatchFaceCompanion;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+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.wearable.DataApi;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.Wearable;
+
+/**
+ * The phone-side config activity for {@code DigitalWatchFaceService}. Like the watch-side config
+ * activity ({@code DigitalWatchFaceWearableConfigActivity}), allows for setting the background
+ * color. Additionally, enables setting the color for hour, minute and second digits.
+ */
+public class DigitalWatchFaceCompanionConfigActivity extends Activity
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
+                ResultCallback<DataApi.DataItemResult> {
+    private static final String TAG = "DigitalWatchFaceConfig";
+
+    // TODO: use the shared constants (needs covering all the samples with Gradle build model)
+    private static final String KEY_BACKGROUND_COLOR = "BACKGROUND_COLOR";
+    private static final String KEY_HOURS_COLOR = "HOURS_COLOR";
+    private static final String KEY_MINUTES_COLOR = "MINUTES_COLOR";
+    private static final String KEY_SECONDS_COLOR = "SECONDS_COLOR";
+    private static final String PATH_WITH_FEATURE = "/watch_face_config/Digital";
+
+    private GoogleApiClient mGoogleApiClient;
+    private String mPeerId;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_digital_watch_face_config);
+
+        mPeerId = getIntent().getStringExtra(WatchFaceCompanion.EXTRA_PEER_ID);
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .addApi(Wearable.API)
+                .build();
+
+        ComponentName name = getIntent().getParcelableExtra(
+                WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
+        TextView label = (TextView)findViewById(R.id.label);
+        label.setText(label.getText() + " (" + name.getClassName() + ")");
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mGoogleApiClient.connect();
+    }
+
+    @Override
+    protected void onStop() {
+        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.disconnect();
+        }
+        super.onStop();
+    }
+
+    @Override // GoogleApiClient.ConnectionCallbacks
+    public void onConnected(Bundle connectionHint) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConnected: " + connectionHint);
+        }
+
+        if (mPeerId != null) {
+            Uri.Builder builder = new Uri.Builder();
+            Uri uri = builder.scheme("wear").path(PATH_WITH_FEATURE).authority(mPeerId).build();
+            Wearable.DataApi.getDataItem(mGoogleApiClient, uri).setResultCallback(this);
+        } else {
+            displayNoConnectedDeviceDialog();
+        }
+    }
+
+    @Override // ResultCallback<DataApi.DataItemResult>
+    public void onResult(DataApi.DataItemResult dataItemResult) {
+        if (dataItemResult.getStatus().isSuccess() && dataItemResult.getDataItem() != null) {
+            DataItem configDataItem = dataItemResult.getDataItem();
+            DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);
+            DataMap config = dataMapItem.getDataMap();
+            setUpAllPickers(config);
+        } else {
+            // If DataItem with the current config can't be retrieved, select the default items on
+            // each picker.
+            setUpAllPickers(null);
+        }
+    }
+
+    @Override // GoogleApiClient.ConnectionCallbacks
+    public void onConnectionSuspended(int cause) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConnectionSuspended: " + cause);
+        }
+    }
+
+    @Override // GoogleApiClient.OnConnectionFailedListener
+    public void onConnectionFailed(ConnectionResult result) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConnectionFailed: " + result);
+        }
+    }
+
+    private void displayNoConnectedDeviceDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        String messageText = getResources().getString(R.string.title_no_device_connected);
+        String okText = getResources().getString(R.string.ok_no_device_connected);
+        builder.setMessage(messageText)
+                .setCancelable(false)
+                .setPositiveButton(okText, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int id) { }
+                });
+        AlertDialog alert = builder.create();
+        alert.show();
+    }
+
+    /**
+     * Sets up selected items for all pickers according to given {@code config} and sets up their
+     * item selection listeners.
+     *
+     * @param config the {@code DigitalWatchFaceService} config {@link DataMap}. If null, the
+     *         default items are selected.
+     */
+    private void setUpAllPickers(DataMap config) {
+        setUpColorPickerSelection(R.id.background, KEY_BACKGROUND_COLOR, config,
+                R.string.color_black);
+        setUpColorPickerSelection(R.id.hours, KEY_HOURS_COLOR, config, R.string.color_white);
+        setUpColorPickerSelection(R.id.minutes, KEY_MINUTES_COLOR, config, R.string.color_white);
+        setUpColorPickerSelection(R.id.seconds, KEY_SECONDS_COLOR, config, R.string.color_gray);
+
+        setUpColorPickerListener(R.id.background, KEY_BACKGROUND_COLOR);
+        setUpColorPickerListener(R.id.hours, KEY_HOURS_COLOR);
+        setUpColorPickerListener(R.id.minutes, KEY_MINUTES_COLOR);
+        setUpColorPickerListener(R.id.seconds, KEY_SECONDS_COLOR);
+    }
+
+    private void setUpColorPickerSelection(int spinnerId, final String configKey, DataMap config,
+            int defaultColorNameResId) {
+        String defaultColorName = getString(defaultColorNameResId);
+        int defaultColor = Color.parseColor(defaultColorName);
+        int color;
+        if (config != null) {
+            color = config.getInt(configKey, defaultColor);
+        } else {
+            color = defaultColor;
+        }
+        Spinner spinner = (Spinner) findViewById(spinnerId);
+        String[] colorNames = getResources().getStringArray(R.array.color_array);
+        for (int i = 0; i < colorNames.length; i++) {
+            if (Color.parseColor(colorNames[i]) == color) {
+                spinner.setSelection(i);
+                break;
+            }
+        }
+    }
+
+    private void setUpColorPickerListener(int spinnerId, final String configKey) {
+        Spinner spinner = (Spinner) findViewById(spinnerId);
+        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> adapterView, View view, int pos, long id) {
+                final String colorName = (String) adapterView.getItemAtPosition(pos);
+                sendConfigUpdateMessage(configKey, Color.parseColor(colorName));
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> adapterView) { }
+        });
+    }
+
+    private void sendConfigUpdateMessage(String configKey, int color) {
+        if (mPeerId != null) {
+            DataMap config = new DataMap();
+            config.putInt(configKey, color);
+            byte[] rawData = config.toByteArray();
+            Wearable.MessageApi.sendMessage(mGoogleApiClient, mPeerId, PATH_WITH_FEATURE, rawData);
+
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Sent watch face config message: " + configKey + " -> "
+                        + Integer.toHexString(color));
+            }
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/TiltWatchFaceConfigActivity.java b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/TiltWatchFaceConfigActivity.java
new file mode 100644
index 0000000..303e72e
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/TiltWatchFaceConfigActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.support.wearable.companion.WatchFaceCompanion;
+import android.widget.TextView;
+
+public class TiltWatchFaceConfigActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_tilt_watch_face_config);
+
+        ComponentName name =
+                getIntent().getParcelableExtra(WatchFaceCompanion.EXTRA_WATCH_FACE_COMPONENT);
+        TextView label = (TextView)findViewById(R.id.label);
+        label.setText(label.getText() + " (" + name.getClassName() + ")");
+    }
+}
diff --git a/wearable/wear/WatchFace/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/WatchFace/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/WatchFace/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/WatchFace/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/WatchFace/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Application/src/main/res/layout/activity_analog_watch_face_config.xml b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_analog_watch_face_config.xml
new file mode 100644
index 0000000..ec816c2
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_analog_watch_face_config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout 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">
+
+    <TextView
+        android:id="@+id/label"
+        android:text="@string/analog_config_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</FrameLayout>
diff --git a/wearable/wear/WatchFace/Application/src/main/res/layout/activity_digital_watch_face_config.xml b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_digital_watch_face_config.xml
new file mode 100644
index 0000000..204d523
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_digital_watch_face_config.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/label"
+        android:text="@string/digital_config_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:text="@string/digital_config_background"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+        <Spinner
+            android:id="@+id/background"
+            android:entries="@array/color_array"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:text="@string/digital_config_hours"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+        <Spinner
+            android:id="@+id/hours"
+            android:entries="@array/color_array"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:text="@string/digital_config_minutes"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+        <Spinner
+            android:id="@+id/minutes"
+            android:entries="@array/color_array"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:text="@string/digital_config_seconds"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+        <Spinner
+            android:id="@+id/seconds"
+            android:entries="@array/color_array"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="3" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/wearable/wear/WatchFace/Application/src/main/res/layout/activity_tilt_watch_face_config.xml b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_tilt_watch_face_config.xml
new file mode 100644
index 0000000..bda2d68
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_tilt_watch_face_config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout 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">
+
+    <TextView
+        android:id="@+id/label"
+        android:text="@string/tilt_config_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</FrameLayout>
diff --git a/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml b/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..aacb108
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <string name="analog_config_text">This is the config activity for the Analog and Card Bounds watch faces</string>
+    <string name="digital_config_text">Digital watch face configuration</string>
+    <string name="tilt_config_text">Tilt watch face configuration</string>
+    <string name="digital_config_background">Background</string>
+    <string name="digital_config_hours">Hours</string>
+    <string name="digital_config_minutes">Minutes</string>
+    <string name="digital_config_seconds">Seconds</string>
+
+    <string name="title_no_device_connected">No wearable device is currently connected.</string>
+    <string name="ok_no_device_connected">OK</string>
+
+    <string name="color_black">Black</string>
+    <string name="color_blue">Blue</string>
+    <string name="color_gray">Gray</string>
+    <string name="color_green">Green</string>
+    <string name="color_navy">Navy</string>
+    <string name="color_red">Red</string>
+    <string name="color_white">White</string>
+
+    <string-array name="color_array">
+        <item>@string/color_black</item>
+        <item>@string/color_blue</item>
+        <item>@string/color_gray</item>
+        <item>@string/color_green</item>
+        <item>@string/color_navy</item>
+        <item>@string/color_red</item>
+        <item>@string/color_white</item>
+    </string-array>
+</resources>
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/wearable/wear/WatchFace/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to wearable/wear/WatchFace/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/wearable/wear/WatchFace/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to wearable/wear/WatchFace/LICENSE
diff --git a/wearable/wear/WatchFace/Wearable/.gitignore b/wearable/wear/WatchFace/Wearable/.gitignore
new file mode 100644
index 0000000..8fd2e1c
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/.gitignore
@@ -0,0 +1,16 @@
+# Copyright 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+src/template/
+src/common/
+build.gradle
diff --git a/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml b/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..14fee36
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.example.android.wearable.watchface" >
+
+    <uses-sdk android:minSdkVersion="21"
+        android:targetSdkVersion="21" />
+
+    <uses-feature android:name="android.hardware.type.watch" />
+
+    <!-- Required to act as a custom watch face. -->
+    <uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <!-- Calendar permission used by CalendarWatchFaceService -->
+    <uses-permission android:name="android.permission.READ_CALENDAR" />
+
+    <application
+            android:allowBackup="true"
+            android:icon="@drawable/ic_launcher"
+            android:label="@string/app_name" >
+
+        <service
+                android:name=".AnalogWatchFaceService"
+                android:label="@string/analog_name"
+                android:permission="android.permission.BIND_WALLPAPER" >
+            <meta-data
+                    android:name="android.service.wallpaper"
+                    android:resource="@xml/watch_face" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview"
+                    android:resource="@drawable/preview_analog" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview_circular"
+                    android:resource="@drawable/preview_analog_circular" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.companionConfigurationAction"
+                    android:value="com.example.android.wearable.watchface.CONFIG_ANALOG" />
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <service
+                android:name=".SweepWatchFaceService"
+                android:label="@string/sweep_name"
+                android:permission="android.permission.BIND_WALLPAPER" >
+            <meta-data
+                    android:name="android.service.wallpaper"
+                    android:resource="@xml/watch_face" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview"
+                    android:resource="@drawable/preview_analog" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview_circular"
+                    android:resource="@drawable/preview_analog_circular" />
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <service
+                android:name=".TiltWatchFaceService"
+                android:label="@string/tilt_name"
+                android:permission="android.permission.BIND_WALLPAPER" >
+            <meta-data
+                    android:name="android.service.wallpaper"
+                    android:resource="@xml/watch_face" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview"
+                    android:resource="@drawable/preview_tilt" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview_circular"
+                    android:resource="@drawable/preview_tilt_circular" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.companionConfigurationAction"
+                    android:value="com.example.android.wearable.watchface.CONFIG_TILT" />
+
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <service
+                android:name=".CardBoundsWatchFaceService"
+                android:label="@string/card_bounds_name"
+                android:permission="android.permission.BIND_WALLPAPER" >
+            <meta-data
+                    android:name="android.service.wallpaper"
+                    android:resource="@xml/watch_face" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview"
+                    android:resource="@drawable/preview_card_bounds" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview_circular"
+                    android:resource="@drawable/preview_card_bounds_circular" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.companionConfigurationAction"
+                    android:value="com.example.android.wearable.watchface.CONFIG_CARD_BOUNDS" />
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <service
+                android:name=".DigitalWatchFaceService"
+                android:label="@string/digital_name"
+                android:permission="android.permission.BIND_WALLPAPER" >
+            <meta-data
+                    android:name="android.service.wallpaper"
+                    android:resource="@xml/watch_face" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview"
+                    android:resource="@drawable/preview_digital" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview_circular"
+                    android:resource="@drawable/preview_digital_circular" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.companionConfigurationAction"
+                    android:value="com.example.android.wearable.watchface.CONFIG_DIGITAL" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
+                    android:value="com.example.android.wearable.watchface.CONFIG_DIGITAL" />
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <!-- All intent-filters for config actions must include the categories
+            com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION
+            and android.intent.category.DEFAULT. -->
+
+        <activity
+                android:name=".DigitalWatchFaceWearableConfigActivity"
+                android:label="@string/digital_config_name">
+            <intent-filter>
+                <action android:name="com.example.android.wearable.watchface.CONFIG_DIGITAL" />
+                <category android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <service
+                android:name=".CalendarWatchFaceService"
+                android:label="@string/calendar_name"
+                android:permission="android.permission.BIND_WALLPAPER" >
+            <meta-data
+                    android:name="android.service.wallpaper"
+                    android:resource="@xml/watch_face" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview"
+                    android:resource="@drawable/preview_calendar" />
+            <meta-data
+                    android:name="com.google.android.wearable.watchface.preview_circular"
+                    android:resource="@drawable/preview_calendar_circular" />
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <service android:name=".DigitalWatchFaceConfigListenerService">
+            <intent-filter>
+                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
+            </intent-filter>
+        </service>
+
+        <meta-data
+                android:name="com.google.android.gms.version"
+                android:value="@integer/google_play_services_version" />
+
+    </application>
+
+</manifest>
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/AnalogWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/AnalogWatchFaceService.java
new file mode 100644
index 0000000..f0fb4f5
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/AnalogWatchFaceService.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sample analog watch face with a ticking second hand. In ambient mode, the second hand isn't
+ * shown. On devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient
+ * mode. The watch face is drawn with less contrast in mute mode.
+ *
+ * {@link SweepWatchFaceService} is similar but has a sweep second hand.
+ */
+public class AnalogWatchFaceService extends CanvasWatchFaceService {
+    private static final String TAG = "AnalogWatchFaceService";
+
+    /**
+     * Update rate in milliseconds for interactive mode. We update once a second to advance the
+     * second hand.
+     */
+    private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine {
+        static final int MSG_UPDATE_TIME = 0;
+
+        Paint mHourPaint;
+        Paint mMinutePaint;
+        Paint mSecondPaint;
+        Paint mTickPaint;
+        boolean mMute;
+        Time mTime;
+
+        /** Handler to update the time once a second in interactive mode. */
+        final Handler mUpdateTimeHandler = new Handler() {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_UPDATE_TIME:
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "updating time");
+                        }
+                        invalidate();
+                        if (shouldTimerBeRunning()) {
+                            long timeMs = System.currentTimeMillis();
+                            long delayMs = INTERACTIVE_UPDATE_RATE_MS
+                                    - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
+                            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+                        }
+                        break;
+                }
+            }
+        };
+
+        final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mTime.clear(intent.getStringExtra("time-zone"));
+                mTime.setToNow();
+            }
+        };
+        boolean mRegisteredTimeZoneReceiver = false;
+
+        /**
+         * Whether the display supports fewer bits for each color in ambient mode. When true, we
+         * disable anti-aliasing in ambient mode.
+         */
+        boolean mLowBitAmbient;
+
+        Bitmap mBackgroundBitmap;
+        Bitmap mBackgroundScaledBitmap;
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+            super.onCreate(holder);
+
+            setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(false)
+                    .build());
+
+            Resources resources = AnalogWatchFaceService.this.getResources();
+            Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
+            mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
+
+            mHourPaint = new Paint();
+            mHourPaint.setARGB(255, 200, 200, 200);
+            mHourPaint.setStrokeWidth(5.f);
+            mHourPaint.setAntiAlias(true);
+            mHourPaint.setStrokeCap(Paint.Cap.ROUND);
+
+            mMinutePaint = new Paint();
+            mMinutePaint.setARGB(255, 200, 200, 200);
+            mMinutePaint.setStrokeWidth(3.f);
+            mMinutePaint.setAntiAlias(true);
+            mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
+
+            mSecondPaint = new Paint();
+            mSecondPaint.setARGB(255, 255, 0, 0);
+            mSecondPaint.setStrokeWidth(2.f);
+            mSecondPaint.setAntiAlias(true);
+            mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
+
+            mTickPaint = new Paint();
+            mTickPaint.setARGB(100, 255, 255, 255);
+            mTickPaint.setStrokeWidth(2.f);
+            mTickPaint.setAntiAlias(true);
+
+            mTime = new Time();
+        }
+
+        @Override
+        public void onDestroy() {
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            super.onDestroy();
+        }
+
+        @Override
+        public void onPropertiesChanged(Bundle properties) {
+            super.onPropertiesChanged(properties);
+            mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
+            }
+        }
+
+        @Override
+        public void onTimeTick() {
+            super.onTimeTick();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
+            }
+            invalidate();
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode) {
+            super.onAmbientModeChanged(inAmbientMode);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+            }
+            if (mLowBitAmbient) {
+                boolean antiAlias = !inAmbientMode;
+                mHourPaint.setAntiAlias(antiAlias);
+                mMinutePaint.setAntiAlias(antiAlias);
+                mSecondPaint.setAntiAlias(antiAlias);
+                mTickPaint.setAntiAlias(antiAlias);
+            }
+            invalidate();
+
+            // Whether the timer should be running depends on whether we're in ambient mode (as well
+            // as whether we're visible), so we may need to start or stop the timer.
+            updateTimer();
+        }
+
+        @Override
+        public void onInterruptionFilterChanged(int interruptionFilter) {
+            super.onInterruptionFilterChanged(interruptionFilter);
+            boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
+            if (mMute != inMuteMode) {
+                mMute = inMuteMode;
+                mHourPaint.setAlpha(inMuteMode ? 100 : 255);
+                mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
+                mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
+                invalidate();
+            }
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            mTime.setToNow();
+
+            int width = bounds.width();
+            int height = bounds.height();
+
+            // Draw the background, scaled to fit.
+            if (mBackgroundScaledBitmap == null
+                    || mBackgroundScaledBitmap.getWidth() != width
+                    || mBackgroundScaledBitmap.getHeight() != height) {
+                mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
+                        width, height, true /* filter */);
+            }
+            canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);
+
+            // Find the center. Ignore the window insets so that, on round watches with a
+            // "chin", the watch face is centered on the entire screen, not just the usable
+            // portion.
+            float centerX = width / 2f;
+            float centerY = height / 2f;
+
+            // Draw the ticks.
+            float innerTickRadius = centerX - 10;
+            float outerTickRadius = centerX;
+            for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
+                float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
+                float innerX = (float) Math.sin(tickRot) * innerTickRadius;
+                float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
+                float outerX = (float) Math.sin(tickRot) * outerTickRadius;
+                float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
+                canvas.drawLine(centerX + innerX, centerY + innerY,
+                        centerX + outerX, centerY + outerY, mTickPaint);
+            }
+
+            float secRot = mTime.second / 30f * (float) Math.PI;
+            int minutes = mTime.minute;
+            float minRot = minutes / 30f * (float) Math.PI;
+            float hrRot = ((mTime.hour + (minutes / 60f)) / 6f ) * (float) Math.PI;
+
+            float secLength = centerX - 20;
+            float minLength = centerX - 40;
+            float hrLength = centerX - 80;
+
+            if (!isInAmbientMode()) {
+                float secX = (float) Math.sin(secRot) * secLength;
+                float secY = (float) -Math.cos(secRot) * secLength;
+                canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mSecondPaint);
+            }
+
+            float minX = (float) Math.sin(minRot) * minLength;
+            float minY = (float) -Math.cos(minRot) * minLength;
+            canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, mMinutePaint);
+
+            float hrX = (float) Math.sin(hrRot) * hrLength;
+            float hrY = (float) -Math.cos(hrRot) * hrLength;
+            canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, mHourPaint);
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            super.onVisibilityChanged(visible);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onVisibilityChanged: " + visible);
+            }
+
+            if (visible) {
+                registerReceiver();
+
+                // Update time zone in case it changed while we weren't visible.
+                mTime.clear(TimeZone.getDefault().getID());
+                mTime.setToNow();
+            } else {
+                unregisterReceiver();
+            }
+
+            // Whether the timer should be running depends on whether we're visible (as well as
+            // whether we're in ambient mode), so we may need to start or stop the timer.
+            updateTimer();
+        }
+
+        private void registerReceiver() {
+            if (mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = true;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+            AnalogWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
+        }
+
+        private void unregisterReceiver() {
+            if (!mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = false;
+            AnalogWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
+        }
+
+        /**
+         * Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently
+         * or stops it if it shouldn't be running but currently is.
+         */
+        private void updateTimer() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "updateTimer");
+            }
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            if (shouldTimerBeRunning()) {
+                mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
+            }
+        }
+
+        /**
+         * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should
+         * only run when we're visible and in interactive mode.
+         */
+        private boolean shouldTimerBeRunning() {
+            return isVisible() && !isInAmbientMode();
+        }
+
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CalendarWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CalendarWatchFaceService.java
new file mode 100644
index 0000000..a8ab955
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CalendarWatchFaceService.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.support.wearable.provider.WearableCalendarContract;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.DynamicLayout;
+import android.text.Editable;
+import android.text.Html;
+import android.text.Layout;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * Proof of concept sample watch face that demonstrates how a watch face can load calendar data.
+ */
+public class CalendarWatchFaceService extends CanvasWatchFaceService {
+    private static final String TAG = "CalendarWatchFace";
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine {
+
+        static final int BACKGROUND_COLOR = Color.BLACK;
+        static final int FOREGROUND_COLOR = Color.WHITE;
+        static final int TEXT_SIZE = 25;
+        static final int MSG_LOAD_MEETINGS = 0;
+
+        /** Editable string containing the text to draw with the number of meetings in bold. */
+        final Editable mEditable = new SpannableStringBuilder();
+
+        /** Width specified when {@link #mLayout} was created. */
+        int mLayoutWidth;
+
+        /** Layout to wrap {@link #mEditable} onto multiple lines. */
+        DynamicLayout mLayout;
+
+        /** Paint used to draw text. */
+        final TextPaint mTextPaint = new TextPaint();
+
+        int mNumMeetings;
+
+        private AsyncTask<Void, Void, Integer> mLoadMeetingsTask;
+
+        /** Handler to load the meetings once a minute in interactive mode. */
+        final Handler mLoadMeetingsHandler = new Handler() {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_LOAD_MEETINGS:
+                        cancelLoadMeetingTask();
+                        mLoadMeetingsTask = new LoadMeetingsTask();
+                        mLoadMeetingsTask.execute();
+                        break;
+                }
+            }
+        };
+
+        private boolean mIsReceiverRegistered;
+
+        private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (Intent.ACTION_PROVIDER_CHANGED.equals(intent.getAction())
+                        && WearableCalendarContract.CONTENT_URI.equals(intent.getData())) {
+                    cancelLoadMeetingTask();
+                    mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
+                }
+            }
+        };
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+            super.onCreate(holder);
+            setWatchFaceStyle(new WatchFaceStyle.Builder(CalendarWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(false)
+                    .build());
+
+            mTextPaint.setColor(FOREGROUND_COLOR);
+            mTextPaint.setTextSize(TEXT_SIZE);
+
+            mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
+        }
+
+        @Override
+        public void onDestroy() {
+            mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS);
+            cancelLoadMeetingTask();
+            super.onDestroy();
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            // Create or update mLayout if necessary.
+            if (mLayout == null || mLayoutWidth != bounds.width()) {
+                mLayoutWidth = bounds.width();
+                mLayout = new DynamicLayout(mEditable, mTextPaint, mLayoutWidth,
+                        Layout.Alignment.ALIGN_NORMAL, 1 /* spacingMult */, 0 /* spacingAdd */,
+                        false /* includePad */);
+            }
+
+            // Update the contents of mEditable.
+            mEditable.clear();
+            mEditable.append(Html.fromHtml(getResources().getQuantityString(
+                    R.plurals.calendar_meetings, mNumMeetings, mNumMeetings)));
+
+            // Draw the text on a solid background.
+            canvas.drawColor(BACKGROUND_COLOR);
+            mLayout.draw(canvas);
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            super.onVisibilityChanged(visible);
+            if (visible) {
+                IntentFilter filter = new IntentFilter(Intent.ACTION_PROVIDER_CHANGED);
+                filter.addDataScheme("content");
+                filter.addDataAuthority(WearableCalendarContract.AUTHORITY, null);
+                registerReceiver(mBroadcastReceiver, filter);
+                mIsReceiverRegistered = true;
+
+                mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
+            } else {
+                if (mIsReceiverRegistered) {
+                    unregisterReceiver(mBroadcastReceiver);
+                    mIsReceiverRegistered = false;
+                }
+                mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS);
+            }
+        }
+
+        private void onMeetingsLoaded(Integer result) {
+            if (result != null) {
+                mNumMeetings = result;
+                invalidate();
+            }
+        }
+
+        private void cancelLoadMeetingTask() {
+            if (mLoadMeetingsTask != null) {
+                mLoadMeetingsTask.cancel(true);
+            }
+        }
+
+        /**
+         * Asynchronous task to load the meetings from the content provider and report the number of
+         * meetings back via {@link #onMeetingsLoaded}.
+         */
+        private class LoadMeetingsTask extends AsyncTask<Void, Void, Integer> {
+            private PowerManager.WakeLock mWakeLock;
+
+            @Override
+            protected Integer doInBackground(Void... voids) {
+                PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+                mWakeLock = powerManager.newWakeLock(
+                        PowerManager.PARTIAL_WAKE_LOCK, "CalendarWatchFaceWakeLock");
+                mWakeLock.acquire();
+
+                long begin = System.currentTimeMillis();
+                Uri.Builder builder =
+                        WearableCalendarContract.Instances.CONTENT_URI.buildUpon();
+                ContentUris.appendId(builder, begin);
+                ContentUris.appendId(builder, begin + DateUtils.DAY_IN_MILLIS);
+                final Cursor cursor = getContentResolver().query(builder.build(),
+                        null, null, null, null);
+                int numMeetings = cursor.getCount();
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Num meetings: " + numMeetings);
+                }
+                return numMeetings;
+            }
+
+            @Override
+            protected void onPostExecute(Integer result) {
+                releaseWakeLock();
+                onMeetingsLoaded(result);
+            }
+
+            @Override
+            protected void onCancelled() {
+                releaseWakeLock();
+            }
+
+            private void releaseWakeLock() {
+                if (mWakeLock != null) {
+                    mWakeLock.release();
+                    mWakeLock = null;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CardBoundsWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CardBoundsWatchFaceService.java
new file mode 100644
index 0000000..359d7af
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CardBoundsWatchFaceService.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+/**
+ * Proof of concept sample watch face that demonstrates how a watch face can detect where the peek
+ * card is. This watch face draws a border around the area where the peeking card is.
+ */
+public class CardBoundsWatchFaceService extends CanvasWatchFaceService {
+
+    private static final String TAG = "CardBoundsWatchFace";
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine {
+
+        static final int BORDER_WIDTH_PX = 5;
+
+        final Rect mCardBounds = new Rect();
+        final Paint mPaint = new Paint();
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+            super.onCreate(holder);
+            setWatchFaceStyle(new WatchFaceStyle.Builder(CardBoundsWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(true)
+                    .setPeekOpacityMode(WatchFaceStyle.PEEK_OPACITY_MODE_TRANSLUCENT)
+                    .build());
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+            }
+            super.onAmbientModeChanged(inAmbientMode);
+            invalidate();
+        }
+
+        @Override
+        public void onPeekCardPositionUpdate(Rect bounds) {
+            super.onPeekCardPositionUpdate(bounds);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onPeekCardPositionUpdate: " + bounds);
+            }
+            super.onPeekCardPositionUpdate(bounds);
+            if (!bounds.equals(mCardBounds)) {
+                mCardBounds.set(bounds);
+                invalidate();
+            }
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            // Clear screen.
+            canvas.drawColor(isInAmbientMode() ? Color.BLACK : Color.BLUE);
+
+            // Draw border around card in interactive mode.
+            if (!isInAmbientMode()) {
+                mPaint.setColor(Color.MAGENTA);
+                canvas.drawRect(mCardBounds.left - BORDER_WIDTH_PX,
+                        mCardBounds.top - BORDER_WIDTH_PX,
+                        mCardBounds.right + BORDER_WIDTH_PX,
+                        mCardBounds.bottom + BORDER_WIDTH_PX, mPaint);
+            }
+
+            // Fill area under card.
+            mPaint.setColor(isInAmbientMode() ? Color.RED : Color.GREEN);
+            canvas.drawRect(mCardBounds, mPaint);
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceConfigListenerService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceConfigListenerService.java
new file mode 100644
index 0000000..725c51a
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceConfigListenerService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link WearableListenerService} listening for {@link DigitalWatchFaceService} config messages
+ * and updating the config {@link com.google.android.gms.wearable.DataItem} accordingly.
+ */
+public class DigitalWatchFaceConfigListenerService extends WearableListenerService
+        implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+    private static final String TAG = "DigitalListenerService";
+
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override // WearableListenerService
+    public void onMessageReceived(MessageEvent messageEvent) {
+        if (!messageEvent.getPath().equals(DigitalWatchFaceUtil.PATH_WITH_FEATURE)) {
+            return;
+        }
+        byte[] rawData = messageEvent.getData();
+        // It's allowed that the message carries only some of the keys used in the config DataItem
+        // and skips the ones that we don't want to change.
+        DataMap configKeysToOverwrite = DataMap.fromByteArray(rawData);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Received watch face config message: " + configKeysToOverwrite);
+        }
+
+        if (mGoogleApiClient == null) {
+            mGoogleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
+                    .addOnConnectionFailedListener(this).addApi(Wearable.API).build();
+        }
+        if (!mGoogleApiClient.isConnected()) {
+            ConnectionResult connectionResult =
+                    mGoogleApiClient.blockingConnect(30, TimeUnit.SECONDS);
+
+            if (!connectionResult.isSuccess()) {
+                Log.e(TAG, "Failed to connect to GoogleApiClient.");
+                return;
+            }
+        }
+
+        DigitalWatchFaceUtil.overwriteKeysInConfigDataMap(mGoogleApiClient, configKeysToOverwrite);
+    }
+
+    @Override // GoogleApiClient.ConnectionCallbacks
+    public void onConnected(Bundle connectionHint) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConnected: " + connectionHint);
+        }
+    }
+
+    @Override  // GoogleApiClient.ConnectionCallbacks
+    public void onConnectionSuspended(int cause) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConnectionSuspended: " + cause);
+        }
+    }
+
+    @Override  // GoogleApiClient.OnConnectionFailedListener
+    public void onConnectionFailed(ConnectionResult result) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onConnectionFailed: " + result);
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceService.java
new file mode 100644
index 0000000..b8b1314
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceService.java
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.WindowInsets;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sample digital watch face with blinking colons and seconds. In ambient mode, the seconds are
+ * replaced with an AM/PM indicator and the colons don't blink. On devices with low-bit ambient
+ * mode, the text is drawn without anti-aliasing in ambient mode. On devices which require burn-in
+ * protection, the hours are drawn in normal rather than bold. The time is drawn with less contrast
+ * and without seconds in mute mode.
+ */
+public class DigitalWatchFaceService extends CanvasWatchFaceService {
+    private static final String TAG = "DigitalWatchFaceService";
+
+    private static final Typeface BOLD_TYPEFACE =
+            Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
+    private static final Typeface NORMAL_TYPEFACE =
+            Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
+
+    /**
+     * Update rate in milliseconds for normal (not ambient and not mute) mode. We update twice
+     * a second to blink the colons.
+     */
+    private static final long NORMAL_UPDATE_RATE_MS = 500;
+
+    /**
+     * Update rate in milliseconds for mute mode. We update every minute, like in ambient mode.
+     */
+    private static final long MUTE_UPDATE_RATE_MS = TimeUnit.MINUTES.toMillis(1);
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine implements DataApi.DataListener,
+            GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+        static final String COLON_STRING = ":";
+
+        /** Alpha value for drawing time when in mute mode. */
+        static final int MUTE_ALPHA = 100;
+
+        /** Alpha value for drawing time when not in mute mode. */
+        static final int NORMAL_ALPHA = 255;
+
+        static final int MSG_UPDATE_TIME = 0;
+
+        /** How often {@link #mUpdateTimeHandler} ticks in milliseconds. */
+        long mInteractiveUpdateRateMs = NORMAL_UPDATE_RATE_MS;
+
+        /** Handler to update the time periodically in interactive mode. */
+        final Handler mUpdateTimeHandler = new Handler() {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_UPDATE_TIME:
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "updating time");
+                        }
+                        invalidate();
+                        if (shouldTimerBeRunning()) {
+                            long timeMs = System.currentTimeMillis();
+                            long delayMs =
+                                    mInteractiveUpdateRateMs - (timeMs % mInteractiveUpdateRateMs);
+                            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+                        }
+                        break;
+                }
+            }
+        };
+
+        GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(DigitalWatchFaceService.this)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .addApi(Wearable.API)
+                .build();
+
+        final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mTime.clear(intent.getStringExtra("time-zone"));
+                mTime.setToNow();
+            }
+        };
+        boolean mRegisteredTimeZoneReceiver = false;
+
+        Paint mBackgroundPaint;
+        Paint mHourPaint;
+        Paint mMinutePaint;
+        Paint mSecondPaint;
+        Paint mAmPmPaint;
+        Paint mColonPaint;
+        float mColonWidth;
+        boolean mMute;
+        Time mTime;
+        boolean mShouldDrawColons;
+        float mXOffset;
+        float mYOffset;
+        String mAmString;
+        String mPmString;
+        int mInteractiveBackgroundColor =
+                DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND;
+        int mInteractiveHourDigitsColor =
+                DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS;
+        int mInteractiveMinuteDigitsColor =
+                DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS;
+        int mInteractiveSecondDigitsColor =
+                DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS;
+
+        /**
+         * Whether the display supports fewer bits for each color in ambient mode. When true, we
+         * disable anti-aliasing in ambient mode.
+         */
+        boolean mLowBitAmbient;
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+            super.onCreate(holder);
+
+            setWatchFaceStyle(new WatchFaceStyle.Builder(DigitalWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(false)
+                    .build());
+            Resources resources = DigitalWatchFaceService.this.getResources();
+            mYOffset = resources.getDimension(R.dimen.digital_y_offset);
+            mAmString = resources.getString(R.string.digital_am);
+            mPmString = resources.getString(R.string.digital_pm);
+
+            mBackgroundPaint = new Paint();
+            mBackgroundPaint.setColor(mInteractiveBackgroundColor);
+            mHourPaint = createTextPaint(mInteractiveHourDigitsColor, BOLD_TYPEFACE);
+            mMinutePaint = createTextPaint(mInteractiveMinuteDigitsColor);
+            mSecondPaint = createTextPaint(mInteractiveSecondDigitsColor);
+            mAmPmPaint = createTextPaint(resources.getColor(R.color.digital_am_pm));
+            mColonPaint = createTextPaint(resources.getColor(R.color.digital_colons));
+
+            mTime = new Time();
+        }
+
+        @Override
+        public void onDestroy() {
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            super.onDestroy();
+        }
+
+        private Paint createTextPaint(int defaultInteractiveColor) {
+            return createTextPaint(defaultInteractiveColor, NORMAL_TYPEFACE);
+        }
+
+        private Paint createTextPaint(int defaultInteractiveColor, Typeface typeface) {
+            Paint paint = new Paint();
+            paint.setColor(defaultInteractiveColor);
+            paint.setTypeface(typeface);
+            paint.setAntiAlias(true);
+            return paint;
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onVisibilityChanged: " + visible);
+            }
+            super.onVisibilityChanged(visible);
+
+            if (visible) {
+                mGoogleApiClient.connect();
+
+                registerReceiver();
+
+                // Update time zone in case it changed while we weren't visible.
+                mTime.clear(TimeZone.getDefault().getID());
+                mTime.setToNow();
+            } else {
+                unregisterReceiver();
+
+                if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+                    Wearable.DataApi.removeListener(mGoogleApiClient, this);
+                    mGoogleApiClient.disconnect();
+                }
+            }
+
+            // Whether the timer should be running depends on whether we're visible (as well as
+            // whether we're in ambient mode), so we may need to start or stop the timer.
+            updateTimer();
+        }
+
+        private void registerReceiver() {
+            if (mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = true;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+            DigitalWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
+        }
+
+        private void unregisterReceiver() {
+            if (!mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = false;
+            DigitalWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
+        }
+
+        @Override
+        public void onApplyWindowInsets(WindowInsets insets) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square"));
+            }
+            super.onApplyWindowInsets(insets);
+
+            // Load resources that have alternate values for round watches.
+            Resources resources = DigitalWatchFaceService.this.getResources();
+            boolean isRound = insets.isRound();
+            mXOffset = resources.getDimension(isRound
+                    ? R.dimen.digital_x_offset_round : R.dimen.digital_x_offset);
+            float textSize = resources.getDimension(isRound
+                    ? R.dimen.digital_text_size_round : R.dimen.digital_text_size);
+            float amPmSize = resources.getDimension(isRound
+                    ? R.dimen.digital_am_pm_size_round : R.dimen.digital_am_pm_size);
+
+            mHourPaint.setTextSize(textSize);
+            mMinutePaint.setTextSize(textSize);
+            mSecondPaint.setTextSize(textSize);
+            mAmPmPaint.setTextSize(amPmSize);
+            mColonPaint.setTextSize(textSize);
+
+            mColonWidth = mColonPaint.measureText(COLON_STRING);
+        }
+
+        @Override
+        public void onPropertiesChanged(Bundle properties) {
+            super.onPropertiesChanged(properties);
+
+            boolean burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
+            mHourPaint.setTypeface(burnInProtection ? NORMAL_TYPEFACE : BOLD_TYPEFACE);
+
+            mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
+
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection
+                        + ", low-bit ambient = " + mLowBitAmbient);
+            }
+        }
+
+        @Override
+        public void onTimeTick() {
+            super.onTimeTick();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
+            }
+            invalidate();
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode) {
+            super.onAmbientModeChanged(inAmbientMode);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+            }
+            adjustPaintColorToCurrentMode(mBackgroundPaint, mInteractiveBackgroundColor,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND);
+            adjustPaintColorToCurrentMode(mHourPaint, mInteractiveHourDigitsColor,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS);
+            adjustPaintColorToCurrentMode(mMinutePaint, mInteractiveMinuteDigitsColor,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS);
+            // Actually, the seconds are not rendered in the ambient mode, so we could pass just any
+            // value as ambientColor here.
+            adjustPaintColorToCurrentMode(mSecondPaint, mInteractiveSecondDigitsColor,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS);
+
+            if (mLowBitAmbient) {
+                boolean antiAlias = !inAmbientMode;
+                mHourPaint.setAntiAlias(antiAlias);
+                mMinutePaint.setAntiAlias(antiAlias);
+                mSecondPaint.setAntiAlias(antiAlias);
+                mAmPmPaint.setAntiAlias(antiAlias);
+                mColonPaint.setAntiAlias(antiAlias);
+            }
+            invalidate();
+
+            // Whether the timer should be running depends on whether we're in ambient mode (as well
+            // as whether we're visible), so we may need to start or stop the timer.
+            updateTimer();
+        }
+
+        private void adjustPaintColorToCurrentMode(Paint paint, int interactiveColor,
+                int ambientColor) {
+            paint.setColor(isInAmbientMode() ? ambientColor : interactiveColor);
+        }
+
+        @Override
+        public void onInterruptionFilterChanged(int interruptionFilter) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onInterruptionFilterChanged: " + interruptionFilter);
+            }
+            super.onInterruptionFilterChanged(interruptionFilter);
+
+            boolean inMuteMode = interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE;
+            // We only need to update once a minute in mute mode.
+            setInteractiveUpdateRateMs(inMuteMode ? MUTE_UPDATE_RATE_MS : NORMAL_UPDATE_RATE_MS);
+
+            if (mMute != inMuteMode) {
+                mMute = inMuteMode;
+                int alpha = inMuteMode ? MUTE_ALPHA : NORMAL_ALPHA;
+                mHourPaint.setAlpha(alpha);
+                mMinutePaint.setAlpha(alpha);
+                mColonPaint.setAlpha(alpha);
+                mAmPmPaint.setAlpha(alpha);
+                invalidate();
+            }
+        }
+
+        public void setInteractiveUpdateRateMs(long updateRateMs) {
+            if (updateRateMs == mInteractiveUpdateRateMs) {
+                return;
+            }
+            mInteractiveUpdateRateMs = updateRateMs;
+
+            // Stop and restart the timer so the new update rate takes effect immediately.
+            if (shouldTimerBeRunning()) {
+                updateTimer();
+            }
+        }
+
+        private void updatePaintIfInteractive(Paint paint, int interactiveColor) {
+            if (!isInAmbientMode() && paint != null) {
+                paint.setColor(interactiveColor);
+            }
+        }
+
+        private void setInteractiveBackgroundColor(int color) {
+            mInteractiveBackgroundColor = color;
+            updatePaintIfInteractive(mBackgroundPaint, color);
+        }
+
+        private void setInteractiveHourDigitsColor(int color) {
+            mInteractiveHourDigitsColor = color;
+            updatePaintIfInteractive(mHourPaint, color);
+        }
+
+        private void setInteractiveMinuteDigitsColor(int color) {
+            mInteractiveMinuteDigitsColor = color;
+            updatePaintIfInteractive(mMinutePaint, color);
+        }
+
+        private void setInteractiveSecondDigitsColor(int color) {
+            mInteractiveSecondDigitsColor = color;
+            updatePaintIfInteractive(mSecondPaint, color);
+        }
+
+        private String formatTwoDigitNumber(int hour) {
+            return String.format("%02d", hour);
+        }
+
+        private int convertTo12Hour(int hour) {
+            int result = hour % 12;
+            return (result == 0) ? 12 : result;
+        }
+
+        private String getAmPmString(int hour) {
+            return (hour < 12) ? mAmString : mPmString;
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            mTime.setToNow();
+
+            // Show colons for the first half of each second so the colons blink on when the time
+            // updates.
+            mShouldDrawColons = (System.currentTimeMillis() % 1000) < 500;
+
+            // Draw the background.
+            canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
+
+            // Draw the hours.
+            float x = mXOffset;
+            String hourString = String.valueOf(convertTo12Hour(mTime.hour));
+            canvas.drawText(hourString, x, mYOffset, mHourPaint);
+            x += mHourPaint.measureText(hourString);
+
+            // In ambient and mute modes, always draw the first colon. Otherwise, draw the
+            // first colon for the first half of each second.
+            if (isInAmbientMode() || mMute || mShouldDrawColons) {
+                canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
+            }
+            x += mColonWidth;
+
+            // Draw the minutes.
+            String minuteString = formatTwoDigitNumber(mTime.minute);
+            canvas.drawText(minuteString, x, mYOffset, mMinutePaint);
+            x += mMinutePaint.measureText(minuteString);
+
+            // In ambient and mute modes, draw AM/PM. Otherwise, draw a second blinking
+            // colon followed by the seconds.
+            if (isInAmbientMode() || mMute) {
+                x += mColonWidth;
+                canvas.drawText(getAmPmString(mTime.hour), x, mYOffset, mAmPmPaint);
+            } else {
+                if (mShouldDrawColons) {
+                    canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
+                }
+                x += mColonWidth;
+                canvas.drawText(formatTwoDigitNumber(mTime.second), x, mYOffset,
+                        mSecondPaint);
+            }
+        }
+
+        /**
+         * Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently
+         * or stops it if it shouldn't be running but currently is.
+         */
+        private void updateTimer() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "updateTimer");
+            }
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            if (shouldTimerBeRunning()) {
+                mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
+            }
+        }
+
+        /**
+         * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should
+         * only run when we're visible and in interactive mode.
+         */
+        private boolean shouldTimerBeRunning() {
+            return isVisible() && !isInAmbientMode();
+        }
+
+        private void updateConfigDataItemAndUiOnStartup() {
+            DigitalWatchFaceUtil.fetchConfigDataMap(mGoogleApiClient,
+                    new DigitalWatchFaceUtil.FetchConfigDataMapCallback() {
+                        @Override
+                        public void onConfigDataMapFetched(DataMap startupConfig) {
+                            // If the DataItem hasn't been created yet or some keys are missing,
+                            // use the default values.
+                            setDefaultValuesForMissingConfigKeys(startupConfig);
+                            DigitalWatchFaceUtil.putConfigDataItem(mGoogleApiClient, startupConfig);
+
+                            updateUiForConfigDataMap(startupConfig);
+                        }
+                    }
+            );
+        }
+
+        private void setDefaultValuesForMissingConfigKeys(DataMap config) {
+            addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_BACKGROUND_COLOR,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND);
+            addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_HOURS_COLOR,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS);
+            addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_MINUTES_COLOR,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS);
+            addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_SECONDS_COLOR,
+                    DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS);
+        }
+
+        private void addIntKeyIfMissing(DataMap config, String key, int color) {
+            if (!config.containsKey(key)) {
+                config.putInt(key, color);
+            }
+        }
+
+        @Override // DataApi.DataListener
+        public void onDataChanged(DataEventBuffer dataEvents) {
+            try {
+                for (DataEvent dataEvent : dataEvents) {
+                    if (dataEvent.getType() != DataEvent.TYPE_CHANGED) {
+                        continue;
+                    }
+
+                    DataItem dataItem = dataEvent.getDataItem();
+                    if (!dataItem.getUri().getPath().equals(
+                            DigitalWatchFaceUtil.PATH_WITH_FEATURE)) {
+                        continue;
+                    }
+
+                    DataMapItem dataMapItem = DataMapItem.fromDataItem(dataItem);
+                    DataMap config = dataMapItem.getDataMap();
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, "Config DataItem updated:" + config);
+                    }
+                    updateUiForConfigDataMap(config);
+                }
+            } finally {
+                dataEvents.close();
+            }
+        }
+
+        private void updateUiForConfigDataMap(final DataMap config) {
+            boolean uiUpdated = false;
+            for (String configKey : config.keySet()) {
+                if (!config.containsKey(configKey)) {
+                    continue;
+                }
+                int color = config.getInt(configKey);
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Found watch face config key: " + configKey + " -> "
+                            + Integer.toHexString(color));
+                }
+                if (updateUiForKey(configKey, color)) {
+                    uiUpdated = true;
+                }
+            }
+            if (uiUpdated) {
+                invalidate();
+            }
+        }
+
+        /**
+         * Updates the color of a UI item according to the given {@code configKey}. Does nothing if
+         * {@code configKey} isn't recognized.
+         *
+         * @return whether UI has been updated
+         */
+        private boolean updateUiForKey(String configKey, int color) {
+            if (configKey.equals(DigitalWatchFaceUtil.KEY_BACKGROUND_COLOR)) {
+                setInteractiveBackgroundColor(color);
+            } else if (configKey.equals(DigitalWatchFaceUtil.KEY_HOURS_COLOR)) {
+                setInteractiveHourDigitsColor(color);
+            } else if (configKey.equals(DigitalWatchFaceUtil.KEY_MINUTES_COLOR)) {
+                setInteractiveMinuteDigitsColor(color);
+            } else if (configKey.equals(DigitalWatchFaceUtil.KEY_SECONDS_COLOR)) {
+                setInteractiveSecondDigitsColor(color);
+            } else {
+                Log.w(TAG, "Ignoring unknown config key: " + configKey);
+                return false;
+            }
+            return true;
+        }
+
+        @Override  // GoogleApiClient.ConnectionCallbacks
+        public void onConnected(Bundle connectionHint) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onConnected: " + connectionHint);
+            }
+            Wearable.DataApi.addListener(mGoogleApiClient, Engine.this);
+            updateConfigDataItemAndUiOnStartup();
+        }
+
+        @Override  // GoogleApiClient.ConnectionCallbacks
+        public void onConnectionSuspended(int cause) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onConnectionSuspended: " + cause);
+            }
+        }
+
+        @Override  // GoogleApiClient.OnConnectionFailedListener
+        public void onConnectionFailed(ConnectionResult result) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onConnectionFailed: " + result);
+            }
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceUtil.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceUtil.java
new file mode 100644
index 0000000..1c4af70
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceUtil.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.graphics.Color;
+import android.net.Uri;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataItem;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+
+public final class DigitalWatchFaceUtil {
+    private static final String TAG = "DigitalWatchFaceUtil";
+
+    /**
+     * The {@link DataMap} key for {@link DigitalWatchFaceService} background color name.
+     * The color name must be a {@link String} recognized by {@link Color#parseColor}.
+     */
+    public static final String KEY_BACKGROUND_COLOR = "BACKGROUND_COLOR";
+
+    /**
+     * The {@link DataMap} key for {@link DigitalWatchFaceService} hour digits color name.
+     * The color name must be a {@link String} recognized by {@link Color#parseColor}.
+     */
+    public static final String KEY_HOURS_COLOR = "HOURS_COLOR";
+
+    /**
+     * The {@link DataMap} key for {@link DigitalWatchFaceService} minute digits color name.
+     * The color name must be a {@link String} recognized by {@link Color#parseColor}.
+     */
+    public static final String KEY_MINUTES_COLOR = "MINUTES_COLOR";
+
+    /**
+     * The {@link DataMap} key for {@link DigitalWatchFaceService} second digits color name.
+     * The color name must be a {@link String} recognized by {@link Color#parseColor}.
+     */
+    public static final String KEY_SECONDS_COLOR = "SECONDS_COLOR";
+
+    /**
+     * The path for the {@link DataItem} containing {@link DigitalWatchFaceService} configuration.
+     */
+    public static final String PATH_WITH_FEATURE = "/watch_face_config/Digital";
+
+    /**
+     * Name of the default interactive mode background color and the ambient mode background color.
+     */
+    public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_BACKGROUND = "Black";
+    public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND =
+            parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_BACKGROUND);
+
+    /**
+     * Name of the default interactive mode hour digits color and the ambient mode hour digits
+     * color.
+     */
+    public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_HOUR_DIGITS = "White";
+    public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS =
+            parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_HOUR_DIGITS);
+
+    /**
+     * Name of the default interactive mode minute digits color and the ambient mode minute digits
+     * color.
+     */
+    public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_MINUTE_DIGITS = "White";
+    public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS =
+            parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_MINUTE_DIGITS);
+
+    /**
+     * Name of the default interactive mode second digits color and the ambient mode second digits
+     * color.
+     */
+    public static final String COLOR_NAME_DEFAULT_AND_AMBIENT_SECOND_DIGITS = "Gray";
+    public static final int COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS =
+            parseColor(COLOR_NAME_DEFAULT_AND_AMBIENT_SECOND_DIGITS);
+
+    /**
+     * Callback interface to perform an action with the current config {@link DataMap} for
+     * {@link DigitalWatchFaceService}.
+     */
+    public interface FetchConfigDataMapCallback {
+        /**
+         * Callback invoked with the current config {@link DataMap} for
+         * {@link DigitalWatchFaceService}.
+         */
+        void onConfigDataMapFetched(DataMap config);
+    }
+
+    private static int parseColor(String colorName) {
+        return Color.parseColor(colorName.toLowerCase());
+    }
+
+    /**
+     * Asynchronously fetches the current config {@link DataMap} for {@link DigitalWatchFaceService}
+     * and passes it to the given callback.
+     * <p>
+     * If the current config {@link DataItem} doesn't exist, it isn't created and the callback
+     * receives an empty DataMap.
+     */
+    public static void fetchConfigDataMap(final GoogleApiClient client,
+            final FetchConfigDataMapCallback callback) {
+        Wearable.NodeApi.getLocalNode(client).setResultCallback(
+                new ResultCallback<NodeApi.GetLocalNodeResult>() {
+                    @Override
+                    public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
+                        String localNode = getLocalNodeResult.getNode().getId();
+                        Uri uri = new Uri.Builder()
+                                .scheme("wear")
+                                .path(DigitalWatchFaceUtil.PATH_WITH_FEATURE)
+                                .authority(localNode)
+                                .build();
+                        Wearable.DataApi.getDataItem(client, uri)
+                                .setResultCallback(new DataItemResultCallback(callback));
+                    }
+                }
+        );
+    }
+
+    /**
+     * Overwrites (or sets, if not present) the keys in the current config {@link DataItem} with
+     * the ones appearing in the given {@link DataMap}. If the config DataItem doesn't exist,
+     * it's created.
+     * <p>
+     * It is allowed that only some of the keys used in the config DataItem appear in
+     * {@code configKeysToOverwrite}. The rest of the keys remains unmodified in this case.
+     */
+    public static void overwriteKeysInConfigDataMap(final GoogleApiClient googleApiClient,
+            final DataMap configKeysToOverwrite) {
+
+        DigitalWatchFaceUtil.fetchConfigDataMap(googleApiClient,
+                new FetchConfigDataMapCallback() {
+                    @Override
+                    public void onConfigDataMapFetched(DataMap currentConfig) {
+                        DataMap overwrittenConfig = new DataMap();
+                        overwrittenConfig.putAll(currentConfig);
+                        overwrittenConfig.putAll(configKeysToOverwrite);
+                        DigitalWatchFaceUtil.putConfigDataItem(googleApiClient, overwrittenConfig);
+                    }
+                }
+        );
+    }
+
+    /**
+     * Overwrites the current config {@link DataItem}'s {@link DataMap} with {@code newConfig}.
+     * If the config DataItem doesn't exist, it's created.
+     */
+    public static void putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfig) {
+        PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_WITH_FEATURE);
+        DataMap configToPut = putDataMapRequest.getDataMap();
+        configToPut.putAll(newConfig);
+        Wearable.DataApi.putDataItem(googleApiClient, putDataMapRequest.asPutDataRequest())
+                .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
+                    @Override
+                    public void onResult(DataApi.DataItemResult dataItemResult) {
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, "putDataItem result status: " + dataItemResult.getStatus());
+                        }
+                    }
+                });
+    }
+
+    private static class DataItemResultCallback implements ResultCallback<DataApi.DataItemResult> {
+
+        private final FetchConfigDataMapCallback mCallback;
+
+        public DataItemResultCallback(FetchConfigDataMapCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void onResult(DataApi.DataItemResult dataItemResult) {
+            if (dataItemResult.getStatus().isSuccess()) {
+                if (dataItemResult.getDataItem() != null) {
+                    DataItem configDataItem = dataItemResult.getDataItem();
+                    DataMapItem dataMapItem = DataMapItem.fromDataItem(configDataItem);
+                    DataMap config = dataMapItem.getDataMap();
+                    mCallback.onConfigDataMapFetched(config);
+                } else {
+                    mCallback.onConfigDataMapFetched(new DataMap());
+                }
+            }
+        }
+    }
+
+    private DigitalWatchFaceUtil() { }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceWearableConfigActivity.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceWearableConfigActivity.java
new file mode 100644
index 0000000..4b309e6
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/DigitalWatchFaceWearableConfigActivity.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.wearable.view.BoxInsetLayout;
+import android.support.wearable.view.CircledImageView;
+import android.support.wearable.view.WearableListView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.Wearable;
+
+/**
+ * The watch-side config activity for {@link DigitalWatchFaceService}, which allows for setting the
+ * background color.
+ */
+public class DigitalWatchFaceWearableConfigActivity extends Activity implements
+        WearableListView.ClickListener, WearableListView.OnScrollListener {
+    private static final String TAG = "DigitalWatchFaceConfig";
+
+    private GoogleApiClient mGoogleApiClient;
+    private TextView mHeader;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_digital_config);
+
+        mHeader = (TextView) findViewById(R.id.header);
+        WearableListView listView = (WearableListView) findViewById(R.id.color_picker);
+        BoxInsetLayout content = (BoxInsetLayout) findViewById(R.id.content);
+        // BoxInsetLayout adds padding by default on round devices. Add some on square devices.
+        content.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+            @Override
+            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+                if (!insets.isRound()) {
+                    v.setPaddingRelative(
+                            (int) getResources().getDimensionPixelSize(R.dimen.content_padding_start),
+                            v.getPaddingTop(),
+                            v.getPaddingEnd(),
+                            v.getPaddingBottom());
+                }
+                return v.onApplyWindowInsets(insets);
+            }
+        });
+
+        listView.setHasFixedSize(true);
+        listView.setClickListener(this);
+        listView.addOnScrollListener(this);
+
+        String[] colors = getResources().getStringArray(R.array.color_array);
+        listView.setAdapter(new ColorListAdapter(colors));
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
+                    @Override
+                    public void onConnected(Bundle connectionHint) {
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, "onConnected: " + connectionHint);
+                        }
+                    }
+
+                    @Override
+                    public void onConnectionSuspended(int cause) {
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, "onConnectionSuspended: " + cause);
+                        }
+                    }
+                })
+                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
+                    @Override
+                    public void onConnectionFailed(ConnectionResult result) {
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, "onConnectionFailed: " + result);
+                        }
+                    }
+                })
+                .addApi(Wearable.API)
+                .build();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mGoogleApiClient.connect();
+    }
+
+    @Override
+    protected void onStop() {
+        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+            mGoogleApiClient.disconnect();
+        }
+        super.onStop();
+    }
+
+    @Override // WearableListView.ClickListener
+    public void onClick(WearableListView.ViewHolder viewHolder) {
+        ColorItemViewHolder colorItemViewHolder = (ColorItemViewHolder) viewHolder;
+        updateConfigDataItem(colorItemViewHolder.mColorItem.getColor());
+        finish();
+    }
+
+    @Override // WearableListView.ClickListener
+    public void onTopEmptyRegionClick() {}
+
+    @Override // WearableListView.OnScrollListener
+    public void onScroll(int scroll) {}
+
+    @Override // WearableListView.OnScrollListener
+    public void onAbsoluteScrollChange(int scroll) {
+        float newTranslation = Math.min(-scroll, 0);
+        mHeader.setTranslationY(newTranslation);
+    }
+
+    @Override // WearableListView.OnScrollListener
+    public void onScrollStateChanged(int scrollState) {}
+
+    @Override // WearableListView.OnScrollListener
+    public void onCentralPositionChanged(int centralPosition) {}
+
+    private void updateConfigDataItem(final int backgroundColor) {
+        DataMap configKeysToOverwrite = new DataMap();
+        configKeysToOverwrite.putInt(DigitalWatchFaceUtil.KEY_BACKGROUND_COLOR,
+                backgroundColor);
+        DigitalWatchFaceUtil.overwriteKeysInConfigDataMap(mGoogleApiClient, configKeysToOverwrite);
+    }
+
+    private class ColorListAdapter extends WearableListView.Adapter {
+        private final String[] mColors;
+
+        public ColorListAdapter(String[] colors) {
+            mColors = colors;
+        }
+
+        @Override
+        public ColorItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new ColorItemViewHolder(new ColorItem(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
+            ColorItemViewHolder colorItemViewHolder = (ColorItemViewHolder) holder;
+            String colorName = mColors[position];
+            colorItemViewHolder.mColorItem.setColor(colorName);
+
+            RecyclerView.LayoutParams layoutParams =
+                    new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.WRAP_CONTENT);
+            int colorPickerItemMargin = (int) getResources()
+                    .getDimension(R.dimen.digital_config_color_picker_item_margin);
+            // Add margins to first and last item to make it possible for user to tap on them.
+            if (position == 0) {
+                layoutParams.setMargins(0, colorPickerItemMargin, 0, 0);
+            } else if (position == mColors.length - 1) {
+                layoutParams.setMargins(0, 0, 0, colorPickerItemMargin);
+            } else {
+                layoutParams.setMargins(0, 0, 0, 0);
+            }
+            colorItemViewHolder.itemView.setLayoutParams(layoutParams);
+        }
+
+        @Override
+        public int getItemCount() {
+            return mColors.length;
+        }
+    }
+
+    /** The layout of a color item including image and label. */
+    private static class ColorItem extends LinearLayout implements
+            WearableListView.OnCenterProximityListener {
+        /** The duration of the expand/shrink animation. */
+        private static final int ANIMATION_DURATION_MS = 150;
+        /** The ratio for the size of a circle in shrink state. */
+        private static final float SHRINK_CIRCLE_RATIO = .75f;
+
+        private static final float SHRINK_LABEL_ALPHA = .5f;
+        private static final float EXPAND_LABEL_ALPHA = 1f;
+
+        private final TextView mLabel;
+        private final CircledImageView mColor;
+
+        private final float mExpandCircleRadius;
+        private final float mShrinkCircleRadius;
+
+        private final ObjectAnimator mExpandCircleAnimator;
+        private final ObjectAnimator mExpandLabelAnimator;
+        private final AnimatorSet mExpandAnimator;
+
+        private final ObjectAnimator mShrinkCircleAnimator;
+        private final ObjectAnimator mShrinkLabelAnimator;
+        private final AnimatorSet mShrinkAnimator;
+
+        public ColorItem(Context context) {
+            super(context);
+            View.inflate(context, R.layout.color_picker_item, this);
+
+            mLabel = (TextView) findViewById(R.id.label);
+            mColor = (CircledImageView) findViewById(R.id.color);
+
+            mExpandCircleRadius = mColor.getCircleRadius();
+            mShrinkCircleRadius = mExpandCircleRadius * SHRINK_CIRCLE_RATIO;
+
+            mShrinkCircleAnimator = ObjectAnimator.ofFloat(mColor, "circleRadius",
+                    mExpandCircleRadius, mShrinkCircleRadius);
+            mShrinkLabelAnimator = ObjectAnimator.ofFloat(mLabel, "alpha",
+                    EXPAND_LABEL_ALPHA, SHRINK_LABEL_ALPHA);
+            mShrinkAnimator = new AnimatorSet().setDuration(ANIMATION_DURATION_MS);
+            mShrinkAnimator.playTogether(mShrinkCircleAnimator, mShrinkLabelAnimator);
+
+            mExpandCircleAnimator = ObjectAnimator.ofFloat(mColor, "circleRadius",
+                    mShrinkCircleRadius, mExpandCircleRadius);
+            mExpandLabelAnimator = ObjectAnimator.ofFloat(mLabel, "alpha",
+                    SHRINK_LABEL_ALPHA, EXPAND_LABEL_ALPHA);
+            mExpandAnimator = new AnimatorSet().setDuration(ANIMATION_DURATION_MS);
+            mExpandAnimator.playTogether(mExpandCircleAnimator, mExpandLabelAnimator);
+        }
+
+        @Override
+        public void onCenterPosition(boolean animate) {
+            if (animate) {
+                mShrinkAnimator.cancel();
+                if (!mExpandAnimator.isRunning()) {
+                    mExpandCircleAnimator.setFloatValues(mColor.getCircleRadius(), mExpandCircleRadius);
+                    mExpandLabelAnimator.setFloatValues(mLabel.getAlpha(), EXPAND_LABEL_ALPHA);
+                    mExpandAnimator.start();
+                }
+            } else {
+                mExpandAnimator.cancel();
+                mColor.setCircleRadius(mExpandCircleRadius);
+                mLabel.setAlpha(EXPAND_LABEL_ALPHA);
+            }
+        }
+
+        @Override
+        public void onNonCenterPosition(boolean animate) {
+            if (animate) {
+                mExpandAnimator.cancel();
+                if (!mShrinkAnimator.isRunning()) {
+                    mShrinkCircleAnimator.setFloatValues(mColor.getCircleRadius(), mShrinkCircleRadius);
+                    mShrinkLabelAnimator.setFloatValues(mLabel.getAlpha(), SHRINK_LABEL_ALPHA);
+                    mShrinkAnimator.start();
+                }
+            } else {
+                mShrinkAnimator.cancel();
+                mColor.setCircleRadius(mShrinkCircleRadius);
+                mLabel.setAlpha(SHRINK_LABEL_ALPHA);
+            }
+        }
+
+        private void setColor(String colorName) {
+            mLabel.setText(colorName);
+            mColor.setCircleColor(Color.parseColor(colorName));
+        }
+
+        private int getColor() {
+            return mColor.getDefaultCircleColor();
+        }
+    }
+
+    private static class ColorItemViewHolder extends WearableListView.ViewHolder {
+        private final ColorItem mColorItem;
+
+        public ColorItemViewHolder(ColorItem colorItem) {
+            super(colorItem);
+            mColorItem = colorItem;
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/Gles2ColoredTriangleList.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/Gles2ColoredTriangleList.java
new file mode 100644
index 0000000..2441c65
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/Gles2ColoredTriangleList.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import android.opengl.GLES20;
+import android.opengl.GLU;
+import android.opengl.GLUtils;
+import android.util.Log;
+
+/**
+ * A list of triangles drawn in a single solid color using OpenGL ES 2.0.
+ */
+public class Gles2ColoredTriangleList {
+    private static final String TAG = "GlColoredTriangleList";
+
+    /** Whether to check for GL errors. This is slow, so not appropriate for production builds. */
+    private static final boolean CHECK_GL_ERRORS = false;
+
+    /** Number of coordinates per vertex in this array: one for each of x, y, and z. */
+    private static final int COORDS_PER_VERTEX = 3;
+
+    /** Number of bytes to store a float in GL. */
+    public static final int BYTES_PER_FLOAT = 4;
+
+    /** Number of bytes per vertex. */
+    private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * BYTES_PER_FLOAT;
+
+    /** Triangles have three vertices. */
+    private static final int VERTICE_PER_TRIANGLE = 3;
+
+    /**
+     * Number of components in an OpenGL color. The components are:<ol>
+     * <li>red
+     * <li>green
+     * <li>blue
+     * <li>alpha
+     * </ol>
+     */
+    private static final int NUM_COLOR_COMPONENTS = 4;
+
+    /** Shaders to render this triangle list. */
+    private final Program mProgram;
+
+    /** The VBO containing the vertex coordinates. */
+    private final FloatBuffer mVertexBuffer;
+
+    /**
+     * Color of this triangle list represented as an array of floats in the range [0, 1] in RGBA
+     * order.
+     */
+    private final float mColor[];
+
+    /** Number of coordinates in this triangle list. */
+    private final int mNumCoords;
+
+    /**
+     * Creates a Gles2ColoredTriangleList to draw a triangle list with the given vertices and color.
+     *
+     * @param program program for drawing triangles
+     * @param triangleCoords flat array of 3D coordinates of triangle vertices in counterclockwise
+     *                       order
+     * @param color color in RGBA order, each in the range [0, 1]
+     */
+    public Gles2ColoredTriangleList(Program program, float[] triangleCoords, float[] color) {
+        if (triangleCoords.length % (VERTICE_PER_TRIANGLE * COORDS_PER_VERTEX) != 0) {
+            throw new IllegalArgumentException("must be multiple"
+                    + " of VERTICE_PER_TRIANGLE * COORDS_PER_VERTEX coordinates");
+        }
+        if (color.length != NUM_COLOR_COMPONENTS) {
+            throw new IllegalArgumentException("wrong number of color components");
+        }
+        mProgram = program;
+        mColor = color;
+
+        ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT);
+
+        // Use the device hardware's native byte order.
+        bb.order(ByteOrder.nativeOrder());
+
+        // Create a FloatBuffer that wraps the ByteBuffer.
+        mVertexBuffer = bb.asFloatBuffer();
+
+        // Add the coordinates to the FloatBuffer.
+        mVertexBuffer.put(triangleCoords);
+
+        // Go back to the start for reading.
+        mVertexBuffer.position(0);
+
+        mNumCoords = triangleCoords.length / COORDS_PER_VERTEX;
+    }
+
+    /**
+     * Draws this triangle list using OpenGL commands.
+     *
+     * @param mvpMatrix the Model View Project matrix to draw this triangle list
+     */
+    public void draw(float[] mvpMatrix) {
+        // Pass the MVP matrix, vertex data, and color to OpenGL.
+        mProgram.bind(mvpMatrix, mVertexBuffer, mColor);
+
+        // Draw the triangle list.
+        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mNumCoords);
+        if (CHECK_GL_ERRORS) checkGlError("glDrawArrays");
+    }
+
+    /**
+     * Checks if any of the GL calls since the last time this method was called set an error
+     * condition. Call this method immediately after calling a GL method. Pass the name of the GL
+     * operation. For example:
+     *
+     * <pre>
+     * mColorHandle = GLES20.glGetUniformLocation(mProgram, "uColor");
+     * MyGLRenderer.checkGlError("glGetUniformLocation");</pre>
+     *
+     * If the operation is not successful, the check throws an exception.
+     *
+     * <p><em>Note</em> This is quite slow so it's best to use it sparingly in production builds.
+     *
+     * @param glOperation name of the OpenGL call to check
+     */
+    private static void checkGlError(String glOperation) {
+        int error = GLES20.glGetError();
+        if (error != GLES20.GL_NO_ERROR) {
+            String errorString = GLU.gluErrorString(error);
+            if (errorString == null) {
+                errorString = GLUtils.getEGLErrorString(error);
+            }
+            String message = glOperation + " caused GL error 0x" + Integer.toHexString(error) +
+                    ": " + errorString;
+            Log.e(TAG, message);
+            throw new RuntimeException(message);
+        }
+    }
+
+    /**
+     * Compiles an OpenGL shader.
+     *
+     * @param type {@link GLES20#GL_VERTEX_SHADER} or {@link GLES20#GL_FRAGMENT_SHADER}
+     * @param shaderCode string containing the shader source code
+     * @return ID for the shader
+     */
+    private static int loadShader(int type, String shaderCode){
+        // Create a vertex or fragment shader.
+        int shader = GLES20.glCreateShader(type);
+        if (CHECK_GL_ERRORS) checkGlError("glCreateShader");
+        if (shader == 0) {
+            throw new IllegalStateException("glCreateShader failed");
+        }
+
+        // Add the source code to the shader and compile it.
+        GLES20.glShaderSource(shader, shaderCode);
+        if (CHECK_GL_ERRORS) checkGlError("glShaderSource");
+        GLES20.glCompileShader(shader);
+        if (CHECK_GL_ERRORS) checkGlError("glCompileShader");
+
+        return shader;
+    }
+
+    /** OpenGL shaders for drawing solid colored triangle lists. */
+    public static class Program {
+        /** Trivial vertex shader that transforms the input vertex by the MVP matrix. */
+        private static final String VERTEX_SHADER_CODE = "" +
+                "uniform mat4 uMvpMatrix;\n" +
+                "attribute vec4 aPosition;\n" +
+                "void main() {\n" +
+                "    gl_Position = uMvpMatrix * aPosition;\n" +
+                "}\n";
+
+        /** Trivial fragment shader that draws with a fixed color. */
+        private static final String FRAGMENT_SHADER_CODE = "" +
+                "precision mediump float;\n" +
+                "uniform vec4 uColor;\n" +
+                "void main() {\n" +
+                "    gl_FragColor = uColor;\n" +
+                "}\n";
+
+        /** ID OpenGL uses to identify this program. */
+        private final int mProgramId;
+
+        /** Handle for uMvpMatrix uniform in vertex shader. */
+        private final int mMvpMatrixHandle;
+
+        /** Handle for aPosition attribute in vertex shader. */
+        private final int mPositionHandle;
+
+        /** Handle for uColor uniform in fragment shader. */
+        private final int mColorHandle;
+
+        /**
+         * Creates a program to draw triangle lists. For optimal drawing efficiency, one program
+         * should be used for all triangle lists being drawn.
+         */
+        public Program() {
+            // Prepare shaders.
+            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
+            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);
+
+            // Create empty OpenGL Program.
+            mProgramId = GLES20.glCreateProgram();
+            if (CHECK_GL_ERRORS) checkGlError("glCreateProgram");
+            if (mProgramId == 0) {
+                throw new IllegalStateException("glCreateProgram failed");
+            }
+
+            // Add the shaders to the program.
+            GLES20.glAttachShader(mProgramId, vertexShader);
+            if (CHECK_GL_ERRORS) checkGlError("glAttachShader");
+            GLES20.glAttachShader(mProgramId, fragmentShader);
+            if (CHECK_GL_ERRORS) checkGlError("glAttachShader");
+
+            // Link the program so it can be executed.
+            GLES20.glLinkProgram(mProgramId);
+            if (CHECK_GL_ERRORS) checkGlError("glLinkProgram");
+
+            // Get a handle to the uMvpMatrix uniform in the vertex shader.
+            mMvpMatrixHandle = GLES20.glGetUniformLocation(mProgramId, "uMvpMatrix");
+            if (CHECK_GL_ERRORS) checkGlError("glGetUniformLocation");
+
+            // Get a handle to the vertex shader's aPosition attribute.
+            mPositionHandle = GLES20.glGetAttribLocation(mProgramId, "aPosition");
+            if (CHECK_GL_ERRORS) checkGlError("glGetAttribLocation");
+
+            // Enable vertex array (VBO).
+            GLES20.glEnableVertexAttribArray(mPositionHandle);
+            if (CHECK_GL_ERRORS) checkGlError("glEnableVertexAttribArray");
+
+            // Get a handle to fragment shader's uColor uniform.
+            mColorHandle = GLES20.glGetUniformLocation(mProgramId, "uColor");
+            if (CHECK_GL_ERRORS) checkGlError("glGetUniformLocation");
+        }
+
+        /**
+         * Tells OpenGL to use this program. Call this method before drawing a sequence of
+         * triangle lists.
+         */
+        public void use() {
+            GLES20.glUseProgram(mProgramId);
+            if (CHECK_GL_ERRORS) checkGlError("glUseProgram");
+        }
+
+        /** Sends the given MVP matrix, vertex data, and color to OpenGL. */
+        public void bind(float[] mvpMatrix, FloatBuffer vertexBuffer, float[] color) {
+            // Pass the MVP matrix to OpenGL.
+            GLES20.glUniformMatrix4fv(mMvpMatrixHandle, 1 /* count */, false /* transpose */,
+                    mvpMatrix, 0 /* offset */);
+            if (CHECK_GL_ERRORS) checkGlError("glUniformMatrix4fv");
+
+            // Pass the VBO with the triangle list's vertices to OpenGL.
+            GLES20.glEnableVertexAttribArray(mPositionHandle);
+            if (CHECK_GL_ERRORS) checkGlError("glEnableVertexAttribArray");
+            GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
+                    false /* normalized */, VERTEX_STRIDE, vertexBuffer);
+            if (CHECK_GL_ERRORS) checkGlError("glVertexAttribPointer");
+
+            // Pass the triangle list's color to OpenGL.
+            GLES20.glUniform4fv(mColorHandle, 1 /* count */, color, 0 /* offset */);
+            if (CHECK_GL_ERRORS) checkGlError("glUniform4fv");
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/SweepWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/SweepWatchFaceService.java
new file mode 100644
index 0000000..44e9569
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/SweepWatchFaceService.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import java.util.TimeZone;
+
+/**
+ * Sample analog watch face with a sweep second hand. In ambient mode, the second hand isn't shown.
+ * On devices with low-bit ambient mode, the hands are drawn without anti-aliasing in ambient mode.
+ * The watch face is drawn with less contrast in mute mode.
+ *
+ * {@link AnalogWatchFaceService} is similar but has a ticking second hand.
+ */
+public class SweepWatchFaceService extends CanvasWatchFaceService {
+    private static final String TAG = "SweepWatchFaceService";
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine {
+        Paint mHourPaint;
+        Paint mMinutePaint;
+        Paint mSecondPaint;
+        Paint mTickPaint;
+        boolean mMute;
+        Time mTime;
+
+        final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mTime.clear(intent.getStringExtra("time-zone"));
+                mTime.setToNow();
+            }
+        };
+        boolean mRegisteredTimeZoneReceiver = false;
+
+        /**
+         * Whether the display supports fewer bits for each color in ambient mode. When true, we
+         * disable anti-aliasing in ambient mode.
+         */
+        boolean mLowBitAmbient;
+
+        Bitmap mBackgroundBitmap;
+        Bitmap mBackgroundScaledBitmap;
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+            super.onCreate(holder);
+
+            setWatchFaceStyle(new WatchFaceStyle.Builder(SweepWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(false)
+                    .build());
+
+            Resources resources = SweepWatchFaceService.this.getResources();
+            Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg);
+            mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap();
+
+            mHourPaint = new Paint();
+            mHourPaint.setARGB(255, 200, 200, 200);
+            mHourPaint.setStrokeWidth(5.f);
+            mHourPaint.setAntiAlias(true);
+            mHourPaint.setStrokeCap(Paint.Cap.ROUND);
+
+            mMinutePaint = new Paint();
+            mMinutePaint.setARGB(255, 200, 200, 200);
+            mMinutePaint.setStrokeWidth(3.f);
+            mMinutePaint.setAntiAlias(true);
+            mMinutePaint.setStrokeCap(Paint.Cap.ROUND);
+
+            mSecondPaint = new Paint();
+            mSecondPaint.setARGB(255, 255, 0, 0);
+            mSecondPaint.setStrokeWidth(2.f);
+            mSecondPaint.setAntiAlias(true);
+            mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
+
+            mTickPaint = new Paint();
+            mTickPaint.setARGB(100, 255, 255, 255);
+            mTickPaint.setStrokeWidth(2.f);
+            mTickPaint.setAntiAlias(true);
+
+            mTime = new Time();
+        }
+
+        @Override
+        public void onPropertiesChanged(Bundle properties) {
+            super.onPropertiesChanged(properties);
+            mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onPropertiesChanged: low-bit ambient = " + mLowBitAmbient);
+            }
+        }
+
+        @Override
+        public void onTimeTick() {
+            super.onTimeTick();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
+            }
+            invalidate();
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode) {
+            super.onAmbientModeChanged(inAmbientMode);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+            }
+            if (mLowBitAmbient) {
+                boolean antiAlias = !inAmbientMode;
+                mHourPaint.setAntiAlias(antiAlias);
+                mMinutePaint.setAntiAlias(antiAlias);
+                mSecondPaint.setAntiAlias(antiAlias);
+                mTickPaint.setAntiAlias(antiAlias);
+            }
+            invalidate();
+        }
+
+        @Override
+        public void onInterruptionFilterChanged(int interruptionFilter) {
+            super.onInterruptionFilterChanged(interruptionFilter);
+            boolean inMuteMode = (interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE);
+            if (mMute != inMuteMode) {
+                mMute = inMuteMode;
+                mHourPaint.setAlpha(inMuteMode ? 100 : 255);
+                mMinutePaint.setAlpha(inMuteMode ? 100 : 255);
+                mSecondPaint.setAlpha(inMuteMode ? 80 : 255);
+                invalidate();
+            }
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "onDraw");
+            }
+            long now = System.currentTimeMillis();
+            mTime.set(now);
+            int milliseconds = (int) (now % 1000);
+
+            int width = bounds.width();
+            int height = bounds.height();
+
+            // Draw the background, scaled to fit.
+            if (mBackgroundScaledBitmap == null
+                    || mBackgroundScaledBitmap.getWidth() != width
+                    || mBackgroundScaledBitmap.getHeight() != height) {
+                mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap,
+                        width, height, true /* filter */);
+            }
+            canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null);
+
+            // Find the center. Ignore the window insets so that, on round watches with a
+            // "chin", the watch face is centered on the entire screen, not just the usable
+            // portion.
+            float centerX = width / 2f;
+            float centerY = height / 2f;
+
+            // Draw the ticks.
+            float innerTickRadius = centerX - 10;
+            float outerTickRadius = centerX;
+            for (int tickIndex = 0; tickIndex < 12; tickIndex++) {
+                float tickRot = (float) (tickIndex * Math.PI * 2 / 12);
+                float innerX = (float) Math.sin(tickRot) * innerTickRadius;
+                float innerY = (float) -Math.cos(tickRot) * innerTickRadius;
+                float outerX = (float) Math.sin(tickRot) * outerTickRadius;
+                float outerY = (float) -Math.cos(tickRot) * outerTickRadius;
+                canvas.drawLine(centerX + innerX, centerY + innerY,
+                        centerX + outerX, centerY + outerY, mTickPaint);
+            }
+
+            float seconds = mTime.second + milliseconds / 1000f;
+            float secRot = seconds / 30f * (float) Math.PI;
+            int minutes = mTime.minute;
+            float minRot = minutes / 30f * (float) Math.PI;
+            float hrRot = ((mTime.hour + (minutes / 60f)) / 6f ) * (float) Math.PI;
+
+            float secLength = centerX - 20;
+            float minLength = centerX - 40;
+            float hrLength = centerX - 80;
+
+            if (!isInAmbientMode()) {
+                float secX = (float) Math.sin(secRot) * secLength;
+                float secY = (float) -Math.cos(secRot) * secLength;
+                canvas.drawLine(centerX, centerY, centerX + secX, centerY + secY, mSecondPaint);
+            }
+
+            float minX = (float) Math.sin(minRot) * minLength;
+            float minY = (float) -Math.cos(minRot) * minLength;
+            canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, mMinutePaint);
+
+            float hrX = (float) Math.sin(hrRot) * hrLength;
+            float hrY = (float) -Math.cos(hrRot) * hrLength;
+            canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, mHourPaint);
+
+            // Draw every frame as long as we're visible and in interactive mode.
+            if (isVisible() && !isInAmbientMode()) {
+                invalidate();
+            }
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            super.onVisibilityChanged(visible);
+
+            if (visible) {
+                registerReceiver();
+
+                // Update time zone in case it changed while we weren't visible.
+                mTime.clear(TimeZone.getDefault().getID());
+                mTime.setToNow();
+
+                invalidate();
+            } else {
+                unregisterReceiver();
+            }
+        }
+
+        private void registerReceiver() {
+            if (mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = true;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+            SweepWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
+        }
+
+        private void unregisterReceiver() {
+            if (!mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = false;
+            SweepWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/TiltWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/TiltWatchFaceService.java
new file mode 100644
index 0000000..6dd01b0
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/TiltWatchFaceService.java
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.wearable.watchface;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.opengl.GLES20;
+import android.opengl.Matrix;
+import android.support.wearable.watchface.Gles2WatchFaceService;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.format.Time;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sample watch face using OpenGL. The watch face is rendered using
+ * {@link Gles2ColoredTriangleList}s. The camera moves around in interactive mode and stops moving
+ * when the watch enters ambient mode.
+ */
+public class TiltWatchFaceService extends Gles2WatchFaceService {
+
+    private static final String TAG = "TiltWatchFaceService";
+
+    /** Expected frame rate in interactive mode. */
+    private static final long FPS = 60;
+
+    /** How long each frame is displayed at expected frame rate. */
+    private static final long FRAME_PERIOD_MS = TimeUnit.SECONDS.toMillis(1) / FPS;
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends Gles2WatchFaceService.Engine {
+        /** Cycle time before the camera motion repeats. */
+        private static final long CYCLE_PERIOD_SECONDS = 5;
+
+        /** Number of camera angles to precompute. */
+        private final int mNumCameraAngles = (int) (CYCLE_PERIOD_SECONDS * FPS);
+
+        /** Projection transformation matrix. Converts from 3D to 2D. */
+        private final float[] mProjectionMatrix = new float[16];
+
+        /**
+         * View transformation matrices to use in interactive mode. Converts from world to camera-
+         * relative coordinates. One matrix per camera position.
+         */
+        private final float[][] mViewMatrices = new float[mNumCameraAngles][16];
+
+        /** The view transformation matrix to use in ambient mode */
+        private final float[] mAmbientViewMatrix = new float[16];
+
+        /**
+         * Model transformation matrices. Converts from model-relative coordinates to world
+         * coordinates. One matrix per degree of rotation.
+         */
+        private final float[][] mModelMatrices = new float[360][16];
+
+        /**
+         * Products of {@link #mViewMatrices} and {@link #mProjectionMatrix}. One matrix per camera
+         * position.
+         */
+        private final float[][] mVpMatrices = new float[mNumCameraAngles][16];
+
+        /** The product of {@link #mAmbientViewMatrix} and {@link #mProjectionMatrix} */
+        private final float[] mAmbientVpMatrix = new float[16];
+
+        /**
+         * Product of {@link #mModelMatrices}, {@link #mViewMatrices}, and
+         * {@link #mProjectionMatrix}.
+         */
+        private final float[] mMvpMatrix = new float[16];
+
+        /** Triangles for the 4 major ticks. These are grouped together to speed up rendering. */
+        private Gles2ColoredTriangleList mMajorTickTriangles;
+
+        /** Triangles for the 8 minor ticks. These are grouped together to speed up rendering. */
+        private Gles2ColoredTriangleList mMinorTickTriangles;
+
+        /** Triangle for the second hand. */
+        private Gles2ColoredTriangleList mSecondHandTriangle;
+
+        /** Triangle for the minute hand. */
+        private Gles2ColoredTriangleList mMinuteHandTriangle;
+
+        /** Triangle for the hour hand. */
+        private Gles2ColoredTriangleList mHourHandTriangle;
+
+        private Time mTime = new Time();
+
+        /** Whether we've registered {@link #mTimeZoneReceiver}. */
+        private boolean mRegisteredTimeZoneReceiver;
+
+        private final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mTime.clear(intent.getStringExtra("time-zone"));
+                mTime.setToNow();
+            }
+        };
+
+        @Override
+        public void onCreate(SurfaceHolder surfaceHolder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+            super.onCreate(surfaceHolder);
+            setWatchFaceStyle(new WatchFaceStyle.Builder(TiltWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setStatusBarGravity(Gravity.RIGHT | Gravity.TOP)
+                    .setHotwordIndicatorGravity(Gravity.LEFT | Gravity.TOP)
+                    .setShowSystemUiTime(false)
+                    .build());
+        }
+
+        @Override
+        public void onGlContextCreated() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onGlContextCreated");
+            }
+            super.onGlContextCreated();
+
+            // Create program for drawing triangles.
+            Gles2ColoredTriangleList.Program triangleProgram =
+                    new Gles2ColoredTriangleList.Program();
+
+            // We only draw triangles which all use the same program so we don't need to switch
+            // programs mid-frame. This means we can tell OpenGL to use this program only once
+            // rather than having to do so for each frame. This makes OpenGL draw faster.
+            triangleProgram.use();
+
+            // Create triangles for the ticks.
+            mMajorTickTriangles = createMajorTicks(triangleProgram);
+            mMinorTickTriangles = createMinorTicks(triangleProgram);
+
+            // Create triangles for the hands.
+            mSecondHandTriangle = createHand(
+                    triangleProgram,
+                    0.02f /* width */,
+                    1.0f /* height */,
+                    new float[]{
+                            1.0f /* red */,
+                            0.0f /* green */,
+                            0.0f /* blue */,
+                            1.0f /* alpha */
+                    }
+            );
+            mMinuteHandTriangle = createHand(
+                    triangleProgram,
+                    0.06f /* width */,
+                    0.8f /* height */,
+                    new float[]{
+                            0.7f /* red */,
+                            0.7f /* green */,
+                            0.7f /* blue */,
+                            1.0f /* alpha */
+                    }
+            );
+            mHourHandTriangle = createHand(
+                    triangleProgram,
+                    0.1f /* width */,
+                    0.5f /* height */,
+                    new float[]{
+                            0.9f /* red */,
+                            0.9f /* green */,
+                            0.9f /* blue */,
+                            1.0f /* alpha */
+                    }
+            );
+
+            // Precompute the clock angles.
+            for (int i = 0; i < mModelMatrices.length; ++i) {
+                Matrix.setRotateM(mModelMatrices[i], 0, i, 0, 0, 1);
+            }
+
+            // Precompute the camera angles.
+            for (int i = 0; i < mNumCameraAngles; ++i) {
+                // Set the camera position (View matrix). When active, move the eye around to show
+                // off that this is 3D.
+                final float cameraAngle = (float) (((float) i) / mNumCameraAngles * 2 * Math.PI);
+                final float eyeX = (float) Math.cos(cameraAngle);
+                final float eyeY = (float) Math.sin(cameraAngle);
+                Matrix.setLookAtM(mViewMatrices[i],
+                        0, // dest index
+                        eyeX, eyeY, -3, // eye
+                        0, 0, 0, // center
+                        0, 1, 0); // up vector
+            }
+
+            Matrix.setLookAtM(mAmbientViewMatrix,
+                    0, // dest index
+                    0, 0, -3, // eye
+                    0, 0, 0, // center
+                    0, 1, 0); // up vector
+        }
+
+        @Override
+        public void onGlSurfaceCreated(int width, int height) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onGlSurfaceCreated: " + width + " x " + height);
+            }
+            super.onGlSurfaceCreated(width, height);
+
+            // Update the projection matrix based on the new aspect ratio.
+            final float aspectRatio = (float) width / height;
+            Matrix.frustumM(mProjectionMatrix,
+                    0 /* offset */,
+                    -aspectRatio /* left */,
+                    aspectRatio /* right */,
+                    -1 /* bottom */,
+                    1 /* top */,
+                    2 /* near */,
+                    7 /* far */);
+
+            // Precompute the products of Projection and View matrices for each camera angle.
+            for (int i = 0; i < mNumCameraAngles; ++i) {
+                Matrix.multiplyMM(mVpMatrices[i], 0, mProjectionMatrix, 0, mViewMatrices[i], 0);
+            }
+
+            Matrix.multiplyMM(mAmbientVpMatrix, 0, mProjectionMatrix, 0, mAmbientViewMatrix, 0);
+        }
+
+        /**
+         * Creates a triangle for a hand on the watch face.
+         *
+         * @param program program for drawing triangles
+         * @param width width of base of triangle
+         * @param length length of triangle
+         * @param color color in RGBA order, each in the range [0, 1]
+         */
+        private Gles2ColoredTriangleList createHand(Gles2ColoredTriangleList.Program program,
+                float width, float length, float[] color) {
+            // Create the data for the VBO.
+            float[] triangleCoords = new float[]{
+                    // in counterclockwise order:
+                    0, length, 0,   // top
+                    -width / 2, 0, 0,   // bottom left
+                    width / 2, 0, 0    // bottom right
+            };
+            return new Gles2ColoredTriangleList(program, triangleCoords, color);
+        }
+
+        /**
+         * Creates a triangle list for the major ticks on the watch face.
+         *
+         * @param program program for drawing triangles
+         */
+        private Gles2ColoredTriangleList createMajorTicks(
+                Gles2ColoredTriangleList.Program program) {
+            // Create the data for the VBO.
+            float[] trianglesCoords = new float[9 * 4];
+            for (int i = 0; i < 4; i++) {
+                float[] triangleCoords = getMajorTickTriangleCoords(i);
+                System.arraycopy(triangleCoords, 0, trianglesCoords, i * 9, triangleCoords.length);
+            }
+
+            return new Gles2ColoredTriangleList(program, trianglesCoords,
+                    new float[]{
+                            1.0f /* red */,
+                            1.0f /* green */,
+                            1.0f /* blue */,
+                            1.0f /* alpha */
+                    }
+            );
+        }
+
+        /**
+         * Creates a triangle list for the minor ticks on the watch face.
+         *
+         * @param program program for drawing triangles
+         */
+        private Gles2ColoredTriangleList createMinorTicks(
+                Gles2ColoredTriangleList.Program program) {
+            // Create the data for the VBO.
+            float[] trianglesCoords = new float[9 * (12 - 4)];
+            int index = 0;
+            for (int i = 0; i < 12; i++) {
+                if (i % 3 == 0) {
+                    // This is where a major tick goes, so skip it.
+                    continue;
+                }
+                float[] triangleCoords = getMinorTickTriangleCoords(i);
+                System.arraycopy(triangleCoords, 0, trianglesCoords, index, triangleCoords.length);
+                index += 9;
+            }
+
+            return new Gles2ColoredTriangleList(program, trianglesCoords,
+                    new float[]{
+                            0.5f /* red */,
+                            0.5f /* green */,
+                            0.5f /* blue */,
+                            1.0f /* alpha */
+                    }
+            );
+        }
+
+        private float[] getMajorTickTriangleCoords(int index) {
+            return getTickTriangleCoords(0.03f /* width */, 0.09f /* length */,
+                    index * 360 / 4 /* angleDegrees */);
+        }
+
+        private float[] getMinorTickTriangleCoords(int index) {
+            return getTickTriangleCoords(0.02f /* width */, 0.06f /* length */,
+                    index * 360 / 12 /* angleDegrees */);
+        }
+
+        private float[] getTickTriangleCoords(float width, float length, int angleDegrees) {
+            // Create the data for the VBO.
+            float[] coords = new float[]{
+                    // in counterclockwise order:
+                    0, 1, 0,   // top
+                    width / 2, length + 1, 0,   // bottom left
+                    -width / 2, length + 1, 0    // bottom right
+            };
+
+            rotateCoords(coords, angleDegrees);
+            return coords;
+        }
+
+        /**
+         * Destructively rotates the given coordinates in the XY plane about the origin by the given
+         * angle.
+         *
+         * @param coords flattened 3D coordinates
+         * @param angleDegrees angle in degrees clockwise when viewed from negative infinity on the
+         *                     Z axis
+         */
+        private void rotateCoords(float[] coords, int angleDegrees) {
+            double angleRadians = Math.toRadians(angleDegrees);
+            double cos = Math.cos(angleRadians);
+            double sin = Math.sin(angleRadians);
+            for (int i = 0; i < coords.length; i += 3) {
+                float x = coords[i];
+                float y = coords[i + 1];
+                coords[i] = (float) (cos * x - sin * y);
+                coords[i + 1] = (float) (sin * x + cos * y);
+            }
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+            }
+            super.onAmbientModeChanged(inAmbientMode);
+            invalidate();
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onVisibilityChanged: " + visible);
+            }
+            super.onVisibilityChanged(visible);
+            if (visible) {
+                registerReceiver();
+
+                // Update time zone in case it changed while we were detached.
+                mTime.clear(TimeZone.getDefault().getID());
+                mTime.setToNow();
+
+                invalidate();
+            } else {
+                unregisterReceiver();
+            }
+        }
+
+        private void registerReceiver() {
+            if (mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = true;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+            TiltWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter);
+        }
+
+        private void unregisterReceiver() {
+            if (!mRegisteredTimeZoneReceiver) {
+                return;
+            }
+            mRegisteredTimeZoneReceiver = false;
+            TiltWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver);
+        }
+
+        @Override
+        public void onTimeTick() {
+            super.onTimeTick();
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
+            }
+            invalidate();
+        }
+
+        @Override
+        public void onDraw() {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "onDraw");
+            }
+            super.onDraw();
+            final float[] vpMatrix;
+
+            // Draw background color and select the appropriate view projection matrix. The
+            // background should always be black in ambient mode. The view projection matrix used is
+            // overhead in ambient. In interactive mode, it's tilted depending on the current time.
+            if (isInAmbientMode()) {
+                GLES20.glClearColor(0, 0, 0, 1);
+                vpMatrix = mAmbientVpMatrix;
+            } else {
+                GLES20.glClearColor(0.5f, 0.2f, 0.2f, 1);
+                final int cameraIndex =
+                        (int) ((System.currentTimeMillis() / FRAME_PERIOD_MS) % mNumCameraAngles);
+                vpMatrix = mVpMatrices[cameraIndex];
+            }
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+
+            // Compute angle indices for the three hands.
+            mTime.setToNow();
+            final int secIndex = mTime.second * 360 / 60;
+            final int minIndex = mTime.minute * 360 / 60;
+            final int hoursIndex = (mTime.hour % 12) * 360 / 12 + mTime.minute * 360 / 60 / 12;
+
+            // Draw triangles from back to front. Don't draw the second hand in ambient mode.
+            {
+                // Combine the model matrix with the projection and camera view.
+                Matrix.multiplyMM(mMvpMatrix, 0, vpMatrix, 0, mModelMatrices[hoursIndex], 0);
+
+                // Draw the triangle.
+                mHourHandTriangle.draw(mMvpMatrix);
+            }
+            {
+                // Combine the model matrix with the projection and camera view.
+                Matrix.multiplyMM(mMvpMatrix, 0, vpMatrix, 0, mModelMatrices[minIndex], 0);
+
+                // Draw the triangle.
+                mMinuteHandTriangle.draw(mMvpMatrix);
+            }
+            if (!isInAmbientMode()) {
+                // Combine the model matrix with the projection and camera view.
+                Matrix.multiplyMM(mMvpMatrix, 0, vpMatrix, 0, mModelMatrices[secIndex], 0);
+
+                // Draw the triangle.
+                mSecondHandTriangle.draw(mMvpMatrix);
+            }
+            {
+                // Draw the major and minor ticks.
+                mMajorTickTriangles.draw(vpMatrix);
+                mMinorTickTriangles.draw(vpMatrix);
+            }
+
+            // Draw every frame as long as we're visible and in interactive mode.
+            if (isVisible() && !isInAmbientMode()) {
+                invalidate();
+            }
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/bg.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/bg.png
new file mode 100644
index 0000000..5199af2
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/bg.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_analog.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_analog.png
new file mode 100644
index 0000000..ed6960d
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_analog.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_analog_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_analog_circular.png
new file mode 100644
index 0000000..a3affe2
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_analog_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_calendar.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_calendar.png
new file mode 100644
index 0000000..928aa1f
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_calendar.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_calendar_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_calendar_circular.png
new file mode 100644
index 0000000..cb1d27b
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_calendar_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_card_bounds.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_card_bounds.png
new file mode 100644
index 0000000..f87b6c5
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_card_bounds.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_card_bounds_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_card_bounds_circular.png
new file mode 100644
index 0000000..387f59e
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_card_bounds_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_digital.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_digital.png
new file mode 100644
index 0000000..4853a64
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_digital.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_digital_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_digital_circular.png
new file mode 100644
index 0000000..efeac34
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_digital_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_tilt.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_tilt.png
new file mode 100644
index 0000000..aab5f18
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_tilt.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_tilt_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_tilt_circular.png
new file mode 100644
index 0000000..31d3a1f
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_tilt_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_digital_config.xml b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_digital_config.xml
new file mode 100644
index 0000000..a368390
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_digital_config.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/config_activity_background">
+    <TextView
+        android:id="@+id/header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="16dp"
+        android:layout_marginTop="14dp"
+        android:textSize="24sp"
+        android:textColor="@color/config_activity_header_text_color"
+        android:text="@string/digital_background_color"
+        android:fontFamily="sans-serif-condensed-light"
+        app:layout_box="left|top"/>
+    <android.support.wearable.view.WearableListView
+        android:id="@+id/color_picker"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"
+        app:layout_box="left|right"/>
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/layout/color_picker_item.xml b/wearable/wear/WatchFace/Wearable/src/main/res/layout/color_picker_item.xml
new file mode 100644
index 0000000..9b07e2a
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/layout/color_picker_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <android.support.wearable.view.CircledImageView
+        android:id="@+id/color"
+        android:layout_width="52dp"
+        android:layout_height="52dp"
+        android:layout_gravity="center_vertical"
+        app:circle_border_color="@color/color_item_circle_border_color"
+        app:circle_radius="26dp"
+        app:circle_border_width="1dp"/>
+
+    <TextView
+        android:id="@+id/label"
+        android:fontFamily="sans-serif-condensed-light"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="10dp"
+        android:textColor="@color/color_item_label_color"
+        android:textSize="18sp"/>
+</merge>
\ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/values/color.xml b/wearable/wear/WatchFace/Wearable/src/main/res/values/color.xml
new file mode 100644
index 0000000..0da08ed
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <color name="digital_am_pm">#aaaaa0</color>
+    <color name="digital_colons">#aaaaa0</color>
+    <color name="config_activity_background">#ffffff</color>
+    <color name="config_activity_header_text_color">#959595</color>
+    <color name="color_item_label_color">#000000</color>
+    <color name="color_item_circle_border_color">#424242</color>
+</resources>
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml b/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..8f04e56
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <dimen name="digital_text_size">40dp</dimen>
+    <dimen name="digital_text_size_round">45dp</dimen>
+    <dimen name="digital_am_pm_size">25dp</dimen>
+    <dimen name="digital_am_pm_size_round">30dp</dimen>
+    <dimen name="digital_x_offset">15dp</dimen>
+    <dimen name="digital_x_offset_round">25dp</dimen>
+    <dimen name="digital_y_offset">90dp</dimen>
+    <dimen name="digital_config_color_picker_item_margin">32dp</dimen>
+    <dimen name="content_padding_start">12dp</dimen>
+</resources>
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml b/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e54591f
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+    <string name="app_name">WatchFace</string>
+    <string name="tilt_name">Sample Tilt</string>
+    <string name="analog_name">Sample Analog</string>
+    <string name="sweep_name">Sample Sweep</string>
+    <string name="card_bounds_name">Sample Card Bounds</string>
+    <string name="digital_name">Sample Digital</string>
+    <string name="digital_background_color">Background color</string>
+    <string name="digital_config_name">Digital watch face configuration</string>
+    <string name="digital_am">AM</string>
+    <string name="digital_pm">PM</string>
+    <string name="calendar_name">Sample Calendar</string>
+    <plurals name="calendar_meetings">
+        <item quantity="one">&lt;br&gt;&lt;br&gt;&lt;br&gt;You have &lt;b&gt;%1$d&lt;/b&gt; meeting in the next 24 hours.</item>
+        <item quantity="other">&lt;br&gt;&lt;br&gt;&lt;br&gt;You have &lt;b&gt;%1$d&lt;/b&gt; meetings in the next 24 hours.</item>
+    </plurals>
+
+    <!-- TODO: this should be shared (needs covering all the samples with Gradle build model) -->
+    <string name="color_black">Black</string>
+    <string name="color_blue">Blue</string>
+    <string name="color_gray">Gray</string>
+    <string name="color_green">Green</string>
+    <string name="color_navy">Navy</string>
+    <string name="color_red">Red</string>
+    <string name="color_white">White</string>
+
+    <string-array name="color_array">
+        <item>@string/color_black</item>
+        <item>@string/color_blue</item>
+        <item>@string/color_gray</item>
+        <item>@string/color_green</item>
+        <item>@string/color_navy</item>
+        <item>@string/color_red</item>
+        <item>@string/color_white</item>
+    </string-array>
+</resources>
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/xml/watch_face.xml b/wearable/wear/WatchFace/Wearable/src/main/res/xml/watch_face.xml
new file mode 100644
index 0000000..aa2e343
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/xml/watch_face.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/ui/notifications/LNotifications/build.gradle b/wearable/wear/WatchFace/build.gradle
similarity index 100%
rename from ui/notifications/LNotifications/build.gradle
rename to wearable/wear/WatchFace/build.gradle
diff --git a/ui/notifications/LNotifications/buildSrc/build.gradle b/wearable/wear/WatchFace/buildSrc/build.gradle
similarity index 100%
rename from ui/notifications/LNotifications/buildSrc/build.gradle
rename to wearable/wear/WatchFace/buildSrc/build.gradle
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/ui/notifications/BasicNotifications/gradlew b/wearable/wear/WatchFace/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to wearable/wear/WatchFace/gradlew
diff --git a/wearable/wear/WatchFace/gradlew.bat b/wearable/wear/WatchFace/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/wearable/wear/WatchFace/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/wearable/wear/WatchFace/screenshots/analog_and_sweep_face.png b/wearable/wear/WatchFace/screenshots/analog_and_sweep_face.png
new file mode 100644
index 0000000..df0820e
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/analog_and_sweep_face.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/calendar_face.png b/wearable/wear/WatchFace/screenshots/calendar_face.png
new file mode 100644
index 0000000..94214c0
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/calendar_face.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/card_bounds_face.png b/wearable/wear/WatchFace/screenshots/card_bounds_face.png
new file mode 100644
index 0000000..0ee827e
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/card_bounds_face.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/card_bounds_face2.png b/wearable/wear/WatchFace/screenshots/card_bounds_face2.png
new file mode 100644
index 0000000..c9c107e
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/card_bounds_face2.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/digital_face.png b/wearable/wear/WatchFace/screenshots/digital_face.png
new file mode 100644
index 0000000..f2d1723
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/digital_face.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/digital_phoneside_config.png b/wearable/wear/WatchFace/screenshots/digital_phoneside_config.png
new file mode 100644
index 0000000..f614d10
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/digital_phoneside_config.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/digital_watchside_config.png b/wearable/wear/WatchFace/screenshots/digital_watchside_config.png
new file mode 100644
index 0000000..d2813c4
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/digital_watchside_config.png
Binary files differ
diff --git a/wearable/wear/WatchFace/screenshots/tilt_face.png b/wearable/wear/WatchFace/screenshots/tilt_face.png
new file mode 100644
index 0000000..69601a7
--- /dev/null
+++ b/wearable/wear/WatchFace/screenshots/tilt_face.png
Binary files differ
diff --git a/wearable/wear/WatchFace/settings.gradle b/wearable/wear/WatchFace/settings.gradle
new file mode 100644
index 0000000..19d00ac
--- /dev/null
+++ b/wearable/wear/WatchFace/settings.gradle
@@ -0,0 +1 @@
+include ':Application', ':Wearable'
diff --git a/wearable/wear/WatchFace/template-params.xml b/wearable/wear/WatchFace/template-params.xml
new file mode 100644
index 0000000..0e6a7b3
--- /dev/null
+++ b/wearable/wear/WatchFace/template-params.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+
+<sample>
+    <name>WatchFace</name>
+    <group>Wearable</group>
+    <package>com.example.android.wearable.watchface</package>
+
+
+
+    <!-- change minSdk if needed-->
+    <minSdk>18</minSdk>
+
+    <dependency>com.google.android.support:wearable:1.1.+</dependency>
+
+
+    <strings>
+        <intro>
+            <![CDATA[
+This sample demonstrates how to create watch faces for android wear and includes a phone app
+and a wearable app. The wearable app has a variety of watch faces including analog, digital,
+opengl, calendar, etc. It also includes a watch-side configuration example. The phone app
+includes a phone-side configuration example.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base"/>
+    <template src="Wear"/>
+
+</sample>
diff --git a/wearable/wear/WatchViewStub/Application/src/main/AndroidManifest.xml b/wearable/wear/WatchViewStub/Application/src/main/AndroidManifest.xml
index aa0d790..b1b7103 100644
--- a/wearable/wear/WatchViewStub/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/WatchViewStub/Application/src/main/AndroidManifest.xml
@@ -16,12 +16,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.watchviewstub">
+    package="com.example.android.google.wearable.watchviewstub">
 
-    <uses-sdk android:minSdkVersion="19"
-              android:targetSdkVersion="19" />
+    <uses-sdk android:minSdkVersion="18"
+              android:targetSdkVersion="21" />
 
     <application android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
         android:label="@string/app_name">
     </application>
 
diff --git a/wearable/wear/WatchViewStub/Application/src/main/res/drawable-hdpi/ic_launcher.png b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..589f229
--- /dev/null
+++ b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchViewStub/Application/src/main/res/drawable-mdpi/ic_launcher.png b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..77dd571
--- /dev/null
+++ b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchViewStub/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..fe34ebe
--- /dev/null
+++ b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchViewStub/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..ab80bcd
--- /dev/null
+++ b/wearable/wear/WatchViewStub/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WatchViewStub/Shared/.gitignore b/wearable/wear/WatchViewStub/Shared/.gitignore
deleted file mode 100644
index 6eb878d..0000000
--- a/wearable/wear/WatchViewStub/Shared/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-src/template/
-src/common/
-build.gradle
diff --git a/wearable/wear/WatchViewStub/Shared/src/main/AndroidManifest.xml b/wearable/wear/WatchViewStub/Shared/src/main/AndroidManifest.xml
deleted file mode 100644
index 846fa9d..0000000
--- a/wearable/wear/WatchViewStub/Shared/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.watchviewstub.common">
-
-    <application android:allowBackup="true"
-        android:label="@string/app_name">
-    </application>
-
-</manifest>
diff --git a/wearable/wear/WatchViewStub/Shared/src/main/res/values/strings.xml b/wearable/wear/WatchViewStub/Shared/src/main/res/values/strings.xml
deleted file mode 100644
index 0f2bb90..0000000
--- a/wearable/wear/WatchViewStub/Shared/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <string name="app_name">Shared</string>
-</resources>
diff --git a/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml b/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml
index 5b33044..33a266d 100644
--- a/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml
@@ -15,10 +15,10 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.example.android.watchviewstub" >
+        package="com.example.android.google.wearable.watchviewstub" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="20" />
+              android:targetSdkVersion="21" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
@@ -28,7 +28,7 @@
             android:label="@string/app_name"
             android:theme="@android:style/Theme.DeviceDefault">
         <activity
-            android:name="com.example.android.watchviewstub.MainActivity"
+            android:name="com.example.android.google.wearable.watchviewstub.MainActivity"
             android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/wearable/wear/WatchViewStub/Wearable/src/main/java/com/example/android/google/wearable/watchviewstub/MainActivity.java b/wearable/wear/WatchViewStub/Wearable/src/main/java/com/example/android/google/wearable/watchviewstub/MainActivity.java
new file mode 100644
index 0000000..e4271d2
--- /dev/null
+++ b/wearable/wear/WatchViewStub/Wearable/src/main/java/com/example/android/google/wearable/watchviewstub/MainActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.google.wearable.watchviewstub;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.view.GestureDetectorCompat;
+import android.support.wearable.view.DismissOverlayView;
+import android.support.wearable.view.WatchViewStub;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.ScaleAnimation;
+import android.widget.RelativeLayout;
+
+public class MainActivity extends Activity {
+    private RelativeLayout mRectBackground;
+    private RelativeLayout mRoundBackground;
+
+    private GestureDetectorCompat mGestureDetector;
+    private DismissOverlayView mDismissOverlayView;
+
+    @Override
+    public void onCreate(Bundle b) {
+        super.onCreate(b);
+        setContentView(R.layout.main_activity);
+
+        WatchViewStub stub = (WatchViewStub) findViewById(R.id.stub);
+        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
+            @Override
+            public void onLayoutInflated(WatchViewStub stub) {
+                mRectBackground = (RelativeLayout) findViewById(R.id.rect_layout);
+                mRoundBackground = (RelativeLayout) findViewById(R.id.round_layout);
+            }
+        });
+
+        mDismissOverlayView = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
+        mGestureDetector = new GestureDetectorCompat(this, new LongPressListener());
+    }
+
+    /**
+     * Animates the layout when clicked. The animation used depends on whether the
+     * device is round or rectangular.
+     */
+    public void onLayoutClicked(View view) {
+        if (mRectBackground != null) {
+            ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.7f, 1.0f, 0.7f,
+                    Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
+            scaleAnimation.setDuration(300);
+            scaleAnimation.setRepeatCount(1);
+            scaleAnimation.setRepeatMode(Animation.REVERSE);
+            mRectBackground.startAnimation(scaleAnimation);
+        }
+        if (mRoundBackground != null) {
+            mRoundBackground.animate().rotationBy(360).setDuration(300).start();
+        }
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
+    }
+
+    private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
+        @Override
+        public void onLongPress(MotionEvent event) {
+            mDismissOverlayView.show();
+        }
+    }
+}
diff --git a/wearable/wear/WatchViewStub/Wearable/src/main/java/com/example/android/watchviewstub/MainActivity.java b/wearable/wear/WatchViewStub/Wearable/src/main/java/com/example/android/watchviewstub/MainActivity.java
deleted file mode 100644
index 1e931dc..0000000
--- a/wearable/wear/WatchViewStub/Wearable/src/main/java/com/example/android/watchviewstub/MainActivity.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.watchviewstub;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.view.GestureDetectorCompat;
-import android.support.wearable.view.DismissOverlayView;
-import android.support.wearable.view.WatchViewStub;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.ScaleAnimation;
-import android.widget.RelativeLayout;
-
-public class MainActivity extends Activity {
-    private RelativeLayout mRectBackground;
-    private RelativeLayout mRoundBackground;
-
-    private GestureDetectorCompat mGestureDetector;
-    private DismissOverlayView mDismissOverlayView;
-
-    @Override
-    public void onCreate(Bundle b) {
-        super.onCreate(b);
-        setContentView(R.layout.main_activity);
-
-        WatchViewStub stub = (WatchViewStub) findViewById(R.id.stub);
-        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
-            @Override
-            public void onLayoutInflated(WatchViewStub stub) {
-                mRectBackground = (RelativeLayout) findViewById(R.id.rect_layout);
-                mRoundBackground = (RelativeLayout) findViewById(R.id.round_layout);
-            }
-        });
-
-        mDismissOverlayView = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
-        mGestureDetector = new GestureDetectorCompat(this, new LongPressListener());
-    }
-
-    /**
-     * Animates the layout when clicked. The animation used depends on whether the
-     * device is round or rectangular.
-     */
-    public void onLayoutClicked(View view) {
-        if (mRectBackground != null) {
-            ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.7f, 1.0f, 0.7f,
-                    Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
-            scaleAnimation.setDuration(300);
-            scaleAnimation.setRepeatCount(1);
-            scaleAnimation.setRepeatMode(Animation.REVERSE);
-            mRectBackground.startAnimation(scaleAnimation);
-        }
-        if (mRoundBackground != null) {
-            mRoundBackground.animate().rotationBy(360).setDuration(300).start();
-        }
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent event) {
-        return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
-    }
-
-    private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
-        @Override
-        public void onLongPress(MotionEvent event) {
-            mDismissOverlayView.show();
-        }
-    }
-}
diff --git a/wearable/wear/WatchViewStub/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/WatchViewStub/gradle/wrapper/gradle-wrapper.properties
index d7f03cf..0c71e76 100644
--- a/wearable/wear/WatchViewStub/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/WatchViewStub/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/wearable/wear/WatchViewStub/screenshots/watch_view_stub_rectangular.png b/wearable/wear/WatchViewStub/screenshots/watch_view_stub_rectangular.png
new file mode 100644
index 0000000..939cb18
--- /dev/null
+++ b/wearable/wear/WatchViewStub/screenshots/watch_view_stub_rectangular.png
Binary files differ
diff --git a/wearable/wear/WatchViewStub/screenshots/watch_view_stub_round.png b/wearable/wear/WatchViewStub/screenshots/watch_view_stub_round.png
new file mode 100644
index 0000000..3055f6d
--- /dev/null
+++ b/wearable/wear/WatchViewStub/screenshots/watch_view_stub_round.png
Binary files differ
diff --git a/wearable/wear/WatchViewStub/settings.gradle b/wearable/wear/WatchViewStub/settings.gradle
index 8522c57..19d00ac 100644
--- a/wearable/wear/WatchViewStub/settings.gradle
+++ b/wearable/wear/WatchViewStub/settings.gradle
@@ -1 +1 @@
-include ':Application', ':Wearable', ':Shared'
+include ':Application', ':Wearable'
diff --git a/wearable/wear/WatchViewStub/template-params.xml b/wearable/wear/WatchViewStub/template-params.xml
index ca129bf..0688686 100644
--- a/wearable/wear/WatchViewStub/template-params.xml
+++ b/wearable/wear/WatchViewStub/template-params.xml
@@ -14,20 +14,12 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
-
-
 <sample>
     <name>WatchViewStub</name>
     <group>Wearable</group>
-    <package>com.example.android.watchviewstub</package>
-
-
-
+    <package>com.example.android.google.wearable.watchviewstub</package>
     <!-- change minSdk if needed-->
-    <minSdk>20</minSdk>
-
-
+    <minSdk>18</minSdk>
     <strings>
         <intro>
             <![CDATA[
@@ -35,10 +27,37 @@
             ]]>
         </intro>
     </strings>
-
     <template src="base"/>
     <template src="Wear"/>
-    <common src="logger"/>
-    <common src="activities"/>
+    <metadata>
+        <status>PUBLISHED</status>
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>Wearable/src/main/res/drawable-xxhdpi/ic_launcher.png</icon>
+        <screenshots>
+            <img>screenshots/watch_view_stub_rectangular.png</img>
+            <img>screenshots/watch_view_stub_round.png</img>
+        </screenshots>
+        <api_refs>
+            <android>android.support.wearable.view.WatchViewStub</android>
+            <android>android.support.wearable.view.WatchViewStub.OnLayoutInflatedListener</android>
+        </api_refs>
+        <description>
+<![CDATA[
+This sample demonstrates how to specify different layouts for round and rectangular screens.
+]]>
+        </description>
+        <intro>
+<![CDATA[
+It is a basic sample that displays a round layout on a round watch and a rectangular one on a
+rectangular watch.
 
+The corresponding layout is being inflated by the system. This sample shows a way to handle
+this programatically by using a `WatchViewStub.OnLayoutInflateListener`.
+]]>
+        </intro>
+    </metadata>
 </sample>