Merge branch 'mnc-mr-docs' into mnc-ub-dev

Large merge to reconnect automerger for docs branch to mainline.

 Conflicts:
	media/Camera2Basic/Application/src/main/java/com/example/android/camera2basic/Camera2BasicFragment.java
	media/Camera2Basic/README.md
	media/Camera2Video/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.java
	media/Camera2Video/README.md
	media/MidiSynth/template-params.xml

Issue: 28000173
Change-Id: I1fe910677f9505caeca1a6bbc7ca508c414ec839
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 8b0620f..361c4ac 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,7 +22,10 @@
 import android.content.RestrictionEntry;
 import android.content.RestrictionsManager;
 import android.content.SharedPreferences;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
 import android.text.Editable;
@@ -33,23 +36,28 @@
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 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.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
  * sample.
  */
 public class AppRestrictionEnforcerFragment extends Fragment implements
-        CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
+        CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener,
+        View.OnClickListener, ItemAddFragment.OnItemAddedListener {
 
     /**
      * Key for {@link SharedPreferences}
@@ -81,7 +89,24 @@
      */
     private static final String RESTRICTION_KEY_APPROVALS = "approvals";
 
+    /**
+     * Key for the bundle restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_PROFILE = "profile";
+    private static final String RESTRICTION_KEY_PROFILE_NAME = "name";
+    private static final String RESTRICTION_KEY_PROFILE_AGE = "age";
+
+    /**
+     * Key for the bundle array restriction in AppRestrictionSchema.
+     */
+    private static final String RESTRICTION_KEY_ITEMS = "items";
+    private static final String RESTRICTION_KEY_ITEM_KEY = "key";
+    private static final String RESTRICTION_KEY_ITEM_VALUE = "value";
+
     private static final String DELIMETER = ",";
+    private static final String SEPARATOR = ":";
+
+    private static final boolean BUNDLE_SUPPORTED = Build.VERSION.SDK_INT >= 23;
 
     /**
      * Current status of the restrictions.
@@ -94,6 +119,9 @@
     private EditText mEditNumber;
     private Spinner mSpinnerRank;
     private LinearLayout mLayoutApprovals;
+    private EditText mEditProfileName;
+    private EditText mEditProfileAge;
+    private LinearLayout mLayoutItems;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -109,6 +137,19 @@
         mEditNumber = (EditText) view.findViewById(R.id.number);
         mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
         mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
+        mEditProfileName = (EditText) view.findViewById(R.id.profile_name);
+        mEditProfileAge = (EditText) view.findViewById(R.id.profile_age);
+        mLayoutItems = (LinearLayout) view.findViewById(R.id.items);
+        view.findViewById(R.id.item_add).setOnClickListener(this);
+        View bundleLayout = view.findViewById(R.id.bundle_layout);
+        View bundleArrayLayout = view.findViewById(R.id.bundle_array_layout);
+        if (BUNDLE_SUPPORTED) {
+            bundleLayout.setVisibility(View.VISIBLE);
+            bundleArrayLayout.setVisibility(View.VISIBLE);
+        } else {
+            bundleLayout.setVisibility(View.GONE);
+            bundleArrayLayout.setVisibility(View.GONE);
+        }
     }
 
     @Override
@@ -156,6 +197,21 @@
         }
     };
 
+    private TextWatcher mWatcherProfile = new EasyTextWatcher() {
+        @Override
+        public void afterTextChanged(Editable s) {
+            try {
+                String name = mEditProfileName.getText().toString();
+                String ageString = mEditProfileAge.getText().toString();
+                if (!TextUtils.isEmpty(ageString)) {
+                    saveProfile(getActivity(), name, Integer.parseInt(ageString));
+                }
+            } catch (NumberFormatException e) {
+                Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
+            }
+        }
+    };
+
     @Override
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         switch (parent.getId()) {
@@ -171,9 +227,42 @@
         // Nothing to do
     }
 
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.item_add:
+                new ItemAddFragment().show(getChildFragmentManager(), "dialog");
+                break;
+            case R.id.item_remove:
+                String key = (String) v.getTag();
+                removeItem(key);
+                mLayoutItems.removeView((View) v.getParent());
+                break;
+        }
+    }
+
+    @Override
+    public void onItemAdded(String key, String value) {
+        key = TextUtils.replace(key,
+                new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString();
+        value = TextUtils.replace(value,
+                new String[]{DELIMETER, SEPARATOR}, new String[]{"", ""}).toString();
+        Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS);
+        Map<String, String> items = new HashMap<>();
+        if (parcelables != null) {
+            for (Parcelable parcelable : parcelables) {
+                Bundle bundle = (Bundle) parcelable;
+                items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY),
+                        bundle.getString(RESTRICTION_KEY_ITEM_VALUE));
+            }
+        }
+        items.put(key, value);
+        insertItemRow(LayoutInflater.from(getActivity()), key, value);
+        saveItems(getActivity(), items);
+    }
+
     /**
-     * Loads the restrictions for the AppRestrictionSchema sample. In this implementation, we just
-     * read the default value for the "can_say_hello" restriction.
+     * Loads the restrictions for the AppRestrictionSchema sample.
      *
      * @param activity The activity
      */
@@ -203,6 +292,28 @@
                                         TextUtils.join(DELIMETER,
                                                 restriction.getAllSelectedStrings())),
                                 DELIMETER));
+            } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_PROFILE.equals(key)) {
+                String name = null;
+                int age = 0;
+                for (RestrictionEntry entry : restriction.getRestrictions()) {
+                    String profileKey = entry.getKey();
+                    if (RESTRICTION_KEY_PROFILE_NAME.equals(profileKey)) {
+                        name = entry.getSelectedString();
+                    } else if (RESTRICTION_KEY_PROFILE_AGE.equals(profileKey)) {
+                        age = entry.getIntValue();
+                    }
+                }
+                name = prefs.getString(RESTRICTION_KEY_PROFILE_NAME, name);
+                age = prefs.getInt(RESTRICTION_KEY_PROFILE_AGE, age);
+                updateProfile(name, age);
+            } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_ITEMS.equals(key)) {
+                String itemsString = prefs.getString(RESTRICTION_KEY_ITEMS, "");
+                HashMap<String, String> items = new HashMap<>();
+                for (String itemString : TextUtils.split(itemsString, DELIMETER)) {
+                    String[] strings = itemString.split(SEPARATOR, 2);
+                    items.put(strings[0], strings[1]);
+                }
+                updateItems(activity, items);
             }
         }
     }
@@ -251,6 +362,72 @@
         }
     }
 
+    private void updateProfile(String name, int age) {
+        if (!BUNDLE_SUPPORTED) {
+            return;
+        }
+        Bundle profile = new Bundle();
+        profile.putString(RESTRICTION_KEY_PROFILE_NAME, name);
+        profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age);
+        mCurrentRestrictions.putBundle(RESTRICTION_KEY_PROFILE, profile);
+        mEditProfileName.removeTextChangedListener(mWatcherProfile);
+        mEditProfileName.setText(name);
+        mEditProfileName.addTextChangedListener(mWatcherProfile);
+        mEditProfileAge.removeTextChangedListener(mWatcherProfile);
+        mEditProfileAge.setText(String.valueOf(age));
+        mEditProfileAge.addTextChangedListener((mWatcherProfile));
+    }
+
+    private void updateItems(Context context, Map<String, String> items) {
+        if (!BUNDLE_SUPPORTED) {
+            return;
+        }
+        mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items));
+        LayoutInflater inflater = LayoutInflater.from(context);
+        mLayoutItems.removeAllViews();
+        for (String key : items.keySet()) {
+            insertItemRow(inflater, key, items.get(key));
+        }
+    }
+
+    private void insertItemRow(LayoutInflater inflater, String key, String value) {
+        View view = inflater.inflate(R.layout.item, mLayoutItems, false);
+        TextView textView = (TextView) view.findViewById(R.id.item_text);
+        textView.setText(getString(R.string.item, key, value));
+        Button remove = (Button) view.findViewById(R.id.item_remove);
+        remove.setTag(key);
+        remove.setOnClickListener(this);
+        mLayoutItems.addView(view);
+    }
+
+    @NonNull
+    private Bundle[] convertToBundles(Map<String, String> items) {
+        Bundle[] bundles = new Bundle[items.size()];
+        int i = 0;
+        for (String key : items.keySet()) {
+            Bundle bundle = new Bundle();
+            bundle.putString(RESTRICTION_KEY_ITEM_KEY, key);
+            bundle.putString(RESTRICTION_KEY_ITEM_VALUE, items.get(key));
+            bundles[i++] = bundle;
+        }
+        return bundles;
+    }
+
+    private void removeItem(String key) {
+        Parcelable[] parcelables = mCurrentRestrictions.getParcelableArray(RESTRICTION_KEY_ITEMS);
+        if (parcelables != null) {
+            Map<String, String> items = new HashMap<>();
+            for (Parcelable parcelable : parcelables) {
+                Bundle bundle = (Bundle) parcelable;
+                if (!key.equals(bundle.getString(RESTRICTION_KEY_ITEM_KEY))) {
+                    items.put(bundle.getString(RESTRICTION_KEY_ITEM_KEY),
+                            bundle.getString(RESTRICTION_KEY_ITEM_VALUE));
+                }
+            }
+            saveItems(getActivity(), items);
+        }
+    }
+
     /**
      * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
      *
@@ -333,6 +510,57 @@
                 TextUtils.join(DELIMETER, approvals)).apply();
     }
 
+    /**
+     * Saves the value for the "profile" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity
+     * @param name     The value to be set for the "name" field.
+     * @param age      The value to be set for the "age" field.
+     */
+    private void saveProfile(Activity activity, String name, int age) {
+        if (!BUNDLE_SUPPORTED) {
+            return;
+        }
+        Bundle profile = new Bundle();
+        profile.putString(RESTRICTION_KEY_PROFILE_NAME, name);
+        profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age);
+        mCurrentRestrictions.putBundle(RESTRICTION_KEY_PROFILE, profile);
+        saveRestrictions(activity);
+        editPreferences(activity).putString(RESTRICTION_KEY_PROFILE_NAME, name).apply();
+    }
+
+    /**
+     * Saves the value for the "items" restriction of AppRestrictionSchema.
+     *
+     * @param activity The activity.
+     * @param items    The values.
+     */
+    private void saveItems(Activity activity, Map<String, String> items) {
+        if (!BUNDLE_SUPPORTED) {
+            return;
+        }
+        mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items));
+        saveRestrictions(activity);
+        StringBuilder builder = new StringBuilder();
+        boolean first = true;
+        for (String key : items.keySet()) {
+            if (first) {
+                first = false;
+            } else {
+                builder.append(DELIMETER);
+            }
+            builder.append(key);
+            builder.append(SEPARATOR);
+            builder.append(items.get(key));
+        }
+        editPreferences(activity).putString(RESTRICTION_KEY_ITEMS, builder.toString()).apply();
+    }
+
+    /**
+     * Saves all the restrictions.
+     *
+     * @param activity The activity.
+     */
     private void saveRestrictions(Activity activity) {
         DevicePolicyManager devicePolicyManager
                 = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/ItemAddFragment.java b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/ItemAddFragment.java
new file mode 100644
index 0000000..cda2726
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/java/com/example/android/apprestrictionenforcer/ItemAddFragment.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.Toast;
+
+/**
+ * Provides a dialog to create a new restriction item for the sample bundle array.
+ */
+public class ItemAddFragment extends DialogFragment implements View.OnClickListener {
+
+    public interface OnItemAddedListener {
+        void onItemAdded(String key, String value);
+    }
+
+    private OnItemAddedListener mListener;
+    private EditText mEditKey;
+    private EditText mEditValue;
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        Fragment parentFragment = getParentFragment();
+        mListener = (OnItemAddedListener) (parentFragment == null ? activity : parentFragment);
+    }
+
+    @Override
+    public void onDetach() {
+        mListener = null;
+        super.onDetach();
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        getDialog().setTitle(R.string.add_item);
+        return inflater.inflate(R.layout.fragment_item_add, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        mEditKey = (EditText) view.findViewById(R.id.key);
+        mEditValue = (EditText) view.findViewById(R.id.value);
+        view.findViewById(R.id.ok).setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.ok:
+                if (addItem()) {
+                    dismiss();
+                }
+                break;
+        }
+    }
+
+    private boolean addItem() {
+        String key = mEditKey.getText().toString();
+        if (TextUtils.isEmpty(key)) {
+            Toast.makeText(getActivity(), "Input the key.", Toast.LENGTH_SHORT).show();
+            return false;
+        }
+        String value = mEditValue.getText().toString();
+        if (TextUtils.isEmpty(value)) {
+            Toast.makeText(getActivity(), "Input the value.", Toast.LENGTH_SHORT).show();
+            return false;
+        }
+        if (mListener != null) {
+            mListener.onItemAdded(key, value);
+        }
+        return true;
+    }
+
+}
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 0118191..b683989 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
@@ -119,6 +119,67 @@
 
         </LinearLayout>
 
+        <LinearLayout
+            android:id="@+id/bundle_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/profile"/>
+
+            <EditText
+                android:id="@+id/profile_name"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.75"
+                android:hint="@string/name"/>
+
+            <EditText
+                android:id="@+id/profile_age"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="0.25"
+                android:hint="@string/age"
+                android:inputType="number"/>
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:id="@+id/bundle_array_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:id="@+id/items_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:text="@string/items"/>
+
+            <LinearLayout
+                android:id="@+id/items"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_alignParentTop="true"
+                android:layout_toEndOf="@id/items_label"
+                android:orientation="vertical"
+                android:paddingEnd="16dp"
+                android:paddingStart="16dp"/>
+
+            <Button
+                android:id="@+id/item_add"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/items"
+                android:layout_toEndOf="@id/items_label"
+                android:text="@string/add"/>
+
+        </RelativeLayout>
+
     </LinearLayout>
 
 </ScrollView>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_item_add.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_item_add.xml
new file mode 100644
index 0000000..f60bb15
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/fragment_item_add.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <EditText
+        android:id="@+id/key"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
+        android:hint="@string/key"/>
+
+    <EditText
+        android:id="@+id/value"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="16dp"
+        android:layout_marginStart="16dp"
+        android:hint="@string/value"/>
+
+    <Button
+        android:id="@+id/ok"
+        style="?android:attr/buttonBarButtonStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@android:string/ok"/>
+
+</LinearLayout>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/layout/item.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/item.xml
new file mode 100644
index 0000000..66e6b3d
--- /dev/null
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/layout/item.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="wrap_content"
+              android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/item_text"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        tools:text="key:value"/>
+
+    <Button
+        android:id="@+id/item_remove"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/remove"/>
+
+</LinearLayout>
diff --git a/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml b/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
index e35daee..ead4152 100644
--- a/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
+++ b/admin/AppRestrictionEnforcer/Application/src/main/res/values/strings.xml
@@ -29,4 +29,14 @@
     <string name="number">Number: </string>
     <string name="rank">Rank: </string>
     <string name="approvals">Approvals: </string>
+    <string name="profile">Profile: </string>
+    <string name="name">Name</string>
+    <string name="age">Age</string>
+    <string name="items">Items: </string>
+    <string name="add">Add</string>
+    <string name="key">Key</string>
+    <string name="value">Value</string>
+    <string name="remove">Remove</string>
+    <string name="item">%1$s: %2$s</string>
+    <string name="add_item">Add a new item</string>
 </resources>
diff --git a/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties b/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties
index f2e517b..377495f 100644
--- a/admin/AppRestrictionEnforcer/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/AppRestrictionEnforcer/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/admin/AppRestrictionEnforcer/template-params.xml b/admin/AppRestrictionEnforcer/template-params.xml
index ff1a7a0..eee3d7e 100644
--- a/admin/AppRestrictionEnforcer/template-params.xml
+++ b/admin/AppRestrictionEnforcer/template-params.xml
@@ -23,7 +23,6 @@
     <package>com.example.android.apprestrictionenforcer</package>
 
     <minSdk>21</minSdk>
-    <compileSdkVersion>21</compileSdkVersion>
 
     <strings>
         <intro>
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 7b8dba8..bbb1ef8 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
@@ -19,7 +19,9 @@
 import android.content.Context;
 import android.content.RestrictionEntry;
 import android.content.RestrictionsManager;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
 import android.text.TextUtils;
@@ -49,6 +51,14 @@
     private static final String KEY_NUMBER = "number";
     private static final String KEY_RANK = "rank";
     private static final String KEY_APPROVALS = "approvals";
+    private static final String KEY_PROFILE = "profile";
+    private static final String KEY_PROFILE_NAME = "name";
+    private static final String KEY_PROFILE_AGE = "age";
+    private static final String KEY_ITEMS = "items";
+    private static final String KEY_ITEM_KEY = "key";
+    private static final String KEY_ITEM_VALUE = "value";
+
+    private static final boolean BUNDLE_SUPPORTED = Build.VERSION.SDK_INT >= 23;
 
     // Message to show when the button is clicked (String restriction)
     private String mMessage;
@@ -59,6 +69,8 @@
     private TextView mTextNumber;
     private TextView mTextRank;
     private TextView mTextApprovals;
+    private TextView mTextProfile;
+    private TextView mTextItems;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -73,7 +85,22 @@
         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);
+        View bundleSeparator = view.findViewById(R.id.bundle_separator);
+        mTextProfile = (TextView) view.findViewById(R.id.your_profile);
+        View bundleArraySeparator = view.findViewById(R.id.bundle_array_separator);
+        mTextItems = (TextView) view.findViewById(R.id.your_items);
         mButtonSayHello.setOnClickListener(this);
+        if (BUNDLE_SUPPORTED) {
+            bundleSeparator.setVisibility(View.VISIBLE);
+            mTextProfile.setVisibility(View.VISIBLE);
+            bundleArraySeparator.setVisibility(View.VISIBLE);
+            mTextItems.setVisibility(View.VISIBLE);
+        } else {
+            bundleSeparator.setVisibility(View.GONE);
+            mTextProfile.setVisibility(View.GONE);
+            bundleArraySeparator.setVisibility(View.GONE);
+            mTextItems.setVisibility(View.GONE);
+        }
     }
 
     @Override
@@ -86,7 +113,8 @@
         RestrictionsManager manager =
                 (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
         Bundle restrictions = manager.getApplicationRestrictions();
-        List<RestrictionEntry> entries = manager.getManifestRestrictions(getActivity().getApplicationContext().getPackageName());
+        List<RestrictionEntry> entries = manager.getManifestRestrictions(
+                getActivity().getApplicationContext().getPackageName());
         for (RestrictionEntry entry : entries) {
             String key = entry.getKey();
             Log.d(TAG, "key: " + key);
@@ -100,6 +128,10 @@
                 updateRank(entry, restrictions);
             } else if (key.equals(KEY_APPROVALS)) {
                 updateApprovals(entry, restrictions);
+            } else if (key.equals(KEY_PROFILE)) {
+                updateProfile(entry, restrictions);
+            } else if (key.equals(KEY_ITEMS)) {
+                updateItems(entry, restrictions);
             }
         }
     }
@@ -161,6 +193,67 @@
         mTextApprovals.setText(getString(R.string.approvals_you_have, text));
     }
 
+    private void updateProfile(RestrictionEntry entry, Bundle restrictions) {
+        if (!BUNDLE_SUPPORTED) {
+            return;
+        }
+        String name = null;
+        int age = 0;
+        if (restrictions == null || !restrictions.containsKey(KEY_PROFILE)) {
+            RestrictionEntry[] entries = entry.getRestrictions();
+            for (RestrictionEntry profileEntry : entries) {
+                String key = profileEntry.getKey();
+                if (key.equals(KEY_PROFILE_NAME)) {
+                    name = profileEntry.getSelectedString();
+                } else if (key.equals(KEY_PROFILE_AGE)) {
+                    age = profileEntry.getIntValue();
+                }
+            }
+        } else {
+            Bundle profile = restrictions.getBundle(KEY_PROFILE);
+            if (profile != null) {
+                name = profile.getString(KEY_PROFILE_NAME);
+                age = profile.getInt(KEY_PROFILE_AGE);
+            }
+        }
+        mTextProfile.setText(getString(R.string.your_profile, name, age));
+    }
+
+    private void updateItems(RestrictionEntry entry, Bundle restrictions) {
+        if (!BUNDLE_SUPPORTED) {
+            return;
+        }
+        StringBuilder builder = new StringBuilder();
+        if (restrictions != null) {
+            Parcelable[] parcelables = restrictions.getParcelableArray(KEY_ITEMS);
+            if (parcelables != null && parcelables.length > 0) {
+                Bundle[] items = new Bundle[parcelables.length];
+                for (int i = 0; i < parcelables.length; i++) {
+                    items[i] = (Bundle) parcelables[i];
+                }
+                boolean first = true;
+                for (Bundle item : items) {
+                    if (!item.containsKey(KEY_ITEM_KEY) || !item.containsKey(KEY_ITEM_VALUE)) {
+                        continue;
+                    }
+                    if (first) {
+                        first = false;
+                    } else {
+                        builder.append(", ");
+                    }
+                    builder.append(item.getString(KEY_ITEM_KEY));
+                    builder.append(":");
+                    builder.append(item.getString(KEY_ITEM_VALUE));
+                }
+            } else {
+                builder.append(getString(R.string.none));
+            }
+        } else {
+            builder.append(getString(R.string.none));
+        }
+        mTextItems.setText(getString(R.string.your_items, builder));
+    }
+
     @Override
     public void onClick(View view) {
         switch (view.getId()) {
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 18ca0a4..02d83e6 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
@@ -59,7 +59,7 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             tools:text="@string/your_rank"/>
 
-        <include layout="@layout/separator"/>
+        <include layout="@layout/separator" android:id="@+id/bundle_separator"/>
 
         <TextView
             android:id="@+id/approvals_you_have"
@@ -68,6 +68,24 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             tools:text="@string/approvals_you_have"/>
 
+        <include layout="@layout/separator"/>
+
+        <TextView
+            android:id="@+id/your_profile"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/your_profile"/>
+
+        <include layout="@layout/separator" android:id="@+id/bundle_array_separator" />
+
+        <TextView
+            android:id="@+id/your_items"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            tools:text="@string/your_items"/>
+
     </LinearLayout>
 
 </ScrollView>
diff --git a/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml b/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
index 558d097..53be746 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/values/restriction_values.xml
@@ -74,4 +74,23 @@
     <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>
 
+    <!-- Bundle restriction -->
+    <string name="description_profile">Sample profile</string>
+    <string name="title_profile">Profile</string>
+
+    <string name="default_profile_name">John</string>
+    <string name="description_profile_name">The name of this person</string>
+    <string name="title_profile_name">Name</string>
+
+    <integer name="default_profile_age">25</integer>
+    <string name="description_profile_age">The age of this person</string>
+    <string name="title_profile_age">Age</string>
+
+    <!-- Bundle array restriction -->
+    <string name="description_items">Sample items</string>
+    <string name="title_items">Items</string>
+    <string name="title_item">Item</string>
+    <string name="title_key">Key</string>
+    <string name="title_value">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 6dce123..1ec68d5 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/values/strings.xml
@@ -25,5 +25,7 @@
     <string name="your_rank">Your rank: %s</string>
     <string name="approvals_you_have">Approvals you have: %s</string>
     <string name="none">none</string>
+    <string name="your_profile">Your profile: %1$s (%2$d)</string>
+    <string name="your_items">Your items: %s</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 9e47f45..1e2ea45 100644
--- a/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
+++ b/admin/AppRestrictionSchema/Application/src/main/res/xml/app_restrictions.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 Copyright 2014 The Android Open Source Project
 
 Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +20,7 @@
     https://developer.android.com/reference/android/content/RestrictionsManager.html
     -->
 
+    <!-- Boolean restriction -->
     <restriction
         android:defaultValue="@bool/default_can_say_hello"
         android:description="@string/description_can_say_hello"
@@ -28,6 +28,7 @@
         android:restrictionType="bool"
         android:title="@string/title_can_say_hello"/>
 
+    <!-- String restriction -->
     <restriction
         android:defaultValue="@string/default_message"
         android:description="@string/description_message"
@@ -35,6 +36,7 @@
         android:restrictionType="string"
         android:title="@string/title_message"/>
 
+    <!-- Integer restriction -->
     <restriction
         android:defaultValue="@integer/default_number"
         android:description="@string/description_number"
@@ -42,6 +44,7 @@
         android:restrictionType="integer"
         android:title="@string/title_number"/>
 
+    <!-- Choice restriction -->
     <restriction
         android:defaultValue="@string/default_rank"
         android:description="@string/description_rank"
@@ -51,6 +54,7 @@
         android:restrictionType="choice"
         android:title="@string/title_rank"/>
 
+    <!-- Multi-select restriction -->
     <restriction
         android:defaultValue="@array/default_approvals"
         android:description="@string/description_approvals"
@@ -60,6 +64,7 @@
         android:restrictionType="multi-select"
         android:title="@string/title_approvals"/>
 
+    <!-- Hidden restriction -->
     <restriction
         android:defaultValue="@string/default_secret_code"
         android:description="@string/description_secret_code"
@@ -67,4 +72,46 @@
         android:restrictionType="hidden"
         android:title="@string/title_secret_code"/>
 
+    <!-- Bundle restriction; useful for grouping restrictions -->
+    <restriction
+        android:description="@string/description_profile"
+        android:key="profile"
+        android:restrictionType="bundle"
+        android:title="@string/title_profile">
+        <restriction
+            android:defaultValue="@string/default_profile_name"
+            android:description="@string/description_profile_name"
+            android:key="name"
+            android:restrictionType="string"
+            android:title="@string/title_profile_name"/>
+        <restriction
+            android:defaultValue="@integer/default_profile_age"
+            android:description="@string/description_profile_age"
+            android:key="age"
+            android:restrictionType="integer"
+            android:title="@string/title_profile_age"/>
+    </restriction>
+
+    <!-- Bundle array restriction -->
+    <restriction
+        android:description="@string/description_items"
+        android:key="items"
+        android:restrictionType="bundle_array"
+        android:title="@string/title_items">
+        <!-- Bundle array must have one bundle restriction -->
+        <restriction
+            android:key="item"
+            android:restrictionType="bundle"
+            android:title="@string/title_item">
+            <restriction
+                android:key="key"
+                android:restrictionType="string"
+                android:title="@string/title_key"/>
+            <restriction
+                android:key="value"
+                android:restrictionType="string"
+                android:title="@string/title_value"/>
+        </restriction>
+    </restriction>
+
 </restrictions>
diff --git a/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties b/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties
index fb79885..c5454eb 100644
--- a/admin/AppRestrictionSchema/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/AppRestrictionSchema/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/admin/AppRestrictionSchema/template-params.xml b/admin/AppRestrictionSchema/template-params.xml
index 6603034..508393e 100644
--- a/admin/AppRestrictionSchema/template-params.xml
+++ b/admin/AppRestrictionSchema/template-params.xml
@@ -21,7 +21,6 @@
     <package>com.example.android.apprestrictionschema</package>
 
     <minSdk>21</minSdk>
-    <compileSdkVersion>21</compileSdkVersion>
 
     <strings>
         <intro>
diff --git a/admin/BasicManagedProfile/gradle/wrapper/gradle-wrapper.properties b/admin/BasicManagedProfile/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/admin/DeviceOwner/gradle/wrapper/gradle-wrapper.properties b/admin/DeviceOwner/gradle/wrapper/gradle-wrapper.properties
index 0973c31..22997e5 100644
--- a/admin/DeviceOwner/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/DeviceOwner/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/admin/NfcProvisioning/gradle/wrapper/gradle-wrapper.properties b/admin/NfcProvisioning/gradle/wrapper/gradle-wrapper.properties
index 23e8e21..862673d 100644
--- a/admin/NfcProvisioning/gradle/wrapper/gradle-wrapper.properties
+++ b/admin/NfcProvisioning/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/background/JobScheduler/gradle/wrapper/gradle-wrapper.properties b/background/JobScheduler/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/background/alarms/RepeatingAlarm/gradle/wrapper/gradle-wrapper.properties b/background/alarms/RepeatingAlarm/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/build.gradle b/build.gradle
index 05ed3ea..1e3cf8a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -103,9 +103,15 @@
 "security/FingerprintDialog",
 "system/RuntimePermissions",
 "system/RuntimePermissionsBasic",
+"wearable/wear/RuntimePermissionsWear",
 "notification/ActiveNotifications",
 "media/Camera2Raw",
 "content/AutoBackupForApps",
+"content/DirectShare",
+"media/MidiScope",
+"media/MidiSynth",
+"security/AsymmetricFingerprintDialog",
+"wearable/wear/WearSpeakerSample",
 ]
 
 List<String> taskNames = [
diff --git a/common/src/java/com/example/android/common/media/CameraHelper.java b/common/src/java/com/example/android/common/media/CameraHelper.java
index 1fa8416..b578767 100644
--- a/common/src/java/com/example/android/common/media/CameraHelper.java
+++ b/common/src/java/com/example/android/common/media/CameraHelper.java
@@ -36,49 +36,57 @@
     public static final int MEDIA_TYPE_VIDEO = 2;
 
     /**
-     * Iterate over supported camera preview sizes to see which one best fits the
+     * Iterate over supported camera video sizes to see which one best fits the
      * dimensions of the given view while maintaining the aspect ratio. If none can,
      * be lenient with the aspect ratio.
      *
-     * @param sizes Supported camera preview sizes.
-     * @param w The width of the view.
-     * @param h The height of the view.
-     * @return Best match camera preview size to fit in the view.
+     * @param supportedVideoSizes Supported camera video sizes.
+     * @param previewSizes Supported camera preview sizes.
+     * @param w     The width of the view.
+     * @param h     The height of the view.
+     * @return Best match camera video size to fit in the view.
      */
-    public static  Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
+    public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes,
+            List<Camera.Size> previewSizes, int w, int h) {
         // Use a very small tolerance because we want an exact match.
         final double ASPECT_TOLERANCE = 0.1;
         double targetRatio = (double) w / h;
-        if (sizes == null)
-            return null;
 
+        // Supported video sizes list might be null, it means that we are allowed to use the preview
+        // sizes
+        List<Camera.Size> videoSizes;
+        if (supportedVideoSizes != null) {
+            videoSizes = supportedVideoSizes;
+        } else {
+            videoSizes = previewSizes;
+        }
         Camera.Size optimalSize = null;
 
-        // Start with max value and refine as we iterate over available preview sizes. This is the
+        // Start with max value and refine as we iterate over available video sizes. This is the
         // minimum difference between view and camera height.
         double minDiff = Double.MAX_VALUE;
 
         // Target view height
         int targetHeight = h;
 
-        // Try to find a preview size that matches aspect ratio and the target view size.
+        // Try to find a video size that matches aspect ratio and the target view size.
         // Iterate over all available sizes and pick the largest size that can fit in the view and
         // still maintain the aspect ratio.
-        for (Camera.Size size : sizes) {
+        for (Camera.Size size : videoSizes) {
             double ratio = (double) size.width / size.height;
             if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                 continue;
-            if (Math.abs(size.height - targetHeight) < minDiff) {
+            if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
                 optimalSize = size;
                 minDiff = Math.abs(size.height - targetHeight);
             }
         }
 
-        // Cannot find preview size that matches the aspect ratio, ignore the requirement
+        // Cannot find video size that matches the aspect ratio, ignore the requirement
         if (optimalSize == null) {
             minDiff = Double.MAX_VALUE;
-            for (Camera.Size size : sizes) {
-                if (Math.abs(size.height - targetHeight) < minDiff) {
+            for (Camera.Size size : videoSizes) {
+                if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
                     optimalSize = size;
                     minDiff = Math.abs(size.height - targetHeight);
                 }
diff --git a/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.properties
index 8712eac..25c3a92 100644
--- a/connectivity/bluetooth/BluetoothAdvertisements/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/bluetooth/BluetoothAdvertisements/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/bluetooth/BluetoothChat/Application/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java b/connectivity/bluetooth/BluetoothChat/Application/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java
index b88b160..a1e7cc0 100644
--- a/connectivity/bluetooth/BluetoothChat/Application/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java
+++ b/connectivity/bluetooth/BluetoothChat/Application/src/main/java/com/example/android/bluetoothchat/BluetoothChatService.java
@@ -473,7 +473,7 @@
             int bytes;
 
             // Keep listening to the InputStream while connected
-            while (true) {
+            while (mState == STATE_CONNECTED) {
                 try {
                     // Read from the InputStream
                     bytes = mmInStream.read(buffer);
diff --git a/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothChat/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/bluetooth/BluetoothLeGatt/gradle/wrapper/gradle-wrapper.properties b/connectivity/bluetooth/BluetoothLeGatt/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/network/BasicNetworking/gradle/wrapper/gradle-wrapper.properties b/connectivity/network/BasicNetworking/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/network/NetworkConnect/gradle/wrapper/gradle-wrapper.properties b/connectivity/network/NetworkConnect/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/nfc/BeamLargeFiles/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/BeamLargeFiles/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/CardEmulation/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties b/connectivity/nfc/CardReader/gradle/wrapper/gradle-wrapper.properties
index 4819af8..34ff29a 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/sync/BasicSyncAdapter/gradle/wrapper/gradle-wrapper.properties b/connectivity/sync/BasicSyncAdapter/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/connectivity/wifidirect/DirectP2P/gradle/wrapper/gradle-wrapper.properties b/connectivity/wifidirect/DirectP2P/gradle/wrapper/gradle-wrapper.properties
index 1b32573..039bfb4 100644
--- a/connectivity/wifidirect/DirectP2P/gradle/wrapper/gradle-wrapper.properties
+++ b/connectivity/wifidirect/DirectP2P/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-2.2.1-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
\ No newline at end of file
diff --git a/content/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties b/content/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties
index afb3296..07fc193 100644
--- a/content/AutoBackupForApps/gradle/wrapper/gradle-wrapper.properties
+++ b/content/AutoBackupForApps/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-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/DirectShare/Application/src/main/res/layout/select_contact.xml b/content/DirectShare/Application/src/main/res/layout/select_contact.xml
new file mode 100644
index 0000000..f219805
--- /dev/null
+++ b/content/DirectShare/Application/src/main/res/layout/select_contact.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="16dp"
+    android:clipToPadding="false"
+    android:divider="@null"
+    android:paddingBottom="4dp"
+    android:paddingTop="4dp" />
diff --git a/content/DirectShare/gradle/wrapper/gradle-wrapper.properties b/content/DirectShare/gradle/wrapper/gradle-wrapper.properties
index afb3296..07fc193 100644
--- a/content/DirectShare/gradle/wrapper/gradle-wrapper.properties
+++ b/content/DirectShare/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-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/DirectShare/template-params.xml b/content/DirectShare/template-params.xml
index d1a6f20..bc69228 100644
--- a/content/DirectShare/template-params.xml
+++ b/content/DirectShare/template-params.xml
@@ -73,7 +73,7 @@
 
 [1]: https://developer.android.com/reference/android/service/chooser/package-summary.html
 [2]: https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html
-[3]: https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html#onGetChooserTargets(android.content.ComponentName, android.content.IntentFilter)
+[3]: https://developer.android.com/reference/android/service/chooser/ChooserTargetService.html#onGetChooserTargets(android.content.ComponentName%2C%20android.content.IntentFilter)
             ]]>
         </intro>
     </metadata>
diff --git a/content/WidgetData/gradle/wrapper/gradle-wrapper.properties b/content/WidgetData/gradle/wrapper/gradle-wrapper.properties
index fe55261..410f92a 100644
--- a/content/WidgetData/gradle/wrapper/gradle-wrapper.properties
+++ b/content/WidgetData/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties b/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties
index 542898d..07fc193 100644
--- a/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties
+++ b/content/contacts/BasicContactables/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +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
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
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
index 4af55db..075f39b 100644
--- 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
@@ -27,7 +27,6 @@
 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;
@@ -124,8 +123,7 @@
             }
         });
         mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_directory_entries);
-        mLayoutManager = new LinearLayoutManager(getActivity());
-        mRecyclerView.setLayoutManager(mLayoutManager);
+        mLayoutManager = mRecyclerView.getLayoutManager();
         mRecyclerView.scrollToPosition(0);
         mAdapter = new DirectoryEntryAdapter(new ArrayList<DirectoryEntry>());
         mRecyclerView.setAdapter(mAdapter);
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
index d63219c..631a8fc 100644
--- 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
@@ -16,6 +16,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:gravity="center_vertical"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
@@ -68,7 +69,9 @@
             android:scrollbars="vertical"
             android:drawSelectorOnTop="true"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"
+            />
 
 </LinearLayout>
 
diff --git a/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.properties b/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.properties
index 3e37868..263f57d 100644
--- a/content/documentsUi/DirectorySelection/gradle/wrapper/gradle-wrapper.properties
+++ b/content/documentsUi/DirectorySelection/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/documentsUi/StorageClient/gradle/wrapper/gradle-wrapper.properties b/content/documentsUi/StorageClient/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudProvider.java b/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudProvider.java
index d8be813..9f9249a 100644
--- a/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudProvider.java
+++ b/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudProvider.java
@@ -51,7 +51,7 @@
  * Manages documents and exposes them to the Android system for sharing.
  */
 public class MyCloudProvider extends DocumentsProvider {
-    private static final String TAG = MyCloudProvider.class.getSimpleName();
+    private static final String TAG = "MyCloudProvider";
 
     // Use these as the default columns to return information about a root if no specific
     // columns are requested in a query.
diff --git a/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudFragment.java b/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/StorageProviderFragment.java
similarity index 96%
rename from content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudFragment.java
rename to content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/StorageProviderFragment.java
index f624e90..80d0296 100644
--- a/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/MyCloudFragment.java
+++ b/content/documentsUi/StorageProvider/Application/src/main/java/com/example/android/storageprovider/StorageProviderFragment.java
@@ -31,9 +31,9 @@
  * Toggles the user's login status via a login menu option, and enables/disables the cloud storage
  * content provider.
  */
-public class MyCloudFragment extends Fragment {
+public class StorageProviderFragment extends Fragment {
 
-    private static final String TAG = "MyCloudFragment";
+    private static final String TAG = "StorageProviderFragment";
     private static final String AUTHORITY = "com.example.android.storageprovider.documents";
     private boolean mLoggedIn = false;
 
diff --git a/content/documentsUi/StorageProvider/README.md b/content/documentsUi/StorageProvider/README.md
index 9040e7b..bc343bc 100644
--- a/content/documentsUi/StorageProvider/README.md
+++ b/content/documentsUi/StorageProvider/README.md
@@ -1,5 +1,5 @@
 
-Android MyCloud Sample
+Android StorageProvider Sample
 ===================================
 
 This sample shows how to implement a simple documents provider using the storage access
@@ -42,7 +42,7 @@
 - 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-MyCloud
+https://github.com/googlesamples/android-StorageProvider
 
 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.
diff --git a/content/documentsUi/StorageProvider/gradle/wrapper/gradle-wrapper.properties b/content/documentsUi/StorageProvider/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/multiuser/AppRestrictions/gradle/wrapper/gradle-wrapper.properties b/content/multiuser/AppRestrictions/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.properties b/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.properties
index 4eda7c5..2b7c590 100644
--- a/content/webview/PermissionRequest/gradle/wrapper/gradle-wrapper.properties
+++ b/content/webview/PermissionRequest/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/experimental/ndkSampleGen/gradle/wrapper/gradle-wrapper.properties b/experimental/ndkSampleGen/gradle/wrapper/gradle-wrapper.properties
index b0ae9b5..f9ac955 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties b/input/gestures/BasicGestureDetect/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties b/input/multitouch/BasicMultitouch/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties b/media/BasicMediaDecoder/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties b/media/BasicMediaRouter/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-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 4bf8534..a29fa14 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
@@ -28,6 +28,7 @@
 import android.content.res.Configuration;
 import android.graphics.ImageFormat;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraAccessException;
@@ -47,6 +48,7 @@
 import android.os.HandlerThread;
 import android.support.annotation.NonNull;
 import android.support.v13.app.FragmentCompat;
+import android.support.v4.content.ContextCompat;
 import android.util.Log;
 import android.util.Size;
 import android.util.SparseIntArray;
@@ -117,6 +119,16 @@
     private static final int STATE_PICTURE_TAKEN = 4;
 
     /**
+     * Max preview width that is guaranteed by Camera2 API
+     */
+    private static final int MAX_PREVIEW_WIDTH = 1920;
+
+    /**
+     * Max preview height that is guaranteed by Camera2 API
+     */
+    private static final int MAX_PREVIEW_HEIGHT = 1080;
+
+    /**
      * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
      * {@link TextureView}.
      */
@@ -259,6 +271,16 @@
     private Semaphore mCameraOpenCloseLock = new Semaphore(1);
 
     /**
+     * Whether the current camera device supports Flash or not.
+     */
+    private boolean mFlashSupported;
+
+    /**
+     * Orientation of the camera sensor
+     */
+    private int mSensorOrientation;
+
+    /**
      * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
      */
     private CameraCaptureSession.CaptureCallback mCaptureCallback
@@ -344,31 +366,48 @@
     }
 
     /**
-     * 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.
+     * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that
+     * is at least as large as the respective texture view size, and that is at most as large as the
+     * respective max size, and whose aspect ratio matches with the specified value. If such size
+     * doesn't exist, choose the largest one that is at most as large as the respective max size,
+     * and whose aspect ratio matches with the specified value.
      *
-     * @param choices     The list of sizes that the camera supports for the intended output class
-     * @param width       The minimum desired width
-     * @param height      The minimum desired height
-     * @param aspectRatio The aspect ratio
+     * @param choices           The list of sizes that the camera supports for the intended output
+     *                          class
+     * @param textureViewWidth  The width of the texture view relative to sensor coordinate
+     * @param textureViewHeight The height of the texture view relative to sensor coordinate
+     * @param maxWidth          The maximum width that can be chosen
+     * @param maxHeight         The maximum height that can be chosen
+     * @param aspectRatio       The aspect ratio
      * @return The optimal {@code Size}, or an arbitrary one if none were big enough
      */
-    private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
+    private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
+            int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
+
         // Collect the supported resolutions that are at least as big as the preview Surface
         List<Size> bigEnough = new ArrayList<>();
+        // Collect the supported resolutions that are smaller than the preview Surface
+        List<Size> notBigEnough = new ArrayList<>();
         int w = aspectRatio.getWidth();
         int h = aspectRatio.getHeight();
         for (Size option : choices) {
-            if (option.getHeight() == option.getWidth() * h / w &&
-                    option.getWidth() >= width && option.getHeight() >= height) {
-                bigEnough.add(option);
+            if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
+                    option.getHeight() == option.getWidth() * h / w) {
+                if (option.getWidth() >= textureViewWidth &&
+                    option.getHeight() >= textureViewHeight) {
+                    bigEnough.add(option);
+                } else {
+                    notBigEnough.add(option);
+                }
             }
         }
 
-        // Pick the smallest of those, assuming we found any
+        // Pick the smallest of those big enough. If there is no one big enough, pick the
+        // largest of those not big enough.
         if (bigEnough.size() > 0) {
             return Collections.min(bigEnough, new CompareSizesByArea());
+        } else if (notBigEnough.size() > 0) {
+            return Collections.max(notBigEnough, new CompareSizesByArea());
         } else {
             Log.e(TAG, "Couldn't find any suitable preview size");
             return choices[0];
@@ -478,11 +517,57 @@
                 mImageReader.setOnImageAvailableListener(
                         mOnImageAvailableListener, mBackgroundHandler);
 
+                // Find out if we need to swap dimension to get the preview size relative to sensor
+                // coordinate.
+                int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
+                //noinspection ConstantConditions
+                mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+                boolean swappedDimensions = false;
+                switch (displayRotation) {
+                    case Surface.ROTATION_0:
+                    case Surface.ROTATION_180:
+                        if (mSensorOrientation == 90 || mSensorOrientation == 270) {
+                            swappedDimensions = true;
+                        }
+                        break;
+                    case Surface.ROTATION_90:
+                    case Surface.ROTATION_270:
+                        if (mSensorOrientation == 0 || mSensorOrientation == 180) {
+                            swappedDimensions = true;
+                        }
+                        break;
+                    default:
+                        Log.e(TAG, "Display rotation is invalid: " + displayRotation);
+                }
+
+                Point displaySize = new Point();
+                activity.getWindowManager().getDefaultDisplay().getSize(displaySize);
+                int rotatedPreviewWidth = width;
+                int rotatedPreviewHeight = height;
+                int maxPreviewWidth = displaySize.x;
+                int maxPreviewHeight = displaySize.y;
+
+                if (swappedDimensions) {
+                    rotatedPreviewWidth = height;
+                    rotatedPreviewHeight = width;
+                    maxPreviewWidth = displaySize.y;
+                    maxPreviewHeight = displaySize.x;
+                }
+
+                if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
+                    maxPreviewWidth = MAX_PREVIEW_WIDTH;
+                }
+
+                if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
+                    maxPreviewHeight = MAX_PREVIEW_HEIGHT;
+                }
+
                 // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera
                 // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
                 // garbage capture data.
                 mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
-                        width, height, largest);
+                        rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
+                        maxPreviewHeight, largest);
 
                 // We fit the aspect ratio of TextureView to the size of preview we picked.
                 int orientation = getResources().getConfiguration().orientation;
@@ -494,6 +579,10 @@
                             mPreviewSize.getHeight(), mPreviewSize.getWidth());
                 }
 
+                // Check if the flash is supported.
+                Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
+                mFlashSupported = available == null ? false : available;
+
                 mCameraId = cameraId;
                 return;
             }
@@ -511,7 +600,7 @@
      * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
      */
     private void openCamera(int width, int height) {
-        if (getActivity().checkSelfPermission(Manifest.permission.CAMERA)
+        if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
                 != PackageManager.PERMISSION_GRANTED) {
             requestCameraPermission();
             return;
@@ -617,8 +706,7 @@
                                 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                         CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                 // Flash is automatically enabled when necessary.
-                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
-                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
+                                setAutoFlash(mPreviewRequestBuilder);
 
                                 // Finally, we start displaying the camera preview.
                                 mPreviewRequest = mPreviewRequestBuilder.build();
@@ -734,12 +822,11 @@
             // Use the same AE and AF modes as the preview.
             captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                     CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
-            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
-                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
+            setAutoFlash(captureBuilder);
 
             // Orientation
             int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
-            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
+            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
 
             CameraCaptureSession.CaptureCallback CaptureCallback
                     = new CameraCaptureSession.CaptureCallback() {
@@ -762,6 +849,20 @@
     }
 
     /**
+     * Retrieves the JPEG orientation from the specified screen rotation.
+     *
+     * @param rotation The screen rotation.
+     * @return The JPEG orientation (one of 0, 90, 270, and 360)
+     */
+    private int getOrientation(int rotation) {
+        // Sensor orientation is 90 for most devices, or 270 for some devices (eg. Nexus 5X)
+        // We have to take that into account and rotate JPEG properly.
+        // For devices with orientation of 90, we simply return our mapping from ORIENTATIONS.
+        // For devices with orientation of 270, we need to rotate the JPEG 180 degrees.
+        return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360;
+    }
+
+    /**
      * Unlock the focus. This method should be called when still image capture sequence is
      * finished.
      */
@@ -770,8 +871,7 @@
             // Reset the auto-focus trigger
             mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                     CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
-            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
-                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
+            setAutoFlash(mPreviewRequestBuilder);
             mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                     mBackgroundHandler);
             // After this, the camera will go back to the normal state of preview.
@@ -803,6 +903,13 @@
         }
     }
 
+    private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
+        if (mFlashSupported) {
+            requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
+                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
+        }
+    }
+
     /**
      * Saves a JPEG {@link Image} into the specified {@link File}.
      */
diff --git a/media/Camera2Basic/README.md b/media/Camera2Basic/README.md
index a77df09..311e5bb 100644
--- a/media/Camera2Basic/README.md
+++ b/media/Camera2Basic/README.md
@@ -43,7 +43,7 @@
 --------------
 
 - Android SDK v23
-- Android Build Tools v23.0.0
+- Android Build Tools v23.0.2
 - Android Support Repository
 
 Screenshots
diff --git a/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties b/media/Camera2Basic/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/Camera2Raw/Application/src/main/java/com/example/android/camera2raw/Camera2RawFragment.java b/media/Camera2Raw/Application/src/main/java/com/example/android/camera2raw/Camera2RawFragment.java
index 47cce38..bf5efe5 100644
--- a/media/Camera2Raw/Application/src/main/java/com/example/android/camera2raw/Camera2RawFragment.java
+++ b/media/Camera2Raw/Application/src/main/java/com/example/android/camera2raw/Camera2RawFragment.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.graphics.ImageFormat;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
 import android.hardware.SensorManager;
@@ -157,6 +158,16 @@
     private static final double ASPECT_RATIO_TOLERANCE = 0.005;
 
     /**
+     * Max preview width that is guaranteed by Camera2 API
+     */
+    private static final int MAX_PREVIEW_WIDTH = 1920;
+
+    /**
+     * Max preview height that is guaranteed by Camera2 API
+     */
+    private static final int MAX_PREVIEW_HEIGHT = 1080;
+
+    /**
      * Tag for the {@link Log}.
      */
     private static final String TAG = "Camera2RawFragment";
@@ -1033,6 +1044,8 @@
 
             // Find the rotation of the device relative to the native device orientation.
             int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
+            Point displaySize = new Point();
+            activity.getWindowManager().getDefaultDisplay().getSize(displaySize);
 
             // Find the rotation of the device relative to the camera sensor's orientation.
             int totalRotation = sensorToDeviceRotation(mCharacteristics, deviceRotation);
@@ -1042,14 +1055,29 @@
             boolean swappedDimensions = totalRotation == 90 || totalRotation == 270;
             int rotatedViewWidth = viewWidth;
             int rotatedViewHeight = viewHeight;
+            int maxPreviewWidth = displaySize.x;
+            int maxPreviewHeight = displaySize.y;
+
             if (swappedDimensions) {
                 rotatedViewWidth = viewHeight;
                 rotatedViewHeight = viewWidth;
+                maxPreviewWidth = displaySize.y;
+                maxPreviewHeight = displaySize.x;
+            }
+
+            // Preview should not be larger than display size and 1080p.
+            if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
+                maxPreviewWidth = MAX_PREVIEW_WIDTH;
+            }
+
+            if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
+                maxPreviewHeight = MAX_PREVIEW_HEIGHT;
             }
 
             // Find the best preview size for these view dimensions and configured JPEG size.
             Size previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
-                    rotatedViewWidth, rotatedViewHeight, largestJpeg);
+                    rotatedViewWidth, rotatedViewHeight, maxPreviewWidth, maxPreviewHeight,
+                    largestJpeg);
 
             if (swappedDimensions) {
                 mTextureView.setAspectRatio(
@@ -1580,31 +1608,47 @@
     }
 
     /**
-     * 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.
+     * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that
+     * is at least as large as the respective texture view size, and that is at most as large as the
+     * respective max size, and whose aspect ratio matches with the specified value. If such size
+     * doesn't exist, choose the largest one that is at most as large as the respective max size,
+     * and whose aspect ratio matches with the specified value.
      *
-     * @param choices     The list of sizes that the camera supports for the intended output class
-     * @param width       The minimum desired width
-     * @param height      The minimum desired height
-     * @param aspectRatio The aspect ratio
+     * @param choices           The list of sizes that the camera supports for the intended output
+     *                          class
+     * @param textureViewWidth  The width of the texture view relative to sensor coordinate
+     * @param textureViewHeight The height of the texture view relative to sensor coordinate
+     * @param maxWidth          The maximum width that can be chosen
+     * @param maxHeight         The maximum height that can be chosen
+     * @param aspectRatio       The aspect ratio
      * @return The optimal {@code Size}, or an arbitrary one if none were big enough
      */
-    private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
+    private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
+            int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
         // Collect the supported resolutions that are at least as big as the preview Surface
         List<Size> bigEnough = new ArrayList<>();
+        // Collect the supported resolutions that are smaller than the preview Surface
+        List<Size> notBigEnough = new ArrayList<>();
         int w = aspectRatio.getWidth();
         int h = aspectRatio.getHeight();
         for (Size option : choices) {
-            if (option.getHeight() == option.getWidth() * h / w &&
-                    option.getWidth() >= width && option.getHeight() >= height) {
-                bigEnough.add(option);
+            if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
+                    option.getHeight() == option.getWidth() * h / w) {
+                if (option.getWidth() >= textureViewWidth &&
+                    option.getHeight() >= textureViewHeight) {
+                    bigEnough.add(option);
+                } else {
+                    notBigEnough.add(option);
+                }
             }
         }
 
-        // Pick the smallest of those, assuming we found any
+        // Pick the smallest of those big enough. If there is no one big enough, pick the
+        // largest of those not big enough.
         if (bigEnough.size() > 0) {
             return Collections.min(bigEnough, new CompareSizesByArea());
+        } else if (notBigEnough.size() > 0) {
+            return Collections.max(notBigEnough, new CompareSizesByArea());
         } else {
             Log.e(TAG, "Couldn't find any suitable preview size");
             return choices[0];
diff --git a/media/Camera2Raw/gradle/wrapper/gradle-wrapper.properties b/media/Camera2Raw/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/media/Camera2Raw/gradle/wrapper/gradle-wrapper.properties
+++ b/media/Camera2Raw/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/Camera2Video/Application/src/main/AndroidManifest.xml b/media/Camera2Video/Application/src/main/AndroidManifest.xml
index 5cb5428..67a90d0 100644
--- a/media/Camera2Video/Application/src/main/AndroidManifest.xml
+++ b/media/Camera2Video/Application/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
 
     <uses-permission android:name="android.permission.CAMERA"/>
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
     <application android:allowBackup="true"
         android:label="@string/app_name"
diff --git a/media/Camera2Video/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.java b/media/Camera2Video/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.java
index 1ea5318..e2853b3 100644
--- a/media/Camera2Video/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.java
+++ b/media/Camera2Video/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.java
@@ -55,9 +55,9 @@
 import android.widget.Button;
 import android.widget.Toast;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -67,7 +67,10 @@
 public class Camera2VideoFragment extends Fragment
         implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback {
 
-    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
+    private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
+    private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
+    private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
+    private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
 
     private static final String TAG = "Camera2VideoFragment";
     private static final int REQUEST_VIDEO_PERMISSIONS = 1;
@@ -79,10 +82,17 @@
     };
 
     static {
-        ORIENTATIONS.append(Surface.ROTATION_0, 90);
-        ORIENTATIONS.append(Surface.ROTATION_90, 0);
-        ORIENTATIONS.append(Surface.ROTATION_180, 270);
-        ORIENTATIONS.append(Surface.ROTATION_270, 180);
+        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
+        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
+        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
+        DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
+    }
+
+    static {
+        INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
+        INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
+        INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
+        INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
     }
 
     /**
@@ -147,11 +157,6 @@
     private Size mVideoSize;
 
     /**
-     * Camera preview.
-     */
-    private CaptureRequest.Builder mPreviewBuilder;
-
-    /**
      * MediaRecorder
      */
     private MediaRecorder mMediaRecorder;
@@ -210,6 +215,10 @@
         }
 
     };
+    private Integer mSensorOrientation;
+    private String mNextVideoAbsolutePath;
+    private CaptureRequest.Builder mPreviewBuilder;
+    private Surface mRecorderSurface;
 
     public static Camera2VideoFragment newInstance() {
         return new Camera2VideoFragment();
@@ -425,6 +434,7 @@
             CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
             StreamConfigurationMap map = characteristics
                     .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
             mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
             mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                     width, height, mVideoSize);
@@ -454,6 +464,7 @@
     private void closeCamera() {
         try {
             mCameraOpenCloseLock.acquire();
+            closePreviewSession();
             if (null != mCameraDevice) {
                 mCameraDevice.close();
                 mCameraDevice = null;
@@ -477,22 +488,16 @@
             return;
         }
         try {
-            setUpMediaRecorder();
+            closePreviewSession();
             SurfaceTexture texture = mTextureView.getSurfaceTexture();
             assert texture != null;
             texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
-            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
-            List<Surface> surfaces = new ArrayList<Surface>();
+            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
 
             Surface previewSurface = new Surface(texture);
-            surfaces.add(previewSurface);
             mPreviewBuilder.addTarget(previewSurface);
 
-            Surface recorderSurface = mMediaRecorder.getSurface();
-            surfaces.add(recorderSurface);
-            mPreviewBuilder.addTarget(recorderSurface);
-
-            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
+            mCameraDevice.createCaptureSession(Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
 
                 @Override
                 public void onConfigured(CameraCaptureSession cameraCaptureSession) {
@@ -510,8 +515,6 @@
             }, mBackgroundHandler);
         } catch (CameraAccessException e) {
             e.printStackTrace();
-        } catch (IOException e) {
-            e.printStackTrace();
         }
     }
 
@@ -575,32 +578,96 @@
         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
-        mMediaRecorder.setOutputFile(getVideoFile(activity).getAbsolutePath());
+        if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) {
+            mNextVideoAbsolutePath = getVideoFilePath(getActivity());
+        }
+        mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);
         mMediaRecorder.setVideoEncodingBitRate(10000000);
         mMediaRecorder.setVideoFrameRate(30);
         mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
         mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
         int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
-        int orientation = ORIENTATIONS.get(rotation);
-        mMediaRecorder.setOrientationHint(orientation);
+        switch (mSensorOrientation) {
+            case SENSOR_ORIENTATION_DEFAULT_DEGREES:
+                mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
+                break;
+            case SENSOR_ORIENTATION_INVERSE_DEGREES:
+                mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
+                break;
+        }
         mMediaRecorder.prepare();
     }
 
-    private File getVideoFile(Context context) {
-        return new File(context.getExternalFilesDir(null), "video.mp4");
+    private String getVideoFilePath(Context context) {
+        return context.getExternalFilesDir(null).getAbsolutePath() + "/"
+                + System.currentTimeMillis() + ".mp4";
     }
 
     private void startRecordingVideo() {
+        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
+            return;
+        }
         try {
-            // UI
-            mButtonVideo.setText(R.string.stop);
-            mIsRecordingVideo = true;
+            closePreviewSession();
+            setUpMediaRecorder();
+            SurfaceTexture texture = mTextureView.getSurfaceTexture();
+            assert texture != null;
+            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+            List<Surface> surfaces = new ArrayList<>();
 
-            // Start recording
-            mMediaRecorder.start();
-        } catch (IllegalStateException e) {
+            // Set up Surface for the camera preview
+            Surface previewSurface = new Surface(texture);
+            surfaces.add(previewSurface);
+            mPreviewBuilder.addTarget(previewSurface);
+
+            // Set up Surface for the MediaRecorder
+            mRecorderSurface = mMediaRecorder.getSurface();
+            surfaces.add(mRecorderSurface);
+            mPreviewBuilder.addTarget(mRecorderSurface);
+
+            // Start a capture session
+            // Once the session starts, we can update the UI and start recording
+            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
+
+                @Override
+                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
+                    mPreviewSession = cameraCaptureSession;
+                    updatePreview();
+                    getActivity().runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            // UI
+                            mButtonVideo.setText(R.string.stop);
+                            mIsRecordingVideo = true;
+
+                            // Start recording
+                            mMediaRecorder.start();
+                        }
+                    });
+                }
+
+                @Override
+                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
+                    Activity activity = getActivity();
+                    if (null != activity) {
+                        Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
+                    }
+                }
+            }, mBackgroundHandler);
+        } catch (CameraAccessException e) {
             e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    private void closePreviewSession() {
+        if (mPreviewSession != null) {
+            mPreviewSession.close();
+            mPreviewSession = null;
         }
     }
 
@@ -611,11 +678,14 @@
         // Stop recording
         mMediaRecorder.stop();
         mMediaRecorder.reset();
+
         Activity activity = getActivity();
         if (null != activity) {
-            Toast.makeText(activity, "Video saved: " + getVideoFile(activity),
+            Toast.makeText(activity, "Video saved: " + mNextVideoAbsolutePath,
                     Toast.LENGTH_SHORT).show();
+            Log.d(TAG, "Video saved: " + mNextVideoAbsolutePath);
         }
+        mNextVideoAbsolutePath = null;
         startPreview();
     }
 
diff --git a/media/Camera2Video/README.md b/media/Camera2Video/README.md
index ac5084c..e1f07bc 100644
--- a/media/Camera2Video/README.md
+++ b/media/Camera2Video/README.md
@@ -44,7 +44,7 @@
 --------------
 
 - Android SDK v23
-- Android Build Tools v23.0.0
+- Android Build Tools v23.0.2
 - Android Support Repository
 
 Screenshots
diff --git a/media/Camera2Video/gradle/wrapper/gradle-wrapper.properties b/media/Camera2Video/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties b/media/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties b/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties
+++ b/media/MediaBrowserService/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MediaEffects/gradle/wrapper/gradle-wrapper.properties b/media/MediaEffects/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MediaRecorder/Application/src/main/java/com/example/android/mediarecorder/MainActivity.java b/media/MediaRecorder/Application/src/main/java/com/example/android/mediarecorder/MainActivity.java
index 8587636..102ff4d 100644
--- a/media/MediaRecorder/Application/src/main/java/com/example/android/mediarecorder/MainActivity.java
+++ b/media/MediaRecorder/Application/src/main/java/com/example/android/mediarecorder/MainActivity.java
@@ -32,6 +32,7 @@
 
 import com.example.android.common.media.CameraHelper;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.List;
 
@@ -45,6 +46,7 @@
     private Camera mCamera;
     private TextureView mPreview;
     private MediaRecorder mMediaRecorder;
+    private File mOutputFile;
 
     private boolean isRecording = false;
     private static final String TAG = "Recorder";
@@ -71,7 +73,15 @@
             // BEGIN_INCLUDE(stop_release_media_recorder)
 
             // stop recording and release camera
-            mMediaRecorder.stop();  // stop the recording
+            try {
+                mMediaRecorder.stop();  // stop the recording
+            } catch (RuntimeException e) {
+                // RuntimeException is thrown when stop() is called immediately after start().
+                // In this case the output file is not properly constructed ans should be deleted.
+                Log.d(TAG, "RuntimeException: stop() is called immediately after start()");
+                //noinspection ResultOfMethodCallIgnored
+                mOutputFile.delete();
+            }
             releaseMediaRecorder(); // release the MediaRecorder object
             mCamera.lock();         // take camera access back from MediaRecorder
 
@@ -137,8 +147,9 @@
         // dimensions of our preview surface.
         Camera.Parameters parameters = mCamera.getParameters();
         List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
-        Camera.Size optimalSize = CameraHelper.getOptimalPreviewSize(mSupportedPreviewSizes,
-                mPreview.getWidth(), mPreview.getHeight());
+        List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes();
+        Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
+                mSupportedPreviewSizes, mPreview.getWidth(), mPreview.getHeight());
 
         // Use the same size for recording profile.
         CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
@@ -174,8 +185,11 @@
         mMediaRecorder.setProfile(profile);
 
         // Step 4: Set output file
-        mMediaRecorder.setOutputFile(CameraHelper.getOutputMediaFile(
-                CameraHelper.MEDIA_TYPE_VIDEO).toString());
+        mOutputFile = CameraHelper.getOutputMediaFile(CameraHelper.MEDIA_TYPE_VIDEO);
+        if (mOutputFile == null) {
+            return false;
+        }
+        mMediaRecorder.setOutputFile(mOutputFile.getPath());
         // END_INCLUDE (configure_media_recorder)
 
         // Step 5: Prepare configured MediaRecorder
@@ -227,4 +241,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/media/MediaRecorder/README.md b/media/MediaRecorder/README.md
index 32c14cd..ed234cd 100644
--- a/media/MediaRecorder/README.md
+++ b/media/MediaRecorder/README.md
@@ -26,7 +26,7 @@
 --------------
 
 - Android SDK v23
-- Android Build Tools v23.0.0
+- Android Build Tools v23.0.2
 - Android Support Repository
 
 Screenshots
diff --git a/media/MediaRecorder/gradle/wrapper/gradle-wrapper.properties b/media/MediaRecorder/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MediaRouter/gradle/wrapper/gradle-wrapper.properties b/media/MediaRouter/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MidiScope/gradle/wrapper/gradle-wrapper.properties b/media/MidiScope/gradle/wrapper/gradle-wrapper.properties
index afb3296..07fc193 100644
--- a/media/MidiScope/gradle/wrapper/gradle-wrapper.properties
+++ b/media/MidiScope/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-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MidiScope/template-params.xml b/media/MidiScope/template-params.xml
index 8caea80..4125248 100644
--- a/media/MidiScope/template-params.xml
+++ b/media/MidiScope/template-params.xml
@@ -43,7 +43,7 @@
         <icon>screenshots/icon-web.png</icon>
         <screenshots>
             <img>screenshots/1-main.png</img>
-            <img>screenshots/2-settings.png</img>
+            <img>screenshots/2-signals.png</img>
         </screenshots>
         <api_refs>
             <android>android.media.midi.MidiManager</android>
diff --git a/media/MidiSynth/gradle/wrapper/gradle-wrapper.properties b/media/MidiSynth/gradle/wrapper/gradle-wrapper.properties
index afb3296..07fc193 100644
--- a/media/MidiSynth/gradle/wrapper/gradle-wrapper.properties
+++ b/media/MidiSynth/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-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/media/MidiSynth/template-params.xml b/media/MidiSynth/template-params.xml
index 7e35471..0d6ef4b 100644
--- a/media/MidiSynth/template-params.xml
+++ b/media/MidiSynth/template-params.xml
@@ -20,14 +20,12 @@
     <package>com.example.android.midisynth</package>
 
     <minSdk>23</minSdk>
-    <targetSdkVersion>23</targetSdkVersion>
-    <compileSdkVersion>23</compileSdkVersion>
     <strings>
         <intro>
-            <![CDATA[
-This sample demonstrates how to use the MIDI API to receive and play MIDI signals coming from an
+<![CDATA[
+This sample demonstrates how to use the MIDI API to receive and play MIDI messages coming from an
 attached input device.
-            ]]>
+]]>
         </intro>
     </strings>
 
@@ -40,7 +38,7 @@
         <technologies>Android</technologies>
         <languages>Java</languages>
         <solutions>Mobile</solutions>
-        <level>INTERMEDIATE</level>
+        <level>EXPERT</level>
         <icon>screenshots/icon-web.png</icon>
         <screenshots>
             <img>screenshots/1-main.png</img>
@@ -50,25 +48,30 @@
             <android>android.media.midi.MidiReceiver</android>
         </api_refs>
         <description>
-            <![CDATA[
-Sample demonstrating how to use the MIDI API to receive and play MIDI signals coming from an
+<![CDATA[
+Sample demonstrating how to use the MIDI API to receive and play MIDI messages coming from an
 attached input device (MIDI keyboard).
-	    ]]>
+]]>
         </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[
-The Android MIDI API ([android.media.midi][1]) allows developers to connect a MIDI device to Android
-and process MIDI signals coming from it. This sample demonstrates some basic features of the MIDI
-API, such as enumeration of currently available devices (Information includes name, vendor,
-capabilities, etc), notification when MIDI devices are plugged in or unplugged, and receiving MIDI
-signals. This sample contains a simple implementation of oscillator and play sound for incoming MIDI
-signals.
+<![CDATA[
+The Android MIDI API ([android.media.midi][1]) allows developers to connect a MIDI device to
+an Android device and process MIDI messages coming from it.
+
+This sample demonstrates some basic features of the MIDI API, such as:
+
+- Enumeration of currently available devices (including name, vendor, capabilities, etc)
+- Notification when MIDI devices are plugged in or unplugged
+- Receiving and processing MIDI messages
+
+This sample contains a simple implementation of an oscillator and note playback.
+
 [1]: https://developer.android.com/reference/android/media/midi/package-summary.html
-	    ]]>
+]]>
         </intro>
     </metadata>
 </sample>
diff --git a/media/ScreenCapture/gradle/wrapper/gradle-wrapper.properties b/media/ScreenCapture/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/media/ScreenCapture/gradle/wrapper/gradle-wrapper.properties
+++ b/media/ScreenCapture/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/notification/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties
index afb3296..07fc193 100644
--- a/notification/ActiveNotifications/gradle/wrapper/gradle-wrapper.properties
+++ b/notification/ActiveNotifications/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-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
+++ b/notification/BasicNotifications/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
+++ b/notification/CustomNotifications/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties
index 9f9f676..828f2e6 100644
--- a/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties
+++ b/notification/LNotifications/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties b/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties
+++ b/notification/MessagingService/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/renderScript/BasicRenderScript/gradle/wrapper/gradle-wrapper.properties b/renderScript/BasicRenderScript/gradle/wrapper/gradle-wrapper.properties
index 6c90366..cb3018f 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/renderScript/RenderScriptIntrinsic/gradle/wrapper/gradle-wrapper.properties b/renderScript/RenderScriptIntrinsic/gradle/wrapper/gradle-wrapper.properties
index 28f01d8..d2ce766 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/security/AsymmetricFingerprintDialog/Application/.gitignore b/security/AsymmetricFingerprintDialog/Application/.gitignore
new file mode 100644
index 0000000..6eb878d
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/.gitignore
@@ -0,0 +1,16 @@
+# 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/security/AsymmetricFingerprintDialog/Application/proguard-project.txt b/security/AsymmetricFingerprintDialog/Application/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/AndroidManifest.xml b/security/AsymmetricFingerprintDialog/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d1cf9f8
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.asymmetricfingerprintdialog"
+          android:versionCode="1"
+          android:versionName="1.0">
+
+    <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
+
+    <application
+            android:name=".InjectedApplication"
+            android:allowBackup="true"
+            android:label="@string/app_name"
+            android:icon="@mipmap/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>
+
+        <activity
+            android:name=".SettingsActivity"
+            android:label="@string/action_settings" />
+    </application>
+</manifest>
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java
new file mode 100644
index 0000000..a56556f
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog;
+
+import com.example.android.asymmetricfingerprintdialog.server.StoreBackend;
+import com.example.android.asymmetricfingerprintdialog.server.Transaction;
+
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.content.SharedPreferences;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+
+import javax.inject.Inject;
+
+/**
+ * A dialog which uses fingerprint APIs to authenticate the user, and falls back to password
+ * authentication if fingerprint is not available.
+ */
+public class FingerprintAuthenticationDialogFragment extends DialogFragment
+        implements TextView.OnEditorActionListener, FingerprintUiHelper.Callback {
+
+    private Button mCancelButton;
+    private Button mSecondDialogButton;
+    private View mFingerprintContent;
+    private View mBackupContent;
+    private EditText mPassword;
+    private CheckBox mUseFingerprintFutureCheckBox;
+    private TextView mPasswordDescriptionTextView;
+    private TextView mNewFingerprintEnrolledTextView;
+
+    private Stage mStage = Stage.FINGERPRINT;
+
+    private FingerprintManager.CryptoObject mCryptoObject;
+    private FingerprintUiHelper mFingerprintUiHelper;
+    private MainActivity mActivity;
+
+    @Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder;
+    @Inject InputMethodManager mInputMethodManager;
+    @Inject SharedPreferences mSharedPreferences;
+    @Inject StoreBackend mStoreBackend;
+
+    @Inject
+    public FingerprintAuthenticationDialogFragment() {}
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Do not create a new Fragment when the Activity is re-created such as orientation changes.
+        setRetainInstance(true);
+        setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);
+
+        // We register a new user account here. Real apps should do this with proper UIs.
+        enroll();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        getDialog().setTitle(getString(R.string.sign_in));
+        View v = inflater.inflate(R.layout.fingerprint_dialog_container, container, false);
+        mCancelButton = (Button) v.findViewById(R.id.cancel_button);
+        mCancelButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                dismiss();
+            }
+        });
+
+        mSecondDialogButton = (Button) v.findViewById(R.id.second_dialog_button);
+        mSecondDialogButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (mStage == Stage.FINGERPRINT) {
+                    goToBackup();
+                } else {
+                    verifyPassword();
+                }
+            }
+        });
+        mFingerprintContent = v.findViewById(R.id.fingerprint_container);
+        mBackupContent = v.findViewById(R.id.backup_container);
+        mPassword = (EditText) v.findViewById(R.id.password);
+        mPassword.setOnEditorActionListener(this);
+        mPasswordDescriptionTextView = (TextView) v.findViewById(R.id.password_description);
+        mUseFingerprintFutureCheckBox = (CheckBox)
+                v.findViewById(R.id.use_fingerprint_in_future_check);
+        mNewFingerprintEnrolledTextView = (TextView)
+                v.findViewById(R.id.new_fingerprint_enrolled_description);
+        mFingerprintUiHelper = mFingerprintUiHelperBuilder.build(
+                (ImageView) v.findViewById(R.id.fingerprint_icon),
+                (TextView) v.findViewById(R.id.fingerprint_status), this);
+        updateStage();
+
+        // If fingerprint authentication is not available, switch immediately to the backup
+        // (password) screen.
+        if (!mFingerprintUiHelper.isFingerprintAuthAvailable()) {
+            goToBackup();
+        }
+        return v;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mStage == Stage.FINGERPRINT) {
+            mFingerprintUiHelper.startListening(mCryptoObject);
+        }
+    }
+
+    public void setStage(Stage stage) {
+        mStage = stage;
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mFingerprintUiHelper.stopListening();
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mActivity = (MainActivity) activity;
+    }
+
+    /**
+     * Sets the crypto object to be passed in when authenticating with fingerprint.
+     */
+    public void setCryptoObject(FingerprintManager.CryptoObject cryptoObject) {
+        mCryptoObject = cryptoObject;
+    }
+
+    /**
+     * Switches to backup (password) screen. This either can happen when fingerprint is not
+     * available or the user chooses to use the password authentication method by pressing the
+     * button. This can also happen when the user had too many fingerprint attempts.
+     */
+    private void goToBackup() {
+        mStage = Stage.PASSWORD;
+        updateStage();
+        mPassword.requestFocus();
+
+        // Show the keyboard.
+        mPassword.postDelayed(mShowKeyboardRunnable, 500);
+
+        // Fingerprint is not used anymore. Stop listening for it.
+        mFingerprintUiHelper.stopListening();
+    }
+
+    /**
+     * Enrolls a user to the fake backend.
+     */
+    private void enroll() {
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            PublicKey publicKey = keyStore.getCertificate(MainActivity.KEY_NAME).getPublicKey();
+            // Provide the public key to the backend. In most cases, the key needs to be transmitted
+            // to the backend over the network, for which Key.getEncoded provides a suitable wire
+            // format (X.509 DER-encoded). The backend can then create a PublicKey instance from the
+            // X.509 encoded form using KeyFactory.generatePublic. This conversion is also currently
+            // needed on API Level 23 (Android M) due to a platform bug which prevents the use of
+            // Android Keystore public keys when their private keys require user authentication.
+            // This conversion creates a new public key which is not backed by Android Keystore and
+            // thus is not affected by the bug.
+            KeyFactory factory = KeyFactory.getInstance(publicKey.getAlgorithm());
+            X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKey.getEncoded());
+            PublicKey verificationKey = factory.generatePublic(spec);
+            mStoreBackend.enroll("user", "password", verificationKey);
+        } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException |
+                IOException | InvalidKeySpecException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Checks whether the current entered password is correct, and dismisses the the dialog and lets
+     * the activity know about the result.
+     */
+    private void verifyPassword() {
+        Transaction transaction = new Transaction("user", 1, new SecureRandom().nextLong());
+        if (!mStoreBackend.verify(transaction, mPassword.getText().toString())) {
+            return;
+        }
+        if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
+            SharedPreferences.Editor editor = mSharedPreferences.edit();
+            editor.putBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
+                    mUseFingerprintFutureCheckBox.isChecked());
+            editor.apply();
+
+            if (mUseFingerprintFutureCheckBox.isChecked()) {
+                // Re-create the key so that fingerprints including new ones are validated.
+                mActivity.createKeyPair();
+                mStage = Stage.FINGERPRINT;
+            }
+        }
+        mPassword.setText("");
+        mActivity.onPurchased(null);
+        dismiss();
+    }
+
+    private final Runnable mShowKeyboardRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mInputMethodManager.showSoftInput(mPassword, 0);
+        }
+    };
+
+    private void updateStage() {
+        switch (mStage) {
+            case FINGERPRINT:
+                mCancelButton.setText(R.string.cancel);
+                mSecondDialogButton.setText(R.string.use_password);
+                mFingerprintContent.setVisibility(View.VISIBLE);
+                mBackupContent.setVisibility(View.GONE);
+                break;
+            case NEW_FINGERPRINT_ENROLLED:
+                // Intentional fall through
+            case PASSWORD:
+                mCancelButton.setText(R.string.cancel);
+                mSecondDialogButton.setText(R.string.ok);
+                mFingerprintContent.setVisibility(View.GONE);
+                mBackupContent.setVisibility(View.VISIBLE);
+                if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
+                    mPasswordDescriptionTextView.setVisibility(View.GONE);
+                    mNewFingerprintEnrolledTextView.setVisibility(View.VISIBLE);
+                    mUseFingerprintFutureCheckBox.setVisibility(View.VISIBLE);
+                }
+                break;
+        }
+    }
+
+    @Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (actionId == EditorInfo.IME_ACTION_GO) {
+            verifyPassword();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onAuthenticated() {
+        // Callback from FingerprintUiHelper. Let the activity know that authentication was
+        // successful.
+        mPassword.setText("");
+        Signature signature = mCryptoObject.getSignature();
+        // Include a client nonce in the transaction so that the nonce is also signed by the private
+        // key and the backend can verify that the same nonce can't be used to prevent replay
+        // attacks.
+        Transaction transaction = new Transaction("user", 1, new SecureRandom().nextLong());
+        try {
+            signature.update(transaction.toByteArray());
+            byte[] sigBytes = signature.sign();
+            if (mStoreBackend.verify(transaction, sigBytes)) {
+                mActivity.onPurchased(sigBytes);
+                dismiss();
+            } else {
+                mActivity.onPurchaseFailed();
+                dismiss();
+            }
+        } catch (SignatureException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void onError() {
+        goToBackup();
+    }
+
+    /**
+     * Enumeration to indicate which authentication method the user is trying to authenticate with.
+     */
+    public enum Stage {
+        FINGERPRINT,
+        NEW_FINGERPRINT_ENROLLED,
+        PASSWORD
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintModule.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintModule.java
new file mode 100644
index 0000000..ae3acf8
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintModule.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog;
+
+import com.example.android.asymmetricfingerprintdialog.server.StoreBackend;
+import com.example.android.asymmetricfingerprintdialog.server.StoreBackendImpl;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.fingerprint.FingerprintManager;
+import android.preference.PreferenceManager;
+import android.security.keystore.KeyProperties;
+import android.view.inputmethod.InputMethodManager;
+
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for Fingerprint APIs.
+ */
+@Module(
+        library = true,
+        injects = {MainActivity.class}
+)
+public class FingerprintModule {
+
+    private final Context mContext;
+
+    public FingerprintModule(Context context) {
+        mContext = context;
+    }
+
+    @Provides
+    public Context providesContext() {
+        return mContext;
+    }
+
+    @Provides
+    public FingerprintManager providesFingerprintManager(Context context) {
+        return context.getSystemService(FingerprintManager.class);
+    }
+
+    @Provides
+    public KeyguardManager providesKeyguardManager(Context context) {
+        return context.getSystemService(KeyguardManager.class);
+    }
+
+    @Provides
+    public KeyStore providesKeystore() {
+        try {
+            return KeyStore.getInstance("AndroidKeyStore");
+        } catch (KeyStoreException e) {
+            throw new RuntimeException("Failed to get an instance of KeyStore", e);
+        }
+    }
+
+    @Provides
+    public KeyPairGenerator providesKeyPairGenerator() {
+        try {
+            return KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+            throw new RuntimeException("Failed to get an instance of KeyPairGenerator", e);
+        }
+    }
+
+    @Provides
+    public Signature providesSignature(KeyStore keyStore) {
+        try {
+            return Signature.getInstance("SHA256withECDSA");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("Failed to get an instance of Signature", e);
+        }
+    }
+
+    @Provides
+    public InputMethodManager providesInputMethodManager(Context context) {
+        return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+    }
+
+    @Provides
+    public SharedPreferences providesSharedPreferences(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context);
+    }
+
+    @Provides
+    public StoreBackend providesStoreBackend() {
+        return new StoreBackendImpl();
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintUiHelper.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintUiHelper.java
new file mode 100644
index 0000000..f654811
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintUiHelper.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.CancellationSignal;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import javax.inject.Inject;
+
+/**
+ * Small helper class to manage text/icon around fingerprint authentication UI.
+ */
+public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback {
+
+    @VisibleForTesting static final long ERROR_TIMEOUT_MILLIS = 1600;
+    @VisibleForTesting static final long SUCCESS_DELAY_MILLIS = 1300;
+
+    private final FingerprintManager mFingerprintManager;
+    private final ImageView mIcon;
+    private final TextView mErrorTextView;
+    private final Callback mCallback;
+    private CancellationSignal mCancellationSignal;
+
+    @VisibleForTesting boolean mSelfCancelled;
+
+    /**
+     * Builder class for {@link FingerprintUiHelper} in which injected fields from Dagger
+     * holds its fields and takes other arguments in the {@link #build} method.
+     */
+    public static class FingerprintUiHelperBuilder {
+        private final FingerprintManager mFingerPrintManager;
+
+        @Inject
+        public FingerprintUiHelperBuilder(FingerprintManager fingerprintManager) {
+            mFingerPrintManager = fingerprintManager;
+        }
+
+        public FingerprintUiHelper build(ImageView icon, TextView errorTextView, Callback callback) {
+            return new FingerprintUiHelper(mFingerPrintManager, icon, errorTextView,
+                    callback);
+        }
+    }
+
+    /**
+     * Constructor for {@link FingerprintUiHelper}. This method is expected to be called from
+     * only the {@link FingerprintUiHelperBuilder} class.
+     */
+    private FingerprintUiHelper(FingerprintManager fingerprintManager,
+            ImageView icon, TextView errorTextView, Callback callback) {
+        mFingerprintManager = fingerprintManager;
+        mIcon = icon;
+        mErrorTextView = errorTextView;
+        mCallback = callback;
+    }
+
+    public boolean isFingerprintAuthAvailable() {
+        return mFingerprintManager.isHardwareDetected()
+                && mFingerprintManager.hasEnrolledFingerprints();
+    }
+
+    public void startListening(FingerprintManager.CryptoObject cryptoObject) {
+        if (!isFingerprintAuthAvailable()) {
+            return;
+        }
+        mCancellationSignal = new CancellationSignal();
+        mSelfCancelled = false;
+        mFingerprintManager
+                .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
+        mIcon.setImageResource(R.drawable.ic_fp_40px);
+    }
+
+    public void stopListening() {
+        if (mCancellationSignal != null) {
+            mSelfCancelled = true;
+            mCancellationSignal.cancel();
+            mCancellationSignal = null;
+        }
+    }
+
+    @Override
+    public void onAuthenticationError(int errMsgId, CharSequence errString) {
+        if (!mSelfCancelled) {
+            showError(errString);
+            mIcon.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onError();
+                }
+            }, ERROR_TIMEOUT_MILLIS);
+        }
+    }
+
+    @Override
+    public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+        showError(helpString);
+    }
+
+    @Override
+    public void onAuthenticationFailed() {
+        showError(mIcon.getResources().getString(
+                R.string.fingerprint_not_recognized));
+    }
+
+    @Override
+    public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+        mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
+        mIcon.setImageResource(R.drawable.ic_fingerprint_success);
+        mErrorTextView.setTextColor(
+                mErrorTextView.getResources().getColor(R.color.success_color, null));
+        mErrorTextView.setText(
+                mErrorTextView.getResources().getString(R.string.fingerprint_success));
+        mIcon.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mCallback.onAuthenticated();
+            }
+        }, SUCCESS_DELAY_MILLIS);
+    }
+
+    private void showError(CharSequence error) {
+        mIcon.setImageResource(R.drawable.ic_fingerprint_error);
+        mErrorTextView.setText(error);
+        mErrorTextView.setTextColor(
+                mErrorTextView.getResources().getColor(R.color.warning_color, null));
+        mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
+        mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
+    }
+
+    @VisibleForTesting
+    Runnable mResetErrorTextRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mErrorTextView.setTextColor(
+                    mErrorTextView.getResources().getColor(R.color.hint_color, null));
+            mErrorTextView.setText(
+                    mErrorTextView.getResources().getString(R.string.fingerprint_hint));
+            mIcon.setImageResource(R.drawable.ic_fp_40px);
+        }
+    };
+
+    public interface Callback {
+
+        void onAuthenticated();
+
+        void onError();
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/InjectedApplication.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/InjectedApplication.java
new file mode 100644
index 0000000..1c3ed7e
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/InjectedApplication.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog;
+
+import android.app.Application;
+import android.util.Log;
+
+import dagger.ObjectGraph;
+
+/**
+ * The Application class of the sample which holds the ObjectGraph in Dagger and enables
+ * dependency injection.
+ */
+public class InjectedApplication extends Application {
+
+    private static final String TAG = InjectedApplication.class.getSimpleName();
+
+    private ObjectGraph mObjectGraph;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        initObjectGraph(new FingerprintModule(this));
+    }
+
+    /**
+     * Initialize the Dagger module. Passing null or mock modules can be used for testing.
+     *
+     * @param module for Dagger
+     */
+    public void initObjectGraph(Object module) {
+        mObjectGraph = module != null ? ObjectGraph.create(module) : null;
+    }
+
+    public void inject(Object object) {
+        if (mObjectGraph == null) {
+            // This usually happens during tests.
+            Log.i(TAG, "Object graph is not initialized.");
+            return;
+        }
+        mObjectGraph.inject(object);
+    }
+
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java
new file mode 100644
index 0000000..26832f2
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.util.Base64;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.spec.ECGenParameterSpec;
+
+import javax.inject.Inject;
+
+/**
+ * Main entry point for the sample, showing a backpack and "Purchase" button.
+ */
+public class MainActivity extends Activity {
+
+    private static final String DIALOG_FRAGMENT_TAG = "myFragment";
+    /** Alias for our key in the Android Key Store */
+    public static final String KEY_NAME = "my_key";
+
+    @Inject KeyguardManager mKeyguardManager;
+    @Inject FingerprintManager mFingerprintManager;
+    @Inject FingerprintAuthenticationDialogFragment mFragment;
+    @Inject KeyStore mKeyStore;
+    @Inject KeyPairGenerator mKeyPairGenerator;
+    @Inject Signature mSignature;
+    @Inject SharedPreferences mSharedPreferences;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ((InjectedApplication) getApplication()).inject(this);
+
+        setContentView(R.layout.activity_main);
+        Button purchaseButton = (Button) findViewById(R.id.purchase_button);
+        if (!mKeyguardManager.isKeyguardSecure()) {
+            // Show a message that the user hasn't set up a fingerprint or lock screen.
+            Toast.makeText(this,
+                    "Secure lock screen hasn't set up.\n"
+                            + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint",
+                    Toast.LENGTH_LONG).show();
+            purchaseButton.setEnabled(false);
+            return;
+        }
+        //noinspection ResourceType
+        if (!mFingerprintManager.hasEnrolledFingerprints()) {
+            purchaseButton.setEnabled(false);
+            // This happens when no fingerprints are registered.
+            Toast.makeText(this,
+                    "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint",
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
+        createKeyPair();
+        purchaseButton.setEnabled(true);
+        purchaseButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                findViewById(R.id.confirmation_message).setVisibility(View.GONE);
+                findViewById(R.id.encrypted_message).setVisibility(View.GONE);
+
+                // Set up the crypto object for later. The object will be authenticated by use
+                // of the fingerprint.
+                if (initSignature()) {
+
+                    // Show the fingerprint dialog. The user has the option to use the fingerprint with
+                    // crypto, or you can fall back to using a server-side verified password.
+                    mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mSignature));
+                    boolean useFingerprintPreference = mSharedPreferences
+                            .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
+                                    true);
+                    if (useFingerprintPreference) {
+                        mFragment.setStage(
+                                FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT);
+                    } else {
+                        mFragment.setStage(
+                                FingerprintAuthenticationDialogFragment.Stage.PASSWORD);
+                    }
+                    mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+                } else {
+                    // This happens if the lock screen has been disabled or or a fingerprint got
+                    // enrolled. Thus show the dialog to authenticate with their password first
+                    // and ask the user if they want to authenticate with fingerprints in the
+                    // future
+                    mFragment.setStage(
+                            FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED);
+                    mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+                }
+            }
+        });
+    }
+
+    /**
+     * Initialize the {@link Signature} instance with the created key in the
+     * {@link #createKeyPair()} method.
+     *
+     * @return {@code true} if initialization is successful, {@code false} if the lock screen has
+     * been disabled or reset after the key was generated, or if a fingerprint got enrolled after
+     * the key was generated.
+     */
+    private boolean initSignature() {
+        try {
+            mKeyStore.load(null);
+            PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
+            mSignature.initSign(key);
+            return true;
+        } catch (KeyPermanentlyInvalidatedException e) {
+            return false;
+        } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException
+                | NoSuchAlgorithmException | InvalidKeyException e) {
+            throw new RuntimeException("Failed to init Cipher", e);
+        }
+    }
+
+    public void onPurchased(byte[] signature) {
+        showConfirmation(signature);
+    }
+
+    public void onPurchaseFailed() {
+        Toast.makeText(this, R.string.purchase_fail, Toast.LENGTH_SHORT).show();
+    }
+
+    // Show confirmation, if fingerprint was used show crypto information.
+    private void showConfirmation(byte[] encrypted) {
+        findViewById(R.id.confirmation_message).setVisibility(View.VISIBLE);
+        if (encrypted != null) {
+            TextView v = (TextView) findViewById(R.id.encrypted_message);
+            v.setVisibility(View.VISIBLE);
+            v.setText(Base64.encodeToString(encrypted, 0 /* flags */));
+        }
+    }
+
+    /**
+     * Generates an asymmetric key pair in the Android Keystore. Every use of the private key must
+     * be authorized by the user authenticating with fingerprint. Public key use is unrestricted.
+     */
+    public void createKeyPair() {
+        // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
+        // for your flow. Use of keys is necessary if you need to know if the set of
+        // enrolled fingerprints has changed.
+        try {
+            // Set the alias of the entry in Android KeyStore where the key will appear
+            // and the constrains (purposes) in the constructor of the Builder
+            mKeyPairGenerator.initialize(
+                    new KeyGenParameterSpec.Builder(KEY_NAME,
+                            KeyProperties.PURPOSE_SIGN)
+                            .setDigests(KeyProperties.DIGEST_SHA256)
+                            .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                            // Require the user to authenticate with a fingerprint to authorize
+                            // every use of the private key
+                            .setUserAuthenticationRequired(true)
+                            .build());
+            mKeyPairGenerator.generateKeyPair();
+        } catch (InvalidAlgorithmParameterException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.menu_main, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int id = item.getItemId();
+
+        if (id == R.id.action_settings) {
+            Intent intent = new Intent(this, SettingsActivity.class);
+            startActivity(intent);
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/SettingsActivity.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/SettingsActivity.java
new file mode 100644
index 0000000..acc5e37
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/SettingsActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+
+public class SettingsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Display the fragment as the main content.
+        getFragmentManager().beginTransaction().replace(android.R.id.content,
+                new SettingsFragment()).commit();
+    }
+
+    /**
+     * Fragment for settings.
+     */
+    public static class SettingsFragment extends PreferenceFragment {
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            addPreferencesFromResource(R.xml.preferences);
+        }
+    }
+}
+
+
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java
new file mode 100644
index 0000000..87921ae
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog.server;
+
+import java.security.PublicKey;
+
+/**
+ * An interface that defines the methods required for the store backend.
+ */
+public interface StoreBackend {
+
+    /**
+     * Verifies the authenticity of the provided transaction by confirming that it was signed with
+     * the private key enrolled for the userId.
+     *
+     * @param transaction          the contents of the purchase transaction, its contents are
+     *                             signed
+     *                             by the
+     *                             private key in the client side.
+     * @param transactionSignature the signature of the transaction's contents.
+     * @return true if the signedSignature was verified, false otherwise. If this method returns
+     * true, the server can consider the transaction is successful.
+     */
+    boolean verify(Transaction transaction, byte[] transactionSignature);
+
+    /**
+     * Verifies the authenticity of the provided transaction by password.
+     *
+     * @param transaction the contents of the purchase transaction, its contents are signed by the
+     *                    private key in the client side.
+     * @param password    the password for the user associated with the {@code transaction}.
+     * @return true if the password is verified.
+     */
+    boolean verify(Transaction transaction, String password);
+
+    /**
+     * Enrolls a public key associated with the userId
+     *
+     * @param userId    the unique ID of the user within the app including server side
+     *                  implementation
+     * @param password  the password for the user for the server side
+     * @param publicKey the public key object to verify the signature from the user
+     * @return true if the enrollment was successful, false otherwise
+     */
+    boolean enroll(String userId, String password, PublicKey publicKey);
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java
new file mode 100644
index 0000000..b28dce4
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog.server;
+
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A fake backend implementation of {@link StoreBackend}.
+ */
+public class StoreBackendImpl implements StoreBackend {
+
+    private final Map<String, PublicKey> mPublicKeys = new HashMap<>();
+    private final Set<Transaction> mReceivedTransactions = new HashSet<>();
+
+    @Override
+    public boolean verify(Transaction transaction, byte[] transactionSignature) {
+        try {
+            if (mReceivedTransactions.contains(transaction)) {
+                // It verifies the equality of the transaction including the client nonce
+                // So attackers can't do replay attacks.
+                return false;
+            }
+            mReceivedTransactions.add(transaction);
+            PublicKey publicKey = mPublicKeys.get(transaction.getUserId());
+            Signature verificationFunction = Signature.getInstance("SHA256withECDSA");
+            verificationFunction.initVerify(publicKey);
+            verificationFunction.update(transaction.toByteArray());
+            if (verificationFunction.verify(transactionSignature)) {
+                // Transaction is verified with the public key associated with the user
+                // Do some post purchase processing in the server
+                return true;
+            }
+        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
+            // In a real world, better to send some error message to the user
+        }
+        return false;
+    }
+
+    @Override
+    public boolean verify(Transaction transaction, String password) {
+        // As this is just a sample, we always assume that the password is right.
+        return true;
+    }
+
+    @Override
+    public boolean enroll(String userId, String password, PublicKey publicKey) {
+        if (publicKey != null) {
+            mPublicKeys.put(userId, publicKey);
+        }
+        // We just ignore the provided password here, but in real life, it is registered to the
+        // backend.
+        return true;
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java
new file mode 100644
index 0000000..789cc0e
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.asymmetricfingerprintdialog.server;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * An entity that represents a single transaction (purchase) of an item.
+ */
+public class Transaction {
+
+    /** The unique ID of the item of the purchase */
+    private final Long mItemId;
+
+    /** The unique user ID who made the transaction */
+    private final String mUserId;
+
+    /**
+     * The random long value that will be also signed by the private key and verified in the server
+     * that the same nonce can't be reused to prevent replay attacks.
+     */
+    private final Long mClientNonce;
+
+    public Transaction(String userId, long itemId, long clientNonce) {
+        mItemId = itemId;
+        mUserId = userId;
+        mClientNonce = clientNonce;
+    }
+
+    public String getUserId() {
+        return mUserId;
+    }
+
+    public byte[] toByteArray() {
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        DataOutputStream dataOutputStream = null;
+        try {
+            dataOutputStream = new DataOutputStream(byteArrayOutputStream);
+            dataOutputStream.writeLong(mItemId);
+            dataOutputStream.writeUTF(mUserId);
+            dataOutputStream.writeLong(mClientNonce);
+            return byteArrayOutputStream.toByteArray();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            try {
+                if (dataOutputStream != null) {
+                    dataOutputStream.close();
+                }
+            } catch (IOException ignore) {
+            }
+            try {
+                byteArrayOutputStream.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Transaction that = (Transaction) o;
+        return Objects.equals(mItemId, that.mItemId) && Objects.equals(mUserId, that.mUserId) &&
+                Objects.equals(mClientNonce, that.mClientNonce);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mItemId, mUserId, mClientNonce);
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-hdpi/ic_fp_40px.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-hdpi/ic_fp_40px.png
new file mode 100644
index 0000000..48ebd8a
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-hdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-mdpi/ic_fp_40px.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-mdpi/ic_fp_40px.png
new file mode 100644
index 0000000..122f442
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-mdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-nodpi/android_robot.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-nodpi/android_robot.png
new file mode 100644
index 0000000..40bf934
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-nodpi/android_robot.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xhdpi/ic_fp_40px.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xhdpi/ic_fp_40px.png
new file mode 100644
index 0000000..e1c9590
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xhdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xxhdpi/ic_fp_40px.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xxhdpi/ic_fp_40px.png
new file mode 100644
index 0000000..f7e8724
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xxhdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xxxhdpi/ic_fp_40px.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xxxhdpi/ic_fp_40px.png
new file mode 100644
index 0000000..0fb8545
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable-xxxhdpi/ic_fp_40px.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/card.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/card.xml
new file mode 100644
index 0000000..691a4c5
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/card.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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">
+
+    <solid
+        android:color="#fefefe"/>
+
+    <corners
+        android:radius="2dp" />
+</shape>
\ No newline at end of file
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/ic_fingerprint_error.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/ic_fingerprint_error.xml
new file mode 100644
index 0000000..be46116
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/ic_fingerprint_error.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="40.0dp"
+        android:height="40.0dp"
+        android:viewportWidth="40.0"
+        android:viewportHeight="40.0">
+    <path
+        android:pathData="M20.0,0.0C8.96,0.0 0.0,8.95 0.0,20.0s8.96,20.0 20.0,20.0c11.04,0.0 20.0,-8.95 20.0,-20.0S31.04,0.0 20.0,0.0z"
+        android:fillColor="#F4511E"/>
+    <path
+        android:pathData="M21.33,29.33l-2.67,0.0l0.0,-2.67l2.67,0.0L21.33,29.33zM21.33,22.67l-2.67,0.0l0.0,-12.0l2.67,0.0L21.33,22.67z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/ic_fingerprint_success.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/ic_fingerprint_success.xml
new file mode 100644
index 0000000..261f3e7
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/drawable/ic_fingerprint_success.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="40.0dp"
+        android:height="40.0dp"
+        android:viewportWidth="40.0"
+        android:viewportHeight="40.0">
+    <path
+        android:pathData="M20.0,20.0m-20.0,0.0a20.0,20.0 0.0,1.0 1.0,40.0 0.0a20.0,20.0 0.0,1.0 1.0,-40.0 0.0"
+        android:fillColor="#009688"/>
+    <path
+        android:pathData="M11.2,21.41l1.63,-1.619999 4.17,4.169998 10.59,-10.589999 1.619999,1.63 -12.209999,12.209999z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/activity_main.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..8f30557
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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">
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+        <ImageView
+                android:layout_width="150dp"
+                android:layout_height="150dp"
+                android:layout_marginTop="32dp"
+                android:layout_marginBottom="32dp"
+                android:layout_gravity="center_horizontal"
+                android:scaleType="fitCenter"
+                android:src="@drawable/android_robot"/>
+
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:layout_marginStart="8dp"
+                android:layout_marginEnd="8dp"
+                android:orientation="vertical"
+                android:background="@drawable/card"
+                android:elevation="4dp"
+                android:paddingTop="16dp"
+                android:paddingBottom="16dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp">
+
+            <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="@android:style/TextAppearance.Material.Headline"
+                    android:text="@string/item_title"/>
+
+            <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textAppearance="@android:style/TextAppearance.Material.Body2"
+                    android:textColor="?android:attr/colorAccent"
+                    android:text="@string/item_price"/>
+
+            <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:textAppearance="@android:style/TextAppearance.Material.Body1"
+                    android:textColor="?android:attr/textColorSecondary"
+                    android:text="@string/item_description"/>
+
+        </LinearLayout>
+        <Button style="@android:style/Widget.Material.Button.Colored"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:layout_marginEnd="4dp"
+                android:layout_gravity="end"
+                android:textColor="?android:attr/textColorPrimaryInverse"
+                android:text="@string/purchase"
+                android:id="@+id/purchase_button"
+                android:layout_alignParentEnd="true"/>
+
+        <TextView
+                android:id="@+id/confirmation_message"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp"
+                android:textAppearance="@android:style/TextAppearance.Material.Body2"
+                android:textColor="?android:attr/colorAccent"
+                android:text="@string/purchase_done"
+                android:visibility="gone"/>
+
+        <TextView
+                android:id="@+id/encrypted_message"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                android:paddingStart="16dp"
+                android:paddingEnd="16dp"
+                android:textAppearance="@android:style/TextAppearance.Material.Body2"
+                android:textColor="?android:attr/colorAccent"
+                android:text="@string/purchase_done"
+                android:visibility="gone"/>
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml
new file mode 100644
index 0000000..2be05b1
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_backup.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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:id="@+id/backup_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="16dp"
+    android:paddingBottom="8dp">
+
+    <FrameLayout
+        android:id="@+id/description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
+        android:layout_marginStart="24dp"
+        android:layout_marginEnd="24dp"
+        >
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+            android:text="@string/password_description"
+            android:id="@+id/password_description"
+            android:textColor="?android:attr/textColorSecondary" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+            android:text="@string/new_fingerprint_enrolled_description"
+            android:id="@+id/new_fingerprint_enrolled_description"
+            android:visibility="gone"
+            android:textColor="?android:attr/textColorSecondary" />
+    </FrameLayout>
+
+    <EditText
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:inputType="textPassword"
+        android:ems="10"
+        android:hint="@string/password"
+        android:imeOptions="actionGo"
+        android:id="@+id/password"
+        android:layout_below="@+id/description"
+        android:layout_marginTop="16dp"
+        android:layout_marginStart="20dp"
+        android:layout_marginEnd="20dp"
+        android:layout_alignParentStart="true" />
+
+    <CheckBox
+        android:id="@+id/use_fingerprint_in_future_check"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/password"
+        android:layout_alignParentStart="true"
+        android:layout_marginTop="16dp"
+        android:layout_marginStart="20dp"
+        android:layout_marginEnd="20dp"
+        android:checked="true"
+        android:visibility="gone"
+        android:text="@string/use_fingerprint_in_future" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_container.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_container.xml
new file mode 100644
index 0000000..08bb1bb
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_container.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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">
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <include layout="@layout/fingerprint_dialog_content" />
+
+        <include
+            layout="@layout/fingerprint_dialog_backup"
+            android:visibility="gone" />
+
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/buttonPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingStart="12dp"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"
+        android:gravity="bottom"
+        style="?android:attr/buttonBarStyle">
+
+        <Space
+            android:id="@+id/spacer"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:visibility="invisible" />
+        <Button
+            android:id="@+id/cancel_button"
+            style="?android:attr/buttonBarNegativeButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+
+        <Button
+            android:id="@+id/second_dialog_button"
+            style="?android:attr/buttonBarPositiveButtonStyle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_content.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_content.xml
new file mode 100644
index 0000000..3929eba
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/layout/fingerprint_dialog_content.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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:id="@+id/fingerprint_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="8dp"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp"
+    android:paddingTop="16dp">
+
+    <TextView
+        android:id="@+id/fingerprint_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:text="@string/fingerprint_description"
+        android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+        android:textColor="?android:attr/textColorSecondary"/>
+
+
+    <ImageView
+        android:id="@+id/fingerprint_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_below="@+id/fingerprint_description"
+        android:layout_marginTop="20dp"
+        android:src="@drawable/ic_fp_40px" />
+
+    <TextView
+        android:id="@+id/fingerprint_status"
+        style="@android:style/TextAppearance.Material.Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignBottom="@+id/fingerprint_icon"
+        android:layout_alignTop="@+id/fingerprint_icon"
+        android:layout_marginStart="16dp"
+        android:layout_toEndOf="@+id/fingerprint_icon"
+        android:gravity="center_vertical"
+        android:text="@string/fingerprint_hint"
+        android:textColor="@color/hint_color" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/menu/menu_main.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..73f5e89
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/menu/menu_main.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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"
+    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+    <item android:id="@+id/action_settings" android:title="@string/action_settings"
+        android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b82d7af
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..b0d26ba
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..135858d
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..02960da
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d425459
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/values/colors.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/values/colors.xml
new file mode 100644
index 0000000..a24f3c8
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="warning_color">#f4511e</color>
+    <color name="hint_color">#42000000</color>
+    <color name="success_color">#009688</color>
+</resources>
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/values/strings.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f44c06d
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/values/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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="action_settings">Settings</string>
+    <string name="cancel">Cancel</string>
+    <string name="use_password">Use password</string>
+    <string name="sign_in">Sign in</string>
+    <string name="ok">Ok</string>
+    <string name="password">Password</string>
+    <string name="fingerprint_description">Confirm fingerprint to continue</string>
+    <string name="fingerprint_hint">Touch sensor</string>
+    <string name="password_description">Enter your store password to continue</string>
+    <string name="purchase">Purchase</string>
+    <string name="fingerprint_not_recognized">Fingerprint not recognized. Try again</string>
+    <string name="fingerprint_success">Fingerprint recognized</string>
+    <string name="item_title">White Mesh Pluto Backpack</string>
+    <string name="item_price">$62.68</string>
+    <string name="item_description">Mesh backpack in white. Black textile trim throughout.</string>
+    <string name="purchase_done">Purchase successful</string>
+    <string name="purchase_fail">Purchase failed</string>
+    <string name="new_fingerprint_enrolled_description">A new fingerprint was added to this device, so your password is required.</string>
+    <string name="use_fingerprint_in_future">Use fingerprint in the future</string>
+    <string name="use_fingerprint_to_authenticate_title">Use fingerprint to authenticate</string>
+    <string name="use_fingerprint_to_authenticate_key" >use_fingerprint_to_authenticate_key</string>
+</resources>
diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/res/xml/preferences.xml b/security/AsymmetricFingerprintDialog/Application/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000..761391d
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/main/res/xml/preferences.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
+    <CheckBoxPreference
+        android:key="@string/use_fingerprint_to_authenticate_key"
+        android:title="@string/use_fingerprint_to_authenticate_title"
+        android:persistent="true"
+        android:defaultValue="true" />
+</PreferenceScreen>
\ No newline at end of file
diff --git a/security/AsymmetricFingerprintDialog/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java b/security/AsymmetricFingerprintDialog/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java
new file mode 100644
index 0000000..a5a9108
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/Application/src/test/java/com/example/android/fingerprintdialog/FingerprintUiHelperTest.java
@@ -0,0 +1,111 @@
+package com.example.android.asymmetricfingerprintdialog;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import android.content.res.Resources;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for {@link FingerprintUiHelper}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FingerprintUiHelperTest {
+
+    private static final int ERROR_MSG_ID = 1;
+    private static final CharSequence ERR_STRING = "ERROR_STRING";
+    private static final int HINT_COLOR = 10;
+
+    @Mock private FingerprintManager mockFingerprintManager;
+    @Mock private ImageView mockIcon;
+    @Mock private TextView mockTextView;
+    @Mock private FingerprintUiHelper.Callback mockCallback;
+    @Mock private FingerprintManager.CryptoObject mockCryptoObject;
+    @Mock private Resources mockResources;
+
+    @Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
+
+    @InjectMocks private FingerprintUiHelper.FingerprintUiHelperBuilder mockBuilder;
+
+    private FingerprintUiHelper mFingerprintUiHelper;
+
+    @Before
+    public void setUp() {
+        mFingerprintUiHelper = mockBuilder.build(mockIcon, mockTextView, mockCallback);
+
+        when(mockFingerprintManager.isHardwareDetected()).thenReturn(true);
+        when(mockFingerprintManager.hasEnrolledFingerprints()).thenReturn(true);
+        when(mockTextView.getResources()).thenReturn(mockResources);
+        when(mockResources.getColor(R.color.hint_color, null)).thenReturn(HINT_COLOR);
+    }
+
+    @Test
+    public void testStartListening_fingerprintAuthAvailable() {
+        mFingerprintUiHelper.startListening(mockCryptoObject);
+
+        verify(mockFingerprintManager).authenticate(eq(mockCryptoObject),
+                isA(CancellationSignal.class), eq(0), eq(mFingerprintUiHelper),
+                any(Handler.class));
+        verify(mockIcon).setImageResource(R.drawable.ic_fp_40px);
+    }
+
+    @Test
+    public void testStartListening_fingerprintAuthNotAvailable() {
+        when(mockFingerprintManager.isHardwareDetected()).thenReturn(false);
+
+        mFingerprintUiHelper.startListening(mockCryptoObject);
+
+        verify(mockFingerprintManager, never()).authenticate(
+                any(FingerprintManager.CryptoObject.class), any(CancellationSignal.class), eq(0),
+                any(FingerprintUiHelper.class), any(Handler.class));
+    }
+
+    @Test
+    public void testOnAuthenticationError() {
+        mFingerprintUiHelper.mSelfCancelled = false;
+
+        mFingerprintUiHelper.onAuthenticationError(ERROR_MSG_ID, ERR_STRING);
+
+        verify(mockIcon).setImageResource(R.drawable.ic_fingerprint_error);
+        verify(mockTextView).removeCallbacks(mFingerprintUiHelper.mResetErrorTextRunnable);
+        verify(mockTextView).postDelayed(mFingerprintUiHelper.mResetErrorTextRunnable,
+                FingerprintUiHelper.ERROR_TIMEOUT_MILLIS);
+        verify(mockIcon).postDelayed(mRunnableArgumentCaptor.capture(),
+                eq(FingerprintUiHelper.ERROR_TIMEOUT_MILLIS));
+
+        mRunnableArgumentCaptor.getValue().run();
+
+        verify(mockCallback).onError();
+    }
+
+    @Test
+    public void testOnAuthenticationSucceeded() {
+        mFingerprintUiHelper.onAuthenticationSucceeded(null);
+
+        verify(mockIcon).setImageResource(R.drawable.ic_fingerprint_success);
+        verify(mockTextView).removeCallbacks(mFingerprintUiHelper.mResetErrorTextRunnable);
+        verify(mockIcon).postDelayed(mRunnableArgumentCaptor.capture(),
+                eq(FingerprintUiHelper.SUCCESS_DELAY_MILLIS));
+
+        mRunnableArgumentCaptor.getValue().run();
+
+        verify(mockCallback).onAuthenticated();
+    }
+}
diff --git a/security/AsymmetricFingerprintDialog/build.gradle b/security/AsymmetricFingerprintDialog/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/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/security/AsymmetricFingerprintDialog/buildSrc/build.gradle b/security/AsymmetricFingerprintDialog/buildSrc/build.gradle
new file mode 100644
index 0000000..8c294c2
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/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/security/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.jar b/security/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties b/security/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties
similarity index 79%
rename from ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
rename to security/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties
index a51db8c..203da3a 100644
--- a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/security/AsymmetricFingerprintDialog/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue May 20 13:33:02 BST 2014
+#Mon Apr 27 11:28:32 JST 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
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradlew b/security/AsymmetricFingerprintDialog/gradlew
similarity index 100%
copy from ui/views/Elevation/ElevationDrag/gradle/gradlew
copy to security/AsymmetricFingerprintDialog/gradlew
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradlew.bat b/security/AsymmetricFingerprintDialog/gradlew.bat
similarity index 100%
copy from ui/views/Elevation/ElevationDrag/gradle/gradlew.bat
copy to security/AsymmetricFingerprintDialog/gradlew.bat
diff --git a/security/AsymmetricFingerprintDialog/screenshots/1-purchase-screen.png b/security/AsymmetricFingerprintDialog/screenshots/1-purchase-screen.png
new file mode 100644
index 0000000..0bf03bd
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/screenshots/1-purchase-screen.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/screenshots/2-fingerprint-dialog.png b/security/AsymmetricFingerprintDialog/screenshots/2-fingerprint-dialog.png
new file mode 100644
index 0000000..5e681f9
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/screenshots/2-fingerprint-dialog.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/screenshots/3-fingerprint-authenticated.png b/security/AsymmetricFingerprintDialog/screenshots/3-fingerprint-authenticated.png
new file mode 100644
index 0000000..d485b1d
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/screenshots/3-fingerprint-authenticated.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/screenshots/4-new-fingerprint-enrolled.png b/security/AsymmetricFingerprintDialog/screenshots/4-new-fingerprint-enrolled.png
new file mode 100644
index 0000000..7dcf080
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/screenshots/4-new-fingerprint-enrolled.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/screenshots/big-icon.png b/security/AsymmetricFingerprintDialog/screenshots/big-icon.png
new file mode 100644
index 0000000..1063d0b
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/screenshots/big-icon.png
Binary files differ
diff --git a/security/AsymmetricFingerprintDialog/settings.gradle b/security/AsymmetricFingerprintDialog/settings.gradle
new file mode 100644
index 0000000..9464a35
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/settings.gradle
@@ -0,0 +1 @@
+include 'Application'
diff --git a/security/AsymmetricFingerprintDialog/template-params.xml b/security/AsymmetricFingerprintDialog/template-params.xml
new file mode 100644
index 0000000..019a132
--- /dev/null
+++ b/security/AsymmetricFingerprintDialog/template-params.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- TODO(thagikura) Add tests for Activity and Fragment once InstrumentationTests can be run
+     on an emulator or a device.
+     At this moment, due to the different API between the image and the SDK, they can't be launched.
+     E.g. Skipping device 'Nexus 5 - MNC', due to different API preview 'MNC' and 'android-MNC'
+     -->
+<sample>
+    <name>AsymmetricFingerprintDialog</name>
+    <group>Security</group>
+    <package>com.example.android.asymmetricfingerprintdialog</package>
+
+    <minSdk>23</minSdk>
+    <targetSdkVersion>23</targetSdkVersion>
+    <compileSdkVersion>23</compileSdkVersion>
+
+    <dependency>com.squareup.dagger:dagger:1.2.2</dependency>
+    <dependency>com.squareup.dagger:dagger-compiler:1.2.2</dependency>
+
+    <!-- TODO(thagikura) These dependencies should be created as testCompile instead of compile but
+         the template system doesn't allow androidTestCompile dependencies. Change it once fixed.
+    -->
+    <dependency>junit:junit:4.12</dependency>
+    <dependency>org.mockito:mockito-core:1.10.19</dependency>
+
+    <strings>
+        <intro>
+            <![CDATA[
+This sample demonstrates how you can use registered fingerprints to authenticate the user
+before proceeding some actions such as purchasing an item. This version uses asymmetric keys.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="base" />
+
+    <metadata>
+        <!-- Values: {DRAFT | PUBLISHED | INTERNAL | DEPRECATED | SUPERCEDED} -->
+        <status>DRAFT</status>
+        <categories>security</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <icon>screenshots/big-icon.png</icon>
+        <screenshots>
+            <img>screenshots/1-purchase-screen.png</img>
+            <img>screenshots/2-fingerprint-dialog.png</img>
+            <img>screenshots/3-fingerprint-authenticated.png</img>
+            <img>screenshots/4-new-fingerprint-enrolled.png</img>
+        </screenshots>
+
+        <api_refs>
+            <android>android.hardware.fingerprint.FingerprintManager</android>
+            <android>android.hardware.fingerprint.FingerprintManager.AuthenticationCallback</android>
+            <android>android.hardware.fingerprint.FingerprintManager.CryptoObject</android>
+            <android>android.security.KeyGenParameterSpec</android>
+            <android>java.security.KeyStore</android>
+            <android>java.security.Signature</android>
+            <android>java.security.KeyPairGenerator</android>
+        </api_refs>
+
+        <description>
+<![CDATA[
+A sample that demonstrates to use registered fingerprints to authenticate the user in your app
+]]>
+        </description>
+
+        <intro>
+<![CDATA[
+This sample demonstrates how you can use registered fingerprints in your app to authenticate the
+user before proceeding some actions such as purchasing an item.
+
+First you need to create an asymmetric key pair in the Android Key Store using [KeyPairGenerator][1]
+in the way that its private key can only be used after the user has authenticated with fingerprint
+and transmit the public key to your backend with the user verified password (In a real world, the
+app should show proper UIs).
+
+By setting [KeyGenParameterSpec.Builder.setUserAuthenticationRequired][2] to true, you can permit the
+use of the key only after the user authenticate it including when authenticated with the user's
+fingerprint.
+
+Then start listening to a fingerprint on the fingerprint sensor by calling
+[FingerprintManager.authenticate][3] with a [Signature][4] initialized with the asymmetric key pair
+created. Or alternatively you can fall back to server-side verified password as an authenticator.
+
+Once the fingerprint (or password) is verified, the
+[FingerprintManager.AuthenticationCallback#onAuthenticationSucceeded()][5] callback is called.
+
+Then you can verify the purchase transaction on server side with the public key passed from the
+client, by verifying the piece of data signed by the Signature.
+
+[1]: https://developer.android.com/reference/java/security/KeyPairGenerator.html
+[2]: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder.html#setUserAuthenticationRequired%28boolean%29
+[3]: https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.html#authenticate%28android.hardware.fingerprint.FingerprintManager.CryptoObject,%20android.os.CancellationSignal,%20int,%20android.hardware.fingerprint.FingerprintManager.AuthenticationCallback,%20android.os.Handler%29
+[4]: https://developer.android.com/reference/java/security/Signature.html
+[5]: https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.AuthenticationCallback.html#onAuthenticationSucceeded%28android.hardware.fingerprint.FingerprintManager.AuthenticationResult%29
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/security/ConfirmCredential/gradle/wrapper/gradle-wrapper.properties b/security/ConfirmCredential/gradle/wrapper/gradle-wrapper.properties
index afb3296..07fc193 100644
--- a/security/ConfirmCredential/gradle/wrapper/gradle-wrapper.properties
+++ b/security/ConfirmCredential/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-2.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/security/ConfirmCredential/template-params.xml b/security/ConfirmCredential/template-params.xml
index fe142c9..d803a89 100644
--- a/security/ConfirmCredential/template-params.xml
+++ b/security/ConfirmCredential/template-params.xml
@@ -21,7 +21,7 @@
      -->
 <sample>
     <name>Confirm Credential</name>
-    <group>security</group>
+    <group>Security</group>
     <package>com.example.android.confirmcredential</package>
 
     <minSdk>23</minSdk>
@@ -81,7 +81,7 @@
 with their device credentials and pass [KeyGenParameterSpec][2].
 
 By setting an integer value to the
-[KeyGeneratorSpec.Builder.setUserAuthenticationValidityDurationSeconds][3], you can consider the
+[KeyGenParameterSpec.Builder.setUserAuthenticationValidityDurationSeconds][3], you can consider the
 user as authenticated if the user has been authenticated with the device credentials
 within the last x seconds.
 
@@ -89,9 +89,9 @@
 to confirm device credentials to the user.
 
 [1]: https://developer.android.com/reference/javax/crypto/KeyGenerator.html
-[2]: https://developer.android.com/reference/android/security/KeyGenParameterSpec.html
-[3]: https://developer.android.com/reference/android/security/KeyGenParameterSpec.Builder#setUserAuthenticationValidityDurationSeconds().html
-[4]: https://developer.android.com/reference/android/app/KeyguardManager.createConfirmDeviceCredentialIntent().html
+[2]: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html
+[3]: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder.html#setUserAuthenticationValidityDurationSeconds%28int%29
+[4]: https://developer.android.com/reference/android/app/KeyguardManager.html#createConfirmDeviceCredentialIntent%28java.lang.CharSequence,%20java.lang.CharSequence%29
             ]]>
         </intro>
     </metadata>
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
index c954bfa..7caf9e6 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
@@ -16,12 +16,10 @@
 
 package com.example.android.fingerprintdialog;
 
-import android.Manifest;
 import android.app.Activity;
 import android.app.KeyguardManager;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.security.keystore.KeyGenParameterSpec;
@@ -64,8 +62,6 @@
     /** Alias for our key in the Android Key Store */
     private static final String KEY_NAME = "my_key";
 
-    private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
-
     @Inject KeyguardManager mKeyguardManager;
     @Inject FingerprintManager mFingerprintManager;
     @Inject FingerprintAuthenticationDialogFragment mFragment;
@@ -79,72 +75,65 @@
         super.onCreate(savedInstanceState);
         ((InjectedApplication) getApplication()).inject(this);
 
-        requestPermissions(new String[]{Manifest.permission.USE_FINGERPRINT},
-                FINGERPRINT_PERMISSION_REQUEST_CODE);
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] state) {
-        if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE
-                && state[0] == PackageManager.PERMISSION_GRANTED) {
-            setContentView(R.layout.activity_main);
-            Button purchaseButton = (Button) findViewById(R.id.purchase_button);
-            if (!mKeyguardManager.isKeyguardSecure()) {
-                // Show a message that the user hasn't set up a fingerprint or lock screen.
-                Toast.makeText(this,
-                        "Secure lock screen hasn't set up.\n"
-                                + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint",
-                        Toast.LENGTH_LONG).show();
-                purchaseButton.setEnabled(false);
-                return;
-            }
-            if (!mFingerprintManager.hasEnrolledFingerprints()) {
-                purchaseButton.setEnabled(false);
-                // This happens when no fingerprints are registered.
-                Toast.makeText(this,
-                        "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint",
-                        Toast.LENGTH_LONG).show();
-                return;
-            }
-            createKey();
-            purchaseButton.setEnabled(true);
-            purchaseButton.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    findViewById(R.id.confirmation_message).setVisibility(View.GONE);
-                    findViewById(R.id.encrypted_message).setVisibility(View.GONE);
-
-                    // Set up the crypto object for later. The object will be authenticated by use
-                    // of the fingerprint.
-                    if (initCipher()) {
-
-                        // Show the fingerprint dialog. The user has the option to use the fingerprint with
-                        // crypto, or you can fall back to using a server-side verified password.
-                        mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
-                        boolean useFingerprintPreference = mSharedPreferences
-                                .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
-                                        true);
-                        if (useFingerprintPreference) {
-                            mFragment.setStage(
-                                    FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT);
-                        } else {
-                            mFragment.setStage(
-                                    FingerprintAuthenticationDialogFragment.Stage.PASSWORD);
-                        }
-                        mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
-                    } else {
-                        // This happens if the lock screen has been disabled or or a fingerprint got
-                        // enrolled. Thus show the dialog to authenticate with their password first
-                        // and ask the user if they want to authenticate with fingerprints in the
-                        // future
-                        mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
-                        mFragment.setStage(
-                                FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED);
-                        mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
-                    }
-                }
-            });
+        setContentView(R.layout.activity_main);
+        Button purchaseButton = (Button) findViewById(R.id.purchase_button);
+        if (!mKeyguardManager.isKeyguardSecure()) {
+            // Show a message that the user hasn't set up a fingerprint or lock screen.
+            Toast.makeText(this,
+                    "Secure lock screen hasn't set up.\n"
+                            + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint",
+                    Toast.LENGTH_LONG).show();
+            purchaseButton.setEnabled(false);
+            return;
         }
+
+        //noinspection ResourceType
+        if (!mFingerprintManager.hasEnrolledFingerprints()) {
+            purchaseButton.setEnabled(false);
+            // This happens when no fingerprints are registered.
+            Toast.makeText(this,
+                    "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint",
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
+        createKey();
+        purchaseButton.setEnabled(true);
+        purchaseButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                findViewById(R.id.confirmation_message).setVisibility(View.GONE);
+                findViewById(R.id.encrypted_message).setVisibility(View.GONE);
+
+                // Set up the crypto object for later. The object will be authenticated by use
+                // of the fingerprint.
+                if (initCipher()) {
+
+                    // Show the fingerprint dialog. The user has the option to use the fingerprint with
+                    // crypto, or you can fall back to using a server-side verified password.
+                    mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
+                    boolean useFingerprintPreference = mSharedPreferences
+                            .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
+                                    true);
+                    if (useFingerprintPreference) {
+                        mFragment.setStage(
+                                FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT);
+                    } else {
+                        mFragment.setStage(
+                                FingerprintAuthenticationDialogFragment.Stage.PASSWORD);
+                    }
+                    mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+                } else {
+                    // This happens if the lock screen has been disabled or or a fingerprint got
+                    // enrolled. Thus show the dialog to authenticate with their password first
+                    // and ask the user if they want to authenticate with fingerprints in the
+                    // future
+                    mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
+                    mFragment.setStage(
+                            FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED);
+                    mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
+                }
+            }
+        });
     }
 
     /**
diff --git a/security/FingerprintDialog/gradle/wrapper/gradle-wrapper.properties b/security/FingerprintDialog/gradle/wrapper/gradle-wrapper.properties
index f943b1e..203da3a 100644
--- a/security/FingerprintDialog/gradle/wrapper/gradle-wrapper.properties
+++ b/security/FingerprintDialog/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/security/FingerprintDialog/template-params.xml b/security/FingerprintDialog/template-params.xml
index ab12696..33db81c 100644
--- a/security/FingerprintDialog/template-params.xml
+++ b/security/FingerprintDialog/template-params.xml
@@ -20,8 +20,8 @@
      E.g. Skipping device 'Nexus 5 - MNC', due to different API preview 'MNC' and 'android-MNC'
      -->
 <sample>
-    <name>Fingerprint Dialog Sample</name>
-    <group>security</group>
+    <name>FingerprintDialog</name>
+    <group>Security</group>
     <package>com.example.android.fingerprintdialog</package>
 
     <minSdk>23</minSdk>
@@ -87,9 +87,9 @@
 
 First you need to create a symmetric key in the Android Key Store using [KeyGenerator][1]
 which can be only be used after the user has authenticated with fingerprint and pass
-a [KeyGeneratorSpec][2].
+a [KeyGenParameterSpec][2].
 
-By setting [KeyGeneratorSpec.Builder.setUserAuthenticationRequired][3] to true, you can permit the
+By setting [KeyGenParameterSpec.Builder.setUserAuthenticationRequired][3] to true, you can permit the
 use of the key only after the user authenticate it including when authenticated with the user's
 fingerprint.
 
@@ -101,11 +101,11 @@
 [FingerprintManager.AuthenticationCallback#onAuthenticationSucceeded()][6] callback is called.
 
 [1]: https://developer.android.com/reference/javax/crypto/KeyGenerator.html
-[2]: https://developer.android.com/reference/android/security/KeyGenParameterSpec.html
-[3]: https://developer.android.com/reference/android/security/KeyGenParameterSpec.Builder#setUserAuthenticationRequired().html
-[4]: https://developer.android.com/reference/android/hardware/FingerprintManager#authenticate().html
+[2]: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html
+[3]: https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder.html#setUserAuthenticationRequired%28boolean%29
+[4]: https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.html#authenticate%28android.hardware.fingerprint.FingerprintManager.CryptoObject,%20android.os.CancellationSignal,%20int,%20android.hardware.fingerprint.FingerprintManager.AuthenticationCallback,%20android.os.Handler%29
 [5]: https://developer.android.com/reference/javax/crypto/Cipher.html
-[6]: https://developer.android.com/reference/android/hardware/FingerprintManager.AuthenticationCallback#onAuthenticationSucceeded().html
+[6]: https://developer.android.com/reference/android/hardware/fingerprint/FingerprintManager.AuthenticationCallback.html#onAuthenticationSucceeded%28android.hardware.fingerprint.FingerprintManager.AuthenticationResult%29
 ]]>
         </intro>
     </metadata>
diff --git a/security/keystore/BasicAndroidKeyStore/Application/src/main/java/com/example/android/basicandroidkeystore/BasicAndroidKeyStoreFragment.java b/security/keystore/BasicAndroidKeyStore/Application/src/main/java/com/example/android/basicandroidkeystore/BasicAndroidKeyStoreFragment.java
index 12873e8..e6244bf 100644
--- a/security/keystore/BasicAndroidKeyStore/Application/src/main/java/com/example/android/basicandroidkeystore/BasicAndroidKeyStoreFragment.java
+++ b/security/keystore/BasicAndroidKeyStore/Application/src/main/java/com/example/android/basicandroidkeystore/BasicAndroidKeyStoreFragment.java
@@ -156,7 +156,7 @@
         // generated.
         Calendar start = new GregorianCalendar();
         Calendar end = new GregorianCalendar();
-        end.add(1, Calendar.YEAR);
+        end.add(Calendar.YEAR, 1);
         //END_INCLUDE(create_valid_dates)
 
 
@@ -316,8 +316,7 @@
         // Verify the data.
         s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate());
         s.update(data);
-        boolean valid = s.verify(signature);
-        return valid;
+        return s.verify(signature);
         // END_INCLUDE(verify_data)
     }
 
diff --git a/security/keystore/BasicAndroidKeyStore/gradle/wrapper/gradle-wrapper.properties b/security/keystore/BasicAndroidKeyStore/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
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
index 9f54d02..9a3fd5e 100644
--- 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
@@ -24,7 +24,6 @@
 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;
@@ -91,10 +90,9 @@
     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);
+        mLayoutManager = mRecyclerView.getLayoutManager();
         mRecyclerView.scrollToPosition(0);
         mRecyclerView.setAdapter(mUsageListAdapter);
         mOpenUsageSettingButton = (Button) rootView.findViewById(R.id.button_open_usage_setting);
@@ -197,7 +195,7 @@
 
         @Override
         public int compare(UsageStats left, UsageStats right) {
-            return (int) (right.getLastTimeUsed() - left.getLastTimeUsed());
+            return Long.compare(right.getLastTimeUsed(), left.getLastTimeUsed());
         }
     }
 
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
index 1d567b7..297bf1e 100644
--- 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
@@ -16,11 +16,13 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:gravity="center_vertical"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:orientation="vertical"
-              android:padding="@dimen/margin_medium">
+              android:padding="@dimen/margin_medium"
+              >
 
     <Button android:id="@+id/button_open_usage_setting"
             android:layout_width="wrap_content"
@@ -50,6 +52,8 @@
             android:scrollbars="vertical"
             android:drawSelectorOnTop="true"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"
+            />
 
 </LinearLayout>
diff --git a/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties b/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties
index 8a27ebf..cd50297 100644
--- a/system/AppUsageStatistics/gradle/wrapper/gradle-wrapper.properties
+++ b/system/AppUsageStatistics/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/system/RuntimePermissions/gradle/wrapper/gradle-wrapper.properties b/system/RuntimePermissions/gradle/wrapper/gradle-wrapper.properties
index 8a27ebf..cd50297 100644
--- a/system/RuntimePermissions/gradle/wrapper/gradle-wrapper.properties
+++ b/system/RuntimePermissions/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/system/RuntimePermissionsBasic/gradle/wrapper/gradle-wrapper.properties b/system/RuntimePermissionsBasic/gradle/wrapper/gradle-wrapper.properties
index 8a27ebf..cd50297 100644
--- a/system/RuntimePermissionsBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/system/RuntimePermissionsBasic/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/testing/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties b/testing/ActivityInstrumentation/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/DrawableTinting/gradle/wrapper/gradle-wrapper.properties b/ui/DrawableTinting/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/Interpolator/gradle/wrapper/gradle-wrapper.properties b/ui/Interpolator/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/accessibility/BasicAccessibility/gradle/wrapper/gradle-wrapper.properties b/ui/accessibility/BasicAccessibility/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbar/DoneBar/gradle/wrapper/gradle-wrapper.properties b/ui/actionbar/DoneBar/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-Basic/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-ListPopupMenu/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-ListViewModalSelect/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml
index 177f448..c039113 100644
--- a/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml
+++ b/ui/actionbarcompat/ActionBarCompat-ShareActionProvider/template-params.xml
@@ -44,8 +44,8 @@
         <level>ADVANCED</level>
         <icon>screenshots/icon-web.png</icon>
         <screenshots>
-            <img>screenshots/1-gridview.png</img>
-            <img>screenshots/2-detail.png</img>
+            <img>screenshots/1-image.png</img>
+            <img>screenshots/2-text.png</img>
         </screenshots>
         <api_refs>
             <android>android.support.v7.widget.ShareActionProvider</android>
diff --git a/ui/actionbarcompat/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties b/ui/actionbarcompat/ActionBarCompat-Styled/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/actionbarcompat/ActionBarCompat-Styled/screenshots/1-activity.png.png b/ui/actionbarcompat/ActionBarCompat-Styled/screenshots/1-activity.png
similarity index 100%
rename from ui/actionbarcompat/ActionBarCompat-Styled/screenshots/1-activity.png.png
rename to ui/actionbarcompat/ActionBarCompat-Styled/screenshots/1-activity.png
Binary files differ
diff --git a/ui/activityscenetransition/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties b/ui/activityscenetransition/ActivitySceneTransitionBasic/gradle/wrapper/gradle-wrapper.properties
index 46b1748..2ea35fd 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/activitytasks/DocumentCentricApps/gradle/wrapper/gradle-wrapper.properties b/ui/activitytasks/DocumentCentricApps/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/activitytasks/DocumentCentricRelinquishIdentity/gradle/wrapper/gradle-wrapper.properties b/ui/activitytasks/DocumentCentricRelinquishIdentity/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/ui/ImageDetailFragment.java b/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/ui/ImageDetailFragment.java
index 506729a..75d16c1 100644
--- a/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/ui/ImageDetailFragment.java
+++ b/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/ui/ImageDetailFragment.java
@@ -23,6 +23,7 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ImageView;
+import android.widget.ProgressBar;
 
 import com.example.android.displayingbitmaps.R;
 import com.example.android.displayingbitmaps.util.ImageFetcher;
@@ -32,10 +33,11 @@
 /**
  * This fragment will populate the children of the ViewPager from {@link ImageDetailActivity}.
  */
-public class ImageDetailFragment extends Fragment {
+public class ImageDetailFragment extends Fragment implements ImageWorker.OnImageLoadedListener {
     private static final String IMAGE_DATA_EXTRA = "extra_image_data";
     private String mImageUrl;
     private ImageView mImageView;
+    private ProgressBar mProgressBar;
     private ImageFetcher mImageFetcher;
 
     /**
@@ -75,6 +77,7 @@
         // Inflate and locate the main ImageView
         final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
         mImageView = (ImageView) v.findViewById(R.id.imageView);
+        mProgressBar = (ProgressBar) v.findViewById(R.id.progressbar);
         return v;
     }
 
@@ -86,7 +89,7 @@
         // cache can be used over all pages in the ViewPager
         if (ImageDetailActivity.class.isInstance(getActivity())) {
             mImageFetcher = ((ImageDetailActivity) getActivity()).getImageFetcher();
-            mImageFetcher.loadImage(mImageUrl, mImageView);
+            mImageFetcher.loadImage(mImageUrl, mImageView, this);
         }
 
         // Pass clicks on the ImageView to the parent activity to handle
@@ -104,4 +107,11 @@
             mImageView.setImageDrawable(null);
         }
     }
+
+    @Override
+    public void onImageLoaded(boolean success) {
+        // Set loading spinner to gone once image has loaded. Cloud also show
+        // an error view here if needed.
+        mProgressBar.setVisibility(View.GONE);
+    }
 }
diff --git a/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/util/ImageWorker.java b/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/util/ImageWorker.java
index f44d00d..d42d3c5 100644
--- a/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/util/ImageWorker.java
+++ b/ui/graphics/DisplayingBitmaps/Application/src/main/java/com/example/android/displayingbitmaps/util/ImageWorker.java
@@ -71,8 +71,9 @@
      *
      * @param data The URL of the image to download.
      * @param imageView The ImageView to bind the downloaded image to.
+     * @param listener A listener that will be called back once the image has been loaded.
      */
-    public void loadImage(Object data, ImageView imageView) {
+    public void loadImage(Object data, ImageView imageView, OnImageLoadedListener listener) {
         if (data == null) {
             return;
         }
@@ -86,9 +87,12 @@
         if (value != null) {
             // Bitmap found in memory cache
             imageView.setImageDrawable(value);
+            if (listener != null) {
+                listener.onImageLoaded(true);
+            }
         } else if (cancelPotentialWork(data, imageView)) {
             //BEGIN_INCLUDE(execute_background_task)
-            final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
+            final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView, listener);
             final AsyncDrawable asyncDrawable =
                     new AsyncDrawable(mResources, mLoadingBitmap, task);
             imageView.setImageDrawable(asyncDrawable);
@@ -102,6 +106,21 @@
     }
 
     /**
+     * Load an image specified by the data parameter into an ImageView (override
+     * {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
+     * disk cache will be used if an {@link ImageCache} has been added using
+     * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCache.ImageCacheParams)}. If the
+     * image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
+     * will be created to asynchronously load the bitmap.
+     *
+     * @param data The URL of the image to download.
+     * @param imageView The ImageView to bind the downloaded image to.
+     */
+    public void loadImage(Object data, ImageView imageView) {
+        loadImage(data, imageView, null);
+    }
+
+    /**
      * Set placeholder bitmap that shows when the the background thread is running.
      *
      * @param bitmap
@@ -238,10 +257,18 @@
     private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
         private Object mData;
         private final WeakReference<ImageView> imageViewReference;
+        private final OnImageLoadedListener mOnImageLoadedListener;
 
         public BitmapWorkerTask(Object data, ImageView imageView) {
             mData = data;
             imageViewReference = new WeakReference<ImageView>(imageView);
+            mOnImageLoadedListener = null;
+        }
+
+        public BitmapWorkerTask(Object data, ImageView imageView, OnImageLoadedListener listener) {
+            mData = data;
+            imageViewReference = new WeakReference<ImageView>(imageView);
+            mOnImageLoadedListener = listener;
         }
 
         /**
@@ -318,6 +345,7 @@
         @Override
         protected void onPostExecute(BitmapDrawable value) {
             //BEGIN_INCLUDE(complete_background_work)
+            boolean success = false;
             // if cancel was called on this task or the "exit early" flag is set then we're done
             if (isCancelled() || mExitTasksEarly) {
                 value = null;
@@ -328,8 +356,12 @@
                 if (BuildConfig.DEBUG) {
                     Log.d(TAG, "onPostExecute - setting bitmap");
                 }
+                success = true;
                 setImageDrawable(imageView, value);
             }
+            if (mOnImageLoadedListener != null) {
+                mOnImageLoadedListener.onImageLoaded(success);
+            }
             //END_INCLUDE(complete_background_work)
         }
 
@@ -358,6 +390,19 @@
     }
 
     /**
+     * Interface definition for callback on image loaded successfully.
+     */
+    public interface OnImageLoadedListener {
+
+        /**
+         * Called once the image has been loaded.
+         * @param success True if the image was loaded successfully, false if
+         *                there was an error.
+         */
+        void onImageLoaded(boolean success);
+    }
+
+    /**
      * A custom Drawable that will be attached to the imageView while the work is in progress.
      * Contains a reference to the actual worker task, so that it can be stopped if a new binding is
      * required, and makes sure that only the last started worker process can bind its result,
diff --git a/ui/graphics/DisplayingBitmaps/Application/src/main/res/layout/image_detail_fragment.xml b/ui/graphics/DisplayingBitmaps/Application/src/main/res/layout/image_detail_fragment.xml
index 97ac520..d8bd2f7 100644
--- a/ui/graphics/DisplayingBitmaps/Application/src/main/res/layout/image_detail_fragment.xml
+++ b/ui/graphics/DisplayingBitmaps/Application/src/main/res/layout/image_detail_fragment.xml
@@ -20,6 +20,7 @@
     android:layout_height="fill_parent" >
 
     <ProgressBar
+        android:id="@+id/progressbar"
         style="?android:attr/progressBarStyleLarge"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/ui/graphics/DisplayingBitmaps/README.md b/ui/graphics/DisplayingBitmaps/README.md
index c0269d8..e47b105 100644
--- a/ui/graphics/DisplayingBitmaps/README.md
+++ b/ui/graphics/DisplayingBitmaps/README.md
@@ -21,7 +21,7 @@
 --------------
 
 - Android SDK v23
-- Android Build Tools v23.0.0
+- Android Build Tools v23.0.2
 - Android Support Repository
 
 Screenshots
diff --git a/ui/graphics/DisplayingBitmaps/gradle/wrapper/gradle-wrapper.properties b/ui/graphics/DisplayingBitmaps/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/graphics/PdfRendererBasic/gradle/wrapper/gradle-wrapper.properties b/ui/graphics/PdfRendererBasic/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/holo/BorderlessButtons/gradle/wrapper/gradle-wrapper.properties b/ui/holo/BorderlessButtons/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/lists/CustomChoiceList/gradle/wrapper/gradle-wrapper.properties b/ui/lists/CustomChoiceList/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/transition/AdapterTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/AdapterTransition/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/transition/BasicTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/BasicTransition/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/transition/CustomTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/CustomTransition/gradle/wrapper/gradle-wrapper.properties
index 4cd80b2..fb05029 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/transition/FragmentTransition/gradle/wrapper/gradle-wrapper.properties b/ui/transition/FragmentTransition/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/CardView/gradle/wrapper/gradle-wrapper.properties b/ui/views/CardView/gradle/wrapper/gradle-wrapper.properties
index 78c4ddd..5fd1493 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/Clipping/ClippingBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/Clipping/ClippingBasic/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/Elevation/ElevationBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/Elevation/ElevationBasic/gradle/wrapper/gradle-wrapper.properties
index 2aebf9c..f1b6ded 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.jar b/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 5838598..0000000
--- a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
Binary files differ
diff --git a/ui/views/Elevation/ElevationDrag/gradle/wrapper/gradle-wrapper.properties b/ui/views/Elevation/ElevationDrag/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.jar b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 0087cd3..0000000
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
Binary files differ
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 12dab4c..0000000
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Oct 15 14:12:11 BST 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/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradlew b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradlew
deleted file mode 100755
index 91a7e26..0000000
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-##  Gradle start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
-    echo "$*"
-}
-
-die ( ) {
-    echo
-    echo "$*"
-    echo
-    exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
-    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-        JAVACMD="$JAVA_HOME/bin/java"
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD="java"
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
-        fi
-        i=$((i+1))
-    done
-    case $i in
-        (0) set -- ;;
-        (1) set -- "$args0" ;;
-        (2) set -- "$args0" "$args1" ;;
-        (3) set -- "$args0" "$args1" "$args2" ;;
-        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
-fi
-
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradlew.bat b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradlew.bat
deleted file mode 100644
index 8a0b282..0000000
--- a/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@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/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/FloatingActionButton/FloatingActionButtonBasic/gradle/wrapper/gradle-wrapper.properties
index a4c577a..f15b32c 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/HorizontalPaging/gradle/wrapper/gradle-wrapper.properties b/ui/views/HorizontalPaging/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java b/ui/views/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java
index 1176757..26d677b 100644
--- a/ui/views/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java
+++ b/ui/views/NavigationDrawer/Application/src/main/java/com/example/android/navigationdrawer/NavigationDrawerActivity.java
@@ -27,7 +27,6 @@
 import android.support.v4.app.ActionBarDrawerToggle;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -89,7 +88,6 @@
         mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
         // improve performance by indicating the list if fixed size.
         mDrawerList.setHasFixedSize(true);
-        mDrawerList.setLayoutManager(new LinearLayoutManager(this));
 
         // set up the drawer's list view with items and click listener
         mDrawerList.setAdapter(new PlanetAdapter(mPlanetTitles, this));
diff --git a/ui/views/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml b/ui/views/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml
index 4e61639..673ef63 100644
--- a/ui/views/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml
+++ b/ui/views/NavigationDrawer/Application/src/main/res/layout/activity_navigation_drawer.xml
@@ -19,9 +19,12 @@
 <!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
 <android.support.v4.widget.DrawerLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    tools:openDrawer="start" >
 
     <!-- As the main content view, the view below consumes the entire
          space available using match_parent in both dimensions. -->
@@ -44,5 +47,6 @@
         android:layout_gravity="left|start"
         android:choiceMode="singleChoice"
         android:divider="@null"
+        app:layoutManager="LinearLayoutManager"
         />
-</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
+</android.support.v4.widget.DrawerLayout>
diff --git a/ui/views/NavigationDrawer/README.md b/ui/views/NavigationDrawer/README.md
index 093ef66..ce9cfe2 100644
--- a/ui/views/NavigationDrawer/README.md
+++ b/ui/views/NavigationDrawer/README.md
@@ -8,8 +8,8 @@
 Pre-requisites
 --------------
 
-- Android SDK v21
-- Android Build Tools v23.0.0
+- Android SDK v23
+- Android Build Tools v23.0.2
 - Android Support Repository
 
 Getting Started
diff --git a/ui/views/NavigationDrawer/gradle/wrapper/gradle-wrapper.properties b/ui/views/NavigationDrawer/gradle/wrapper/gradle-wrapper.properties
index 5f735f1..5288c6d 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/NavigationDrawer/template-params.xml b/ui/views/NavigationDrawer/template-params.xml
index 559f32e..e85b0dd 100644
--- a/ui/views/NavigationDrawer/template-params.xml
+++ b/ui/views/NavigationDrawer/template-params.xml
@@ -26,13 +26,13 @@
 
     <!-- change minSdk if needed-->
     <minSdk>21</minSdk>
-    <compileSdkVersion>21</compileSdkVersion>
+    <compileSdkVersion>23</compileSdkVersion>
 
 
-    <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>
+    <dependency>com.android.support:support-v13:23.1.1</dependency>
+    <dependency>com.android.support:appcompat-v7:23.1.1</dependency>
+    <dependency>com.android.support:recyclerview-v7:23.1.1</dependency>
+    <dependency>com.android.support:cardview-v7:23.1.1</dependency>
 
     <strings>
         <intro>
diff --git a/ui/views/RecyclerView/gradle/wrapper/gradle-wrapper.properties b/ui/views/RecyclerView/gradle/wrapper/gradle-wrapper.properties
index 0e0523e..8505d8d 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.jar b/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 5838598..0000000
--- a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.jar
+++ /dev/null
Binary files differ
diff --git a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties b/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 62a8a6c..0000000
--- a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Fri May 23 13:44:29 BST 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/views/RevealEffect/RevealEffectBasic/gradle/gradlew b/ui/views/RevealEffect/RevealEffectBasic/gradle/gradlew
deleted file mode 100755
index 91a7e26..0000000
--- a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-##  Gradle start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
-    echo "$*"
-}
-
-die ( ) {
-    echo
-    echo "$*"
-    echo
-    exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
-    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-        JAVACMD="$JAVA_HOME/bin/java"
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD="java"
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
-        fi
-        i=$((i+1))
-    done
-    case $i in
-        (0) set -- ;;
-        (1) set -- "$args0" ;;
-        (2) set -- "$args0" "$args1" ;;
-        (3) set -- "$args0" "$args1" "$args2" ;;
-        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
-fi
-
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradlew.bat b/ui/views/RevealEffect/RevealEffectBasic/gradle/gradlew.bat
deleted file mode 100644
index aec9973..0000000
--- a/ui/views/RevealEffect/RevealEffectBasic/gradle/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@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/ui/views/RevealEffect/RevealEffectBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/RevealEffect/RevealEffectBasic/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-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 c25449e..fb05029 100644
--- a/ui/views/SlidingTabs/SlidingTabsBasic/gradle/wrapper/gradle-wrapper.properties
+++ b/ui/views/SlidingTabs/SlidingTabsBasic/gradle/wrapper/gradle-wrapper.properties
@@ -3,5 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
-
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-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 0c8edb4..fac5adc 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic/gradle/wrapper/gradle-wrapper.properties b/ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic/gradle/wrapper/gradle-wrapper.properties
index 2727e36..653fca1 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-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 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews/gradle/wrapper/gradle-wrapper.properties b/ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/TextSwitcher/gradle/wrapper/gradle-wrapper.properties b/ui/views/TextSwitcher/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/window/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/ui/window/AdvancedImmersiveMode/gradle/wrapper/gradle-wrapper.properties
index faba5b7..699a491 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/window/BasicImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/ui/window/BasicImmersiveMode/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/window/ImmersiveMode/gradle/wrapper/gradle-wrapper.properties b/ui/window/ImmersiveMode/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/views/TextLinkify/gradle/wrapper/gradle-wrapper.properties b/views/TextLinkify/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml b/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml
index aa8a14a..ad6cccd 100644
--- a/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/AgendaData/Application/src/main/AndroidManifest.xml
@@ -18,16 +18,22 @@
           package="com.example.android.wearable.agendadata">
 
     <uses-sdk android:minSdkVersion="18"
-        android:targetSdkVersion="21" />
+        android:targetSdkVersion="23" />
 
+    <!-- BEGIN_INCLUDE(manifest) -->
+
+    <!-- Note that all required permissions are declared here in the Android manifest.
+         On Android M and above, use of these permissions is only requested at run time. -->
     <uses-permission android:name="android.permission.READ_CALENDAR" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
 
+    <!-- END_INCLUDE(manifest) -->
+
     <application
-            android:icon="@drawable/ic_launcher"
-            android:label="@string/app_name"
-            android:theme="@android:style/Theme.Holo.Light"
-            >
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/Theme.AppCompat.Light">
 
         <meta-data
                 android:name="com.google.android.gms.version"
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
index c39a5ed..9d65b7e 100644
--- 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
@@ -250,6 +250,11 @@
         public PutDataMapRequest toPutDataMapRequest(){
             final PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(
                     makeDataItemPath(eventId, begin));
+            /* In most cases (as in this one), you don't need your DataItem appear instantly. By
+            default, delivery of normal DataItems to the Wear network might be delayed in order to
+            improve battery life for user devices. However, if you can't tolerate a delay in the
+            sync of your DataItems, you can mark them as urgent via setUrgent().
+             */
             DataMap data = putDataMapRequest.getDataMap();
             data.putString(DATA_ITEM_URI, putDataMapRequest.getUri().toString());
             data.putLong(ID, id);
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
index 34e327b..6a9678a 100644
--- 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
@@ -18,11 +18,16 @@
 
 import static com.example.android.wearable.agendadata.Constants.TAG;
 
-import android.app.Activity;
+import android.Manifest;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.AppCompatActivity;
 import android.util.Log;
 import android.view.View;
 import android.widget.ScrollView;
@@ -40,27 +45,42 @@
 import com.google.android.gms.wearable.NodeApi;
 import com.google.android.gms.wearable.Wearable;
 
-import java.util.List;
+/**
+ * Syncs or deletes calendar events (event time, description, and background image) to your
+ * Wearable via the Wearable DataApi at the click of a button. Includes code to handle dynamic M+
+ * permissions as well.
+ */
+public class MainActivity extends AppCompatActivity implements
+        NodeApi.NodeListener,
+        ConnectionCallbacks,
+        OnConnectionFailedListener,
+        ActivityCompat.OnRequestPermissionsResultCallback {
 
-public class MainActivity extends Activity implements NodeApi.NodeListener, ConnectionCallbacks,
-        OnConnectionFailedListener {
-
-    /** Request code for launching the Intent to resolve Google Play services errors. */
+    /* Request code for launching the Intent to resolve Google Play services errors. */
     private static final int REQUEST_RESOLVE_ERROR = 1000;
 
+    /* Id to identify calendar and contact permissions request. */
+    private static final int REQUEST_CALENDAR_AND_CONTACTS = 0;
+
+
     private GoogleApiClient mGoogleApiClient;
     private boolean mResolvingError = false;
 
     private TextView mLogTextView;
     ScrollView mScroller;
 
+    private View mLayout;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.main);
+        mLayout = findViewById(R.id.main_layout);
+
         mLogTextView = (TextView) findViewById(R.id.log);
         mScroller = (ScrollView) findViewById(R.id.scroller);
+
         mGoogleApiClient = new GoogleApiClient.Builder(this)
                 .addApi(Wearable.API)
                 .addConnectionCallbacks(this)
@@ -85,11 +105,94 @@
         super.onStop();
     }
 
-    public void onGetEventsClicked(View v) {
+    public void onGetEventsClicked(View view) {
+
+        Log.i(TAG, "onGetEventsClicked(): Checking permission.");
+
+        // BEGIN_INCLUDE(calendar_and_contact_permissions)
+        // Check if the Calendar permission is already available.
+        boolean calendarApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        boolean contactsApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (!calendarApproved || !contactsApproved) {
+            // Calendar and/or Contact permissions have not been granted.
+           requestCalendarAndContactPermissions();
+
+        } else {
+            // Calendar permissions is already available, start service
+            Log.i(TAG, "Permissions already granted. Starting service.");
+            pushCalendarToWear();
+        }
+        // END_INCLUDE(calendar_and_contact_permissions)
+
+    }
+
+    private void pushCalendarToWear() {
         startService(new Intent(this, CalendarQueryService.class));
     }
 
-    public void onDeleteEventsClicked(View v) {
+    /*
+     * Requests Calendar and Contact permissions.
+     * If the permission has been denied previously, a SnackBar will prompt the user to grant the
+     * permission, otherwise it is requested directly.
+     */
+    private void requestCalendarAndContactPermissions() {
+        Log.i(TAG, "CALENDAR permission has NOT been granted. Requesting permission.");
+
+        // BEGIN_INCLUDE(calendar_and_contact_permissions_request)
+
+        boolean showCalendarPermissionRationale =
+                ActivityCompat.shouldShowRequestPermissionRationale(this,
+                        Manifest.permission.READ_CALENDAR);
+        boolean showContactsPermissionRationale =
+                ActivityCompat.shouldShowRequestPermissionRationale(this,
+                        Manifest.permission.READ_CONTACTS);
+
+        if (showCalendarPermissionRationale || showContactsPermissionRationale) {
+            /*
+             * Provide an additional rationale to the user if the permission was not granted and
+             * the user would benefit from additional context for the use of the permission. For
+             * example, if the user has previously denied the permission.
+             */
+            Log.i(TAG, "Display calendar & contact permissions rationale for additional context.");
+
+            Snackbar.make(mLayout, R.string.permissions_rationale,
+                    Snackbar.LENGTH_INDEFINITE)
+                    .setAction(R.string.ok, new View.OnClickListener() {
+                        @Override
+                        public void onClick(View view) {
+                            ActivityCompat.requestPermissions(MainActivity.this,
+                                    new String[] {
+                                            Manifest.permission.READ_CALENDAR,
+                                            Manifest.permission.READ_CONTACTS},
+                                    REQUEST_CALENDAR_AND_CONTACTS);
+                        }
+                    })
+                    .show();
+
+
+        } else {
+
+            // Calendar/Contact permissions have not been granted yet. Request it directly.
+            ActivityCompat.requestPermissions(
+                    this,
+                    new String[]{
+                            Manifest.permission.READ_CALENDAR,
+                            Manifest.permission.READ_CONTACTS
+                    },
+                    REQUEST_CALENDAR_AND_CONTACTS);
+        }
+        // END_INCLUDE(calendar_and_contact_permissions_request)
+    }
+
+
+
+    public void onDeleteEventsClicked(View view) {
         if (mGoogleApiClient.isConnected()) {
             Wearable.DataApi.getDataItems(mGoogleApiClient)
                     .setResultCallback(new ResultCallback<DataItemBuffer>() {
@@ -100,9 +203,8 @@
                                     deleteDataItems(result);
                                 } else {
                                     if (Log.isLoggable(TAG, Log.DEBUG)) {
-                                        Log.d(TAG,"onDeleteEventsClicked(): failed to get Data "
+                                        Log.d(TAG, "onDeleteEventsClicked(): failed to get Data "
                                                 + "Items");
-
                                     }
                                 }
                             } finally {
@@ -120,9 +222,11 @@
         if (mGoogleApiClient.isConnected()) {
             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.
+                /*
+                 * In a real calendar application, this might delete the corresponding calendar
+                 * events from the calendar data provider. However, we simply delete the DataItem,
+                 * but leave the phone's calendar data intact for this simple sample.
+                 */
                 Wearable.DataApi.deleteDataItems(mGoogleApiClient, dataItemUri)
                         .setResultCallback(new ResultCallback<DataApi.DeleteDataItemsResult>() {
                             @Override
@@ -141,17 +245,6 @@
         }
     }
 
-    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");
@@ -165,7 +258,7 @@
     @Override
     public void onConnected(Bundle connectionHint) {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Connected to Google Api Service");
+            Log.d(TAG, "Connected to Google Api Service.");
         }
         mResolvingError = false;
         Wearable.NodeApi.addListener(mGoogleApiClient, this);
@@ -193,10 +286,77 @@
                 result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
             } catch (IntentSender.SendIntentException e) {
                 // There was an error with the resolution intent. Try again.
+                mResolvingError = false;
                 mGoogleApiClient.connect();
             }
         } else {
             mResolvingError = false;
         }
     }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onActivityResult request/result codes: " + requestCode + "/" + resultCode);
+        }
+
+        if (requestCode == REQUEST_RESOLVE_ERROR) {
+            mResolvingError = false;
+            if (resultCode == RESULT_OK) {
+                // Make sure the app is not already connected or attempting to connect
+                if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) {
+                    mGoogleApiClient.connect();
+                }
+            }
+        }
+    }
+
+    /**
+     * Callback received when a permissions request has been completed.
+     */
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onRequestPermissionsResult(): " + permissions);
+        }
+
+        if (requestCode == REQUEST_CALENDAR_AND_CONTACTS) {
+            // BEGIN_INCLUDE(permissions_result)
+            // Received permission result for calendar permission.
+            Log.i(TAG, "Received response for Calendar permission request.");
+
+            // Check if all required permissions have been granted.
+            if ((grantResults.length == 2)
+                    && (grantResults[0] == PackageManager.PERMISSION_GRANTED)
+                    && (grantResults[1] == PackageManager.PERMISSION_GRANTED)) {
+                // Calendar/Contact permissions have been granted, pull all calendar events
+                Log.i(TAG, "All permission has now been granted. Showing preview.");
+                Snackbar.make(mLayout, R.string.permisions_granted, Snackbar.LENGTH_SHORT).show();
+
+                pushCalendarToWear();
+
+            } else {
+                Log.i(TAG, "CALENDAR and/or CONTACT permissions were NOT granted.");
+                Snackbar.make(mLayout, R.string.permissions_denied, Snackbar.LENGTH_SHORT).show();
+            }
+            // END_INCLUDE(permissions_result)
+
+        } else {
+            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
+
+    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);
+            }
+        });
+    }
 }
diff --git a/wearable/wear/AgendaData/Application/src/main/res/layout/main.xml b/wearable/wear/AgendaData/Application/src/main/res/layout/main.xml
index 8e82cdd..57fc99a 100644
--- a/wearable/wear/AgendaData/Application/src/main/res/layout/main.xml
+++ b/wearable/wear/AgendaData/Application/src/main/res/layout/main.xml
@@ -15,10 +15,17 @@
 -->
 
 <LinearLayout
+    android:id="@+id/main_layout"
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="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"
+    tools:context=".MainActivity">
 
     <Button
         android:layout_height="wrap_content"
@@ -37,7 +44,7 @@
         android:textAppearance="?android:textAppearanceLarge"
         android:layout_marginTop="6dp"
         android:padding="6dp"
-        android:text="@string/log"
+        android:text="@string/log_label"
         android:textAllCaps="true"
         android:textColor="@android:color/white"
         android:background="@android:color/black"/>
diff --git a/wearable/wear/AgendaData/Application/src/main/res/values/strings.xml b/wearable/wear/AgendaData/Application/src/main/res/values/strings.xml
index 9969f4f..84cb60d 100644
--- a/wearable/wear/AgendaData/Application/src/main/res/values/strings.xml
+++ b/wearable/wear/AgendaData/Application/src/main/res/values/strings.xml
@@ -17,5 +17,9 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="get_events">Sync calendar events to wearable</string>
     <string name="delete_events">Delete calendar events from wearable</string>
-    <string name="log">Log</string>
+    <string name="log_label">Deletion Log:</string>
+    <string name="permisions_granted">Permissions granted. Send Calendar events to Wear device.</string>
+    <string name="permissions_denied">Permission requests were denied. Can\'t send calendar events.</string>
+    <string name="permissions_rationale"><![CDATA[Calendar & Contact permissions are required to push calendar to Wear device.]]></string>
+    <string name="ok">OK</string>
 </resources>
diff --git a/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml b/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml
index dcab622..e6dbab7 100644
--- a/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/AgendaData/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.wearable.agendadata" >
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="21" />
+        android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
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
index 0cbda71..02a3861 100644
--- 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
@@ -72,7 +72,7 @@
             if (event.getType() == DataEvent.TYPE_DELETED) {
                 deleteDataItem(event.getDataItem());
             } else if (event.getType() == DataEvent.TYPE_CHANGED) {
-                UpdateNotificationForDataItem(event.getDataItem());
+                updateNotificationForDataItem(event.getDataItem());
             }
         }
     }
@@ -89,7 +89,7 @@
     /**
      * Posts a local notification to show calendar card.
      */
-    private void UpdateNotificationForDataItem(DataItem dataItem) {
+    private void updateNotificationForDataItem(DataItem dataItem) {
         DataMapItem mapDataItem = DataMapItem.fromDataItem(dataItem);
         DataMap data = mapDataItem.getDataMap();
 
diff --git a/wearable/wear/AgendaData/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/AgendaData/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/AgendaData/template-params.xml b/wearable/wear/AgendaData/template-params.xml
index 3331da2..a6b4882 100644
--- a/wearable/wear/AgendaData/template-params.xml
+++ b/wearable/wear/AgendaData/template-params.xml
@@ -23,7 +23,10 @@
     <package>com.example.android.wearable.agendadata</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
+
+    <dependency>com.android.support:design:23.1.1</dependency>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -33,10 +36,10 @@
         <intro>
             <![CDATA[
             Syncs calendar events to your wearable at the press of a button, using the Wearable
-            DataApi to transmit data such as event time, description, and background image. The DataItems 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.
+            DataApi to transmit data such as event time, description, and background image. The
+            DataItems 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.
             ]]>
         </intro>
     </strings>
@@ -57,8 +60,8 @@
         </screenshots>
         <api_refs>
             <android>android.app.IntentService</android>
-            <ext>com.google.android.gms.wearable.DataApi</ext>
-            <ext>com.google.android.gms.wearable.Node</ext>
+            <ext>gms:com.google.android.gms.wearable.DataApi</ext>
+            <ext>gms:com.google.android.gms.wearable.Node</ext>
         </api_refs>
         <description>
 <![CDATA[
diff --git a/wearable/wear/AlwaysOn/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/AlwaysOn/gradle/wrapper/gradle-wrapper.properties
index 7d3b483..4364027 100644
--- a/wearable/wear/AlwaysOn/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/AlwaysOn/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/AlwaysOn/template-params.xml b/wearable/wear/AlwaysOn/template-params.xml
index e5794ee..b3aac9a 100644
--- a/wearable/wear/AlwaysOn/template-params.xml
+++ b/wearable/wear/AlwaysOn/template-params.xml
@@ -19,11 +19,11 @@
     <group>Wearable</group>
     <package>com.example.android.wearable.wear.alwayson</package>
 
-    <dependency_wearable>com.google.android.support:wearable:1.2.0</dependency_wearable>
+    <dependency_wearable>com.google.android.support:wearable:1.3.0</dependency_wearable>
+
     <provided_dependency_wearable>com.google.android.wearable:wearable:1.0.0</provided_dependency_wearable>
 
-    <minSdk>20</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
@@ -79,4 +79,4 @@
 ]]>
         </intro>
     </metadata>
-</sample>
\ No newline at end of file
+</sample>
diff --git a/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml b/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml
index e80846d..ed1cec3 100644
--- a/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/DataLayer/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.wearable.datalayer" >
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="22" />
+              android:targetSdkVersion="23" />
 
     <uses-feature android:name="android.hardware.camera" android:required="false" />
 
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
index 1c67c0e..4afeec9 100644
--- 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
@@ -381,7 +381,9 @@
         public void run() {
             PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(COUNT_PATH);
             putDataMapRequest.getDataMap().putInt(COUNT_KEY, count++);
+
             PutDataRequest request = putDataMapRequest.asPutDataRequest();
+            request.setUrgent();
 
             LOGD(TAG, "Generating DataItem: " + request);
             if (!mGoogleApiClient.isConnected()) {
@@ -442,6 +444,8 @@
         dataMap.getDataMap().putAsset(IMAGE_KEY, asset);
         dataMap.getDataMap().putLong("time", new Date().getTime());
         PutDataRequest request = dataMap.asPutDataRequest();
+        request.setUrgent();
+
         Wearable.DataApi.putDataItem(mGoogleApiClient, request)
                 .setResultCallback(new ResultCallback<DataItemResult>() {
                     @Override
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
index 678e428..b3cb253 100644
--- 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
@@ -23,6 +23,7 @@
 import android.app.FragmentManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.wearable.view.DotsPageIndicator;
@@ -41,6 +42,7 @@
 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.PendingResult;
 import com.google.android.gms.common.api.ResultCallback;
 import com.google.android.gms.wearable.Asset;
 import com.google.android.gms.wearable.CapabilityApi;
@@ -85,7 +87,6 @@
     private static final String CAPABILITY_2_NAME = "capability_2";
 
     private GoogleApiClient mGoogleApiClient;
-    private Handler mHandler;
     private GridViewPager mPager;
     private DataFragment mDataFragment;
     private AssetFragment mAssetFragment;
@@ -93,7 +94,6 @@
     @Override
     public void onCreate(Bundle b) {
         super.onCreate(b);
-        mHandler = new Handler();
         setContentView(R.layout.main_activity);
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         setupViews();
@@ -137,15 +137,6 @@
         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() {
-                mDataFragment.appendItem(title, text);
-            }
-        });
-    }
-
     @Override
     public void onDataChanged(DataEventBuffer dataEvents) {
         LOGD(TAG, "onDataChanged(): " + dataEvents);
@@ -155,29 +146,22 @@
                 String path = event.getDataItem().getUri().getPath();
                 if (DataLayerListenerService.IMAGE_PATH.equals(path)) {
                     DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
-                    Asset photo = dataMapItem.getDataMap()
+                    Asset photoAsset = 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 on second page..");
-                            moveToPage(1);
-                            mAssetFragment.setBackgroundImage(bitmap);
-                        }
-                    });
+                    // Loads image on background thread.
+                    new LoadBitmapAsyncTask().execute(photoAsset);
 
                 } else if (DataLayerListenerService.COUNT_PATH.equals(path)) {
                     LOGD(TAG, "Data Changed for COUNT_PATH");
-                    generateEvent("DataItem Changed", event.getDataItem().toString());
+                    mDataFragment.appendItem("DataItem Changed", event.getDataItem().toString());
                 } else {
                     LOGD(TAG, "Unrecognized path: " + path);
                 }
 
             } else if (event.getType() == DataEvent.TYPE_DELETED) {
-                generateEvent("DataItem Deleted", event.getDataItem().toString());
+                mDataFragment.appendItem("DataItem Deleted", event.getDataItem().toString());
             } else {
-                generateEvent("Unknown data event type", "Type = " + event.getType());
+                mDataFragment.appendItem("Unknown data event type", "Type = " + event.getType());
             }
         }
     }
@@ -199,20 +183,27 @@
      * Find the connected nodes that provide at least one of the given capabilities
      */
     private void showNodes(final String... capabilityNames) {
-        Wearable.CapabilityApi.getAllCapabilities(mGoogleApiClient,
-                CapabilityApi.FILTER_REACHABLE).setResultCallback(
 
+        PendingResult<CapabilityApi.GetAllCapabilitiesResult> pendingCapabilityResult =
+                Wearable.CapabilityApi.getAllCapabilities(
+                        mGoogleApiClient,
+                        CapabilityApi.FILTER_REACHABLE);
+
+        pendingCapabilityResult.setResultCallback(
                 new ResultCallback<CapabilityApi.GetAllCapabilitiesResult>() {
                     @Override
                     public void onResult(
                             CapabilityApi.GetAllCapabilitiesResult getAllCapabilitiesResult) {
+
                         if (!getAllCapabilitiesResult.getStatus().isSuccess()) {
                             Log.e(TAG, "Failed to get capabilities");
                             return;
                         }
-                        Map<String, CapabilityInfo>
-                                capabilitiesMap = getAllCapabilitiesResult.getAllCapabilities();
+
+                        Map<String, CapabilityInfo> capabilitiesMap =
+                                getAllCapabilitiesResult.getAllCapabilities();
                         Set<Node> nodes = new HashSet<>();
+
                         if (capabilitiesMap.isEmpty()) {
                             showDiscoveredNodes(nodes);
                             return;
@@ -231,7 +222,7 @@
                         for (Node node : nodes) {
                             nodesList.add(node.getDisplayName());
                         }
-                        Log.d(TAG, "Connected Nodes: " + (nodesList.isEmpty()
+                        LOGD(TAG, "Connected Nodes: " + (nodesList.isEmpty()
                                 ? "No connected device was found for the given capabilities"
                                 : TextUtils.join(",", nodesList)));
                         String msg;
@@ -246,39 +237,20 @@
                 });
     }
 
-    /**
-     * 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());
+        mDataFragment.appendItem("Message", event.toString());
     }
 
     @Override
     public void onPeerConnected(Node node) {
-        generateEvent("Node Connected", node.getId());
+        mDataFragment.appendItem("Node Connected", node.getId());
     }
 
     @Override
     public void onPeerDisconnected(Node node) {
-        generateEvent("Node Disconnected", node.getId());
+        mDataFragment.appendItem("Node Disconnected", node.getId());
     }
 
     private void setupViews() {
@@ -330,4 +302,43 @@
         }
 
     }
+
+    /*
+     * Extracts {@link android.graphics.Bitmap} data from the
+     * {@link com.google.android.gms.wearable.Asset}
+     */
+    private class LoadBitmapAsyncTask extends AsyncTask<Asset, Void, Bitmap> {
+
+        @Override
+        protected Bitmap doInBackground(Asset... params) {
+
+            if(params.length > 0) {
+
+                Asset asset = params[0];
+
+                InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
+                        mGoogleApiClient, asset).await().getInputStream();
+
+                if (assetInputStream == null) {
+                    Log.w(TAG, "Requested an unknown Asset.");
+                    return null;
+                }
+                return BitmapFactory.decodeStream(assetInputStream);
+
+            } else {
+                Log.e(TAG, "Asset must be non-null");
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Bitmap bitmap) {
+
+            if(bitmap != null) {
+                LOGD(TAG, "Setting background image on second page..");
+                moveToPage(1);
+                mAssetFragment.setBackgroundImage(bitmap);
+            }
+        }
+    }
 }
diff --git a/wearable/wear/DataLayer/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/DataLayer/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/DataLayer/template-params.xml b/wearable/wear/DataLayer/template-params.xml
index f40b9d6..6df31f5 100644
--- a/wearable/wear/DataLayer/template-params.xml
+++ b/wearable/wear/DataLayer/template-params.xml
@@ -20,7 +20,8 @@
     <package>com.example.android.wearable.datalayer</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -50,9 +51,9 @@
             <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>
+            <ext>gms:com.google.android.gms.wearable.DataApi</ext>
+            <ext>gms:com.google.android.gms.wearable.DataEvent</ext>
+            <ext>gms:com.google.android.gms.wearable.WearableListenerService</ext>
         </api_refs>
         <description>
 <![CDATA[
diff --git a/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml b/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml
index d8060a8..e3e6de1 100644
--- a/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/DelayedConfirmation/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.wearable.delayedconfirmation" >
 
     <uses-sdk android:minSdkVersion="18"
-        android:targetSdkVersion="22" />
+        android:targetSdkVersion="23" />
 
     <application
             android:allowBackup="true"
diff --git a/wearable/wear/DelayedConfirmation/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/DelayedConfirmation/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/DelayedConfirmation/template-params.xml b/wearable/wear/DelayedConfirmation/template-params.xml
index 5f77d65..cfc3957 100644
--- a/wearable/wear/DelayedConfirmation/template-params.xml
+++ b/wearable/wear/DelayedConfirmation/template-params.xml
@@ -23,7 +23,8 @@
     <package>com.example.android.wearable.delayedconfirmation</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -45,4 +46,9 @@
     <template src="base"/>
     <template src="Wear"/>
 
+    <metadata>
+        <status>DEPRECATED</status>
+        <categories>Wearable</categories>
+    </metadata>
+
 </sample>
diff --git a/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml b/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml
index 8f35c56..b544ed0 100644
--- a/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/ElizaChat/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
     package="com.example.android.wearable.elizachat" >
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="23" />
 
     <application
         android:allowBackup="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
index 3bef19c..2406668 100644
--- 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
@@ -96,7 +96,7 @@
                 .setContentText(mLastResponse)
                 .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.bg_eliza))
                 .setSmallIcon(R.drawable.bg_eliza)
-                .setPriority(NotificationCompat.PRIORITY_MIN);
+                .setPriority(NotificationCompat.PRIORITY_DEFAULT);
 
         Intent intent = new Intent(ACTION_RESPONSE);
         PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
diff --git a/wearable/wear/ElizaChat/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/ElizaChat/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/ElizaChat/template-params.xml b/wearable/wear/ElizaChat/template-params.xml
index ea26b5c..19c8129 100644
--- a/wearable/wear/ElizaChat/template-params.xml
+++ b/wearable/wear/ElizaChat/template-params.xml
@@ -23,7 +23,7 @@
     <package>com.example.android.wearable.elizachat</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
 
     <strings>
         <intro>
@@ -39,7 +39,7 @@
     <template src="base"/>
 
     <metadata>
-        <status>PUBLISHED</status>
+        <status>DEPRECATED</status>
         <categories>Wearable</categories>
         <technologies>Android</technologies>
         <languages>Java</languages>
diff --git a/wearable/wear/EmbeddedApp/LICENSE b/wearable/wear/EmbeddedApp/LICENSE
index 1af981f..4f22946 100644
--- a/wearable/wear/EmbeddedApp/LICENSE
+++ b/wearable/wear/EmbeddedApp/LICENSE
@@ -1,4 +1,6 @@
-                                 Apache License
+Apache License
+--------------
+
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
 
@@ -178,7 +180,7 @@
    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 "[]"
+      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
@@ -186,7 +188,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright 2014 The Android Open Source Project
+   Copyright {yyyy} {name of copyright owner}
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
@@ -199,3 +201,447 @@
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
+
+All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav 
+and *.ogg) are licensed under the CC-BY-NC license. All other files are 
+licensed under the Apache 2 license.
+
+CC-BY-NC License
+----------------
+
+Attribution-NonCommercial-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+	wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More_considerations
+     for the public: 
+	wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-ShareAlike 4.0 International Public License
+("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Adapter's License means the license You apply to Your Copyright
+     and Similar Rights in Your contributions to Adapted Material in
+     accordance with the terms and conditions of this Public License.
+
+  c. BY-NC-SA Compatible License means a license listed at
+     creativecommons.org/compatiblelicenses, approved by Creative
+     Commons as essentially the equivalent of this Public License.
+
+  d. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  e. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  f. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  g. License Elements means the license attributes listed in the name
+     of a Creative Commons Public License. The License Elements of this
+     Public License are Attribution, NonCommercial, and ShareAlike.
+
+  h. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  i. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  j. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  k. NonCommercial means not primarily intended for or directed towards
+     commercial advantage or monetary compensation. For purposes of
+     this Public License, the exchange of the Licensed Material for
+     other material subject to Copyright and Similar Rights by digital
+     file-sharing or similar means is NonCommercial provided there is
+     no payment of monetary compensation in connection with the
+     exchange.
+
+  l. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  m. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  n. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part, for NonCommercial purposes only; and
+
+            b. produce, reproduce, and Share Adapted Material for
+               NonCommercial purposes only.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. Additional offer from the Licensor -- Adapted Material.
+               Every recipient of Adapted Material from You
+               automatically receives an offer from the Licensor to
+               exercise the Licensed Rights in the Adapted Material
+               under the conditions of the Adapter's License You apply.
+
+            c. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties, including when
+          the Licensed Material is used other than for NonCommercial
+          purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material (including in modified
+          form), You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+  b. ShareAlike.
+
+     In addition to the conditions in Section 3(a), if You Share
+     Adapted Material You produce, the following conditions also apply.
+
+       1. The Adapter's License You apply must be a Creative Commons
+          license with the same License Elements, this version or
+          later, or a BY-NC-SA Compatible License.
+
+       2. You must include the text of, or the URI or hyperlink to, the
+          Adapter's License You apply. You may satisfy this condition
+          in any reasonable manner based on the medium, means, and
+          context in which You Share Adapted Material.
+
+       3. You may not offer or impose any additional or different terms
+          or conditions on, or apply any Effective Technological
+          Measures to, Adapted Material that restrict exercise of the
+          rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database for NonCommercial purposes
+     only;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material,
+     including for purposes of Section 3(b); and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public licenses.
+Notwithstanding, Creative Commons may elect to apply one of its public
+licenses to material it publishes and in those instances will be
+considered the "Licensor." Except for the limited purpose of indicating
+that material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the public
+licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml b/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml
index 4863d66..aab1348 100644
--- a/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/EmbeddedApp/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
     package="com.example.android.wearable.embeddedapp" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/EmbeddedApp/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/EmbeddedApp/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/EmbeddedApp/template-params.xml b/wearable/wear/EmbeddedApp/template-params.xml
index 13186e5..8f7f7c7 100644
--- a/wearable/wear/EmbeddedApp/template-params.xml
+++ b/wearable/wear/EmbeddedApp/template-params.xml
@@ -23,7 +23,8 @@
     <package>com.example.android.wearable.embeddedapp</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -41,7 +42,7 @@
     <template src="Wear"/>
 
     <metadata>
-        <status>PUBLISHED</status>
+        <status>DEPRECATED</status>
         <categories>Wearable</categories>
         <technologies>Android</technologies>
         <languages>Java</languages>
diff --git a/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml b/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml
index af108af..a59cd7d 100644
--- a/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/FindMyPhone/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.example.android.wearable.findphone">
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="22" />
+              android:targetSdkVersion="23" />
 
     <uses-permission android:name="android.permission.VIBRATE" />
     <application
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
index c6c6d67..a51a9b2 100644
--- 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
@@ -100,6 +100,7 @@
             // when it receives the change.
             PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_SOUND_ALARM);
             putDataMapRequest.getDataMap().putBoolean(FIELD_ALARM_ON, alarmOn);
+            putDataMapRequest.setUrgent();
             Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest())
                     .await();
         } else {
diff --git a/wearable/wear/FindMyPhone/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/FindMyPhone/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/FindMyPhone/template-params.xml b/wearable/wear/FindMyPhone/template-params.xml
index e8d71c6..8443b77 100644
--- a/wearable/wear/FindMyPhone/template-params.xml
+++ b/wearable/wear/FindMyPhone/template-params.xml
@@ -23,7 +23,8 @@
     <package>com.example.android.wearable.findphone</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -43,4 +44,9 @@
     <template src="base"/>
     <template src="Wear"/>
 
+    <metadata>
+        <status>DEPRECATED</status>
+        <categories>Wearable</categories>
+    </metadata>
+
 </sample>
diff --git a/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml
index 738ba9d..1eb15d0 100644
--- a/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Flashlight/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.example.android.wearable.flashlight" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Flashlight/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Flashlight/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/Flashlight/template-params.xml b/wearable/wear/Flashlight/template-params.xml
index e1ec869..9af6eb4 100644
--- a/wearable/wear/Flashlight/template-params.xml
+++ b/wearable/wear/Flashlight/template-params.xml
@@ -22,21 +22,21 @@
     <group>Wearable</group>
     <package>com.example.android.wearable.flashlight</package>
 
-    <!-- change minSdk if needed-->
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
             <![CDATA[
             Wearable activity that uses your wearable screen as a flashlight. There is also
-            a party-mode option, if you want to make things interesting.
+            a party-mode option (swipe left), if you want to make things interesting.
             ]]>
         </intro>
     </strings>
 
     <metadata>
-        <status>PUBLISHED</status>
+        <status>DEPRECATED</status>
         <categories>Wearable</categories>
         <technologies>Android</technologies>
         <language>Java</language>
diff --git a/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml b/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml
index d07a265..d1eabc3 100644
--- a/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Geofencing/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.example.android.wearable.geofencing">
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
 
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
index 53117e3..e3f6139 100644
--- 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
@@ -89,6 +89,7 @@
                 final PutDataMapRequest putDataMapRequest =
                         PutDataMapRequest.create(GEOFENCE_DATA_ITEM_PATH);
                 putDataMapRequest.getDataMap().putString(KEY_GEOFENCE_ID, triggeredGeoFenceId);
+                putDataMapRequest.setUrgent();
                 if (mGoogleApiClient.isConnected()) {
                     Wearable.DataApi.putDataItem(
                             mGoogleApiClient, putDataMapRequest.asPutDataRequest()).await();
diff --git a/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml
index 082f396..f25cc44 100644
--- a/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Geofencing/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.wearable.geofencing" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Geofencing/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Geofencing/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/Geofencing/template-params.xml b/wearable/wear/Geofencing/template-params.xml
index 00fd3b3..26f0b64 100644
--- a/wearable/wear/Geofencing/template-params.xml
+++ b/wearable/wear/Geofencing/template-params.xml
@@ -24,8 +24,9 @@
 
     <minSdk>18</minSdk>
     <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
-    <dependency>com.google.android.gms:play-services-location:7.3.0</dependency>
+    <dependency>com.google.android.gms:play-services-location</dependency>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -46,7 +47,7 @@
     <template src="Wear"/>
 
     <metadata>
-        <status>PUBLISHED</status>
+        <status>DEPRECATED</status>
         <categories>Wearable, Sensors</categories>
         <technologies>Android</technologies>
         <languages>Java</languages>
diff --git a/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml b/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml
index 5c362dc..e25cd63 100644
--- a/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/GridViewPager/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
     package="com.example.android.wearable.gridviewpager" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/GridViewPager/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/GridViewPager/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/GridViewPager/template-params.xml b/wearable/wear/GridViewPager/template-params.xml
index f6dc567..66e4bf4 100644
--- a/wearable/wear/GridViewPager/template-params.xml
+++ b/wearable/wear/GridViewPager/template-params.xml
@@ -22,8 +22,7 @@
     <group>Wearable</group>
     <package>com.example.android.wearable.gridviewpager</package>
 
-    <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
diff --git a/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml b/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml
index 02b7a4f..f6cf220 100644
--- a/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/JumpingJack/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
           package="com.example.android.wearable.jumpingjack">
 
    <uses-sdk android:minSdkVersion="20"
-             android:targetSdkVersion="21" />
+             android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/JumpingJack/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/JumpingJack/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/JumpingJack/template-params.xml b/wearable/wear/JumpingJack/template-params.xml
index 692a6a5..9512f32 100644
--- a/wearable/wear/JumpingJack/template-params.xml
+++ b/wearable/wear/JumpingJack/template-params.xml
@@ -22,8 +22,7 @@
     <group>Wearable</group>
     <package>com.example.android.wearable.jumpingjack</package>
 
-    <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
@@ -66,7 +65,7 @@
 [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
+This example counts how many times Jumping Jacks are performed by detecting the value
 of the Gravity sensor by the following code:
 
 ```java
diff --git a/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml b/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml
index 3f1274d..6a17ad8 100644
--- a/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Notifications/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.support.wearable.notifications" >
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="23" />
 
     <uses-permission android:name="android.permission.VIBRATE" />
 
diff --git a/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml
index 34a29ff..a446fd9 100644
--- a/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Notifications/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.support.wearable.notifications" >
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="21" />
+        android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Notifications/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Notifications/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/Notifications/template-params.xml b/wearable/wear/Notifications/template-params.xml
index c4936ea..64d2e5b 100644
--- a/wearable/wear/Notifications/template-params.xml
+++ b/wearable/wear/Notifications/template-params.xml
@@ -23,7 +23,8 @@
     <package>com.example.android.support.wearable.notifications</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
diff --git a/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml b/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml
index 801a473..8fabd42 100644
--- a/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/Quiz/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.wearable.quiz" >
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="22" />
+              android:targetSdkVersion="23" />
 
     <application
             android:allowBackup="true"
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
index de8eb74..d1e2d73 100644
--- 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
@@ -248,7 +248,9 @@
             dataMap.putInt(QUESTION_INDEX, questionIndex);
             dataMap.putStringArray(ANSWERS, answers);
             dataMap.putInt(CORRECT_ANSWER_INDEX, correctAnswerIndex);
-            return request.asPutDataRequest();
+            PutDataRequest putDataRequest = request.asPutDataRequest();
+            putDataRequest.setUrgent();
+            return putDataRequest;
         }
     }
 
@@ -496,7 +498,10 @@
                 dataMap.putBoolean(QUESTION_WAS_DELETED, false);
                 if (!mHasQuestionBeenAsked && dataMap.getInt(QUESTION_INDEX) == 0) {
                     // Ask the first question now.
-                    Wearable.DataApi.putDataItem(mGoogleApiClient, request.asPutDataRequest());
+                    PutDataRequest putDataRequest = request.asPutDataRequest();
+                    // Set to high priority in case it isn't already.
+                    putDataRequest.setUrgent();
+                    Wearable.DataApi.putDataItem(mGoogleApiClient, putDataRequest);
                     setHasQuestionBeenAsked(true);
                 } else {
                     // Enqueue future questions.
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
index 353903c..d715411 100644
--- 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
@@ -76,6 +76,7 @@
         DataMap dataMap = putDataMapRequest.getDataMap();
         dataMap.putBoolean(QUESTION_WAS_DELETED, true);
         PutDataRequest request = putDataMapRequest.asPutDataRequest();
+        request.setUrgent();
         Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
         mGoogleApiClient.disconnect();
     }
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
index 7b8f730..50425b0 100644
--- 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
@@ -88,6 +88,7 @@
         dataMap.putBoolean(CHOSEN_ANSWER_CORRECT, chosenAnswerCorrect);
         dataMap.putBoolean(QUESTION_WAS_ANSWERED, true);
         PutDataRequest request = putDataMapRequest.asPutDataRequest();
+        request.setUrgent();
         Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
 
         // Remove this question notification.
diff --git a/wearable/wear/Quiz/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Quiz/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/Quiz/template-params.xml b/wearable/wear/Quiz/template-params.xml
index 297bf19..7be9593 100644
--- a/wearable/wear/Quiz/template-params.xml
+++ b/wearable/wear/Quiz/template-params.xml
@@ -23,7 +23,8 @@
     <package>com.example.android.wearable.quiz</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -45,5 +46,9 @@
 
     <template src="base"/>
     <template src="Wear"/>
+    <metadata>
+        <status>DEPRECATED</status>
+        <categories>Wearable</categories>
+    </metadata>
 
 </sample>
diff --git a/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml b/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml
index 1786d27..141da9a 100644
--- a/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/RecipeAssistant/Application/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
     package="com.example.android.wearable.recipeassistant" >
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="23" />
 
     <application
         android:allowBackup="true"
diff --git a/wearable/wear/RecipeAssistant/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/RecipeAssistant/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/RecipeAssistant/template-params.xml b/wearable/wear/RecipeAssistant/template-params.xml
index 943b368..c261aa1 100644
--- a/wearable/wear/RecipeAssistant/template-params.xml
+++ b/wearable/wear/RecipeAssistant/template-params.xml
@@ -23,7 +23,7 @@
     <package>com.example.android.wearable.recipeassistant</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
 
     <strings>
         <intro>
@@ -42,4 +42,9 @@
     <common src="logger"/>
     <common src="activities"/>
 
+    <metadata>
+        <status>DEPRECATED</status>
+        <categories>Wearable</categories>
+    </metadata>
+
 </sample>
diff --git a/wearable/wear/RuntimePermissionsWear/Application/.gitignore b/wearable/wear/RuntimePermissionsWear/Application/.gitignore
new file mode 100644
index 0000000..6eb878d
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/.gitignore
@@ -0,0 +1,16 @@
+# 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/RuntimePermissionsWear/Application/README-singleview.txt b/wearable/wear/RuntimePermissionsWear/Application/README-singleview.txt
new file mode 100644
index 0000000..0cacd46
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/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/wearable/wear/RuntimePermissionsWear/Application/src/main/AndroidManifest.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..861cad3
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,75 @@
+<?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.wearable.runtimepermissions"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk
+        android:minSdkVersion="18"
+        android:targetSdkVersion="23" />
+
+    <!-- Permissions for phone. -->
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <!-- Permissions for wearable:
+    Earlier watches require their permissions to be a subset of the phone apps permission in order
+    for the wear app to be installed. Therefore, you must include the permissions here as well as in
+    the wear manifest.
+    -->
+    <uses-permission android:name="android.permission.BODY_SENSORS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@mipmap/ic_launcher"
+        android:theme="@style/Theme.AppCompat.Light">
+        <activity
+            android:name=".MainPhoneActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".PhonePermissionRequestActivity"
+            android:label="@string/title_activity_phone_permission_request"
+            android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
+        </activity>
+
+        <activity
+            android:name=".WearPermissionRequestActivity"
+            android:label="@string/title_activity_wear_permission_request"
+            android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
+        </activity>
+
+        <service
+            android:name=".IncomingRequestPhoneService"
+            android:enabled="true"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
+            </intent-filter>
+        </service>
+    </application>
+
+
+</manifest>
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/IncomingRequestPhoneService.java b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/IncomingRequestPhoneService.java
new file mode 100644
index 0000000..4cad2fa
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/IncomingRequestPhoneService.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.support.v4.app.ActivityCompat;
+import android.util.Log;
+
+import com.example.android.wearable.runtimepermissions.common.Constants;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.MessageApi;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Handles all incoming requests for phone data (and permissions) from wear devices.
+ */
+public class IncomingRequestPhoneService extends WearableListenerService {
+
+    private static final String TAG = "IncomingRequestService";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(TAG, "onCreate()");
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent messageEvent) {
+        super.onMessageReceived(messageEvent);
+        Log.d(TAG, "onMessageReceived(): " + messageEvent);
+
+        String messagePath = messageEvent.getPath();
+
+        if (messagePath.equals(Constants.MESSAGE_PATH_PHONE)) {
+
+            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
+            int requestType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
+
+            if (requestType == Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION) {
+                promptUserForStoragePermission(messageEvent.getSourceNodeId());
+
+            } else if (requestType == Constants.COMM_TYPE_REQUEST_DATA) {
+                respondWithStorageInformation(messageEvent.getSourceNodeId());
+            }
+        }
+    }
+
+    private void promptUserForStoragePermission(String nodeId) {
+        boolean storagePermissionApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (storagePermissionApproved) {
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
+            sendMessage(nodeId, dataMap);
+        } else {
+            // Launch Phone Activity to grant storage permissions.
+            Intent startIntent = new Intent(this, PhonePermissionRequestActivity.class);
+            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            /* This extra is included to alert MainPhoneActivity to send back the permission
+             * results after the user has made their decision in PhonePermissionRequestActivity
+             * and it finishes.
+             */
+            startIntent.putExtra(MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, true);
+            startActivity(startIntent);
+        }
+    }
+
+    private void respondWithStorageInformation(String nodeId) {
+
+        boolean storagePermissionApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (!storagePermissionApproved) {
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED);
+            sendMessage(nodeId, dataMap);
+        } else {
+            /* To keep the sample simple, we are only displaying the top level list of directories.
+             * Otherwise, it will return a message that the media wasn't available.
+             */
+            StringBuilder stringBuilder = new StringBuilder();
+
+            if (isExternalStorageReadable()) {
+                File externalStorageDirectory = Environment.getExternalStorageDirectory();
+                String[] fileList = externalStorageDirectory.list();
+
+                if (fileList.length > 0) {
+                    stringBuilder.append("List of directories on phone:\n");
+                    for (String file : fileList) {
+                        stringBuilder.append(" - " + file + "\n");
+                    }
+                } else {
+                    stringBuilder.append("No files in external storage.");
+                }
+            } else {
+                stringBuilder.append("No external media is available.");
+            }
+
+            // Send valid results
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_DATA);
+            dataMap.putString(Constants.KEY_PAYLOAD, stringBuilder.toString());
+            sendMessage(nodeId, dataMap);
+
+        }
+    }
+
+    private void sendMessage(String nodeId, DataMap dataMap) {
+        Log.d(TAG, "sendMessage() Node: " + nodeId);
+
+        GoogleApiClient client = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .build();
+        client.blockingConnect(Constants.CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+
+
+        PendingResult<MessageApi.SendMessageResult> pendingMessageResult =
+                Wearable.MessageApi.sendMessage(
+                        client,
+                        nodeId,
+                        Constants.MESSAGE_PATH_WEAR,
+                        dataMap.toByteArray());
+
+        MessageApi.SendMessageResult sendMessageResult =
+                pendingMessageResult.await(
+                        Constants.CONNECTION_TIME_OUT_MS,
+                        TimeUnit.MILLISECONDS);
+
+        if (!sendMessageResult.getStatus().isSuccess()) {
+            Log.d(TAG, "Sending message failed, status: "
+                    + sendMessageResult.getStatus());
+        } else {
+            Log.d(TAG, "Message sent successfully");
+        }
+        client.disconnect();
+    }
+
+    private boolean isExternalStorageReadable() {
+        String state = Environment.getExternalStorageState();
+
+        return Environment.MEDIA_MOUNTED.equals(state)
+                || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/MainPhoneActivity.java b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/MainPhoneActivity.java
new file mode 100644
index 0000000..196b03b
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/MainPhoneActivity.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.Looper;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.example.android.wearable.runtimepermissions.common.Constants;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
+import com.google.android.gms.wearable.DataMap;
+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.Wearable;
+
+import java.io.File;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Displays data that requires runtime permissions both locally (READ_EXTERNAL_STORAGE) and
+ * remotely on wear (BODY_SENSORS).
+ *
+ * The class also handles sending back the results of a permission request from a remote wear device
+ * when the permission has not been approved yet on the phone (uses EXTRA as trigger). In that case,
+ * the IncomingRequestPhoneService launches the splash Activity (PhonePermissionRequestActivity) to
+ * inform user of permission request. After the user decides what to do, it falls back to this
+ * Activity (which has all the GoogleApiClient code) to handle sending data across and keeps user
+ * in app experience.
+ */
+public class MainPhoneActivity extends AppCompatActivity implements
+        GoogleApiClient.ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener,
+        CapabilityApi.CapabilityListener,
+        MessageApi.MessageListener,
+        ResultCallback<MessageApi.SendMessageResult> {
+
+    private static final String TAG = "MainPhoneActivity";
+
+    /*
+     * Alerts Activity that the initial request for permissions came from wear, and the Activity
+     * needs to send back the results (data or permission rejection).
+     */
+    public static final String EXTRA_PROMPT_PERMISSION_FROM_WEAR =
+            "com.example.android.wearable.runtimepermissions.extra.PROMPT_PERMISSION_FROM_WEAR";
+
+    private static final int REQUEST_WEAR_PERMISSION_RATIONALE = 1;
+
+    private boolean mWearBodySensorsPermissionApproved;
+    private boolean mPhoneStoragePermissionApproved;
+
+    private boolean mWearRequestingPhoneStoragePermission;
+
+    private Button mWearBodySensorsPermissionButton;
+    private Button mPhoneStoragePermissionButton;
+    private TextView mOutputTextView;
+
+    private Set<Node> mWearNodeIds;
+
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.d(TAG, "onCreate()");
+        super.onCreate(savedInstanceState);
+
+        /*
+         * Since this is a remote permission, we initialize it to false and then check the remote
+         * permission once the GoogleApiClient is connected.
+         */
+        mWearBodySensorsPermissionApproved = false;
+
+        setContentView(R.layout.activity_main);
+
+        // Checks if wear app requested phone permission (permission request opens later if true).
+        mWearRequestingPhoneStoragePermission =
+                getIntent().getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_WEAR, false);
+
+        mPhoneStoragePermissionButton =
+                (Button) findViewById(R.id.phoneStoragePermissionButton);
+
+        mWearBodySensorsPermissionButton =
+                (Button) findViewById(R.id.wearBodySensorsPermissionButton);
+
+        mOutputTextView = (TextView) findViewById(R.id.output);
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    public void onClickWearBodySensors(View view) {
+
+        logToUi("Requested info from wear device(s). New approval may be required.");
+
+        DataMap dataMap = new DataMap();
+        dataMap.putInt(Constants.KEY_COMM_TYPE, Constants.COMM_TYPE_REQUEST_DATA);
+        sendMessage(dataMap);
+    }
+
+    public void onClickPhoneStorage(View view) {
+
+        if (mPhoneStoragePermissionApproved) {
+            logToUi(getPhoneStorageInformation());
+
+        } else {
+            // On 23+ (M+) devices, Storage permission not granted. Request permission.
+            Intent startIntent = new Intent(this, PhonePermissionRequestActivity.class);
+            startActivity(startIntent);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        Log.d(TAG, "onPause()");
+        super.onPause();
+        if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) {
+            Wearable.CapabilityApi.removeCapabilityListener(
+                    mGoogleApiClient,
+                    this,
+                    Constants.CAPABILITY_WEAR_APP);
+            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+            mGoogleApiClient.disconnect();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        Log.d(TAG, "onResume()");
+        super.onResume();
+
+        /* Enables app to handle 23+ (M+) style permissions. It also covers user changing
+         * permission in settings and coming back to the app.
+         */
+        mPhoneStoragePermissionApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (mPhoneStoragePermissionApproved) {
+            mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_approved, 0, 0, 0);
+        }
+
+        if (mGoogleApiClient != null) {
+            mGoogleApiClient.connect();
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(TAG, "onActivityResult()");
+        if (requestCode == REQUEST_WEAR_PERMISSION_RATIONALE) {
+
+            if (resultCode == Activity.RESULT_OK) {
+                logToUi("Requested permission on wear device(s).");
+
+                DataMap dataMap = new DataMap();
+                dataMap.putInt(Constants.KEY_COMM_TYPE,
+                        Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION);
+                sendMessage(dataMap);
+            }
+        }
+    }
+
+    @Override
+    public void onConnected(Bundle bundle) {
+        Log.d(TAG, "onConnected()");
+
+        // Set up listeners for capability and message changes.
+        Wearable.CapabilityApi.addCapabilityListener(
+                mGoogleApiClient,
+                this,
+                Constants.CAPABILITY_WEAR_APP);
+        Wearable.MessageApi.addListener(mGoogleApiClient, this);
+
+        // Initial check of capabilities to find the wear nodes.
+        PendingResult<CapabilityApi.GetCapabilityResult> pendingResult =
+                Wearable.CapabilityApi.getCapability(
+                        mGoogleApiClient,
+                        Constants.CAPABILITY_WEAR_APP,
+                        CapabilityApi.FILTER_REACHABLE);
+
+        pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
+            @Override
+            public void onResult(CapabilityApi.GetCapabilityResult getCapabilityResult) {
+
+                CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
+                String capabilityName = capabilityInfo.getName();
+
+                boolean wearSupportsSampleApp =
+                        capabilityName.equals(Constants.CAPABILITY_WEAR_APP);
+
+                if (wearSupportsSampleApp) {
+                    mWearNodeIds = capabilityInfo.getNodes();
+
+                    /*
+                     * Upon getting all wear nodes, we now need to check if the original request to
+                     * launch this activity (and PhonePermissionRequestActivity) was initiated by
+                     * a wear device. If it was, we need to send back the permission results (data
+                     * or rejection of permission) to the wear device.
+                     *
+                     * Also, note we set variable to false, this enables the user to continue
+                     * changing permissions without sending updates to the wear every time.
+                     */
+                    if (mWearRequestingPhoneStoragePermission) {
+                        mWearRequestingPhoneStoragePermission = false;
+                        sendWearPermissionResults();
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+        Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        Log.e(TAG, "onConnectionFailed(): connection to location client failed");
+    }
+
+
+    public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
+        Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo);
+
+        mWearNodeIds = capabilityInfo.getNodes();
+    }
+
+    public void onMessageReceived(MessageEvent messageEvent) {
+        Log.d(TAG, "onMessageReceived(): " + messageEvent);
+
+        String messagePath = messageEvent.getPath();
+
+        if (messagePath.equals(Constants.MESSAGE_PATH_PHONE)) {
+            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
+
+            int commType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
+
+            if (commType == Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED) {
+                mWearBodySensorsPermissionApproved = false;
+                updateWearButtonOnUiThread();
+
+                /* Because our request for remote data requires a remote permission, we now launch
+                 * a splash activity informing the user we need those permissions (along with
+                 * other helpful information to approve).
+                 */
+                Intent wearPermissionRationale =
+                        new Intent(this, WearPermissionRequestActivity.class);
+                startActivityForResult(wearPermissionRationale, REQUEST_WEAR_PERMISSION_RATIONALE);
+
+            } else if (commType == Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION) {
+                mWearBodySensorsPermissionApproved = true;
+                updateWearButtonOnUiThread();
+                logToUi("User approved permission on remote device, requesting data again.");
+                DataMap outgoingDataRequestDataMap = new DataMap();
+                outgoingDataRequestDataMap.putInt(Constants.KEY_COMM_TYPE,
+                        Constants.COMM_TYPE_REQUEST_DATA);
+                sendMessage(outgoingDataRequestDataMap);
+
+            } else if (commType == Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION) {
+                mWearBodySensorsPermissionApproved = false;
+                updateWearButtonOnUiThread();
+                logToUi("User denied permission on remote device.");
+
+            } else if (commType == Constants.COMM_TYPE_RESPONSE_DATA) {
+                mWearBodySensorsPermissionApproved = true;
+                String storageDetails = dataMap.getString(Constants.KEY_PAYLOAD);
+                updateWearButtonOnUiThread();
+                logToUi(storageDetails);
+
+            } else {
+                Log.d(TAG, "Unrecognized communication type received.");
+            }
+        }
+    }
+
+    @Override
+    public void onResult(MessageApi.SendMessageResult sendMessageResult) {
+        if (!sendMessageResult.getStatus().isSuccess()) {
+            Log.d(TAG, "Sending message failed, onResult: " + sendMessageResult);
+            updateWearButtonOnUiThread();
+            logToUi("Sending message failed.");
+
+        } else {
+            Log.d(TAG, "Message sent.");
+        }
+    }
+
+    private void sendMessage(DataMap dataMap) {
+        Log.d(TAG, "sendMessage(): " + mWearNodeIds);
+
+        if ((mWearNodeIds != null) && (!mWearNodeIds.isEmpty())) {
+
+            PendingResult<MessageApi.SendMessageResult> pendingResult;
+
+            for (Node node : mWearNodeIds) {
+
+                pendingResult = Wearable.MessageApi.sendMessage(
+                        mGoogleApiClient,
+                        node.getId(),
+                        Constants.MESSAGE_PATH_WEAR,
+                        dataMap.toByteArray());
+
+                pendingResult.setResultCallback(this, Constants.CONNECTION_TIME_OUT_MS,
+                        TimeUnit.SECONDS);
+            }
+        } else {
+            // Unable to retrieve node with proper capability
+            mWearBodySensorsPermissionApproved = false;
+            updateWearButtonOnUiThread();
+            logToUi("Wear devices not available to send message.");
+        }
+    }
+
+    private void updateWearButtonOnUiThread() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (mWearBodySensorsPermissionApproved) {
+                    mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                            R.drawable.ic_permission_approved, 0, 0, 0);
+                } else {
+                    mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                            R.drawable.ic_permission_denied, 0, 0, 0);
+                }
+            }
+        });
+    }
+
+    /*
+     * Handles all messages for the UI coming on and off the main thread. Not all callbacks happen
+     * on the main thread.
+     */
+    private void logToUi(final String message) {
+
+        boolean mainUiThread = (Looper.myLooper() == Looper.getMainLooper());
+
+        if (mainUiThread) {
+
+            if (!message.isEmpty()) {
+                Log.d(TAG, message);
+                mOutputTextView.setText(message);
+            }
+
+        } else {
+            if (!message.isEmpty()) {
+
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+
+                        Log.d(TAG, message);
+                        mOutputTextView.setText(message);
+                    }
+                });
+            }
+        }
+    }
+
+    private String getPhoneStorageInformation() {
+
+        StringBuilder stringBuilder = new StringBuilder();
+
+        String state = Environment.getExternalStorageState();
+        boolean isExternalStorageReadable = Environment.MEDIA_MOUNTED.equals(state)
+                || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
+
+        if (isExternalStorageReadable) {
+            File externalStorageDirectory = Environment.getExternalStorageDirectory();
+            String[] fileList = externalStorageDirectory.list();
+
+            if (fileList.length > 0) {
+
+                stringBuilder.append("List of files\n");
+                for (String file : fileList) {
+                    stringBuilder.append(" - " + file + "\n");
+                }
+
+            } else {
+                stringBuilder.append("No files in external storage.");
+            }
+
+        } else {
+            stringBuilder.append("No external media is available.");
+        }
+
+        return stringBuilder.toString();
+    }
+
+    private void sendWearPermissionResults() {
+
+        Log.d(TAG, "sendWearPermissionResults()");
+
+        DataMap dataMap = new DataMap();
+
+        if (mPhoneStoragePermissionApproved) {
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
+        } else {
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION);
+        }
+        sendMessage(dataMap);
+    }
+}
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/PhonePermissionRequestActivity.java b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/PhonePermissionRequestActivity.java
new file mode 100644
index 0000000..0b10e35
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/PhonePermissionRequestActivity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * This is a simple splash screen (activity) for giving more details on why the user should approve
+ * phone permissions for storage. If they choose to move forward, the permission screen
+ * is brought up. Either way (approve or disapprove), this will exit to the MainPhoneActivity after
+ * they are finished with their final decision.
+ *
+ * If this activity is started by our service (IncomingRequestPhoneService) it is marked via an
+ * extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR). That service only starts
+ * this activity if the phone permission hasn't been approved for the data wear is trying to access.
+ * When the user decides within this Activity what to do with the permission request, it closes and
+ * opens the MainPhoneActivity (to maintain the app experience). It also again passes along the same
+ * extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR) to alert MainPhoneActivity to
+ * send the results of the user's decision to the wear device.
+ */
+public class PhonePermissionRequestActivity extends AppCompatActivity implements
+        ActivityCompat.OnRequestPermissionsResultCallback {
+
+    private static final String TAG = "PhoneRationale";
+
+    /* Id to identify Location permission request. */
+    private static final int PERMISSION_REQUEST_READ_STORAGE = 1;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // If permissions granted, we start the main activity (shut this activity down).
+        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+                == PackageManager.PERMISSION_GRANTED) {
+            startMainActivity();
+        }
+
+        setContentView(R.layout.activity_phone_permission_request);
+    }
+
+    public void onClickApprovePermissionRequest(View view) {
+        Log.d(TAG, "onClickApprovePermissionRequest()");
+
+        // On 23+ (M+) devices, External storage permission not granted. Request permission.
+        ActivityCompat.requestPermissions(
+                this,
+                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+                PERMISSION_REQUEST_READ_STORAGE);
+    }
+
+    public void onClickDenyPermissionRequest(View view) {
+        Log.d(TAG, "onClickDenyPermissionRequest()");
+        startMainActivity();
+    }
+
+    /*
+     * Callback received when a permissions request has been completed.
+     */
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+
+        String permissionResult = "Request code: " + requestCode + ", Permissions: " + permissions
+                + ", Results: " + grantResults;
+        Log.d(TAG, "onRequestPermissionsResult(): " + permissionResult);
+
+        if (requestCode == PERMISSION_REQUEST_READ_STORAGE) {
+            // Close activity regardless of user's decision (decision picked up in main activity).
+            startMainActivity();
+        }
+    }
+
+    private void startMainActivity() {
+
+        Intent mainActivityIntent = new Intent(this, MainPhoneActivity.class);
+
+        /*
+         * If service started this Activity (b/c wear requested data where permissions were not
+         * approved), tells MainPhoneActivity to send results to wear device (via this extra).
+         */
+        boolean serviceStartedActivity = getIntent().getBooleanExtra(
+                MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, false);
+
+        if (serviceStartedActivity) {
+           mainActivityIntent.putExtra(
+                   MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, true);
+        }
+
+        startActivity(mainActivityIntent);
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/WearPermissionRequestActivity.java b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/WearPermissionRequestActivity.java
new file mode 100644
index 0000000..3340ef6
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/java/com/example/android/wearable/runtimepermissions/WearPermissionRequestActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * This is a simple splash screen (activity) for giving more details on why the user should approve
+ * phone permissions for storage. If they choose to move forward, the permission screen
+ * is brought up. Either way (approve or disapprove), this will exit to the MainPhoneActivity after
+ * they are finished with their final decision.
+ *
+ * If this activity is started by our service (IncomingRequestPhoneService) it is marked via an
+ * extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR). That service only starts
+ * this activity if the phone permission hasn't been approved for the data wear is trying to access.
+ * When the user decides within this Activity what to do with the permission request, it closes and
+ * opens the MainPhoneActivity (to maintain the app experience). It also again passes along the same
+ * extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR) to alert MainPhoneActivity to
+ * send the results of the user's decision to the wear device.
+ */
+public class WearPermissionRequestActivity extends AppCompatActivity implements
+        ActivityCompat.OnRequestPermissionsResultCallback {
+
+    private static final String TAG = "WearRationale";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_wear_permission_request);
+    }
+
+    public void onClickApprovePermissionRequest(View view) {
+        Log.d(TAG, "onClickApprovePermissionRequest()");
+        setResult(Activity.RESULT_OK);
+        finish();
+    }
+
+    public void onClickDenyPermissionRequest(View view) {
+        Log.d(TAG, "onClickDenyPermissionRequest()");
+        setResult(Activity.RESULT_CANCELED);
+        finish();
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_file_folder.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_file_folder.png
new file mode 100644
index 0000000..8fb69a5
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_file_folder.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_hardware_watch.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_hardware_watch.png
new file mode 100644
index 0000000..e05cb6a
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_hardware_watch.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_permission_approved.png
new file mode 100644
index 0000000..7989330
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_permission_denied.png
new file mode 100644
index 0000000..814bb63
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-hdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_file_folder.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_file_folder.png
new file mode 100644
index 0000000..ef11a06
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_file_folder.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_hardware_watch.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_hardware_watch.png
new file mode 100644
index 0000000..5f5900e
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_hardware_watch.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_permission_approved.png
new file mode 100644
index 0000000..1e63d37
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_permission_denied.png
new file mode 100644
index 0000000..45a0d87
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-mdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_file_folder.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_file_folder.png
new file mode 100644
index 0000000..6877103
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_file_folder.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_hardware_watch.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_hardware_watch.png
new file mode 100644
index 0000000..7c6773c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_hardware_watch.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_permission_approved.png
new file mode 100644
index 0000000..24d1efb
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_permission_denied.png
new file mode 100644
index 0000000..17f093d
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xhdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_file_folder.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_file_folder.png
new file mode 100644
index 0000000..3f2db91
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_file_folder.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_hardware_watch.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_hardware_watch.png
new file mode 100644
index 0000000..e8a5f74
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_hardware_watch.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_permission_approved.png
new file mode 100644
index 0000000..f29c5a3
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_permission_denied.png
new file mode 100644
index 0000000..52b0671
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxhdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_file_folder.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_file_folder.png
new file mode 100644
index 0000000..de3c50f
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_file_folder.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_hardware_watch.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_hardware_watch.png
new file mode 100644
index 0000000..8daad4f
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_hardware_watch.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_permission_approved.png
new file mode 100644
index 0000000..ec642b5
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_permission_denied.png
new file mode 100644
index 0000000..35d6c4f
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/drawable-xxxhdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_main.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..d35cb0c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<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"
+    tools:context=".MainActivity"
+    tools:deviceIds="wear_square"
+    android:padding="12dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/wearBodySensorsPermissionButton"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawableLeft="@drawable/ic_permission_denied"
+            android:text="@string/button_wear_label_activity_main"
+            android:onClick="onClickWearBodySensors" />
+
+        <Button
+            android:id="@+id/phoneStoragePermissionButton"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawableLeft="@drawable/ic_permission_denied"
+            android:text="@string/button_phone_label_activity_main"
+            android:onClick="onClickPhoneStorage" />
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/output"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="3"
+        android:text="@string/hello_phone_activity_main"
+        android:padding="8dp"
+        android:textSize="16sp" />
+</LinearLayout>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_phone_permission_request.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_phone_permission_request.xml
new file mode 100644
index 0000000..f6c5720
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_phone_permission_request.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:background="#4c9699">
+
+<Button
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:color/transparent"
+    android:stateListAnimator="@null"
+    android:text="@string/no_thanks_activity_phone_permission_request"
+    android:id="@+id/deny_permission_request"
+    android:onClick="onClickDenyPermissionRequest"
+    android:textColor="#ffffff"
+    android:layout_alignParentBottom="true"
+    android:layout_alignParentLeft="true"
+    android:layout_alignParentStart="true" />
+
+<Button
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:color/transparent"
+    android:stateListAnimator="@null"
+    android:text="@string/continue_activity_phone_permission_request"
+    android:id="@+id/approve_permission_request"
+    android:onClick="onClickApprovePermissionRequest"
+    android:layout_alignTop="@+id/deny_permission_request"
+    android:layout_alignParentRight="true"
+    android:layout_alignParentEnd="true"
+    android:textStyle="bold"
+    android:textColor="#ffffff" />
+
+<TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:text="@string/main_message_activity_phone_permission_request"
+    android:id="@+id/mainMessageTextView"
+    android:textColor="#ffffff"
+    android:textStyle="bold"
+    android:layout_below="@+id/imageView"
+    android:layout_alignParentLeft="true"
+    android:layout_alignParentStart="true"
+    android:layout_marginTop="117dp" />
+
+<TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:text="@string/details_message_activity_phone_permission_request"
+    android:id="@+id/detailsTextView"
+    android:textColor="#ffffff"
+    android:layout_below="@+id/mainMessageTextView"
+    android:layout_alignParentLeft="true"
+    android:layout_alignParentStart="true" />
+
+<ImageView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:id="@+id/imageView"
+    android:src="@drawable/ic_file_folder"
+    android:layout_alignParentTop="true"
+    android:layout_centerHorizontal="true"
+    android:layout_marginTop="60dp" />
+
+</RelativeLayout>
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_wear_permission_request.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_wear_permission_request.xml
new file mode 100644
index 0000000..b656cf5
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/layout/activity_wear_permission_request.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:background="#4c9699">
+
+<Button
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:color/transparent"
+    android:stateListAnimator="@null"
+    android:text="@string/no_thanks_activity_wear_permission_request"
+    android:id="@+id/deny_permission_request"
+    android:onClick="onClickDenyPermissionRequest"
+    android:textColor="#ffffff"
+    android:layout_alignParentBottom="true"
+    android:layout_alignParentLeft="true"
+    android:layout_alignParentStart="true" />
+
+<Button
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:color/transparent"
+    android:stateListAnimator="@null"
+    android:text="@string/continue_activity_wear_permission_request"
+    android:id="@+id/approve_permission_request"
+    android:onClick="onClickApprovePermissionRequest"
+    android:layout_alignTop="@+id/deny_permission_request"
+    android:layout_alignParentRight="true"
+    android:layout_alignParentEnd="true"
+    android:textStyle="bold"
+    android:textColor="#ffffff" />
+
+<TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:text="@string/main_message_activity_wear_permission_request"
+    android:id="@+id/mainMessageTextView"
+    android:textColor="#ffffff"
+    android:textStyle="bold"
+    android:layout_below="@+id/imageView"
+    android:layout_alignParentLeft="true"
+    android:layout_alignParentStart="true"
+    android:layout_marginTop="117dp" />
+<!--TODO: R.string.dialog_message_activity_main -->
+<TextView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceMedium"
+    android:text="@string/details_message_activity_wear_permission_request"
+    android:id="@+id/detailsTextView"
+    android:textColor="#ffffff"
+    android:layout_below="@+id/mainMessageTextView"
+    android:layout_alignParentLeft="true"
+    android:layout_alignParentStart="true" />
+
+<ImageView
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:id="@+id/imageView"
+    android:src="@drawable/ic_hardware_watch"
+    android:layout_alignParentTop="true"
+    android:layout_centerHorizontal="true"
+    android:layout_marginTop="60dp" />
+
+</RelativeLayout>
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values-w820dp/dimens.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..74184fc
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/dimens.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..e9366a9
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<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/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/strings.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..95a2c83
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<resources>
+
+    <string name="hello_phone_activity_main">Happy equals approved, sad equals denied.\n\nTo see results or request permissions, click on the buttons above.</string>
+    <string name="denied_permission_activity_main">You do not have the correct permissions. Tap sad face to bring up permission dialog again.</string>
+    <string name="button_wear_label_activity_main">Wear Sensors</string>
+    <string name="button_phone_label_activity_main">Phone Storage</string>
+
+    <string name="title_activity_phone_permission_request">PhonePermissionRequestActivity</string>
+    <string name="main_message_activity_phone_permission_request">See your directory structure by letting us read your phone\'s storage.</string>
+    <string name="details_message_activity_phone_permission_request">Your phone and watch experience need access to your phone\'s storage to show your top level directories.</string>
+    <string name="no_thanks_activity_phone_permission_request">No Thanks</string>
+    <string name="continue_activity_phone_permission_request">Continue</string>
+
+    <string name="title_activity_wear_permission_request">WearPermissionRequestActivity</string>
+    <string name="main_message_activity_wear_permission_request">See your total sensor count by letting us read your wear\'s sensors.</string>
+    <string name="details_message_activity_wear_permission_request">Your phone and watch experience need access to your wear\'s sensors to show sensor count.</string>
+    <string name="no_thanks_activity_wear_permission_request">No Thanks</string>
+    <string name="continue_activity_wear_permission_request">Open on Watch</string>
+
+</resources>
diff --git a/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/wear.xml b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/wear.xml
new file mode 100644
index 0000000..2787972
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Application/src/main/res/values/wear.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<resources>
+    <string-array name="android_wear_capabilities">
+        <item>phone_app_runtime_permissions</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Shared/.gitignore b/wearable/wear/RuntimePermissionsWear/Shared/.gitignore
new file mode 100644
index 0000000..6eb878d
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Shared/.gitignore
@@ -0,0 +1,16 @@
+# 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/RuntimePermissionsWear/Shared/src/main/AndroidManifest.xml b/wearable/wear/RuntimePermissionsWear/Shared/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fa262fa
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Shared/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.wearable.runtimepermissions.common">
+
+    <application android:allowBackup="true" android:label="@string/app_name">
+
+    </application>
+
+</manifest>
diff --git a/wearable/wear/RuntimePermissionsWear/Shared/src/main/java/com/example/android/wearable/runtimepermissions/common/Constants.java b/wearable/wear/RuntimePermissionsWear/Shared/src/main/java/com/example/android/wearable/runtimepermissions/common/Constants.java
new file mode 100644
index 0000000..d124400
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Shared/src/main/java/com/example/android/wearable/runtimepermissions/common/Constants.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions.common;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A collection of constants that is shared between the wearable and handset apps.
+ */
+public class Constants {
+
+    // Shared
+    public static final long CONNECTION_TIME_OUT_MS = TimeUnit.SECONDS.toMillis(5);
+
+    public static final String KEY_COMM_TYPE = "communicationType";
+    public static final String KEY_PAYLOAD = "payload";
+
+    // Requests
+    public static final int COMM_TYPE_REQUEST_PROMPT_PERMISSION = 1;
+    public static final int COMM_TYPE_REQUEST_DATA = 2;
+
+    // Responses
+    public static final int COMM_TYPE_RESPONSE_PERMISSION_REQUIRED = 1001;
+    public static final int COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION = 1002;
+    public static final int COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION = 1003;
+    public static final int COMM_TYPE_RESPONSE_DATA = 1004;
+
+    // Phone
+    public static final String CAPABILITY_PHONE_APP = "phone_app_runtime_permissions";
+    public static final String MESSAGE_PATH_PHONE = "/phone_message_path";
+
+    // Wear
+    public static final String CAPABILITY_WEAR_APP = "wear_app_runtime_permissions";
+    public static final String MESSAGE_PATH_WEAR = "/wear_message_path";
+
+    private Constants() {}
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Shared/src/main/res/values/strings.xml b/wearable/wear/RuntimePermissionsWear/Shared/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cc0aaa9
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Shared/src/main/res/values/strings.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<resources>
+    <string name="app_name">Shared</string>
+</resources>
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/.gitignore b/wearable/wear/RuntimePermissionsWear/Wearable/.gitignore
new file mode 100644
index 0000000..6eb878d
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/.gitignore
@@ -0,0 +1,16 @@
+# 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/RuntimePermissionsWear/Wearable/src/main/AndroidManifest.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..43218d7
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/AndroidManifest.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.wearable.runtimepermissions" >
+
+    <uses-feature android:name="android.hardware.type.watch" />
+
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.BODY_SENSORS" />
+
+    <uses-sdk
+        android:minSdkVersion="21"
+        android:targetSdkVersion="23" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@android:style/Theme.DeviceDefault" >
+
+        <meta-data
+            android:name="com.google.android.gms.version"
+            android:value="@integer/google_play_services_version" />
+
+        <!-- If you want your app to run on pre-22, then set required to false -->
+        <uses-library
+            android:name="com.google.android.wearable"
+            android:required="false" />
+
+        <activity
+            android:name=".MainWearActivity"
+            android:label="@string/app_name"
+            android:launchMode="singleInstance">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".RequestPermissionOnPhoneActivity"
+            android:label="@string/title_activity_request_permission_on_phone"
+            android:theme="@android:style/Theme.DeviceDefault.Light">
+        </activity>
+
+        <service
+            android:name=".IncomingRequestWearService"
+            android:enabled="true"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
+            </intent-filter>
+        </service>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/IncomingRequestWearService.java b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/IncomingRequestWearService.java
new file mode 100644
index 0000000..5dc2467
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/IncomingRequestWearService.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.support.v4.app.ActivityCompat;
+import android.util.Log;
+
+import com.example.android.wearable.runtimepermissions.common.Constants;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
+import com.google.android.gms.wearable.DataMap;
+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.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Handles all incoming requests for wear data (and permissions) from phone devices.
+ */
+public class IncomingRequestWearService extends WearableListenerService {
+
+    private static final String TAG = "IncomingRequestService";
+
+    public IncomingRequestWearService() {
+        Log.d(TAG, "IncomingRequestWearService()");
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(TAG, "onCreate()");
+    }
+
+    @Override
+    public void onMessageReceived(MessageEvent messageEvent) {
+        Log.d(TAG, "onMessageReceived(): " + messageEvent);
+
+        String messagePath = messageEvent.getPath();
+
+        if (messagePath.equals(Constants.MESSAGE_PATH_WEAR)) {
+            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
+
+            int requestType = dataMap.getInt(Constants.KEY_COMM_TYPE);
+
+            if (requestType == Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION) {
+                promptUserForSensorPermission();
+
+            } else if (requestType == Constants.COMM_TYPE_REQUEST_DATA) {
+                respondWithSensorInformation();
+            }
+        }
+    }
+
+    private void promptUserForSensorPermission() {
+        Log.d(TAG, "promptUserForSensorPermission()");
+
+        boolean sensorPermissionApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (sensorPermissionApproved) {
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
+            sendMessage(dataMap);
+        } else {
+            // Launch Activity to grant sensor permissions.
+            Intent startIntent = new Intent(this, MainWearActivity.class);
+            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startIntent.putExtra(MainWearActivity.EXTRA_PROMPT_PERMISSION_FROM_PHONE, true);
+            startActivity(startIntent);
+        }
+    }
+
+    private void respondWithSensorInformation() {
+        Log.d(TAG, "respondWithSensorInformation()");
+
+        boolean sensorPermissionApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (!sensorPermissionApproved) {
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED);
+            sendMessage(dataMap);
+        } else {
+            /* To keep the sample simple, we are only displaying the number of sensors. You could do
+             * something much more complicated.
+             */
+            SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+            List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
+            int numberOfSensorsOnDevice = sensorList.size();
+
+            String sensorSummary = numberOfSensorsOnDevice + " sensors on wear device(s)!";
+            DataMap dataMap = new DataMap();
+            dataMap.putInt(Constants.KEY_COMM_TYPE,
+                    Constants.COMM_TYPE_RESPONSE_DATA);
+            dataMap.putString(Constants.KEY_PAYLOAD, sensorSummary);
+            sendMessage(dataMap);
+        }
+    }
+
+    private void sendMessage(DataMap dataMap) {
+
+        Log.d(TAG, "sendMessage(): " + dataMap);
+
+        GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .build();
+        ConnectionResult connectionResult =
+                googleApiClient.blockingConnect(
+                        Constants.CONNECTION_TIME_OUT_MS,
+                        TimeUnit.MILLISECONDS);
+
+        if (!connectionResult.isSuccess()) {
+            Log.d(TAG, "Google API Client failed to connect.");
+            return;
+        }
+
+        PendingResult<CapabilityApi.GetCapabilityResult> pendingCapabilityResult =
+                Wearable.CapabilityApi.getCapability(
+                        googleApiClient,
+                        Constants.CAPABILITY_PHONE_APP,
+                        CapabilityApi.FILTER_REACHABLE);
+
+        CapabilityApi.GetCapabilityResult getCapabilityResult =
+                pendingCapabilityResult.await(
+                        Constants.CONNECTION_TIME_OUT_MS,
+                        TimeUnit.MILLISECONDS);
+
+        if (!getCapabilityResult.getStatus().isSuccess()) {
+            Log.d(TAG, "CapabilityApi failed to return any results.");
+            googleApiClient.disconnect();
+            return;
+        }
+
+        CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
+        String phoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
+
+        PendingResult<MessageApi.SendMessageResult> pendingMessageResult =
+                Wearable.MessageApi.sendMessage(
+                        googleApiClient,
+                        phoneNodeId,
+                        Constants.MESSAGE_PATH_PHONE,
+                        dataMap.toByteArray());
+
+        MessageApi.SendMessageResult sendMessageResult =
+                pendingMessageResult.await(Constants.CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
+
+        if (!sendMessageResult.getStatus().isSuccess()) {
+            Log.d(TAG, "Sending message failed, onResult: " + sendMessageResult.getStatus());
+        } else {
+            Log.d(TAG, "Message sent successfully");
+        }
+
+        googleApiClient.disconnect();
+    }
+
+    /*
+     * There should only ever be one phone in a node set (much less w/ the correct capability), so
+     * I am just grabbing the first one (which should be the only one).
+     */
+    private String pickBestNodeId(Set<Node> nodes) {
+
+        Log.d(TAG, "pickBestNodeId: " + nodes);
+
+
+        String bestNodeId = null;
+        /* Find a nearby node or pick one arbitrarily. There should be only one phone connected
+         * that supports this sample.
+         */
+        for (Node node : nodes) {
+            if (node.isNearby()) {
+                return node.getId();
+            }
+            bestNodeId = node.getId();
+        }
+        return bestNodeId;
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/MainWearActivity.java b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/MainWearActivity.java
new file mode 100644
index 0000000..b2a2595
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/MainWearActivity.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.wearable.activity.WearableActivity;
+import android.support.wearable.view.WatchViewStub;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.example.android.wearable.runtimepermissions.common.Constants;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
+import com.google.android.gms.wearable.DataMap;
+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.Wearable;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Displays data that requires runtime permissions both locally (BODY_SENSORS) and remotely on
+ * the phone (READ_EXTERNAL_STORAGE).
+ *
+ * The class is also launched by IncomingRequestWearService when the permission for the data the
+ * phone is trying to access hasn't been granted (wear's sensors). If granted in that scenario,
+ * this Activity also sends back the results of the permission request to the phone device (and
+ * the sensor data if approved).
+ */
+public class MainWearActivity extends WearableActivity implements
+        GoogleApiClient.ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener,
+        CapabilityApi.CapabilityListener,
+        MessageApi.MessageListener,
+        ActivityCompat.OnRequestPermissionsResultCallback {
+
+    private static final String TAG = "MainWearActivity";
+
+    /* Id to identify local permission request for body sensors. */
+    private static final int PERMISSION_REQUEST_READ_BODY_SENSORS = 1;
+
+    /* Id to identify starting/closing RequestPermissionOnPhoneActivity (startActivityForResult). */
+    private static final int REQUEST_PHONE_PERMISSION = 1;
+
+    public static final String EXTRA_PROMPT_PERMISSION_FROM_PHONE =
+            "com.example.android.wearable.runtimepermissions.extra.PROMPT_PERMISSION_FROM_PHONE";
+
+    private boolean mWearBodySensorsPermissionApproved;
+    private boolean mPhoneStoragePermissionApproved;
+
+    private boolean mPhoneRequestingWearSensorPermission;
+
+    private Button mWearBodySensorsPermissionButton;
+    private Button mPhoneStoragePermissionButton;
+    private TextView mOutputTextView;
+
+    private String mPhoneNodeId;
+
+    private GoogleApiClient mGoogleApiClient;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.d(TAG, "onCreate()");
+        super.onCreate(savedInstanceState);;
+
+        /*
+         * Since this is a remote permission, we initialize it to false and then check the remote
+         * permission once the GoogleApiClient is connected.
+         */
+        mPhoneStoragePermissionApproved = false;
+
+        setContentView(R.layout.activity_main);
+        setAmbientEnabled();
+
+        // Checks if phone app requested wear permission (permission request opens later if true).
+        mPhoneRequestingWearSensorPermission =
+                getIntent().getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_PHONE, false);
+
+        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
+        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
+            @Override
+            public void onLayoutInflated(WatchViewStub stub) {
+
+                mWearBodySensorsPermissionButton =
+                        (Button) stub.findViewById(R.id.wearBodySensorsPermissionButton);
+
+                if (mWearBodySensorsPermissionApproved) {
+                    mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                            R.drawable.ic_permission_approved, 0, 0, 0);
+                }
+
+                mPhoneStoragePermissionButton =
+                        (Button) stub.findViewById(R.id.phoneStoragePermissionButton);
+
+                mOutputTextView = (TextView) stub.findViewById(R.id.output);
+
+                if (mPhoneRequestingWearSensorPermission) {
+                    launchPermissionDialogForPhone();
+                }
+
+            }
+        });
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Wearable.API)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    public void onClickWearBodySensors(View view) {
+
+        if (mWearBodySensorsPermissionApproved) {
+
+            // To keep the sample simple, we are only displaying the number of sensors.
+            SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+            List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
+            int numberOfSensorsOnDevice = sensorList.size();
+
+            logToUi(numberOfSensorsOnDevice + " sensors on device(s)!");
+
+        } else {
+            logToUi("Requested local permission.");
+            // On 23+ (M+) devices, GPS permission not granted. Request permission.
+            ActivityCompat.requestPermissions(
+                    this,
+                    new String[]{Manifest.permission.BODY_SENSORS},
+                    PERMISSION_REQUEST_READ_BODY_SENSORS);
+        }
+    }
+
+    public void onClickPhoneStorage(View view) {
+
+        logToUi("Requested info from phone. New approval may be required.");
+        DataMap dataMap = new DataMap();
+        dataMap.putInt(Constants.KEY_COMM_TYPE,
+                Constants.COMM_TYPE_REQUEST_DATA);
+        sendMessage(dataMap);
+    }
+
+    @Override
+    protected void onPause() {
+        Log.d(TAG, "onPause()");
+        super.onPause();
+        if ((mGoogleApiClient != null) && mGoogleApiClient.isConnected()) {
+            Wearable.CapabilityApi.removeCapabilityListener(
+                    mGoogleApiClient,
+                    this,
+                    Constants.CAPABILITY_PHONE_APP);
+            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
+            mGoogleApiClient.disconnect();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        Log.d(TAG, "onResume()");
+        super.onResume();
+        if (mGoogleApiClient != null) {
+            mGoogleApiClient.connect();
+        }
+
+        // Enables app to handle 23+ (M+) style permissions.
+        mWearBodySensorsPermissionApproved =
+                ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
+                        == PackageManager.PERMISSION_GRANTED;
+    }
+
+     /*
+      * Because this wear activity is marked "android:launchMode='singleInstance'" in the manifest,
+      * we need to allow the permissions dialog to be opened up from the phone even if the wear app
+      * is in the foreground. By overriding onNewIntent, we can cover that use case.
+      */
+    @Override
+    protected void onNewIntent (Intent intent) {
+        Log.d(TAG, "onNewIntent()");
+        super.onNewIntent(intent);
+
+        // Checks if phone app requested wear permissions (opens up permission request if true).
+        mPhoneRequestingWearSensorPermission =
+                intent.getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_PHONE, false);
+
+        if (mPhoneRequestingWearSensorPermission) {
+            launchPermissionDialogForPhone();
+        }
+    }
+
+    @Override
+    public void onEnterAmbient(Bundle ambientDetails) {
+        Log.d(TAG, "onEnterAmbient() " + ambientDetails);
+
+        if (mWearBodySensorsPermissionApproved) {
+            mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_approved_bw, 0, 0, 0);
+        } else {
+            mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_denied_bw, 0, 0, 0);
+        }
+
+        if (mPhoneStoragePermissionApproved) {
+            mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_approved_bw, 0, 0, 0);
+        } else {
+            mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_denied_bw, 0, 0, 0);
+        }
+        super.onEnterAmbient(ambientDetails);
+    }
+
+    @Override
+    public void onExitAmbient() {
+        Log.d(TAG, "onExitAmbient()");
+
+        if (mWearBodySensorsPermissionApproved) {
+            mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_approved, 0, 0, 0);
+        } else {
+            mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_denied, 0, 0, 0);
+        }
+
+        if (mPhoneStoragePermissionApproved) {
+            mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_approved, 0, 0, 0);
+        } else {
+            mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                    R.drawable.ic_permission_denied, 0, 0, 0);
+        }
+        super.onExitAmbient();
+    }
+
+    @Override
+    public void onConnected(Bundle bundle) {
+        Log.d(TAG, "onConnected()");
+
+        // Set up listeners for capability and message changes.
+        Wearable.CapabilityApi.addCapabilityListener(
+                mGoogleApiClient,
+                this,
+                Constants.CAPABILITY_PHONE_APP);
+        Wearable.MessageApi.addListener(mGoogleApiClient, this);
+
+        // Initial check of capabilities to find the phone.
+        PendingResult<CapabilityApi.GetCapabilityResult> pendingResult =
+                Wearable.CapabilityApi.getCapability(
+                        mGoogleApiClient,
+                        Constants.CAPABILITY_PHONE_APP,
+                        CapabilityApi.FILTER_REACHABLE);
+
+        pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
+            @Override
+            public void onResult(CapabilityApi.GetCapabilityResult getCapabilityResult) {
+
+                if (getCapabilityResult.getStatus().isSuccess()) {
+                    CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
+                    mPhoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
+
+                } else {
+                    Log.d(TAG, "Failed CapabilityApi result: "
+                            + getCapabilityResult.getStatus());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onConnectionSuspended(int i) {
+        Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult connectionResult) {
+        Log.e(TAG, "onConnectionFailed(): connection to location client failed");
+    }
+
+    public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
+        Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo);
+
+        mPhoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
+    }
+
+    /*
+     * Callback received when a permissions request has been completed.
+     */
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+
+        String permissionResult = "Request code: " + requestCode + ", Permissions: " + permissions
+                + ", Results: " + grantResults;
+        Log.d(TAG, "onRequestPermissionsResult(): " + permissionResult);
+
+
+        if (requestCode == PERMISSION_REQUEST_READ_BODY_SENSORS) {
+
+            if ((grantResults.length == 1)
+                    && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
+
+                mWearBodySensorsPermissionApproved = true;
+                mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                        R.drawable.ic_permission_approved, 0, 0, 0);
+
+                // To keep the sample simple, we are only displaying the number of sensors.
+                SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+                List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
+                int numberOfSensorsOnDevice = sensorList.size();
+
+                String sensorSummary = numberOfSensorsOnDevice + " sensors on this device!";
+                logToUi(sensorSummary);
+
+                if (mPhoneRequestingWearSensorPermission) {
+                    // Resets so this isn't triggered every time permission is changed in app.
+                    mPhoneRequestingWearSensorPermission = false;
+
+                    // Send 'approved' message to remote phone since it started Activity.
+                    DataMap dataMap = new DataMap();
+                    dataMap.putInt(Constants.KEY_COMM_TYPE,
+                            Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
+                    sendMessage(dataMap);
+                }
+
+            } else {
+
+                mWearBodySensorsPermissionApproved = false;
+                mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                        R.drawable.ic_permission_denied, 0, 0, 0);
+
+                if (mPhoneRequestingWearSensorPermission) {
+                    // Resets so this isn't triggered every time permission is changed in app.
+                    mPhoneRequestingWearSensorPermission = false;
+                    // Send 'denied' message to remote phone since it started Activity.
+                    DataMap dataMap = new DataMap();
+                    dataMap.putInt(Constants.KEY_COMM_TYPE,
+                            Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION);
+                    sendMessage(dataMap);
+                }
+            }
+        }
+    }
+
+    public void onMessageReceived(MessageEvent messageEvent) {
+        Log.d(TAG, "onMessageReceived(): " + messageEvent);
+
+        String messagePath = messageEvent.getPath();
+
+        if (messagePath.equals(Constants.MESSAGE_PATH_WEAR)) {
+
+            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
+            int commType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
+
+            if (commType == Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED) {
+                mPhoneStoragePermissionApproved = false;
+                updatePhoneButtonOnUiThread();
+
+                /* Because our request for remote data requires a remote permission, we now launch
+                 * a splash activity informing the user we need those permissions (along with
+                 * other helpful information to approve).
+                 */
+                Intent phonePermissionRationaleIntent =
+                        new Intent(this, RequestPermissionOnPhoneActivity.class);
+                startActivityForResult(phonePermissionRationaleIntent, REQUEST_PHONE_PERMISSION);
+
+            } else if (commType == Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION) {
+                mPhoneStoragePermissionApproved = true;
+                updatePhoneButtonOnUiThread();
+                logToUi("User approved permission on remote device, requesting data again.");
+                DataMap outgoingDataRequestDataMap = new DataMap();
+                outgoingDataRequestDataMap.putInt(Constants.KEY_COMM_TYPE,
+                        Constants.COMM_TYPE_REQUEST_DATA);
+                sendMessage(outgoingDataRequestDataMap);
+
+            } else if (commType == Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION) {
+                mPhoneStoragePermissionApproved = false;
+                updatePhoneButtonOnUiThread();
+                logToUi("User denied permission on remote device.");
+
+            } else if (commType == Constants.COMM_TYPE_RESPONSE_DATA) {
+                mPhoneStoragePermissionApproved = true;
+                String storageDetails = dataMap.getString(Constants.KEY_PAYLOAD);
+                updatePhoneButtonOnUiThread();
+                logToUi(storageDetails);
+            }
+        }
+    }
+
+    private void sendMessage(DataMap dataMap) {
+        Log.d(TAG, "sendMessage(): " + mPhoneNodeId);
+
+        if (mPhoneNodeId != null) {
+
+            PendingResult<MessageApi.SendMessageResult> pendingResult =
+                    Wearable.MessageApi.sendMessage(
+                            mGoogleApiClient,
+                            mPhoneNodeId,
+                            Constants.MESSAGE_PATH_PHONE,
+                            dataMap.toByteArray());
+
+            pendingResult.setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
+                @Override
+                public void onResult(MessageApi.SendMessageResult sendMessageResult) {
+
+                    if (!sendMessageResult.getStatus().isSuccess()) {
+                        updatePhoneButtonOnUiThread();
+                        logToUi("Sending message failed.");
+
+                    } else {
+                        Log.d(TAG, "Message sent successfully.");
+                    }
+                }
+            }, Constants.CONNECTION_TIME_OUT_MS, TimeUnit.SECONDS);
+
+        } else {
+            // Unable to retrieve node with proper capability
+            mPhoneStoragePermissionApproved = false;
+            updatePhoneButtonOnUiThread();
+            logToUi("Phone not available to send message.");
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // Check which request we're responding to
+        if (requestCode == REQUEST_PHONE_PERMISSION) {
+            // Make sure the request was successful
+            if (resultCode == RESULT_OK) {
+                logToUi("Requested permission on phone.");
+                DataMap dataMap = new DataMap();
+                dataMap.putInt(Constants.KEY_COMM_TYPE,
+                        Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION);
+                sendMessage(dataMap);
+            }
+        }
+    }
+
+    /*
+     * There should only ever be one phone in a node set (much less w/ the correct capability), so
+     * I am just grabbing the first one (which should be the only one).
+     */
+    private String pickBestNodeId(Set<Node> nodes) {
+
+        String bestNodeId = null;
+        // Find a nearby node or pick one arbitrarily.
+        for (Node node : nodes) {
+            if (node.isNearby()) {
+                return node.getId();
+            }
+            bestNodeId = node.getId();
+        }
+        return bestNodeId;
+    }
+
+    /*
+     * If Phone triggered the wear app for permissions, we open up the permission
+     * dialog after inflation.
+     */
+    private void launchPermissionDialogForPhone() {
+        Log.d(TAG, "launchPermissionDialogForPhone()");
+
+        if (!mWearBodySensorsPermissionApproved) {
+            // On 23+ (M+) devices, GPS permission not granted. Request permission.
+            ActivityCompat.requestPermissions(
+                    MainWearActivity.this,
+                    new String[]{Manifest.permission.BODY_SENSORS},
+                    PERMISSION_REQUEST_READ_BODY_SENSORS);
+        }
+    }
+
+    private void updatePhoneButtonOnUiThread() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+
+                if (mPhoneStoragePermissionApproved) {
+
+                    if (isAmbient()) {
+                        mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                                R.drawable.ic_permission_approved_bw, 0, 0, 0);
+                    } else {
+                        mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                                R.drawable.ic_permission_approved, 0, 0, 0);
+                    }
+
+                } else {
+
+                    if (isAmbient()) {
+                        mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                                R.drawable.ic_permission_denied_bw, 0, 0, 0);
+                    } else {
+                        mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
+                                R.drawable.ic_permission_denied, 0, 0, 0);
+                    }
+                }
+            }
+        });
+    }
+
+    /*
+     * Handles all messages for the UI coming on and off the main thread. Not all callbacks happen
+     * on the main thread.
+     */
+    private void logToUi(final String message) {
+
+        boolean mainUiThread = (Looper.myLooper() == Looper.getMainLooper());
+
+        if (mainUiThread) {
+
+            if (!message.isEmpty()) {
+                Log.d(TAG, message);
+                mOutputTextView.setText(message);
+            }
+
+        } else {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (!message.isEmpty()) {
+                        Log.d(TAG, message);
+                        mOutputTextView.setText(message);
+                    }
+                }
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/RequestPermissionOnPhoneActivity.java b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/RequestPermissionOnPhoneActivity.java
new file mode 100644
index 0000000..f4ee7fc
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/java/com/example/android/wearable/runtimepermissions/RequestPermissionOnPhoneActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.runtimepermissions;
+
+import android.os.Bundle;
+import android.support.wearable.activity.WearableActivity;
+import android.view.View;
+
+/**
+ * Asks user if they want to open permission screen on their remote device (phone).
+ */
+public class RequestPermissionOnPhoneActivity extends WearableActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_request_permission_on_phone);
+        setAmbientEnabled();
+    }
+
+    public void onClickPermissionPhoneStorage(View view) {
+        setResult(RESULT_OK);
+        finish();
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_cc_open_on_phone.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_cc_open_on_phone.png
new file mode 100644
index 0000000..618a44f
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_cc_open_on_phone.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_approved.png
new file mode 100644
index 0000000..7989330
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_approved_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_approved_bw.png
new file mode 100644
index 0000000..bbd7e8a
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_approved_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_denied.png
new file mode 100644
index 0000000..814bb63
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_denied_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_denied_bw.png
new file mode 100644
index 0000000..accd6c0
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-hdpi/ic_permission_denied_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_cc_open_on_phone.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_cc_open_on_phone.png
new file mode 100644
index 0000000..e66ba6b
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_cc_open_on_phone.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_approved.png
new file mode 100644
index 0000000..1e63d37
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_approved_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_approved_bw.png
new file mode 100644
index 0000000..16050cb
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_approved_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_denied.png
new file mode 100644
index 0000000..45a0d87
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_denied_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_denied_bw.png
new file mode 100644
index 0000000..376d471
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-mdpi/ic_permission_denied_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_cc_open_on_phone.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_cc_open_on_phone.png
new file mode 100644
index 0000000..5522d6c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_cc_open_on_phone.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_approved.png
new file mode 100644
index 0000000..24d1efb
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_approved_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_approved_bw.png
new file mode 100644
index 0000000..2682e30
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_approved_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_denied.png
new file mode 100644
index 0000000..17f093d
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_denied_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_denied_bw.png
new file mode 100644
index 0000000..cd9d000
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xhdpi/ic_permission_denied_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_approved.png
new file mode 100644
index 0000000..f29c5a3
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_approved_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_approved_bw.png
new file mode 100644
index 0000000..1bcf27a
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_approved_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_denied.png
new file mode 100644
index 0000000..52b0671
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_denied_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_denied_bw.png
new file mode 100644
index 0000000..2292033
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxhdpi/ic_permission_denied_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_approved.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_approved.png
new file mode 100644
index 0000000..ec642b5
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_approved.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_approved_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_approved_bw.png
new file mode 100644
index 0000000..c9eab85
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_approved_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_denied.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_denied.png
new file mode 100644
index 0000000..35d6c4f
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_denied.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_denied_bw.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_denied_bw.png
new file mode 100644
index 0000000..81e5355
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/drawable-xxxhdpi/ic_permission_denied_bw.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/activity_main.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..588ff9a
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<android.support.wearable.view.WatchViewStub
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/watch_view_stub"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:rectLayout="@layout/rect_activity_main"
+    app:roundLayout="@layout/round_activity_main"
+    tools:context=".MainActivity"
+    tools:deviceIds="wear">
+</android.support.wearable.view.WatchViewStub>
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/activity_request_permission_on_phone.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/activity_request_permission_on_phone.xml
new file mode 100644
index 0000000..c8a5d05
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/activity_request_permission_on_phone.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<android.support.wearable.view.BoxInsetLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:deviceIds="wear"
+    android:background="@color/white"
+    tools:context="com.example.android.wearable.runtimepermissions.RequestPermissionOnPhoneActivity"
+    android:paddingStart="30dp"
+    android:paddingTop="18dp"
+    android:paddingRight="18dp">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:onClick="onClickPermissionPhoneStorage"
+        android:orientation="vertical"
+        app:layout_box="all">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/permission_message_activity_request_permission_on_phone"
+            android:textSize="16sp"
+            android:paddingRight="6dp"
+            android:textColor="#000000"/>
+
+        <android.support.v4.widget.Space
+            android:layout_width="18dp"
+            android:layout_height="18dp"/>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="center">
+
+            <android.support.wearable.view.CircledImageView
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                app:circle_radius="20dp"
+                app:circle_color="#0086D4"
+                android:src="@drawable/ic_cc_open_on_phone"/>
+
+            <android.support.v4.widget.Space
+                android:layout_width="8dp"
+                android:layout_height="8dp"/>
+
+            <TextView
+                android:id="@+id/openOnPhone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAlignment="center"
+                android:textSize="16sp"
+                android:text="@string/open_on_phone_message_activity_request_permission_on_phone"
+                android:textColor="#0086D4"/>
+        </LinearLayout>
+    </LinearLayout>
+</android.support.wearable.view.BoxInsetLayout>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/rect_activity_main.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/rect_activity_main.xml
new file mode 100644
index 0000000..5a44894
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/rect_activity_main.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<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"
+    tools:context=".MainActivity"
+    tools:deviceIds="wear_square">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <Button
+            android:id="@+id/wearBodySensorsPermissionButton"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawableLeft="@drawable/ic_permission_denied"
+            android:textSize="8sp"
+            android:text="@string/button_wear_label_activity_main"
+            android:onClick="onClickWearBodySensors" />
+
+        <Button
+            android:id="@+id/phoneStoragePermissionButton"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawableLeft="@drawable/ic_permission_denied"
+            android:textSize="8sp"
+            android:text="@string/button_phone_label_activity_main"
+            android:onClick="onClickPhoneStorage" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/output"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="2"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
+        android:text="@string/hello_wear_activity_main" />
+</LinearLayout>
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/round_activity_main.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/round_activity_main.xml
new file mode 100644
index 0000000..d35012d
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/layout/round_activity_main.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<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"
+    tools:context=".MainActivity"
+    tools:deviceIds="wear_round"
+    android:paddingTop="24dp"
+    android:paddingLeft="10dp"
+    android:paddingRight="10dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <Button
+            android:id="@+id/wearBodySensorsPermissionButton"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawableLeft="@drawable/ic_permission_denied"
+            android:textSize="8sp"
+            android:text="@string/button_wear_label_activity_main"
+            android:onClick="onClickWearBodySensors" />
+
+        <Button
+            android:id="@+id/phoneStoragePermissionButton"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:drawableLeft="@drawable/ic_permission_denied"
+            android:textSize="8sp"
+            android:text="@string/button_phone_label_activity_main"
+            android:onClick="onClickPhoneStorage" />
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/output"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="2"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
+        android:text="@string/hello_wear_activity_main" />
+
+</LinearLayout>
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-hdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-mdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-xhdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/dimens.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..2f2eb2a
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/dimens.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>
+    <dimen name="pair_button_diameter">40dp</dimen>
+    <dimen name="circle_border_normal_width">10dp</dimen>
+    <dimen name="circle_padding">5dp</dimen>
+    <dimen name="circle_radius">35dp</dimen>
+    <dimen name="circle_radius_pressed">40dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/strings.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/strings.xml
new file mode 100644
index 0000000..67f460a
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<resources>
+    <string name="app_name">Runtime Permissions</string>
+
+    <string name="hello_wear_activity_main">Happy equals approved, sad equals denied.\n\nTo see results or request permissions, click on the buttons above.</string>
+    <string name="button_wear_label_activity_main">Wear Sensors</string>
+    <string name="button_phone_label_activity_main">Phone Storage</string>
+
+    <string name="title_activity_request_permission_on_phone">PhonePermissionRationale</string>
+    <string name="permission_message_activity_request_permission_on_phone">App requires access to your phone\'s storage.</string>
+    <string name="open_on_phone_message_activity_request_permission_on_phone">Open on phone</string>
+</resources>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/wear.xml b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/wear.xml
new file mode 100644
index 0000000..42e922f
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/Wearable/src/main/res/values/wear.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<resources>
+    <string-array name="android_wear_capabilities">
+        <item>wear_app_runtime_permissions</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/wearable/wear/RuntimePermissionsWear/build.gradle b/wearable/wear/RuntimePermissionsWear/build.gradle
new file mode 100644
index 0000000..b95a860
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/build.gradle
@@ -0,0 +1,12 @@
+
+
+// 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/wearable/wear/RuntimePermissionsWear/buildSrc/build.gradle b/wearable/wear/RuntimePermissionsWear/buildSrc/build.gradle
new file mode 100644
index 0000000..7c150e4
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/buildSrc/build.gradle
@@ -0,0 +1,16 @@
+
+repositories {
+    jcenter()
+}
+dependencies {
+    compile 'org.freemarker:freemarker:2.3.20'
+}
+
+sourceSets {
+    main {
+        groovy {
+            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+        }
+    }
+}
+
diff --git a/wearable/wear/RuntimePermissionsWear/gradle/wrapper/gradle-wrapper.jar b/wearable/wear/RuntimePermissionsWear/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/RuntimePermissionsWear/gradle/wrapper/gradle-wrapper.properties
similarity index 79%
copy from ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
copy to wearable/wear/RuntimePermissionsWear/gradle/wrapper/gradle-wrapper.properties
index a51db8c..07fc193 100644
--- a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/RuntimePermissionsWear/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue May 20 13:33:02 BST 2014
+#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
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradlew b/wearable/wear/RuntimePermissionsWear/gradlew
similarity index 100%
copy from ui/views/Elevation/ElevationDrag/gradle/gradlew
copy to wearable/wear/RuntimePermissionsWear/gradlew
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradlew.bat b/wearable/wear/RuntimePermissionsWear/gradlew.bat
similarity index 100%
copy from ui/views/Elevation/ElevationDrag/gradle/gradlew.bat
copy to wearable/wear/RuntimePermissionsWear/gradlew.bat
diff --git a/wearable/wear/RuntimePermissionsWear/screenshots/screenshot-phone.png b/wearable/wear/RuntimePermissionsWear/screenshots/screenshot-phone.png
new file mode 100644
index 0000000..080a3c8
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/screenshots/screenshot-phone.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/screenshots/screenshot-wear.png b/wearable/wear/RuntimePermissionsWear/screenshots/screenshot-wear.png
new file mode 100644
index 0000000..2228d69
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/screenshots/screenshot-wear.png
Binary files differ
diff --git a/wearable/wear/RuntimePermissionsWear/settings.gradle b/wearable/wear/RuntimePermissionsWear/settings.gradle
new file mode 100644
index 0000000..8c3a205
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/settings.gradle
@@ -0,0 +1,2 @@
+
+include ':Application', ':Wearable', ':Shared'
diff --git a/wearable/wear/RuntimePermissionsWear/template-params.xml b/wearable/wear/RuntimePermissionsWear/template-params.xml
new file mode 100644
index 0000000..9c2d4f6
--- /dev/null
+++ b/wearable/wear/RuntimePermissionsWear/template-params.xml
@@ -0,0 +1,127 @@
+<?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>RuntimePermissionsWear</name>
+    <group>Wearable</group>
+    <package>com.example.android.wearable.runtimepermissions</package>
+
+    <minSdk>18</minSdk>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>23</targetSdkVersionWear>
+
+    <wearable>
+        <has_handheld_app>true</has_handheld_app>
+    </wearable>
+
+    <dependency>com.android.support:appcompat-v7:23.1.1</dependency>
+    <dependency>com.android.support:design:23.1.1</dependency>
+    <provided_dependency_wearable>com.google.android.wearable:wearable:1.0.0</provided_dependency_wearable>
+
+    <strings>
+        <intro>
+<![CDATA[
+A sample that shows how you can handle remote data that requires permissions both on
+a wearable device and a mobile device.
+]]>
+        </intro>
+    </strings>
+
+    <!-- The basic templates have already been enabled. Uncomment more as desired. -->
+    <template src="base" />
+    <template src="WearPlusShared"/>
+
+    <metadata>
+        <!-- Values: {DRAFT | PUBLISHED | INTERNAL | DEPRECATED | SUPERCEDED} -->
+        <status>PUBLISHED</status>
+        <!-- See http://go/sample-categories for details on the next 4 fields. -->
+        <categories>Wearable, Permissions</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <level>INTERMEDIATE</level>
+        <!-- Dimensions: 512x512, PNG fomrat -->
+        <icon>screenshots/icon-web.png</icon>
+        <!-- Path to screenshots. Use <img> tags for each. -->
+        <screenshots>
+            <img>screenshots/screenshot-wear.png</img>
+            <img>screenshots/screenshot-phone.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.support.v4.app.ActivityCompat</android>
+            <android>android.support.v7.app.AppCompatActivity</android>
+            <android>android.support.wearable.activity.WearableActivity</android>
+            <android>android.support.wearable.view.WatchViewStub</android>
+            <android>com.google.android.gms.common.api.GoogleApiClient</android>
+            <android>com.google.android.gms.wearable.CapabilityApi</android>
+            <android>com.google.android.gms.wearable.CapabilityInfo</android>
+            <android>com.google.android.gms.wearable.DataMap</android>
+            <android>com.google.android.gms.wearable.MessageApi</android>
+            <android>com.google.android.gms.wearable.MessageEvent</android>
+            <android>com.google.android.gms.wearable.Node</android>
+            <android>com.google.android.gms.wearable.Wearable</android>
+            <android>com.google.android.gms.wearable.WearableListenerService</android>
+        </api_refs>
+
+        <!-- 1-3 line description of the sample here.
+
+            Avoid simply rearranging the sample's title. What does this sample actually
+            accomplish, and how does it do it? -->
+        <description>
+A sample that shows how you can handle remote data that requires permissions both on
+a wearable device and a mobile device.
+        </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[
+Steps for trying out this sample:
+* Compile and install the mobile app onto your mobile device or emulator.
+* Compile and install the wearable app onto your Wear device or emulator.
+(**Note:** wearable apps are not automatically pushed from your mobile device
+unless you build a production release, see [here][3] for more info).
+* Start the mobile or wear app. Each app contains two buttons: one for showing
+local data and another for showing remote data.
+* Click either button to view the data. Both local and remote data require
+[dangerous permissions][4] to be approved before displaying the data for
+devices running 23 or above. You will be asked to approve the access if you
+do not have the proper permissions.
+* The happy icon signifies you have access to the data while the sad icon
+signifies you do or may not have access (and may be asked to approve access).
+
+This sample demonstrates how to access data and trigger permission approval
+on remote devices. It uses [Services][5] and the [Wearable MessageApi][2] to
+communicate between devices.
+
+To find out more about wear, visit our [developer Wear page][1].
+
+[1]: http://developer.android.com/wear/
+[2]: https://developer.android.com/reference/com/google/android/gms/wearable/MessageApi.html
+[3]: https://developer.android.com/training/wearables/apps/creating.html#Install
+[4]: http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
+[5]: http://developer.android.com/guide/components/services.html
+]]>
+        </intro>
+    </metadata>
+</sample>
diff --git a/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml b/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml
index f99d785..f9e8978 100644
--- a/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/SkeletonWearableApp/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.google.wearable.app" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/SkeletonWearableApp/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/SkeletonWearableApp/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/SkeletonWearableApp/template-params.xml b/wearable/wear/SkeletonWearableApp/template-params.xml
index b0f4b36..1441c1f 100644
--- a/wearable/wear/SkeletonWearableApp/template-params.xml
+++ b/wearable/wear/SkeletonWearableApp/template-params.xml
@@ -19,8 +19,7 @@
     <group>Wearable</group>
     <package>com.example.android.google.wearable.app</package>
 
-    <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
@@ -35,7 +34,7 @@
     <template src="base-build"/>
     <template src="Wear"/>
     <metadata>
-        <status>PUBLISHED</status>
+        <status>DEPRECATED</status>
         <categories>Getting Started, Wearable</categories>
         <technologies>Android</technologies>
         <languages>Java</languages>
diff --git a/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml b/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml
index 44284d4..be88f6d 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/SpeedTracker/Application/src/main/AndroidManifest.xml
@@ -2,25 +2,35 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.wearable.speedtracker" >
 
+
+    <uses-sdk
+        android:minSdkVersion="18"
+        android:targetSdkVersion="23" />
+
+    <!-- BEGIN_INCLUDE(manifest) -->
+
+    <!-- Note that all required permissions are declared here in the Android manifest.
+         On Android M and above, use of permissions not in the normal permission group are
+         requested at run time. -->
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        android:maxSdkVersion="18"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
-    
+    <!-- END_INCLUDE(manifest) -->
+
     <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="21" />
 
     <application
         android:name=".PhoneApplication"
         android:allowBackup="true"
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name"
-        android:theme="@style/AppTheme" >
+        android:theme="@style/Theme.AppCompat.Light" >
         <meta-data
             android:name="com.google.android.maps.v2.API_KEY"
             android:value="@string/map_v2_api_key"/>
diff --git a/wearable/wear/SpeedTracker/Application/src/main/java/com/example/android/wearable/speedtracker/PhoneMainActivity.java b/wearable/wear/SpeedTracker/Application/src/main/java/com/example/android/wearable/speedtracker/PhoneMainActivity.java
index 76f609b..c645bdd 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/java/com/example/android/wearable/speedtracker/PhoneMainActivity.java
+++ b/wearable/wear/SpeedTracker/Application/src/main/java/com/example/android/wearable/speedtracker/PhoneMainActivity.java
@@ -23,10 +23,10 @@
 import com.google.android.gms.maps.model.LatLngBounds;
 import com.google.android.gms.maps.model.PolylineOptions;
 
-import android.app.Activity;
 import android.app.DatePickerDialog;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.View;
@@ -45,7 +45,8 @@
  * a map. This data is then saved into an internal database and the corresponding data items are
  * deleted.
  */
-public class PhoneMainActivity extends Activity implements DatePickerDialog.OnDateSetListener {
+public class PhoneMainActivity extends AppCompatActivity implements
+        DatePickerDialog.OnDateSetListener {
 
     private static final String TAG = "PhoneMainActivity";
     private static final int BOUNDING_BOX_PADDING_PX = 50;
diff --git a/wearable/wear/SpeedTracker/Application/src/main/res/layout/main_activity.xml b/wearable/wear/SpeedTracker/Application/src/main/res/layout/main_activity.xml
index a18c644..17a8f6a 100644
--- a/wearable/wear/SpeedTracker/Application/src/main/res/layout/main_activity.xml
+++ b/wearable/wear/SpeedTracker/Application/src/main/res/layout/main_activity.xml
@@ -21,7 +21,8 @@
     <RelativeLayout
         android:id="@+id/top_container"
         android:layout_width="fill_parent"
-        android:layout_height="wrap_content">
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp">
         <Button
             android:id="@+id/date_picker"
             android:layout_width="wrap_content"
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml b/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml
index ab19d5e..c9cbad1 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/AndroidManifest.xml
@@ -19,18 +19,22 @@
 
     <uses-feature android:name="android.hardware.type.watch"/>
     <uses-feature android:name="android.hardware.location.gps" android:required="true" />
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>\
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <uses-sdk
         android:minSdkVersion="20"
-        android:targetSdkVersion="21" />
+        android:targetSdkVersion="23" />
 
     <application
         android:allowBackup="true"
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name"
         android:theme="@android:style/Theme.DeviceDefault">
+
+        <!--If you want your app to run on pre-22, then set required to false -->
+        <uses-library android:name="com.google.android.wearable" android:required="false" />
+
         <meta-data android:name="com.google.android.gms.version"
                    android:value="@integer/google_play_services_version"/>
         <activity
@@ -38,7 +42,6 @@
             android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
-
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
@@ -48,12 +51,6 @@
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
         </activity>
-        <activity
-            android:name=".ui.LocationSettingActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-            </intent-filter>
-        </activity>
     </application>
 
 </manifest>
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/SpeedPickerActivity.java b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/SpeedPickerActivity.java
index d55d7df..d178891 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/SpeedPickerActivity.java
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/SpeedPickerActivity.java
@@ -17,9 +17,8 @@
 package com.example.android.wearable.speedtracker;
 
 import android.app.Activity;
-import android.content.SharedPreferences;
+import android.content.Intent;
 import android.os.Bundle;
-import android.preference.PreferenceManager;
 import android.support.wearable.view.WearableListView;
 import android.widget.TextView;
 
@@ -31,6 +30,9 @@
  */
 public class SpeedPickerActivity extends Activity implements WearableListView.ClickListener {
 
+    public static final String EXTRA_NEW_SPEED_LIMIT =
+            "com.example.android.wearable.speedtracker.extra.NEW_SPEED_LIMIT";
+
     /* Speeds, in mph, that will be shown on the list */
     private int[] speeds = {25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75};
 
@@ -75,9 +77,13 @@
 
     @Override
     public void onClick(WearableListView.ViewHolder viewHolder) {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
-        pref.edit().putInt(WearableMainActivity.PREFS_SPEED_LIMIT_KEY,
-                speeds[viewHolder.getPosition()]).apply();
+
+        int newSpeedLimit = speeds[viewHolder.getPosition()];
+
+        Intent resultIntent = new Intent(Intent.ACTION_PICK);
+        resultIntent.putExtra(EXTRA_NEW_SPEED_LIMIT, newSpeedLimit);
+        setResult(RESULT_OK, resultIntent);
+
         finish();
     }
 
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 f3015bf..25f424c 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
@@ -28,7 +28,7 @@
 import com.google.android.gms.wearable.PutDataRequest;
 import com.google.android.gms.wearable.Wearable;
 
-import android.app.Activity;
+import android.Manifest;
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -38,18 +38,19 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.wearable.activity.WearableActivity;
 import android.util.Log;
 import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.example.android.wearable.speedtracker.common.Constants;
 import com.example.android.wearable.speedtracker.common.LocationEntry;
-import com.example.android.wearable.speedtracker.ui.LocationSettingActivity;
 
 import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
 
 /**
  * The main activity for the wearable app. User can pick a speed limit, and after this activity
@@ -58,33 +59,54 @@
  * and if the user exceeds the speed limit, it will turn red. In order to show the user that GPS
  * location data is coming in, a small green dot keeps on blinking while GPS data is available.
  */
-public class WearableMainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks,
-        GoogleApiClient.OnConnectionFailedListener, LocationListener {
+public class WearableMainActivity extends WearableActivity implements
+        GoogleApiClient.ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener,
+        ActivityCompat.OnRequestPermissionsResultCallback,
+        LocationListener {
 
     private static final String TAG = "WearableActivity";
 
-    private static final long UPDATE_INTERVAL_MS = 5 * 1000;
-    private static final long FASTEST_INTERVAL_MS = 5 * 1000;
+    private static final long UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(5);
+    private static final long FASTEST_INTERVAL_MS = TimeUnit.SECONDS.toMillis(5);
 
-    public static final float MPH_IN_METERS_PER_SECOND = 2.23694f;
+    private static final float MPH_IN_METERS_PER_SECOND = 2.23694f;
 
-    public static final String PREFS_SPEED_LIMIT_KEY = "speed_limit";
-    public static final int SPEED_LIMIT_DEFAULT_MPH = 45;
+    private static final int SPEED_LIMIT_DEFAULT_MPH = 45;
+
     private static final long INDICATOR_DOT_FADE_AWAY_MS = 500L;
 
-    private GoogleApiClient mGoogleApiClient;
-    private TextView mSpeedLimitText;
-    private TextView mCurrentSpeedText;
-    private ImageView mSaveImageView;
-    private TextView mAcquiringGps;
-    private TextView mCurrentSpeedMphText;
+    // Request codes for changing speed limit and location permissions.
+    private static final int REQUEST_PICK_SPEED_LIMIT = 0;
 
-    private int mCurrentSpeedLimit;
-    private float mCurrentSpeed;
-    private View mDot;
-    private Handler mHandler = new Handler();
+    // Id to identify Location permission request.
+    private static final int REQUEST_GPS_PERMISSION = 1;
+
+    // Shared Preferences for saving speed limit and location permission between app launches.
+    private static final String PREFS_SPEED_LIMIT_KEY = "SpeedLimit";
+
     private Calendar mCalendar;
-    private boolean mSaveGpsLocation;
+
+    private TextView mSpeedLimitTextView;
+    private TextView mSpeedTextView;
+    private ImageView mGpsPermissionImageView;
+    private TextView mCurrentSpeedMphTextView;
+    private TextView mGpsIssueTextView;
+    private View mBlinkingGpsStatusDotView;
+
+    private String mGpsPermissionNeededMessage;
+    private String mAcquiringGpsMessage;
+
+    private int mSpeedLimit;
+    private float mSpeed;
+
+    private boolean mGpsPermissionApproved;
+
+    private boolean mWaitingForGpsSignal;
+
+    private GoogleApiClient mGoogleApiClient;
+
+    private Handler mHandler = new Handler();
 
     private enum SpeedState {
         BELOW(R.color.speed_below), CLOSE(R.color.speed_close), ABOVE(R.color.speed_above);
@@ -104,20 +126,53 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        Log.d(TAG, "onCreate()");
+
+
         setContentView(R.layout.main_activity);
+
+        /*
+         * Enables Always-on, so our app doesn't shut down when the watch goes into ambient mode.
+         * Best practice is to override onEnterAmbient(), onUpdateAmbient(), and onExitAmbient() to
+         * optimize the display for ambient mode. However, for brevity, we aren't doing that here
+         * to focus on learning location and permissions. For more information on best practices
+         * in ambient mode, check this page:
+         * https://developer.android.com/training/wearables/apps/always-on.html
+         */
+        setAmbientEnabled();
+
+        mCalendar = Calendar.getInstance();
+
+        // Enables app to handle 23+ (M+) style permissions.
+        mGpsPermissionApproved =
+            ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
+                    == PackageManager.PERMISSION_GRANTED;
+
+        mGpsPermissionNeededMessage = getString(R.string.permission_rationale);
+        mAcquiringGpsMessage = getString(R.string.acquiring_gps);
+
+
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        mSpeedLimit = sharedPreferences.getInt(PREFS_SPEED_LIMIT_KEY, SPEED_LIMIT_DEFAULT_MPH);
+
+        mSpeed = 0;
+
+        mWaitingForGpsSignal = true;
+
+
+        /*
+         * If this hardware doesn't support GPS, we warn the user. Note that when such device is
+         * connected to a phone with GPS capabilities, the framework automatically routes the
+         * location requests from the phone. However, if the phone becomes disconnected and the
+         * wearable doesn't support GPS, no location is recorded until the phone is reconnected.
+         */
         if (!hasGps()) {
-            // If this hardware doesn't support GPS, we prefer to exit.
-            // Note that when such device is connected to a phone with GPS capabilities, the
-            // framework automatically routes the location requests to the phone. For this
-            // application, this would not be desirable so we exit the app but for some other
-            // applications, that might be a valid scenario.
-            Log.w(TAG, "This hardware doesn't have GPS, so we exit");
+            Log.w(TAG, "This hardware doesn't have GPS, so we warn user.");
             new AlertDialog.Builder(this)
                     .setMessage(getString(R.string.gps_not_available))
                     .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int id) {
-                            finish();
                             dialog.cancel();
                         }
                     })
@@ -125,7 +180,6 @@
                         @Override
                         public void onDismiss(DialogInterface dialog) {
                             dialog.cancel();
-                            finish();
                         }
                     })
                     .setCancelable(false)
@@ -133,164 +187,216 @@
                     .show();
         }
 
+
         setupViews();
-        updateSpeedVisibility(false);
-        setSpeedLimit();
+
         mGoogleApiClient = new GoogleApiClient.Builder(this)
                 .addApi(LocationServices.API)
                 .addApi(Wearable.API)
                 .addConnectionCallbacks(this)
                 .addOnConnectionFailedListener(this)
                 .build();
-        mGoogleApiClient.connect();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected()) &&
+                (mGoogleApiClient.isConnecting())) {
+            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
+            mGoogleApiClient.disconnect();
+        }
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mGoogleApiClient != null) {
+            mGoogleApiClient.connect();
+        }
     }
 
     private void setupViews() {
-        mSpeedLimitText = (TextView) findViewById(R.id.max_speed_text);
-        mCurrentSpeedText = (TextView) findViewById(R.id.current_speed_text);
-        mSaveImageView = (ImageView) findViewById(R.id.saving);
-        ImageButton settingButton = (ImageButton) findViewById(R.id.settings);
-        mAcquiringGps = (TextView) findViewById(R.id.acquiring_gps);
-        mCurrentSpeedMphText = (TextView) findViewById(R.id.current_speed_mph);
-        mDot = findViewById(R.id.dot);
+        mSpeedLimitTextView = (TextView) findViewById(R.id.max_speed_text);
+        mSpeedTextView = (TextView) findViewById(R.id.current_speed_text);
+        mCurrentSpeedMphTextView = (TextView) findViewById(R.id.current_speed_mph);
 
-        settingButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Intent speedIntent = new Intent(WearableMainActivity.this,
-                        SpeedPickerActivity.class);
-                startActivity(speedIntent);
-            }
-        });
+        mGpsPermissionImageView = (ImageView) findViewById(R.id.gps_permission);
+        mGpsIssueTextView = (TextView) findViewById(R.id.gps_issue_text);
+        mBlinkingGpsStatusDotView = findViewById(R.id.dot);
 
-        mSaveImageView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Intent savingIntent = new Intent(WearableMainActivity.this,
-                        LocationSettingActivity.class);
-                startActivity(savingIntent);
-            }
-        });
+        updateActivityViewsBasedOnLocationPermissions();
     }
 
-    private void setSpeedLimit(int speedLimit) {
-        mSpeedLimitText.setText(getString(R.string.speed_limit, speedLimit));
+    public void onSpeedLimitClick(View view) {
+        Intent speedIntent = new Intent(WearableMainActivity.this,
+                SpeedPickerActivity.class);
+        startActivityForResult(speedIntent, REQUEST_PICK_SPEED_LIMIT);
     }
 
-    private void setSpeedLimit() {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
-        mCurrentSpeedLimit = pref.getInt(PREFS_SPEED_LIMIT_KEY, SPEED_LIMIT_DEFAULT_MPH);
-        setSpeedLimit(mCurrentSpeedLimit);
-    }
+    public void onGpsPermissionClick(View view) {
 
-    private void setCurrentSpeed(float speed) {
-        mCurrentSpeed = speed;
-        mCurrentSpeedText.setText(String.format(getString(R.string.speed_format), speed));
-        adjustColor();
+        if (!mGpsPermissionApproved) {
+
+            Log.i(TAG, "Location permission has NOT been granted. Requesting permission.");
+
+            // On 23+ (M+) devices, GPS permission not granted. Request permission.
+            ActivityCompat.requestPermissions(
+                    this,
+                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+                    REQUEST_GPS_PERMISSION);
+        }
     }
 
     /**
-     * Adjusts the color of the speed based on its value relative to the speed limit.
+     * Adjusts the visibility of views based on location permissions.
      */
-    private void adjustColor() {
-        SpeedState state = SpeedState.ABOVE;
-        if (mCurrentSpeed <= mCurrentSpeedLimit - 5) {
-            state = SpeedState.BELOW;
-        } else if (mCurrentSpeed <= mCurrentSpeedLimit) {
-            state = SpeedState.CLOSE;
-        }
+    private void updateActivityViewsBasedOnLocationPermissions() {
 
-        mCurrentSpeedText.setTextColor(getResources().getColor(state.getColor()));
+        /*
+         * If the user has approved location but we don't have a signal yet, we let the user know
+         * we are waiting on the GPS signal (this sometimes takes a little while). Otherwise, the
+         * user might think something is wrong.
+         */
+        if (mGpsPermissionApproved && mWaitingForGpsSignal) {
+
+            // We are getting a GPS signal w/ user permission.
+            mGpsIssueTextView.setText(mAcquiringGpsMessage);
+            mGpsIssueTextView.setVisibility(View.VISIBLE);
+            mGpsPermissionImageView.setImageResource(R.drawable.ic_gps_saving_grey600_96dp);
+
+            mSpeedTextView.setVisibility(View.GONE);
+            mSpeedLimitTextView.setVisibility(View.GONE);
+            mCurrentSpeedMphTextView.setVisibility(View.GONE);
+
+        } else if (mGpsPermissionApproved) {
+
+            mGpsIssueTextView.setVisibility(View.GONE);
+
+            mSpeedTextView.setVisibility(View.VISIBLE);
+            mSpeedLimitTextView.setVisibility(View.VISIBLE);
+            mCurrentSpeedMphTextView.setVisibility(View.VISIBLE);
+            mGpsPermissionImageView.setImageResource(R.drawable.ic_gps_saving_grey600_96dp);
+
+        } else {
+
+            // User needs to enable location for the app to work.
+            mGpsIssueTextView.setVisibility(View.VISIBLE);
+            mGpsIssueTextView.setText(mGpsPermissionNeededMessage);
+            mGpsPermissionImageView.setImageResource(R.drawable.ic_gps_not_saving_grey600_96dp);
+
+            mSpeedTextView.setVisibility(View.GONE);
+            mSpeedLimitTextView.setVisibility(View.GONE);
+            mCurrentSpeedMphTextView.setVisibility(View.GONE);
+        }
+    }
+
+    private void updateSpeedInViews() {
+
+        if (mGpsPermissionApproved) {
+
+            mSpeedLimitTextView.setText(getString(R.string.speed_limit, mSpeedLimit));
+            mSpeedTextView.setText(String.format(getString(R.string.speed_format), mSpeed));
+
+            // Adjusts the color of the speed based on its value relative to the speed limit.
+            SpeedState state = SpeedState.ABOVE;
+            if (mSpeed <= mSpeedLimit - 5) {
+                state = SpeedState.BELOW;
+            } else if (mSpeed <= mSpeedLimit) {
+                state = SpeedState.CLOSE;
+            }
+
+            mSpeedTextView.setTextColor(getResources().getColor(state.getColor()));
+
+            // Causes the (green) dot blinks when new GPS location data is acquired.
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mBlinkingGpsStatusDotView.setVisibility(View.VISIBLE);
+                }
+            });
+            mBlinkingGpsStatusDotView.setVisibility(View.VISIBLE);
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mBlinkingGpsStatusDotView.setVisibility(View.INVISIBLE);
+                }
+            }, INDICATOR_DOT_FADE_AWAY_MS);
+        }
     }
 
     @Override
     public void onConnected(Bundle bundle) {
-        LocationRequest locationRequest = LocationRequest.create()
-                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
-                .setInterval(UPDATE_INTERVAL_MS)
-                .setFastestInterval(FASTEST_INTERVAL_MS);
 
-        LocationServices.FusedLocationApi
-                .requestLocationUpdates(mGoogleApiClient, locationRequest, this)
-                .setResultCallback(new ResultCallback<Status>() {
+        Log.d(TAG, "onConnected()");
 
-                    @Override
-                    public void onResult(Status status) {
-                        if (status.getStatus().isSuccess()) {
-                            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                                Log.d(TAG, "Successfully requested location updates");
+        /*
+         * mGpsPermissionApproved covers 23+ (M+) style permissions. If that is already approved or
+         * the device is pre-23, the app uses mSaveGpsLocation to save the user's location
+         * preference.
+         */
+        if (mGpsPermissionApproved) {
+
+            LocationRequest locationRequest = LocationRequest.create()
+                    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
+                    .setInterval(UPDATE_INTERVAL_MS)
+                    .setFastestInterval(FASTEST_INTERVAL_MS);
+
+            LocationServices.FusedLocationApi
+                    .requestLocationUpdates(mGoogleApiClient, locationRequest, this)
+                    .setResultCallback(new ResultCallback<Status>() {
+
+                        @Override
+                        public void onResult(Status status) {
+                            if (status.getStatus().isSuccess()) {
+                                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                    Log.d(TAG, "Successfully requested location updates");
+                                }
+                            } else {
+                                Log.e(TAG,
+                                        "Failed in requesting location updates, "
+                                                + "status code: "
+                                                + status.getStatusCode() + ", message: " + status
+                                                .getStatusMessage());
                             }
-                        } else {
-                            Log.e(TAG,
-                                    "Failed in requesting location updates, "
-                                            + "status code: "
-                                            + status.getStatusCode() + ", message: " + status
-                                            .getStatusMessage());
                         }
-                    }
-                });
+                    });
+        }
     }
 
     @Override
     public void onConnectionSuspended(int i) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
-        }
+        Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
+
         LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
     }
 
     @Override
     public void onConnectionFailed(ConnectionResult connectionResult) {
-        Log.e(TAG, "onConnectionFailed(): connection to location client failed");
+        Log.e(TAG, "onConnectionFailed(): " + connectionResult.getErrorMessage());
     }
 
     @Override
     public void onLocationChanged(Location location) {
-        updateSpeedVisibility(true);
-        setCurrentSpeed(location.getSpeed() * MPH_IN_METERS_PER_SECOND);
-        flashDot();
+        Log.d(TAG, "onLocationChanged() : " + location);
+
+
+        if (mWaitingForGpsSignal) {
+            mWaitingForGpsSignal = false;
+            updateActivityViewsBasedOnLocationPermissions();
+        }
+
+        mSpeed = location.getSpeed() * MPH_IN_METERS_PER_SECOND;
+        updateSpeedInViews();
         addLocationEntry(location.getLatitude(), location.getLongitude());
     }
 
-    /**
-     * Causes the (green) dot blinks when new GPS location data is acquired.
-     */
-    private void flashDot() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mDot.setVisibility(View.VISIBLE);
-            }
-        });
-        mDot.setVisibility(View.VISIBLE);
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                mDot.setVisibility(View.INVISIBLE);
-            }
-        }, INDICATOR_DOT_FADE_AWAY_MS);
-    }
-
-    /**
-     * Adjusts the visibility of speed indicator based on the arrival of GPS data.
-     */
-    private void updateSpeedVisibility(boolean speedVisible) {
-        if (speedVisible) {
-            mAcquiringGps.setVisibility(View.GONE);
-            mCurrentSpeedText.setVisibility(View.VISIBLE);
-            mCurrentSpeedMphText.setVisibility(View.VISIBLE);
-        } else {
-            mAcquiringGps.setVisibility(View.VISIBLE);
-            mCurrentSpeedText.setVisibility(View.GONE);
-            mCurrentSpeedMphText.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Adds a data item to the data Layer storage
+    /*
+     * Adds a data item to the data Layer storage.
      */
     private void addLocationEntry(double latitude, double longitude) {
-        if (!mSaveGpsLocation || !mGoogleApiClient.isConnected()) {
+        if (!mGpsPermissionApproved || !mGoogleApiClient.isConnected()) {
             return;
         }
         mCalendar.setTimeInMillis(System.currentTimeMillis());
@@ -302,6 +408,7 @@
         putDataMapRequest.getDataMap()
                 .putLong(Constants.KEY_TIME, entry.calendar.getTimeInMillis());
         PutDataRequest request = putDataMapRequest.asPutDataRequest();
+        request.setUrgent();
         Wearable.DataApi.putDataItem(mGoogleApiClient, request)
                 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
                     @Override
@@ -315,29 +422,56 @@
                 });
     }
 
+    /**
+     * Handles user choices for both speed limit and location permissions (GPS tracking).
+     */
     @Override
-    protected void onStop() {
-        super.onStop();
-        if (mGoogleApiClient.isConnected()) {
-            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+
+        if (requestCode == REQUEST_PICK_SPEED_LIMIT) {
+            if (resultCode == RESULT_OK) {
+                // The user updated the speed limit.
+                int newSpeedLimit =
+                        data.getIntExtra(SpeedPickerActivity.EXTRA_NEW_SPEED_LIMIT, mSpeedLimit);
+
+                SharedPreferences sharedPreferences =
+                        PreferenceManager.getDefaultSharedPreferences(this);
+                SharedPreferences.Editor editor = sharedPreferences.edit();
+                editor.putInt(WearableMainActivity.PREFS_SPEED_LIMIT_KEY, newSpeedLimit);
+                editor.apply();
+
+                mSpeedLimit = newSpeedLimit;
+
+                updateSpeedInViews();
+            }
         }
-        mGoogleApiClient.disconnect();
     }
 
+    /**
+     * Callback received when a permissions request has been completed.
+     */
     @Override
-    protected void onResume() {
-        super.onResume();
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        mCalendar = Calendar.getInstance();
-        setSpeedLimit();
-        adjustColor();
-        updateRecordingIcon();
-    }
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
 
-    private void updateRecordingIcon() {
-        mSaveGpsLocation = LocationSettingActivity.getGpsRecordingStatusFromPreferences(this);
-        mSaveImageView.setImageResource(mSaveGpsLocation ? R.drawable.ic_gps_saving_grey600_96dp
-                : R.drawable.ic_gps_not_saving_grey600_96dp);
+        Log.d(TAG, "onRequestPermissionsResult(): " + permissions);
+
+
+        if (requestCode == REQUEST_GPS_PERMISSION) {
+            Log.i(TAG, "Received response for GPS permission request.");
+
+            if ((grantResults.length == 1)
+                    && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
+                Log.i(TAG, "GPS permission granted.");
+                mGpsPermissionApproved = true;
+            } else {
+                Log.i(TAG, "GPS permission NOT granted.");
+                mGpsPermissionApproved = false;
+            }
+
+            updateActivityViewsBasedOnLocationPermissions();
+
+        }
     }
 
     /**
@@ -346,4 +480,4 @@
     private boolean hasGps() {
         return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS);
     }
-}
+}
\ No newline at end of file
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/LocationSettingActivity.java b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/LocationSettingActivity.java
deleted file mode 100644
index 1f8be71..0000000
--- a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/LocationSettingActivity.java
+++ /dev/null
@@ -1,75 +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.wearable.speedtracker.ui;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.view.View;
-import android.widget.TextView;
-
-import com.example.android.wearable.speedtracker.R;
-
-/**
- * A simple activity that allows the user to start or stop recording of GPS location data.
- */
-public class LocationSettingActivity extends Activity {
-
-    private static final String PREFS_KEY_SAVE_GPS = "save-gps";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.saving_activity);
-        TextView textView = (TextView) findViewById(R.id.textView);
-        textView.setText(getGpsRecordingStatusFromPreferences(this) ? R.string.stop_saving_gps
-                : R.string.start_saving_gps);
-
-    }
-
-    public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.submitBtn:
-                saveGpsRecordingStatusToPreferences(LocationSettingActivity.this,
-                        !getGpsRecordingStatusFromPreferences(this));
-                break;
-            case R.id.cancelBtn:
-                break;
-        }
-        finish();
-    }
-
-    /**
-     * Get the persisted value for whether the app should record the GPS location data or not. If
-     * there is no prior value persisted, it returns {@code false}.
-     */
-    public static boolean getGpsRecordingStatusFromPreferences(Context context) {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
-        return pref.getBoolean(PREFS_KEY_SAVE_GPS, false);
-    }
-
-    /**
-     * Persists the user selection to whether save the GPS location data or not.
-     */
-    public static void saveGpsRecordingStatusToPreferences(Context context, boolean value) {
-        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
-        pref.edit().putBoolean(PREFS_KEY_SAVE_GPS, value).apply();
-
-    }
-}
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerListAdapter.java b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerListAdapter.java
index e3b284b..df25a6a 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerListAdapter.java
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/java/com/example/android/wearable/speedtracker/ui/SpeedPickerListAdapter.java
@@ -41,6 +41,9 @@
         mDataSet = dataset;
     }
 
+    /**
+     * Displays all possible speed limit choices.
+     */
     public static class ItemViewHolder extends WearableListView.ViewHolder {
 
         private TextView mTextView;
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 a1b9081..a2b678e 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
@@ -29,11 +29,13 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerInParent="true"
+        android:paddingLeft="16dp"
         android:fontFamily="sans-serif-light"
+        android:textAlignment="center"
         android:textSize="17sp"
         android:textStyle="italic"
-        android:id="@+id/acquiring_gps"
-        android:text="@string/acquiring_gps"/>
+        android:id="@+id/gps_issue_text"
+        android:text=""/>
 
     <TextView
         android:layout_width="wrap_content"
@@ -84,18 +86,20 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@drawable/ic_gps_not_saving_grey600_96dp"
-        android:id="@+id/saving"
+        android:id="@+id/gps_permission"
+        android:onClick="onGpsPermissionClick"
         android:layout_alignParentBottom="true"
         android:layout_alignParentLeft="true"
         android:layout_marginBottom="20dp"
-        android:layout_marginLeft="60dp" />
+        android:layout_marginLeft="50dp" />
 
     <ImageButton
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:id="@+id/settings"
+        android:id="@+id/speed_limit_setting"
+        android:onClick="onSpeedLimitClick"
         android:background="@drawable/settings"
         android:layout_alignParentRight="true"
-        android:layout_alignBottom="@+id/saving"
-        android:layout_marginRight="60dp"/>
+        android:layout_alignBottom="@+id/gps_permission"
+        android:layout_marginRight="50dp"/>
 </RelativeLayout>
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/saving_activity.xml b/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/saving_activity.xml
deleted file mode 100644
index c37d959..0000000
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/layout/saving_activity.xml
+++ /dev/null
@@ -1,63 +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.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent" android:layout_height="match_parent">
-    <View
-        android:id="@+id/center"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_centerInParent="true"/>
-    <TextView
-        android:id="@+id/textView"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerHorizontal="true"
-        android:layout_above="@id/center"
-        android:layout_marginBottom="18dp"
-        android:fontFamily="sans-serif-light"
-        android:textSize="18sp"
-        android:text="@string/start_saving_gps"/>
-    <android.support.wearable.view.CircledImageView
-        android:id="@+id/cancelBtn"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_below="@id/center"
-        android:layout_toLeftOf="@id/center"
-        android:layout_marginEnd="10dp"
-        android:src="@drawable/ic_cancel_80"
-        app:circle_color="@color/grey"
-        android:onClick="onClick"
-        app:circle_padding="@dimen/circle_padding"
-        app:circle_radius="@dimen/circle_radius"
-        app:circle_radius_pressed="@dimen/circle_radius_pressed" />
-    <android.support.wearable.view.CircledImageView
-        android:id="@+id/submitBtn"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_below="@id/center"
-        android:layout_toRightOf="@id/center"
-        android:layout_marginStart="10dp"
-        android:src="@drawable/ic_confirmation_80"
-        app:circle_color="@color/blue"
-        android:onClick="onClick"
-        app:circle_padding="@dimen/circle_padding"
-        app:circle_radius="@dimen/circle_radius"
-        app:circle_radius_pressed="@dimen/circle_radius_pressed" />
-</RelativeLayout>
\ No newline at end of file
diff --git a/wearable/wear/SpeedTracker/Wearable/src/main/res/values/strings.xml b/wearable/wear/SpeedTracker/Wearable/src/main/res/values/strings.xml
index dda3ecd..b0c3747 100644
--- a/wearable/wear/SpeedTracker/Wearable/src/main/res/values/strings.xml
+++ b/wearable/wear/SpeedTracker/Wearable/src/main/res/values/strings.xml
@@ -25,11 +25,16 @@
     <string name="speed_limit">Limit: %1$d mph</string>
     <string name="acquiring_gps">Acquiring GPS Fix ...</string>
     <string name="speed_for_list">%1$d mph</string>
-    <string name="start_saving_gps">Start Recording GPS?</string>
-    <string name="stop_saving_gps">Stop Recording GPS?</string>
+
+    <string name="enable_disable_gps_label">Enable Location Permission?</string>
+
     <string name="mph">mph</string>
     <string name="speed_limit_header">Speed Limit</string>
-    <string name="gps_not_available">GPS not available.</string>
+    <string name="gps_not_available">No GPS on device. Will use phone GPS when available.</string>
     <string name="ok">OK</string>
     <string name="speed_format">%.0f</string>
+
+    <string name="permission_rationale">App requires location permission to function, tap GPS icon.</string>
+
+
 </resources>
diff --git a/wearable/wear/SpeedTracker/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/SpeedTracker/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/SpeedTracker/template-params.xml b/wearable/wear/SpeedTracker/template-params.xml
index 1cf3161..982e1b0 100644
--- a/wearable/wear/SpeedTracker/template-params.xml
+++ b/wearable/wear/SpeedTracker/template-params.xml
@@ -23,15 +23,17 @@
     <package>com.example.android.wearable.speedtracker</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>23</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
     </wearable>
 
-    <dependency>com.google.android.gms:play-services-location:7.3.0</dependency>
-    <dependency_wearable>com.google.android.gms:play-services-location:7.3.0</dependency_wearable>
+    <dependency>com.android.support:design:23.1.1</dependency>
 
+    <dependency>com.google.android.gms:play-services-location</dependency>
+    <dependency_wearable>com.google.android.gms:play-services-location</dependency_wearable>
 
     <strings>
         <intro>
diff --git a/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml b/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml
index 1737c7d..04a69e0 100644
--- a/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/SynchronizedNotifications/Application/src/main/AndroidManifest.xml
@@ -23,7 +23,7 @@
     android:versionName="1.0">
 
     <uses-sdk android:minSdkVersion="18"
-        android:targetSdkVersion="21" />
+        android:targetSdkVersion="23" />
 
     <application android:allowBackup="true"
         android:label="@string/app_name"
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
index 8d5cca4..4621879 100644
--- 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
@@ -31,7 +31,6 @@
 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;
 
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
index 240af9b..837c7ad 100644
--- 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
@@ -19,17 +19,11 @@
 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;
@@ -61,7 +55,7 @@
 public class SynchronizedNotificationsFragment extends Fragment
         implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
 
-    private static final String TAG = "SynchronizedNotificationsFragment";
+    private static final String TAG = "SynchronizedFragment";
     private GoogleApiClient mGoogleApiClient;
 
     @Override
@@ -87,7 +81,8 @@
                         Constants.WATCH_ONLY_PATH);
                 return true;
             case R.id.btn_different:
-                buildMirroredNotifications(getString(R.string.phone_both), getString(R.string.watch_both), now());
+                buildMirroredNotifications(
+                        getString(R.string.phone_both), getString(R.string.watch_both), now());
                 return true;
         }
         return false;
@@ -110,8 +105,12 @@
         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);
+            PendingIntent pendingIntent =
+                    PendingIntent.getService(
+                            this.getActivity(),
+                            0,
+                            dismissIntent,
+                            PendingIntent.FLAG_UPDATE_CURRENT);
             builder.setDeleteIntent(pendingIntent);
         }
         NotificationManagerCompat.from(this.getActivity()).notify(notificationId, builder.build());
@@ -127,6 +126,7 @@
             putDataMapRequest.getDataMap().putString(Constants.KEY_CONTENT, content);
             putDataMapRequest.getDataMap().putString(Constants.KEY_TITLE, title);
             PutDataRequest request = putDataMapRequest.asPutDataRequest();
+            request.setUrgent();
             Wearable.DataApi.putDataItem(mGoogleApiClient, request)
                     .setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
                         @Override
diff --git a/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml b/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml
index f9b0d9c..5c4d259 100644
--- a/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/SynchronizedNotifications/Wearable/src/main/AndroidManifest.xml
@@ -20,7 +20,7 @@
     package="com.example.android.wearable.synchronizednotifications">
 
     <uses-sdk android:minSdkVersion="20"
-        android:targetSdkVersion="21" />
+        android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
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
index 8b46bf3..b5040d4 100644
--- 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
@@ -35,7 +35,6 @@
 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;
 
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
index 6ef2f1b..9d1eff4 100644
--- 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
@@ -18,6 +18,9 @@
 import android.app.Activity;
 import android.os.Bundle;
 
+/**
+ * Empty Activity.
+ */
 public class WearableActivity extends Activity {
 
     @Override
diff --git a/wearable/wear/SynchronizedNotifications/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/SynchronizedNotifications/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/SynchronizedNotifications/template-params.xml b/wearable/wear/SynchronizedNotifications/template-params.xml
index 799b226..97c2244 100644
--- a/wearable/wear/SynchronizedNotifications/template-params.xml
+++ b/wearable/wear/SynchronizedNotifications/template-params.xml
@@ -23,7 +23,8 @@
     <package>com.example.android.wearable.synchronizednotifications</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
diff --git a/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml b/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml
index 364fb5a..59634fc 100644
--- a/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/Timer/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.wearable.timer" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/Timer/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/Timer/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/Timer/template-params.xml b/wearable/wear/Timer/template-params.xml
index 1188402..5f9d767 100644
--- a/wearable/wear/Timer/template-params.xml
+++ b/wearable/wear/Timer/template-params.xml
@@ -22,8 +22,7 @@
     <group>Wearable</group>
     <package>com.example.android.wearable.timer</package>
 
-    <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
@@ -37,4 +36,9 @@
     <template src="base-build"/>
     <template src="Wear"/>
 
+    <metadata>
+        <status>DEPRECATED</status>
+        <categories>Wearable</categories>
+    </metadata>
+
 </sample>
diff --git a/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml b/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml
index 5433c94..d946cdb 100644
--- a/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/WatchFace/Application/src/main/AndroidManifest.xml
@@ -18,13 +18,18 @@
         package="com.example.android.wearable.watchface" >
 
     <uses-sdk android:minSdkVersion="18"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="23" />
 
     <!-- Permissions required by the wearable app -->
-    <uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <!-- Requests to calendar are only made on the wear side (CalendarWatchFaceService.java), so
+    no runtime permissions are needed on the phone side. -->
     <uses-permission android:name="android.permission.READ_CALENDAR" />
 
+    <!-- Location permission used by FitDistanceWatchFaceService -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
     <!-- All intent-filters for config actions must include the categories
         com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION and
         android.intent.category.DEFAULT. -->
@@ -55,6 +60,18 @@
             </intent-filter>
         </activity>
 
+        <!-- This activity is needed to allow the user to authorize Google Fit for the Fit Distance
+             WatchFace (required to view distance). -->
+        <activity
+            android:name=".FitDistanceWatchFaceConfigActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="com.example.android.wearable.watchface.CONFIG_FIT_DISTANCE" />
+                <category android:name="com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity
                 android:name=".OpenGLWatchFaceConfigActivity"
                 android:label="@string/app_name">
diff --git a/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/FitDistanceWatchFaceConfigActivity.java b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/FitDistanceWatchFaceConfigActivity.java
new file mode 100644
index 0000000..1d8e4c9
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/java/com/example/android/wearable/watchface/FitDistanceWatchFaceConfigActivity.java
@@ -0,0 +1,255 @@
+package com.example.android.wearable.watchface;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GooglePlayServicesUtil;
+import com.google.android.gms.common.Scopes;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Scope;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.fitness.Fitness;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Switch;
+import android.widget.Toast;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Allows users of the Fit WatchFace to tie their Google Fit account to the WatchFace.
+ */
+public class FitDistanceWatchFaceConfigActivity extends Activity implements
+        GoogleApiClient.ConnectionCallbacks,
+        GoogleApiClient.OnConnectionFailedListener {
+
+    private static final String TAG = "FitDistanceConfig";
+
+    // Request code for launching the Intent to resolve authorization.
+    private static final int REQUEST_OAUTH = 1;
+
+    // Shared Preference used to record if the user has enabled Google Fit previously.
+    private static final String PREFS_FIT_ENABLED_BY_USER =
+            "com.example.android.wearable.watchface.preferences.FIT_ENABLED_BY_USER";
+
+    /* Tracks whether an authorization activity is stacking over the current activity, i.e., when
+     *  a known auth error is being resolved, such as showing the account chooser or presenting a
+     *  consent dialog. This avoids common duplications as might happen on screen rotations, etc.
+     */
+    private static final String EXTRA_AUTH_STATE_PENDING =
+            "com.example.android.wearable.watchface.extra.AUTH_STATE_PENDING";
+
+    private static final long FIT_DISABLE_TIMEOUT_SECS = TimeUnit.SECONDS.toMillis(5);;
+
+    private boolean mResolvingAuthorization;
+
+    private boolean mFitEnabled;
+
+    private GoogleApiClient mGoogleApiClient;
+
+    private Switch mFitAuthSwitch;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_fit_watch_face_config);
+
+        mFitAuthSwitch = (Switch) findViewById(R.id.fit_auth_switch);
+
+        if (savedInstanceState != null) {
+            mResolvingAuthorization =
+                    savedInstanceState.getBoolean(EXTRA_AUTH_STATE_PENDING, false);
+        } else {
+            mResolvingAuthorization = false;
+        }
+
+        // Checks if user previously enabled/approved Google Fit.
+        SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
+        mFitEnabled =
+                sharedPreferences.getBoolean(PREFS_FIT_ENABLED_BY_USER, false);
+
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Fitness.HISTORY_API)
+                .addApi(Fitness.RECORDING_API)
+                .addApi(Fitness.CONFIG_API)
+                .addScope(new Scope(Scopes.FITNESS_LOCATION_READ_WRITE))
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        if ((mFitEnabled) && (mGoogleApiClient != null)) {
+
+            mFitAuthSwitch.setChecked(true);
+            mFitAuthSwitch.setEnabled(true);
+
+            mGoogleApiClient.connect();
+
+        } else {
+
+            mFitAuthSwitch.setChecked(false);
+            mFitAuthSwitch.setEnabled(true);
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) {
+            mGoogleApiClient.disconnect();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle bundle) {
+        super.onSaveInstanceState(bundle);
+        bundle.putBoolean(EXTRA_AUTH_STATE_PENDING, mResolvingAuthorization);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            mResolvingAuthorization =
+                    savedInstanceState.getBoolean(EXTRA_AUTH_STATE_PENDING, false);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(TAG, "onActivityResult()");
+
+        if (requestCode == REQUEST_OAUTH) {
+            mResolvingAuthorization = false;
+
+            if (resultCode == RESULT_OK) {
+                setUserFitPreferences(true);
+
+                if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) {
+                    mGoogleApiClient.connect();
+                }
+            } else {
+                // User cancelled authorization, reset the switch.
+                setUserFitPreferences(false);
+            }
+        }
+    }
+
+    @Override
+    public void onConnected(Bundle connectionHint) {
+        Log.d(TAG, "onConnected: " + connectionHint);
+    }
+
+    @Override
+    public void onConnectionSuspended(int cause) {
+
+        if (cause == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
+            Log.i(TAG, "Connection lost.  Cause: Network Lost.");
+        } else if (cause == GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
+            Log.i(TAG, "Connection lost.  Reason: Service Disconnected");
+        } else {
+            Log.i(TAG, "onConnectionSuspended: " + cause);
+        }
+
+        mFitAuthSwitch.setChecked(false);
+        mFitAuthSwitch.setEnabled(true);
+    }
+
+    @Override
+    public void onConnectionFailed(ConnectionResult result) {
+        Log.d(TAG, "Connection to Google Fit failed. Cause: " + result.toString());
+
+        if (!result.hasResolution()) {
+            // User cancelled authorization, reset the switch.
+            mFitAuthSwitch.setChecked(false);
+            mFitAuthSwitch.setEnabled(true);
+            // Show the localized error dialog
+            GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
+            return;
+        }
+
+        // Resolve failure if not already trying/authorizing.
+        if (!mResolvingAuthorization) {
+            try {
+                Log.i(TAG, "Attempting to resolve failed GoogleApiClient connection");
+                mResolvingAuthorization = true;
+                result.startResolutionForResult(this, REQUEST_OAUTH);
+            } catch (IntentSender.SendIntentException e) {
+                Log.e(TAG, "Exception while starting resolution activity", e);
+            }
+        }
+    }
+
+    public void onSwitchClicked(View view) {
+
+        boolean userWantsToEnableFit = mFitAuthSwitch.isChecked();
+
+        if (userWantsToEnableFit) {
+
+            Log.d(TAG, "User wants to enable Fit.");
+            if ((mGoogleApiClient != null) && (!mGoogleApiClient.isConnected())) {
+                mGoogleApiClient.connect();
+            }
+
+        } else {
+            Log.d(TAG, "User wants to disable Fit.");
+
+            // Disable switch until disconnect request is finished.
+            mFitAuthSwitch.setEnabled(false);
+
+            PendingResult<Status> pendingResult = Fitness.ConfigApi.disableFit(mGoogleApiClient);
+
+            pendingResult.setResultCallback(new ResultCallback<Status>() {
+                @Override
+                public void onResult(Status status) {
+
+                    if (status.isSuccess()) {
+                        Toast.makeText(
+                                FitDistanceWatchFaceConfigActivity.this,
+                                "Disconnected from Google Fit.",
+                                Toast.LENGTH_LONG).show();
+
+                        setUserFitPreferences(false);
+
+                        mGoogleApiClient.disconnect();
+
+
+                    } else {
+                        Toast.makeText(
+                                FitDistanceWatchFaceConfigActivity.this,
+                                "Unable to disconnect from Google Fit. See logcat for details.",
+                                Toast.LENGTH_LONG).show();
+
+                        // Re-set the switch since auth failed.
+                        setUserFitPreferences(true);
+                    }
+                }
+            }, FIT_DISABLE_TIMEOUT_SECS, TimeUnit.SECONDS);
+        }
+    }
+
+    private void setUserFitPreferences(boolean userFitPreferences) {
+
+        mFitEnabled = userFitPreferences;
+        SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.putBoolean(PREFS_FIT_ENABLED_BY_USER, userFitPreferences);
+        editor.commit();
+
+        mFitAuthSwitch.setChecked(userFitPreferences);
+        mFitAuthSwitch.setEnabled(true);
+    }
+}
diff --git a/wearable/wear/WatchFace/Application/src/main/res/layout/activity_fit_watch_face_config.xml b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_fit_watch_face_config.xml
new file mode 100644
index 0000000..73d1489
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/layout/activity_fit_watch_face_config.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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    tools:context="com.example.android.wearable.watchface.FitDistanceWatchFaceConfigActivity">
+
+    <Switch
+        android:id="@+id/fit_auth_switch"
+        android:text="@string/fit_config_switch_text"
+        android:enabled="false"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:onClick="onSwitchClicked"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/wearable/wear/WatchFace/Application/src/main/res/values-w820dp/dimens.xml b/wearable/wear/WatchFace/Application/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/wearable/wear/WatchFace/Application/src/main/res/values/dimens.xml b/wearable/wear/WatchFace/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..56dca87
--- /dev/null
+++ b/wearable/wear/WatchFace/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<!-- Example customization of dimensions originally defined in res/values/dimens.xml
+     (such as screen margins) for screens with more than 820dp of available width. This
+     would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+     <dimen name="activity_horizontal_margin">64dp</dimen>
+     <dimen name="activity_vertical_margin">10dp</dimen>
+</resources>
diff --git a/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml b/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml
index 6c6834f..275dcd3 100644
--- a/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml
+++ b/wearable/wear/WatchFace/Application/src/main/res/values/strings.xml
@@ -22,6 +22,8 @@
     <string name="digital_config_minutes">Minutes</string>
     <string name="digital_config_seconds">Seconds</string>
 
+    <string name="fit_config_switch_text">Google Fit</string>
+
     <string name="title_no_device_connected">No wearable device is currently connected.</string>
     <string name="ok_no_device_connected">OK</string>
 
diff --git a/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml b/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
index c96d730..ce042e4 100644
--- a/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/WatchFace/Wearable/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ Copyright (C) 2014 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,118 +14,128 @@
      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" >
+    package="com.example.android.wearable.watchface" >
 
-    <uses-sdk android:minSdkVersion="21"
-        android:targetSdkVersion="21" />
+    <uses-sdk
+        android:minSdkVersion="21"
+        android:targetSdkVersion="23" />
 
     <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" />
 
+    <!-- Location permission used by FitDistanceWatchFaceService -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
     <application
-            android:allowBackup="true"
-            android:icon="@drawable/ic_launcher"
-            android:label="@string/app_name" >
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+
+        <meta-data
+            android:name="com.google.android.gms.version"
+            android:value="@integer/google_play_services_version" />
+
+        <uses-library android:name="com.google.android.wearable" android:required="false" />
 
         <service
-                android:name=".AnalogWatchFaceService"
-                android:label="@string/analog_name"
-                android:permission="android.permission.BIND_WALLPAPER" >
+            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" />
+                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" />
+                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" />
+                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=".OpenGLWatchFaceService"
-                android:label="@string/opengl_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_opengl" />
-            <meta-data
-                    android:name="com.google.android.wearable.watchface.preview_circular"
-                    android:resource="@drawable/preview_opengl_circular" />
-            <meta-data
-                    android:name="com.google.android.wearable.watchface.companionConfigurationAction"
-                    android:value="com.example.android.wearable.watchface.CONFIG_OPENGL" />
+                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=".CardBoundsWatchFaceService"
-                android:label="@string/card_bounds_name"
-                android:permission="android.permission.BIND_WALLPAPER" >
+            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" />
+                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" />
+                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_card_bounds_circular" />
-            <meta-data
-                    android:name="com.google.android.wearable.watchface.companionConfigurationAction"
-                    android:value="com.example.android.wearable.watchface.CONFIG_CARD_BOUNDS" />
+                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=".InteractiveWatchFaceService"
-                android:label="@string/interactive_name"
-                android:permission="android.permission.BIND_WALLPAPER" >
+            android:name=".OpenGLWatchFaceService"
+            android:label="@string/opengl_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_opengl" />
+            <meta-data
+                android:name="com.google.android.wearable.watchface.preview_circular"
+                android:resource="@drawable/preview_opengl_circular" />
+            <meta-data
+                android:name="com.google.android.wearable.watchface.companionConfigurationAction"
+                android:value="com.example.android.wearable.watchface.CONFIG_OPENGL" />
+
+            <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=".InteractiveWatchFaceService"
+            android:label="@string/interactive_name"
+            android:permission="android.permission.BIND_WALLPAPER" >
             <meta-data
                 android:name="android.service.wallpaper"
                 android:resource="@xml/watch_face" />
@@ -134,81 +145,130 @@
             <meta-data
                 android:name="com.google.android.wearable.watchface.preview_circular"
                 android:resource="@drawable/preview_interactive_circular" />
+
             <intent-filter>
                 <action android:name="android.service.wallpaper.WallpaperService" />
-                <category
-                    android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+
+                <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" >
+            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" />
+                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" />
+                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" />
+                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" />
+                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" />
+                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
+        <!--
+             All intent-filters for config actions must include the categories
             com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION
-            and android.intent.category.DEFAULT. -->
+            and android.intent.category.DEFAULT.
+        -->
 
         <activity
-                android:name=".DigitalWatchFaceWearableConfigActivity"
-                android:label="@string/digital_config_name">
+            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" >
+            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" />
+                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" />
+                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" />
+                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">
+        <service android:name=".DigitalWatchFaceConfigListenerService" >
             <intent-filter>
                 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
             </intent-filter>
         </service>
+        <service
+            android:name=".FitDistanceWatchFaceService"
+            android:label="@string/fit_distance_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_distance" />
+            <meta-data
+                android:name="com.google.android.wearable.watchface.preview_circular"
+                android:resource="@drawable/preview_distance_circular" />
+            <meta-data
+                android:name="com.google.android.wearable.watchface.companionConfigurationAction"
+                android:value="com.example.android.wearable.watchface.CONFIG_FIT_DISTANCE" />
 
-        <meta-data
-                android:name="com.google.android.gms.version"
-                android:value="@integer/google_play_services_version" />
+            <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=".FitStepsWatchFaceService"
+            android:label="@string/fit_steps_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_fit" />
+            <meta-data
+                android:name="com.google.android.wearable.watchface.preview_circular"
+                android:resource="@drawable/preview_fit_circular" />
+
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+
+                <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+            </intent-filter>
+        </service>
+
+        <activity
+            android:name=".CalendarWatchFacePermissionActivity"
+            android:label="@string/title_activity_calendar_watch_face_permission" >
+        </activity>
     </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
index fb86ac7..9fd73a5 100644
--- 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
@@ -87,7 +87,7 @@
 
         /* Colors for all hands (hour, minute, seconds, ticks) based on photo loaded. */
         private int mWatchHandColor;
-        private int mWatchHandHightlightColor;
+        private int mWatchHandHighlightColor;
         private int mWatchHandShadowColor;
 
         private Paint mHourPaint;
@@ -151,7 +151,7 @@
 
             /* Set defaults for colors */
             mWatchHandColor = Color.WHITE;
-            mWatchHandHightlightColor = Color.RED;
+            mWatchHandHighlightColor = Color.RED;
             mWatchHandShadowColor = Color.BLACK;
 
             mHourPaint = new Paint();
@@ -169,7 +169,7 @@
             mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
 
             mSecondPaint = new Paint();
-            mSecondPaint.setColor(mWatchHandHightlightColor);
+            mSecondPaint.setColor(mWatchHandHighlightColor);
             mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
             mSecondPaint.setAntiAlias(true);
             mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
@@ -193,7 +193,7 @@
                                     Log.d(TAG, "Palette: " + palette);
                                 }
 
-                                mWatchHandHightlightColor = palette.getVibrantColor(Color.RED);
+                                mWatchHandHighlightColor = palette.getVibrantColor(Color.RED);
                                 mWatchHandColor = palette.getLightVibrantColor(Color.WHITE);
                                 mWatchHandShadowColor = palette.getDarkMutedColor(Color.BLACK);
                                 updateWatchHandStyle();
@@ -261,7 +261,7 @@
             } else {
                 mHourPaint.setColor(mWatchHandColor);
                 mMinutePaint.setColor(mWatchHandColor);
-                mSecondPaint.setColor(mWatchHandHightlightColor);
+                mSecondPaint.setColor(mWatchHandHighlightColor);
                 mTickAndCirclePaint.setColor(mWatchHandColor);
 
                 mHourPaint.setAntiAlias(true);
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CalendarWatchFacePermissionActivity.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CalendarWatchFacePermissionActivity.java
new file mode 100644
index 0000000..7effd33
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/CalendarWatchFacePermissionActivity.java
@@ -0,0 +1,56 @@
+package com.example.android.wearable.watchface;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.wearable.activity.WearableActivity;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * Simple Activity for displaying Calendar Permission Rationale to user.
+ */
+public class CalendarWatchFacePermissionActivity extends WearableActivity {
+
+    private static final String TAG = "PermissionActivity";
+
+    /* Id to identify permission request for calendar. */
+    private static final int PERMISSION_REQUEST_READ_CALENDAR = 1;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_calendar_watch_face_permission);
+        setAmbientEnabled();
+    }
+
+    public void onClickEnablePermission(View view) {
+        Log.d(TAG, "onClickEnablePermission()");
+
+        // On 23+ (M+) devices, GPS permission not granted. Request permission.
+        ActivityCompat.requestPermissions(
+                this,
+                new String[]{Manifest.permission.READ_CALENDAR},
+                PERMISSION_REQUEST_READ_CALENDAR);
+
+    }
+
+    /*
+     * Callback received when a permissions request has been completed.
+     */
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+
+        Log.d(TAG, "onRequestPermissionsResult()");
+
+        if (requestCode == PERMISSION_REQUEST_READ_CALENDAR) {
+            if ((grantResults.length == 1)
+                    && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
+                finish();
+            }
+        }
+    }
+}
\ No newline at end of file
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
index a8ab955..98a251c 100644
--- 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
@@ -16,11 +16,13 @@
 
 package com.example.android.wearable.watchface;
 
+import android.Manifest;
 import android.content.BroadcastReceiver;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -30,8 +32,10 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
+import android.support.v4.app.ActivityCompat;
 import android.support.wearable.provider.WearableCalendarContract;
 import android.support.wearable.watchface.CanvasWatchFaceService;
+import android.support.wearable.watchface.WatchFaceService;
 import android.support.wearable.watchface.WatchFaceStyle;
 import android.text.DynamicLayout;
 import android.text.Editable;
@@ -74,31 +78,37 @@
         final TextPaint mTextPaint = new TextPaint();
 
         int mNumMeetings;
+        private boolean mCalendarPermissionApproved;
+        private String mCalendarNotApprovedMessage;
 
         private AsyncTask<Void, Void, Integer> mLoadMeetingsTask;
 
+        private boolean mIsReceiverRegistered;
+
         /** 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();
+
+                        // Loads meetings.
+                        if (mCalendarPermissionApproved) {
+                            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);
                 }
             }
@@ -106,29 +116,59 @@
 
         @Override
         public void onCreate(SurfaceHolder holder) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "onCreate");
-            }
             super.onCreate(holder);
+            Log.d(TAG, "onCreate");
+
+            mCalendarNotApprovedMessage =
+                    getResources().getString(R.string.calendar_permission_not_approved);
+
+            /* Accepts tap events to allow permission changes by user. */
             setWatchFaceStyle(new WatchFaceStyle.Builder(CalendarWatchFaceService.this)
                     .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
                     .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
                     .setShowSystemUiTime(false)
+                    .setAcceptsTapEvents(true)
                     .build());
 
             mTextPaint.setColor(FOREGROUND_COLOR);
             mTextPaint.setTextSize(TEXT_SIZE);
 
-            mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
+            // Enables app to handle 23+ (M+) style permissions.
+            mCalendarPermissionApproved =
+                    ActivityCompat.checkSelfPermission(
+                            getApplicationContext(),
+                            Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED;
+
+            if (mCalendarPermissionApproved) {
+                mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS);
+            }
         }
 
         @Override
         public void onDestroy() {
             mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS);
-            cancelLoadMeetingTask();
             super.onDestroy();
         }
 
+        /*
+         * Captures tap event (and tap type) and increments correct tap type total.
+         */
+        @Override
+        public void onTapCommand(int tapType, int x, int y, long eventTime) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Tap Command: " + tapType);
+            }
+
+            // Ignore lint error (fixed in wearable support library 1.4)
+            if (tapType == WatchFaceService.TAP_TYPE_TAP && !mCalendarPermissionApproved) {
+                Intent permissionIntent = new Intent(
+                        getApplicationContext(),
+                        CalendarWatchFacePermissionActivity.class);
+                permissionIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(permissionIntent);
+            }
+        }
+
         @Override
         public void onDraw(Canvas canvas, Rect bounds) {
             // Create or update mLayout if necessary.
@@ -141,8 +181,13 @@
 
             // Update the contents of mEditable.
             mEditable.clear();
-            mEditable.append(Html.fromHtml(getResources().getQuantityString(
-                    R.plurals.calendar_meetings, mNumMeetings, mNumMeetings)));
+
+            if (mCalendarPermissionApproved) {
+                mEditable.append(Html.fromHtml(getResources().getQuantityString(
+                        R.plurals.calendar_meetings, mNumMeetings, mNumMeetings)));
+            } else {
+                mEditable.append(Html.fromHtml(mCalendarNotApprovedMessage));
+            }
 
             // Draw the text on a solid background.
             canvas.drawColor(BACKGROUND_COLOR);
@@ -151,15 +196,24 @@
 
         @Override
         public void onVisibilityChanged(boolean visible) {
+            Log.d(TAG, "onVisibilityChanged()");
             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);
+                // Enables app to handle 23+ (M+) style permissions.
+                mCalendarPermissionApproved = ActivityCompat.checkSelfPermission(
+                        getApplicationContext(),
+                        Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED;
+
+                if (mCalendarPermissionApproved) {
+                    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);
@@ -204,9 +258,9 @@
                 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);
-                }
+
+                Log.d(TAG, "Num meetings: " + numMeetings);
+
                 return numMeetings;
             }
 
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
index 1c4af70..e13440d 100644
--- 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
@@ -165,6 +165,7 @@
      */
     public static void putConfigDataItem(GoogleApiClient googleApiClient, DataMap newConfig) {
         PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_WITH_FEATURE);
+        putDataMapRequest.setUrgent();
         DataMap configToPut = putDataMapRequest.getDataMap();
         configToPut.putAll(newConfig);
         Wearable.DataApi.putDataItem(googleApiClient, putDataMapRequest.asPutDataRequest())
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/FitDistanceWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/FitDistanceWatchFaceService.java
new file mode 100644
index 0000000..6dee04e
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/FitDistanceWatchFaceService.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.Color;
+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.WatchFaceStyle;
+import android.text.format.DateFormat;
+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.Scopes;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Scope;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.fitness.Fitness;
+import com.google.android.gms.fitness.FitnessStatusCodes;
+import com.google.android.gms.fitness.data.DataPoint;
+import com.google.android.gms.fitness.data.DataType;
+import com.google.android.gms.fitness.data.Field;
+import com.google.android.gms.fitness.result.DailyTotalResult;
+
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Displays the user's daily distance total via Google Fit. Distance is polled initially when the
+ * Google API Client successfully connects and once a minute after that via the onTimeTick callback.
+ * If you want more frequent updates, you will want to add your own  Handler.
+ *
+ * Authentication IS a requirement to request distance from Google Fit on Wear. Otherwise, distance
+ * will always come back as zero (or stay at whatever the distance was prior to you
+ * de-authorizing watchface). To authenticate and communicate with Google Fit, you must create a
+ * project in the Google Developers Console, activate the Fitness API, create an OAuth 2.0
+ * client ID, and register the public certificate from your app's signed APK. More details can be
+ * found here: https://developers.google.com/fit/android/get-started#step_3_enable_the_fitness_api
+ *
+ * In ambient mode, the seconds are replaced with an AM/PM indicator.
+ *
+ * On devices with low-bit ambient mode, the text is drawn without anti-aliasing. On devices which
+ * require burn-in protection, the hours are drawn in normal rather than bold.
+ *
+ */
+public class FitDistanceWatchFaceService extends CanvasWatchFaceService {
+
+    private static final String TAG = "DistanceWatchFace";
+
+    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 active mode (non-ambient).
+     */
+    private static final long ACTIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1);
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine implements
+            GoogleApiClient.ConnectionCallbacks,
+            GoogleApiClient.OnConnectionFailedListener,
+            ResultCallback<DailyTotalResult> {
+
+        private static final int BACKGROUND_COLOR = Color.BLACK;
+        private static final int TEXT_HOURS_MINS_COLOR = Color.WHITE;
+        private static final int TEXT_SECONDS_COLOR = Color.GRAY;
+        private static final int TEXT_AM_PM_COLOR = Color.GRAY;
+        private static final int TEXT_COLON_COLOR = Color.GRAY;
+        private static final int TEXT_DISTANCE_COUNT_COLOR = Color.GRAY;
+
+        private static final String COLON_STRING = ":";
+
+        private static final int MSG_UPDATE_TIME = 0;
+
+        /* Handler to update the time periodically in interactive mode. */
+        private final Handler mUpdateTimeHandler = new Handler() {
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_UPDATE_TIME:
+                        Log.v(TAG, "updating time");
+                        invalidate();
+                        if (shouldUpdateTimeHandlerBeRunning()) {
+                            long timeMs = System.currentTimeMillis();
+                            long delayMs =
+                                    ACTIVE_INTERVAL_MS - (timeMs % ACTIVE_INTERVAL_MS);
+                            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+                        }
+                        break;
+                }
+            }
+        };
+
+        /**
+         * Handles time zone and locale changes.
+         */
+        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mCalendar.setTimeZone(TimeZone.getDefault());
+                invalidate();
+            }
+        };
+
+        /**
+         * Unregistering an unregistered receiver throws an exception. Keep track of the
+         * registration state to prevent that.
+         */
+        private boolean mRegisteredReceiver = false;
+
+        private Paint mHourPaint;
+        private Paint mMinutePaint;
+        private Paint mSecondPaint;
+        private Paint mAmPmPaint;
+        private Paint mColonPaint;
+        private Paint mDistanceCountPaint;
+
+        private float mColonWidth;
+
+        private Calendar mCalendar;
+
+        private float mXOffset;
+        private float mXDistanceOffset;
+        private float mYOffset;
+        private float mLineHeight;
+
+        private String mAmString;
+        private String mPmString;
+
+
+        /**
+         * Whether the display supports fewer bits for each color in ambient mode. When true, we
+         * disable anti-aliasing in ambient mode.
+         */
+        private boolean mLowBitAmbient;
+
+        /*
+         * Google API Client used to make Google Fit requests for step data.
+         */
+        private GoogleApiClient mGoogleApiClient;
+
+        private boolean mDistanceRequested;
+
+        private float mDistanceTotal = 0;
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            Log.d(TAG, "onCreate");
+
+            super.onCreate(holder);
+
+            mDistanceRequested = false;
+            mGoogleApiClient = new GoogleApiClient.Builder(FitDistanceWatchFaceService.this)
+                    .addConnectionCallbacks(this)
+                    .addOnConnectionFailedListener(this)
+                    .addApi(Fitness.HISTORY_API)
+                    .addApi(Fitness.RECORDING_API)
+                    .addScope(new Scope(Scopes.FITNESS_LOCATION_READ))
+                    // When user has multiple accounts, useDefaultAccount() allows Google Fit to
+                    // associated with the main account for steps. It also replaces the need for
+                    // a scope request.
+                    .useDefaultAccount()
+                    .build();
+
+            setWatchFaceStyle(new WatchFaceStyle.Builder(FitDistanceWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(false)
+                    .build());
+
+            Resources resources = getResources();
+
+            mYOffset = resources.getDimension(R.dimen.fit_y_offset);
+            mLineHeight = resources.getDimension(R.dimen.fit_line_height);
+            mAmString = resources.getString(R.string.fit_am);
+            mPmString = resources.getString(R.string.fit_pm);
+
+            mHourPaint = createTextPaint(TEXT_HOURS_MINS_COLOR, BOLD_TYPEFACE);
+            mMinutePaint = createTextPaint(TEXT_HOURS_MINS_COLOR);
+            mSecondPaint = createTextPaint(TEXT_SECONDS_COLOR);
+            mAmPmPaint = createTextPaint(TEXT_AM_PM_COLOR);
+            mColonPaint = createTextPaint(TEXT_COLON_COLOR);
+            mDistanceCountPaint = createTextPaint(TEXT_DISTANCE_COUNT_COLOR);
+
+            mCalendar = Calendar.getInstance();
+
+        }
+
+        @Override
+        public void onDestroy() {
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            super.onDestroy();
+        }
+
+        private Paint createTextPaint(int color) {
+            return createTextPaint(color, NORMAL_TYPEFACE);
+        }
+
+        private Paint createTextPaint(int color, Typeface typeface) {
+            Paint paint = new Paint();
+            paint.setColor(color);
+            paint.setTypeface(typeface);
+            paint.setAntiAlias(true);
+            return paint;
+        }
+
+        @Override
+        public void onVisibilityChanged(boolean visible) {
+            Log.d(TAG, "onVisibilityChanged: " + visible);
+
+            super.onVisibilityChanged(visible);
+
+            if (visible) {
+                mGoogleApiClient.connect();
+
+                registerReceiver();
+
+                // Update time zone and date formats, in case they changed while we weren't visible.
+                mCalendar.setTimeZone(TimeZone.getDefault());
+            } else {
+                unregisterReceiver();
+
+                if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+                    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 (mRegisteredReceiver) {
+                return;
+            }
+            mRegisteredReceiver = true;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+            FitDistanceWatchFaceService.this.registerReceiver(mReceiver, filter);
+        }
+
+        private void unregisterReceiver() {
+            if (!mRegisteredReceiver) {
+                return;
+            }
+            mRegisteredReceiver = false;
+            FitDistanceWatchFaceService.this.unregisterReceiver(mReceiver);
+        }
+
+        @Override
+        public void onApplyWindowInsets(WindowInsets insets) {
+            Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square"));
+
+            super.onApplyWindowInsets(insets);
+
+            // Load resources that have alternate values for round watches.
+            Resources resources = FitDistanceWatchFaceService.this.getResources();
+            boolean isRound = insets.isRound();
+            mXOffset = resources.getDimension(isRound
+                    ? R.dimen.fit_x_offset_round : R.dimen.fit_x_offset);
+            mXDistanceOffset =
+                    resources.getDimension(
+                            isRound ?
+                            R.dimen.fit_steps_or_distance_x_offset_round :
+                            R.dimen.fit_steps_or_distance_x_offset);
+            float textSize = resources.getDimension(isRound
+                    ? R.dimen.fit_text_size_round : R.dimen.fit_text_size);
+            float amPmSize = resources.getDimension(isRound
+                    ? R.dimen.fit_am_pm_size_round : R.dimen.fit_am_pm_size);
+
+            mHourPaint.setTextSize(textSize);
+            mMinutePaint.setTextSize(textSize);
+            mSecondPaint.setTextSize(textSize);
+            mAmPmPaint.setTextSize(amPmSize);
+            mColonPaint.setTextSize(textSize);
+            mDistanceCountPaint.setTextSize(
+                    resources.getDimension(R.dimen.fit_steps_or_distance_text_size));
+
+            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);
+
+            Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection
+                    + ", low-bit ambient = " + mLowBitAmbient);
+
+        }
+
+        @Override
+        public void onTimeTick() {
+            super.onTimeTick();
+            Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode());
+            getTotalDistance();
+            invalidate();
+        }
+
+        @Override
+        public void onAmbientModeChanged(boolean inAmbientMode) {
+            super.onAmbientModeChanged(inAmbientMode);
+            Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
+
+            if (mLowBitAmbient) {
+                boolean antiAlias = !inAmbientMode;;
+                mHourPaint.setAntiAlias(antiAlias);
+                mMinutePaint.setAntiAlias(antiAlias);
+                mSecondPaint.setAntiAlias(antiAlias);
+                mAmPmPaint.setAntiAlias(antiAlias);
+                mColonPaint.setAntiAlias(antiAlias);
+                mDistanceCountPaint.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 String formatTwoDigitNumber(int hour) {
+            return String.format("%02d", hour);
+        }
+
+        private String getAmPmString(int amPm) {
+            return amPm == Calendar.AM ? mAmString : mPmString;
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            long now = System.currentTimeMillis();
+            mCalendar.setTimeInMillis(now);
+            boolean is24Hour = DateFormat.is24HourFormat(FitDistanceWatchFaceService.this);
+
+            // Draw the background.
+            canvas.drawColor(BACKGROUND_COLOR);
+
+            // Draw the hours.
+            float x = mXOffset;
+            String hourString;
+            if (is24Hour) {
+                hourString = formatTwoDigitNumber(mCalendar.get(Calendar.HOUR_OF_DAY));
+            } else {
+                int hour = mCalendar.get(Calendar.HOUR);
+                if (hour == 0) {
+                    hour = 12;
+                }
+                hourString = String.valueOf(hour);
+            }
+            canvas.drawText(hourString, x, mYOffset, mHourPaint);
+            x += mHourPaint.measureText(hourString);
+
+            // Draw first colon (between hour and minute).
+            canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
+
+            x += mColonWidth;
+
+            // Draw the minutes.
+            String minuteString = formatTwoDigitNumber(mCalendar.get(Calendar.MINUTE));
+            canvas.drawText(minuteString, x, mYOffset, mMinutePaint);
+            x += mMinutePaint.measureText(minuteString);
+
+            // In interactive mode, draw a second colon followed by the seconds.
+            // Otherwise, if we're in 12-hour mode, draw AM/PM
+            if (!isInAmbientMode()) {
+                canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
+
+                x += mColonWidth;
+                canvas.drawText(formatTwoDigitNumber(
+                        mCalendar.get(Calendar.SECOND)), x, mYOffset, mSecondPaint);
+            } else if (!is24Hour) {
+                x += mColonWidth;
+                canvas.drawText(getAmPmString(
+                        mCalendar.get(Calendar.AM_PM)), x, mYOffset, mAmPmPaint);
+            }
+
+            // Only render distance if there is no peek card, so they do not bleed into each other
+            // in ambient mode.
+            if (getPeekCardPosition().isEmpty()) {
+                canvas.drawText(
+                        getString(R.string.fit_distance, mDistanceTotal),
+                        mXDistanceOffset,
+                        mYOffset + mLineHeight,
+                        mDistanceCountPaint);
+            }
+        }
+
+        /**
+         * 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() {
+            Log.d(TAG, "updateTimer");
+
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            if (shouldUpdateTimeHandlerBeRunning()) {
+                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 shouldUpdateTimeHandlerBeRunning() {
+            return isVisible() && !isInAmbientMode();
+        }
+
+        private void getTotalDistance() {
+
+            Log.d(TAG, "getTotalDistance()");
+
+            if ((mGoogleApiClient != null)
+                    && (mGoogleApiClient.isConnected())
+                    && (!mDistanceRequested)) {
+
+                mDistanceRequested = true;
+
+                PendingResult<DailyTotalResult> distanceResult =
+                        Fitness.HistoryApi.readDailyTotal(
+                                mGoogleApiClient,
+                                DataType.TYPE_DISTANCE_DELTA);
+
+                distanceResult.setResultCallback(this);
+            }
+        }
+
+        @Override
+        public void onConnected(Bundle connectionHint) {
+            Log.d(TAG, "mGoogleApiAndFitCallbacks.onConnected: " + connectionHint);
+
+            mDistanceRequested = false;
+
+            // Subscribe covers devices that do not have Google Fit installed.
+            subscribeToDistance();
+
+            getTotalDistance();
+        }
+
+        /*
+         * Subscribes to distance.
+         */
+        private void subscribeToDistance() {
+
+            if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnecting())) {
+
+                Fitness.RecordingApi.subscribe(mGoogleApiClient, DataType.TYPE_DISTANCE_DELTA)
+                        .setResultCallback(new ResultCallback<Status>() {
+                            @Override
+                            public void onResult(Status status) {
+                                if (status.isSuccess()) {
+                                    if (status.getStatusCode()
+                                            == FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) {
+                                        Log.i(TAG, "Existing subscription for activity detected.");
+                                    } else {
+                                        Log.i(TAG, "Successfully subscribed!");
+                                    }
+                                } else {
+                                    Log.i(TAG, "There was a problem subscribing.");
+                                }
+                            }
+                        });
+            }
+        }
+
+        @Override
+        public void onConnectionSuspended(int cause) {
+            Log.d(TAG, "mGoogleApiAndFitCallbacks.onConnectionSuspended: " + cause);
+        }
+
+        @Override
+        public void onConnectionFailed(ConnectionResult result) {
+            Log.d(TAG, "mGoogleApiAndFitCallbacks.onConnectionFailed: " + result);
+        }
+
+        @Override
+        public void onResult(DailyTotalResult dailyTotalResult) {
+            Log.d(TAG, "mGoogleApiAndFitCallbacks.onResult(): " + dailyTotalResult);
+
+            mDistanceRequested = false;
+
+            if (dailyTotalResult.getStatus().isSuccess()) {
+
+                List<DataPoint> points = dailyTotalResult.getTotal().getDataPoints();
+
+                if (!points.isEmpty()) {
+                    mDistanceTotal = points.get(0).getValue(Field.FIELD_DISTANCE).asFloat();
+                    Log.d(TAG, "distance updated: " + mDistanceTotal);
+                }
+            } else {
+                Log.e(TAG, "onResult() failed! " + dailyTotalResult.getStatus().getStatusMessage());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/FitStepsWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/FitStepsWatchFaceService.java
new file mode 100644
index 0000000..1f7b298
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/FitStepsWatchFaceService.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.fitness.Fitness;
+import com.google.android.gms.fitness.FitnessStatusCodes;
+import com.google.android.gms.fitness.data.DataPoint;
+import com.google.android.gms.fitness.data.DataType;
+import com.google.android.gms.fitness.data.Field;
+import com.google.android.gms.fitness.result.DailyTotalResult;
+
+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.Color;
+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.WatchFaceStyle;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.WindowInsets;
+
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The step count watch face shows user's daily step total via Google Fit (matches Google Fit app).
+ * Steps are polled initially when the Google API Client successfully connects and once a minute
+ * after that via the onTimeTick callback. If you want more frequent updates, you will want to add
+ * your own  Handler.
+ *
+ * Authentication is not a requirement to request steps from Google Fit on Wear.
+ *
+ * In ambient mode, the seconds are replaced with an AM/PM indicator.
+ *
+ * On devices with low-bit ambient mode, the text is drawn without anti-aliasing. On devices which
+ * require burn-in protection, the hours are drawn in normal rather than bold.
+ *
+ */
+public class FitStepsWatchFaceService extends CanvasWatchFaceService {
+
+    private static final String TAG = "StepCountWatchFace";
+
+    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 active mode (non-ambient).
+     */
+    private static final long ACTIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1);
+
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+
+    private class Engine extends CanvasWatchFaceService.Engine implements
+            GoogleApiClient.ConnectionCallbacks,
+            GoogleApiClient.OnConnectionFailedListener,
+            ResultCallback<DailyTotalResult> {
+
+        private static final int BACKGROUND_COLOR = Color.BLACK;
+        private static final int TEXT_HOURS_MINS_COLOR = Color.WHITE;
+        private static final int TEXT_SECONDS_COLOR = Color.GRAY;
+        private static final int TEXT_AM_PM_COLOR = Color.GRAY;
+        private static final int TEXT_COLON_COLOR = Color.GRAY;
+        private static final int TEXT_STEP_COUNT_COLOR = Color.GRAY;
+
+        private static final String COLON_STRING = ":";
+
+        private static final int MSG_UPDATE_TIME = 0;
+
+        /* Handler to update the time periodically in interactive mode. */
+        private 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 (shouldUpdateTimeHandlerBeRunning()) {
+                            long timeMs = System.currentTimeMillis();
+                            long delayMs =
+                                    ACTIVE_INTERVAL_MS - (timeMs % ACTIVE_INTERVAL_MS);
+                            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+                        }
+                        break;
+                }
+            }
+        };
+
+        /**
+         * Handles time zone and locale changes.
+         */
+        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                mCalendar.setTimeZone(TimeZone.getDefault());
+                invalidate();
+            }
+        };
+
+        /**
+         * Unregistering an unregistered receiver throws an exception. Keep track of the
+         * registration state to prevent that.
+         */
+        private boolean mRegisteredReceiver = false;
+
+        private Paint mHourPaint;
+        private Paint mMinutePaint;
+        private Paint mSecondPaint;
+        private Paint mAmPmPaint;
+        private Paint mColonPaint;
+        private Paint mStepCountPaint;
+
+        private float mColonWidth;
+
+        private Calendar mCalendar;
+
+        private float mXOffset;
+        private float mXStepsOffset;
+        private float mYOffset;
+        private float mLineHeight;
+
+        private String mAmString;
+        private String mPmString;
+
+
+        /**
+         * Whether the display supports fewer bits for each color in ambient mode. When true, we
+         * disable anti-aliasing in ambient mode.
+         */
+        private boolean mLowBitAmbient;
+
+        /*
+         * Google API Client used to make Google Fit requests for step data.
+         */
+        private GoogleApiClient mGoogleApiClient;
+
+        private boolean mStepsRequested;
+
+        private int mStepsTotal = 0;
+
+        @Override
+        public void onCreate(SurfaceHolder holder) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onCreate");
+            }
+
+            super.onCreate(holder);
+
+            mStepsRequested = false;
+            mGoogleApiClient = new GoogleApiClient.Builder(FitStepsWatchFaceService.this)
+                    .addConnectionCallbacks(this)
+                    .addOnConnectionFailedListener(this)
+                    .addApi(Fitness.HISTORY_API)
+                    .addApi(Fitness.RECORDING_API)
+                    // When user has multiple accounts, useDefaultAccount() allows Google Fit to
+                    // associated with the main account for steps. It also replaces the need for
+                    // a scope request.
+                    .useDefaultAccount()
+                    .build();
+
+            setWatchFaceStyle(new WatchFaceStyle.Builder(FitStepsWatchFaceService.this)
+                    .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
+                    .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
+                    .setShowSystemUiTime(false)
+                    .build());
+
+            Resources resources = getResources();
+
+            mYOffset = resources.getDimension(R.dimen.fit_y_offset);
+            mLineHeight = resources.getDimension(R.dimen.fit_line_height);
+            mAmString = resources.getString(R.string.fit_am);
+            mPmString = resources.getString(R.string.fit_pm);
+
+            mHourPaint = createTextPaint(TEXT_HOURS_MINS_COLOR, BOLD_TYPEFACE);
+            mMinutePaint = createTextPaint(TEXT_HOURS_MINS_COLOR);
+            mSecondPaint = createTextPaint(TEXT_SECONDS_COLOR);
+            mAmPmPaint = createTextPaint(TEXT_AM_PM_COLOR);
+            mColonPaint = createTextPaint(TEXT_COLON_COLOR);
+            mStepCountPaint = createTextPaint(TEXT_STEP_COUNT_COLOR);
+
+            mCalendar = Calendar.getInstance();
+
+        }
+
+        @Override
+        public void onDestroy() {
+            mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
+            super.onDestroy();
+        }
+
+        private Paint createTextPaint(int color) {
+            return createTextPaint(color, NORMAL_TYPEFACE);
+        }
+
+        private Paint createTextPaint(int color, Typeface typeface) {
+            Paint paint = new Paint();
+            paint.setColor(color);
+            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 and date formats, in case they changed while we weren't visible.
+                mCalendar.setTimeZone(TimeZone.getDefault());
+            } else {
+                unregisterReceiver();
+
+                if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
+                    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 (mRegisteredReceiver) {
+                return;
+            }
+            mRegisteredReceiver = true;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
+            FitStepsWatchFaceService.this.registerReceiver(mReceiver, filter);
+        }
+
+        private void unregisterReceiver() {
+            if (!mRegisteredReceiver) {
+                return;
+            }
+            mRegisteredReceiver = false;
+            FitStepsWatchFaceService.this.unregisterReceiver(mReceiver);
+        }
+
+        @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 = FitStepsWatchFaceService.this.getResources();
+            boolean isRound = insets.isRound();
+            mXOffset = resources.getDimension(isRound
+                    ? R.dimen.fit_x_offset_round : R.dimen.fit_x_offset);
+            mXStepsOffset =  resources.getDimension(isRound
+                    ? R.dimen.fit_steps_or_distance_x_offset_round : R.dimen.fit_steps_or_distance_x_offset);
+            float textSize = resources.getDimension(isRound
+                    ? R.dimen.fit_text_size_round : R.dimen.fit_text_size);
+            float amPmSize = resources.getDimension(isRound
+                    ? R.dimen.fit_am_pm_size_round : R.dimen.fit_am_pm_size);
+
+            mHourPaint.setTextSize(textSize);
+            mMinutePaint.setTextSize(textSize);
+            mSecondPaint.setTextSize(textSize);
+            mAmPmPaint.setTextSize(amPmSize);
+            mColonPaint.setTextSize(textSize);
+            mStepCountPaint.setTextSize(resources.getDimension(R.dimen.fit_steps_or_distance_text_size));
+
+            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());
+            }
+
+            getTotalSteps();
+            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);
+                mAmPmPaint.setAntiAlias(antiAlias);
+                mColonPaint.setAntiAlias(antiAlias);
+                mStepCountPaint.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 String formatTwoDigitNumber(int hour) {
+            return String.format("%02d", hour);
+        }
+
+        private String getAmPmString(int amPm) {
+            return amPm == Calendar.AM ? mAmString : mPmString;
+        }
+
+        @Override
+        public void onDraw(Canvas canvas, Rect bounds) {
+            long now = System.currentTimeMillis();
+            mCalendar.setTimeInMillis(now);
+            boolean is24Hour = DateFormat.is24HourFormat(FitStepsWatchFaceService.this);
+
+            // Draw the background.
+            canvas.drawColor(BACKGROUND_COLOR);
+
+            // Draw the hours.
+            float x = mXOffset;
+            String hourString;
+            if (is24Hour) {
+                hourString = formatTwoDigitNumber(mCalendar.get(Calendar.HOUR_OF_DAY));
+            } else {
+                int hour = mCalendar.get(Calendar.HOUR);
+                if (hour == 0) {
+                    hour = 12;
+                }
+                hourString = String.valueOf(hour);
+            }
+            canvas.drawText(hourString, x, mYOffset, mHourPaint);
+            x += mHourPaint.measureText(hourString);
+
+            // Draw first colon (between hour and minute).
+            canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
+
+            x += mColonWidth;
+
+            // Draw the minutes.
+            String minuteString = formatTwoDigitNumber(mCalendar.get(Calendar.MINUTE));
+            canvas.drawText(minuteString, x, mYOffset, mMinutePaint);
+            x += mMinutePaint.measureText(minuteString);
+
+            // In interactive mode, draw a second colon followed by the seconds.
+            // Otherwise, if we're in 12-hour mode, draw AM/PM
+            if (!isInAmbientMode()) {
+                canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint);
+
+                x += mColonWidth;
+                canvas.drawText(formatTwoDigitNumber(
+                        mCalendar.get(Calendar.SECOND)), x, mYOffset, mSecondPaint);
+            } else if (!is24Hour) {
+                x += mColonWidth;
+                canvas.drawText(getAmPmString(
+                        mCalendar.get(Calendar.AM_PM)), x, mYOffset, mAmPmPaint);
+            }
+
+            // Only render steps if there is no peek card, so they do not bleed into each other
+            // in ambient mode.
+            if (getPeekCardPosition().isEmpty()) {
+                canvas.drawText(
+                        getString(R.string.fit_steps, mStepsTotal),
+                        mXStepsOffset,
+                        mYOffset + mLineHeight,
+                        mStepCountPaint);
+            }
+        }
+
+        /**
+         * 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 (shouldUpdateTimeHandlerBeRunning()) {
+                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 shouldUpdateTimeHandlerBeRunning() {
+            return isVisible() && !isInAmbientMode();
+        }
+
+        private void getTotalSteps() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "getTotalSteps()");
+            }
+
+            if ((mGoogleApiClient != null)
+                    && (mGoogleApiClient.isConnected())
+                    && (!mStepsRequested)) {
+
+                mStepsRequested = true;
+
+                PendingResult<DailyTotalResult> stepsResult =
+                        Fitness.HistoryApi.readDailyTotal(
+                                mGoogleApiClient,
+                                DataType.TYPE_STEP_COUNT_DELTA);
+
+                stepsResult.setResultCallback(this);
+            }
+        }
+
+        @Override
+        public void onConnected(Bundle connectionHint) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "mGoogleApiAndFitCallbacks.onConnected: " + connectionHint);
+            }
+            mStepsRequested = false;
+
+            // The subscribe step covers devices that do not have Google Fit installed.
+            subscribeToSteps();
+
+            getTotalSteps();
+        }
+
+        /*
+         * Subscribes to step count (for phones that don't have Google Fit app).
+         */
+        private void subscribeToSteps() {
+            Fitness.RecordingApi.subscribe(mGoogleApiClient, DataType.TYPE_STEP_COUNT_DELTA)
+                    .setResultCallback(new ResultCallback<Status>() {
+                        @Override
+                        public void onResult(Status status) {
+                            if (status.isSuccess()) {
+                                if (status.getStatusCode()
+                                        == FitnessStatusCodes.SUCCESS_ALREADY_SUBSCRIBED) {
+                                    Log.i(TAG, "Existing subscription for activity detected.");
+                                } else {
+                                    Log.i(TAG, "Successfully subscribed!");
+                                }
+                            } else {
+                                Log.i(TAG, "There was a problem subscribing.");
+                            }
+                        }
+                    });
+        }
+
+        @Override
+        public void onConnectionSuspended(int cause) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "mGoogleApiAndFitCallbacks.onConnectionSuspended: " + cause);
+            }
+        }
+
+        @Override
+        public void onConnectionFailed(ConnectionResult result) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "mGoogleApiAndFitCallbacks.onConnectionFailed: " + result);
+            }
+        }
+
+        @Override
+        public void onResult(DailyTotalResult dailyTotalResult) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "mGoogleApiAndFitCallbacks.onResult(): " + dailyTotalResult);
+            }
+
+            mStepsRequested = false;
+
+            if (dailyTotalResult.getStatus().isSuccess()) {
+
+                List<DataPoint> points = dailyTotalResult.getTotal().getDataPoints();;
+
+                if (!points.isEmpty()) {
+                    mStepsTotal = points.get(0).getValue(Field.FIELD_STEPS).asInt();
+                    Log.d(TAG, "steps updated: " + mStepsTotal);
+                }
+            } else {
+                Log.e(TAG, "onResult() failed! " + dailyTotalResult.getStatus().getStatusMessage());
+            }
+        }
+    }
+}
diff --git a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/InteractiveWatchFaceService.java b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/InteractiveWatchFaceService.java
index 1a6f25b..7a50208 100644
--- a/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/InteractiveWatchFaceService.java
+++ b/wearable/wear/WatchFace/Wearable/src/main/java/com/example/android/wearable/watchface/InteractiveWatchFaceService.java
@@ -30,7 +30,7 @@
 import android.view.WindowInsets;
 
 /**
- * Demostrates interactive watch face capabilities, i.e., touching the display and registering
+ * Demonstrates interactive watch face capabilities, i.e., touching the display and registering
  * three different events: touch, touch-cancel and tap. The watch face UI will show the count of
  * these events as they occur. See the {@code onTapCommand} below.
  */
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
index 0ba2ab9..a94097f 100644
--- 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
@@ -78,7 +78,7 @@
 
         /* Colors for all hands (hour, minute, seconds, ticks) based on photo loaded. */
         private int mWatchHandColor;
-        private int mWatchHandHightlightColor;
+        private int mWatchHandHighlightColor;
         private int mWatchHandShadowColor;
 
         private Paint mHourPaint;
@@ -123,7 +123,7 @@
 
             /* Set defaults for colors */
             mWatchHandColor = Color.WHITE;
-            mWatchHandHightlightColor = Color.RED;
+            mWatchHandHighlightColor = Color.RED;
             mWatchHandShadowColor = Color.BLACK;
 
             mHourPaint = new Paint();
@@ -141,7 +141,7 @@
             mMinutePaint.setShadowLayer(SHADOW_RADIUS, 0, 0, mWatchHandShadowColor);
 
             mSecondPaint = new Paint();
-            mSecondPaint.setColor(mWatchHandHightlightColor);
+            mSecondPaint.setColor(mWatchHandHighlightColor);
             mSecondPaint.setStrokeWidth(SECOND_TICK_STROKE_WIDTH);
             mSecondPaint.setAntiAlias(true);
             mSecondPaint.setStrokeCap(Paint.Cap.ROUND);
@@ -165,7 +165,7 @@
                                     Log.d(TAG, "Palette: " + palette);
                                 }
 
-                                mWatchHandHightlightColor = palette.getVibrantColor(Color.RED);
+                                mWatchHandHighlightColor = palette.getVibrantColor(Color.RED);
                                 mWatchHandColor = palette.getLightVibrantColor(Color.WHITE);
                                 mWatchHandShadowColor = palette.getDarkMutedColor(Color.BLACK);
                                 updateWatchHandStyle();
@@ -226,7 +226,7 @@
             } else {
                 mHourPaint.setColor(mWatchHandColor);
                 mMinutePaint.setColor(mWatchHandColor);
-                mSecondPaint.setColor(mWatchHandHightlightColor);
+                mSecondPaint.setColor(mWatchHandHighlightColor);
                 mTickAndCirclePaint.setColor(mWatchHandColor);
 
                 mHourPaint.setAntiAlias(true);
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png
new file mode 100644
index 0000000..6bae68f
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_distance.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_distance.png
new file mode 100644
index 0000000..a96f355
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_distance.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_distance_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_distance_circular.png
new file mode 100644
index 0000000..912d85b
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_distance_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_fit.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_fit.png
new file mode 100644
index 0000000..04b8b5e
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_fit.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_fit_circular.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_fit_circular.png
new file mode 100644
index 0000000..b421e28
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-hdpi/preview_fit_circular.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png
new file mode 100644
index 0000000..3f47b54
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/ic_lock_open_white_24dp.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/ic_lock_open_white_24dp.png
new file mode 100644
index 0000000..cbe9e1c
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xhdpi/ic_lock_open_white_24dp.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xxhdpi/ic_lock_open_white_24dp.png b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xxhdpi/ic_lock_open_white_24dp.png
new file mode 100644
index 0000000..1d1b0f4
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/drawable-xxhdpi/ic_lock_open_white_24dp.png
Binary files differ
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_calendar_watch_face_permission.xml b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_calendar_watch_face_permission.xml
new file mode 100644
index 0000000..bf0e3f6
--- /dev/null
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/layout/activity_calendar_watch_face_permission.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.wearable.view.BoxInsetLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/container"
+    android:background="@color/white"
+    android:paddingTop="32dp"
+    android:paddingLeft="36dp"
+    android:paddingRight="22dp"
+    tools:context="com.example.android.wearable.watchface.CalendarWatchFacePermissionActivity"
+    tools:deviceIds="wear">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:onClick="onClickEnablePermission"
+        android:orientation="vertical"
+        app:layout_box="all">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textSize="16sp"
+            android:paddingBottom="18dp"
+            android:textColor="#000000"
+            android:text="@string/calendar_permission_text"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <android.support.wearable.view.CircledImageView
+                android:id="@+id/circle"
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                app:circle_radius="20dp"
+                app:circle_color="#0086D4"
+                android:src="@drawable/ic_lock_open_white_24dp"/>
+
+            <android.support.v4.widget.Space
+                android:layout_width="8dp"
+                android:layout_height="8dp"/>
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:textSize="16sp"
+                android:textColor="#0086D4"
+                android:text="Enable Permission"/>
+
+
+        </LinearLayout>
+
+    </LinearLayout>
+</android.support.wearable.view.BoxInsetLayout>
\ No newline at end of file
diff --git a/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml b/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml
index 4973466..0b0672b 100644
--- a/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/dimens.xml
@@ -32,4 +32,15 @@
     <dimen name="interactive_y_offset">72dp</dimen>
     <dimen name="interactive_y_offset_round">84dp</dimen>
     <dimen name="interactive_line_height">25dp</dimen>
+    <dimen name="fit_text_size">40dp</dimen>
+    <dimen name="fit_text_size_round">45dp</dimen>
+    <dimen name="fit_steps_or_distance_text_size">20dp</dimen>
+    <dimen name="fit_am_pm_size">25dp</dimen>
+    <dimen name="fit_am_pm_size_round">30dp</dimen>
+    <dimen name="fit_x_offset">15dp</dimen>
+    <dimen name="fit_x_offset_round">25dp</dimen>
+    <dimen name="fit_steps_or_distance_x_offset">20dp</dimen>
+    <dimen name="fit_steps_or_distance_x_offset_round">30dp</dimen>
+    <dimen name="fit_y_offset">80dp</dimen>
+    <dimen name="fit_line_height">25dp</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
index 19bc3e7..4090995 100644
--- a/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
+++ b/wearable/wear/WatchFace/Wearable/src/main/res/values/strings.xml
@@ -25,11 +25,22 @@
     <string name="digital_config_name">Digital watch face configuration</string>
     <string name="digital_am">AM</string>
     <string name="digital_pm">PM</string>
+
+    <string name="fit_steps_name">Sample Fit Steps</string>
+    <string name="fit_distance_name">Sample Fit Distance</string>
+    <string name="fit_am">AM</string>
+    <string name="fit_pm">PM</string>
+    <string name="fit_steps">%1$d steps</string>
+    <string name="fit_distance">%1$,.2f meters</string>
+
     <string name="calendar_name">Sample Calendar</string>
+    <string name="calendar_permission_not_approved">&lt;br&gt;&lt;br&gt;&lt;br&gt;WatchFace requires Calendar permission. Click on this WatchFace or visit Settings &gt; Permissions to approve.</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>
+    <string name="title_activity_calendar_watch_face_permission">Calendar Permission Activity</string>
+    <string name="calendar_permission_text">WatchFace requires Calendar access.</string>
 
     <!-- TODO: this should be shared (needs covering all the samples with Gradle build model) -->
     <string name="color_black">Black</string>
diff --git a/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 100644
--- a/wearable/wear/WatchFace/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/WatchFace/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/WatchFace/template-params.xml b/wearable/wear/WatchFace/template-params.xml
index cedf8db..e339fe2 100644
--- a/wearable/wear/WatchFace/template-params.xml
+++ b/wearable/wear/WatchFace/template-params.xml
@@ -23,10 +23,13 @@
     <package>com.example.android.wearable.watchface</package>
 
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>23</targetSdkVersionWear>
 
-    <dependency_wearable>com.android.support:palette-v7:21.0.0</dependency_wearable>
+    <dependency_wearable>com.android.support:palette-v7:23.1.1</dependency_wearable>
     <dependency>com.google.android.support:wearable:1.3.0</dependency>
+    <dependency>com.google.android.gms:play-services-fitness</dependency>
+    <dependency_wearable>com.google.android.gms:play-services-fitness</dependency_wearable>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -37,8 +40,13 @@
             <![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, interactive, etc. It also includes a watch-side configuration example.
+opengl, calendar, steps, interactive, etc. It also includes a watch-side configuration example.
 The phone app includes a phone-side configuration example.
+
+Additional note on Steps WatchFace Sample, if the user has not installed or setup the Google Fit app
+on their phone and their Wear device has not configured the Google Fit Wear App, then you may get
+zero steps until one of the two is setup. Please note, many Wear devices configure the Google Fit
+Wear App beforehand.
             ]]>
         </intro>
     </strings>
diff --git a/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml b/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml
index 33a266d..774817b 100644
--- a/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/WatchViewStub/Wearable/src/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
         package="com.example.android.google.wearable.watchviewstub" >
 
     <uses-sdk android:minSdkVersion="20"
-              android:targetSdkVersion="21" />
+              android:targetSdkVersion="22" />
 
     <uses-feature android:name="android.hardware.type.watch" />
 
diff --git a/wearable/wear/WatchViewStub/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/WatchViewStub/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..07fc193 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=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/WatchViewStub/template-params.xml b/wearable/wear/WatchViewStub/template-params.xml
index ff7e7f8..39568aa 100644
--- a/wearable/wear/WatchViewStub/template-params.xml
+++ b/wearable/wear/WatchViewStub/template-params.xml
@@ -19,8 +19,7 @@
     <group>Wearable</group>
     <package>com.example.android.google.wearable.watchviewstub</package>
 
-    <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <strings>
         <intro>
diff --git a/wearable/wear/WearSpeakerSample/CONTRIBUTING b/wearable/wear/WearSpeakerSample/CONTRIBUTING
new file mode 100644
index 0000000..fe1f588
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/CONTRIBUTING
@@ -0,0 +1,34 @@
+# 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://cla.developers.google.com).
+  * 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://cla.developers.google.com).
+
+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
+   [Android Code Style Guide]
+   (https://source.android.com/source/code-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/wearable/wear/WearSpeakerSample/build.gradle b/wearable/wear/WearSpeakerSample/build.gradle
new file mode 100644
index 0000000..25ba12a
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/build.gradle
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:1.3.0'
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
+
+// 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/wearable/wear/WearSpeakerSample/buildSrc/build.gradle b/wearable/wear/WearSpeakerSample/buildSrc/build.gradle
new file mode 100644
index 0000000..7cebf71
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/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/wearable/wear/WearSpeakerSample/gradle.properties b/wearable/wear/WearSpeakerSample/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/wearable/wear/WearSpeakerSample/gradle/wrapper/gradle-wrapper.jar b/wearable/wear/WearSpeakerSample/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/WearSpeakerSample/gradle/wrapper/gradle-wrapper.properties
similarity index 79%
copy from ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
copy to wearable/wear/WearSpeakerSample/gradle/wrapper/gradle-wrapper.properties
index a51db8c..08a6fd5 100644
--- a/ui/views/Elevation/ElevationDrag/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/WearSpeakerSample/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue May 20 13:33:02 BST 2014
+#Sun Oct 04 13:39:51 PDT 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
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradlew b/wearable/wear/WearSpeakerSample/gradlew
similarity index 100%
rename from ui/views/Elevation/ElevationDrag/gradle/gradlew
rename to wearable/wear/WearSpeakerSample/gradlew
diff --git a/ui/views/Elevation/ElevationDrag/gradle/gradlew.bat b/wearable/wear/WearSpeakerSample/gradlew.bat
similarity index 100%
rename from ui/views/Elevation/ElevationDrag/gradle/gradlew.bat
rename to wearable/wear/WearSpeakerSample/gradlew.bat
diff --git a/wearable/wear/WearSpeakerSample/screenshots/1.png b/wearable/wear/WearSpeakerSample/screenshots/1.png
new file mode 100644
index 0000000..d98e6fa
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/screenshots/1.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/screenshots/2.png b/wearable/wear/WearSpeakerSample/screenshots/2.png
new file mode 100644
index 0000000..226da1f
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/screenshots/2.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/screenshots/3.png b/wearable/wear/WearSpeakerSample/screenshots/3.png
new file mode 100644
index 0000000..d7467d7
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/screenshots/3.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/screenshots/4.png b/wearable/wear/WearSpeakerSample/screenshots/4.png
new file mode 100644
index 0000000..1044ee1
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/screenshots/4.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/settings.gradle b/wearable/wear/WearSpeakerSample/settings.gradle
new file mode 100644
index 0000000..8d97c99
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+include ':wear'
diff --git a/wearable/wear/WearSpeakerSample/template-params.xml b/wearable/wear/WearSpeakerSample/template-params.xml
new file mode 100644
index 0000000..f07d4c5
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/template-params.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015 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.
+-->
+<sample>
+    <name>WearSpeakerSample</name>
+    <group>Wearable</group>
+    <package>com.example.android.wearable.speaker</package>
+
+    <strings>
+        <intro>
+<![CDATA[
+A sample that shows how you can record voice using the microphone on a wearable and
+play the recorded voice or an mp3 file, if the wearable device has a built-in speaker.
+
+This sample doesn't have any companion phone app so you need to install this directly
+on your watch (using "adb").
+]]>
+        </intro>
+    </strings>
+
+    <template src="unmanaged" />
+
+    <metadata>
+        <!-- Values: {DRAFT | PUBLISHED | INTERNAL | DEPRECATED | SUPERCEDED} -->
+        <status>PUBLISHED</status>
+        <!-- See http://go/sample-categories for details on the next 4 fields. -->
+        <categories>Wearable</categories>
+        <technologies>Android</technologies>
+        <languages>Java</languages>
+        <solutions>Mobile</solutions>
+        <!-- Values: {BEGINNER | INTERMEDIATE | ADVANCED | EXPERT} -->
+        <level>INTERMEDIATE</level>
+        <!-- Dimensions: 512x512, PNG fomrat -->
+        <icon>screenshots/1.png</icon>
+        <!-- Path to screenshots. Use <img> tags for each. -->
+        <!-- <screenshots>
+            <img>screenshots/composite-1.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.media.AudioTrack</android>
+          <android>android.media.AudioRecord</android>
+        </api_refs>
+
+        <!-- 1-3 line description of the sample here.
+
+            Avoid simply rearranging the sample's title. What does this sample actually
+            accomplish, and how does it do it? -->
+        <description>
+<![CDATA[
+A sample that shows how you can record voice using the microphone on a wearable and
+play the recorded voice or an mp3 file, if the wearable device has a built-in speaker.
+
+This sample doesn't have any companion phone app so you need to install this directly
+on your watch (using "adb").
+]]>
+        </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[
+]]>
+        </intro> -->
+    </metadata>
+</sample>
diff --git a/wearable/wear/WearSpeakerSample/wear/.gitignore b/wearable/wear/WearSpeakerSample/wear/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/wearable/wear/WearSpeakerSample/wear/build.gradle b/wearable/wear/WearSpeakerSample/wear/build.gradle
new file mode 100644
index 0000000..8d3e550
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+apply plugin: 'com.android.application'
+
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion "23.0.1"
+
+    defaultConfig {
+        applicationId "com.example.android.wearable.speaker"
+        minSdkVersion 21
+        targetSdkVersion 23
+        versionCode 1
+        versionName "1.0"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    compile 'com.google.android.support:wearable:1.3.0'
+    compile 'com.google.android.gms:play-services-wearable:8.3.0'
+    compile 'com.android.support:appcompat-v7:23.1.0'
+
+}
diff --git a/wearable/wear/WearSpeakerSample/wear/proguard-rules.pro b/wearable/wear/WearSpeakerSample/wear/proguard-rules.pro
new file mode 100644
index 0000000..002bc05
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${SDK}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/AndroidManifest.xml b/wearable/wear/WearSpeakerSample/wear/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..135d3e0
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.wearable.speaker" >
+
+    <uses-feature android:name="android.hardware.type.watch" />
+
+    <!-- the following permission is required to record audio using a microphone -->
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@android:style/Theme.DeviceDefault" >
+        <uses-library android:name="com.google.android.wearable" android:required="false" />
+        <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/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/MainActivity.java b/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/MainActivity.java
new file mode 100644
index 0000000..e7a4870
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/MainActivity.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015 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.speaker;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.wearable.activity.WearableActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * We first get the required permission to use the MIC. If it is granted, then we continue with
+ * the application and present the UI with three icons: a MIC icon (if pressed, user can record up
+ * to 10 seconds), a Play icon (if clicked, it wil playback the recorded audio file) and a music
+ * note icon (if clicked, it plays an MP3 file that is included in the app).
+ */
+public class MainActivity extends WearableActivity implements UIAnimation.UIStateListener,
+        SoundRecorder.OnVoicePlaybackStateChangedListener {
+
+    private static final String TAG = "MainActivity";
+    private static final int PERMISSIONS_REQUEST_CODE = 100;
+    private static final long COUNT_DOWN_MS = TimeUnit.SECONDS.toMillis(10);
+    private static final long MILLIS_IN_SECOND = TimeUnit.SECONDS.toMillis(1);
+    private static final String VOICE_FILE_NAME = "audiorecord.pcm";
+    private MediaPlayer mMediaPlayer;
+    private AppState mState = AppState.READY;
+    private UIAnimation.UIState mUiState = UIAnimation.UIState.HOME;
+    private SoundRecorder mSoundRecorder;
+
+    private UIAnimation mUIAnimation;
+    private ProgressBar mProgressBar;
+    private CountDownTimer mCountDownTimer;
+
+    enum AppState {
+        READY, PLAYING_VOICE, PLAYING_MUSIC, RECORDING
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main_activity);
+        mProgressBar = (ProgressBar) findViewById(R.id.progress);
+        mProgressBar.setMax((int) (COUNT_DOWN_MS / MILLIS_IN_SECOND));
+        setAmbientEnabled();
+    }
+
+    private void setProgressBar(long progressInMillis) {
+        mProgressBar.setProgress((int) (progressInMillis / MILLIS_IN_SECOND));
+    }
+
+    @Override
+    public void onUIStateChanged(UIAnimation.UIState state) {
+        Log.d(TAG, "UI State is: " + state);
+        if (mUiState == state) {
+            return;
+        }
+        switch (state) {
+            case MUSIC_UP:
+                mState = AppState.PLAYING_MUSIC;
+                mUiState = state;
+                playMusic();
+                break;
+            case MIC_UP:
+                mState = AppState.RECORDING;
+                mUiState = state;
+                mSoundRecorder.startRecording();
+                setProgressBar(COUNT_DOWN_MS);
+                mCountDownTimer = new CountDownTimer(COUNT_DOWN_MS, MILLIS_IN_SECOND) {
+                    @Override
+                    public void onTick(long millisUntilFinished) {
+                        mProgressBar.setVisibility(View.VISIBLE);
+                        setProgressBar(millisUntilFinished);
+                        Log.d(TAG, "Time Left: " + millisUntilFinished / MILLIS_IN_SECOND);
+                    }
+
+                    @Override
+                    public void onFinish() {
+                        mProgressBar.setProgress(0);
+                        mProgressBar.setVisibility(View.INVISIBLE);
+                        mSoundRecorder.stopRecording();
+                        mUIAnimation.transitionToHome();
+                        mUiState = UIAnimation.UIState.HOME;
+                        mState = AppState.READY;
+                        mCountDownTimer = null;
+                    }
+                };
+                mCountDownTimer.start();
+                break;
+            case SOUND_UP:
+                mState = AppState.PLAYING_VOICE;
+                mUiState = state;
+                mSoundRecorder.startPlay();
+                break;
+            case HOME:
+                switch (mState) {
+                    case PLAYING_MUSIC:
+                        mState = AppState.READY;
+                        mUiState = state;
+                        stopMusic();
+                        break;
+                    case PLAYING_VOICE:
+                        mState = AppState.READY;
+                        mUiState = state;
+                        mSoundRecorder.stopPlaying();
+                        break;
+                    case RECORDING:
+                        mState = AppState.READY;
+                        mUiState = state;
+                        mSoundRecorder.stopRecording();
+                        if (mCountDownTimer != null) {
+                            mCountDownTimer.cancel();
+                            mCountDownTimer = null;
+                        }
+                        mProgressBar.setVisibility(View.INVISIBLE);
+                        setProgressBar(COUNT_DOWN_MS);
+                        break;
+                }
+                break;
+        }
+    }
+
+    /**
+     * Plays back the MP3 file embedded in the application
+     */
+    private void playMusic() {
+        if (mMediaPlayer == null) {
+            mMediaPlayer = MediaPlayer.create(this, R.raw.sound);
+            mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer mp) {
+                    // we need to transition to the READY/Home state
+                    Log.d(TAG, "Music Finished");
+                    mUIAnimation.transitionToHome();
+                }
+            });
+        }
+        mMediaPlayer.start();
+    }
+
+    /**
+     * Stops the playback of the MP3 file.
+     */
+    private void stopMusic() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.stop();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+    }
+
+    /**
+     * Checks the permission that this app needs and if it has not been granted, it will
+     * prompt the user to grant it, otherwise it shuts down the app.
+     */
+    private void checkPermissions() {
+        boolean recordAudioPermissionGranted =
+                ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
+                        == PackageManager.PERMISSION_GRANTED;
+
+        if (recordAudioPermissionGranted) {
+            start();
+        } else {
+            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECORD_AUDIO},
+                    PERMISSIONS_REQUEST_CODE);
+        }
+
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode,
+            String permissions[], int[] grantResults) {
+        if (requestCode == PERMISSIONS_REQUEST_CODE) {
+            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                start();
+            } else {
+                // Permission has been denied before. At this point we should show a dialog to
+                // user and explain why this permission is needed and direct him to go to the
+                // Permissions settings for the app in the System settings. For this sample, we
+                // simply exit to get to the important part.
+                Toast.makeText(this, R.string.exiting_for_permissions, Toast.LENGTH_LONG).show();
+                finish();
+            }
+        }
+    }
+
+    /**
+     * Starts the main flow of the application.
+     */
+    private void start() {
+        mSoundRecorder = new SoundRecorder(this, VOICE_FILE_NAME, this);
+        int[] thumbResources = new int[] {R.id.mic, R.id.play, R.id.music};
+        ImageView[] thumbs = new ImageView[3];
+        for(int i=0; i < 3; i++) {
+            thumbs[i] = (ImageView) findViewById(thumbResources[i]);
+        }
+        View containerView = findViewById(R.id.container);
+        ImageView expandedView = (ImageView) findViewById(R.id.expanded);
+        int animationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
+        mUIAnimation = new UIAnimation(containerView, thumbs, expandedView, animationDuration,
+                this);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        if (speakerIsSupported()) {
+            checkPermissions();
+        } else {
+            findViewById(R.id.container2).setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Toast.makeText(MainActivity.this, R.string.no_speaker_supported,
+                            Toast.LENGTH_SHORT).show();
+                }
+            });
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        if (mSoundRecorder != null) {
+            mSoundRecorder.cleanup();
+            mSoundRecorder = null;
+        }
+        if (mCountDownTimer != null) {
+            mCountDownTimer.cancel();
+        }
+
+        if (mMediaPlayer != null) {
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+        super.onStop();
+    }
+
+    @Override
+    public void onPlaybackStopped() {
+        mUIAnimation.transitionToHome();
+        mUiState = UIAnimation.UIState.HOME;
+        mState = AppState.READY;
+    }
+
+    /**
+     * Determines if the wear device has a built-in speaker and if it is supported. Speaker, even if
+     * physically present, is only supported in Android M+ on a wear device..
+     */
+    public final boolean speakerIsSupported() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            PackageManager packageManager = getPackageManager();
+            // The results from AudioManager.getDevices can't be trusted unless the device
+            // advertises FEATURE_AUDIO_OUTPUT.
+            if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+                return false;
+            }
+            AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+            AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+            for (AudioDeviceInfo device : devices) {
+                if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/SoundRecorder.java b/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/SoundRecorder.java
new file mode 100644
index 0000000..a45bdd2
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/SoundRecorder.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2015 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.speaker;
+
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+import android.media.MediaRecorder;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * A helper class to provide methods to record audio input from the MIC to the internal storage
+ * and to playback the same recorded audio file.
+ */
+public class SoundRecorder {
+
+    private static final String TAG = "SoundRecorder";
+    private static final int RECORDING_RATE = 8000; // can go up to 44K, if needed
+    private static final int CHANNEL_IN = AudioFormat.CHANNEL_IN_MONO;
+    private static final int CHANNELS_OUT = AudioFormat.CHANNEL_OUT_MONO;
+    private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;
+    private static int BUFFER_SIZE = AudioRecord
+            .getMinBufferSize(RECORDING_RATE, CHANNEL_IN, FORMAT);
+
+    private final String mOutputFileName;
+    private final AudioManager mAudioManager;
+    private final Handler mHandler;
+    private final Context mContext;
+    private State mState = State.IDLE;
+
+    private OnVoicePlaybackStateChangedListener mListener;
+    private AsyncTask<Void, Void, Void> mRecordingAsyncTask;
+    private AsyncTask<Void, Void, Void> mPlayingAsyncTask;
+
+    enum State {
+        IDLE, RECORDING, PLAYING
+    }
+
+    public SoundRecorder(Context context, String outputFileName,
+            OnVoicePlaybackStateChangedListener listener) {
+        mOutputFileName = outputFileName;
+        mListener = listener;
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mHandler = new Handler(Looper.getMainLooper());
+        mContext = context;
+    }
+
+    /**
+     * Starts recording from the MIC.
+     */
+    public void startRecording() {
+        if (mState != State.IDLE) {
+            Log.w(TAG, "Requesting to start recording while state was not IDLE");
+            return;
+        }
+
+        mRecordingAsyncTask = new AsyncTask<Void, Void, Void>() {
+
+            private AudioRecord mAudioRecord;
+
+            @Override
+            protected void onPreExecute() {
+                mState = State.RECORDING;
+            }
+
+            @Override
+            protected Void doInBackground(Void... params) {
+                mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
+                        RECORDING_RATE, CHANNEL_IN, FORMAT, BUFFER_SIZE * 3);
+                BufferedOutputStream bufferedOutputStream = null;
+                try {
+                    bufferedOutputStream = new BufferedOutputStream(
+                            mContext.openFileOutput(mOutputFileName, Context.MODE_PRIVATE));
+                    byte[] buffer = new byte[BUFFER_SIZE];
+                    mAudioRecord.startRecording();
+                    while (!isCancelled()) {
+                        int read = mAudioRecord.read(buffer, 0, buffer.length);
+                        bufferedOutputStream.write(buffer, 0, read);
+                    }
+                } catch (IOException | NullPointerException | IndexOutOfBoundsException e) {
+                    Log.e(TAG, "Failed to record data: " + e);
+                } finally {
+                    if (bufferedOutputStream != null) {
+                        try {
+                            bufferedOutputStream.close();
+                        } catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                    mAudioRecord.release();
+                    mAudioRecord = null;
+                }
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Void aVoid) {
+                mState = State.IDLE;
+                mRecordingAsyncTask = null;
+            }
+
+            @Override
+            protected void onCancelled() {
+                if (mState == State.RECORDING) {
+                    Log.d(TAG, "Stopping the recording ...");
+                    mState = State.IDLE;
+                } else {
+                    Log.w(TAG, "Requesting to stop recording while state was not RECORDING");
+                }
+                mRecordingAsyncTask = null;
+            }
+        };
+
+        mRecordingAsyncTask.execute();
+    }
+
+    public void stopRecording() {
+        if (mRecordingAsyncTask != null) {
+            mRecordingAsyncTask.cancel(true);
+        }
+    }
+
+    public void stopPlaying() {
+        if (mPlayingAsyncTask != null) {
+            mPlayingAsyncTask.cancel(true);
+        }
+    }
+
+    /**
+     * Starts playback of the recorded audio file.
+     */
+    public void startPlay() {
+        if (mState != State.IDLE) {
+            Log.w(TAG, "Requesting to play while state was not IDLE");
+            return;
+        }
+
+        if (!new File(mContext.getFilesDir(), mOutputFileName).exists()) {
+            // there is no recording to play
+            if (mListener != null) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onPlaybackStopped();
+                    }
+                });
+            }
+            return;
+        }
+        final int intSize = AudioTrack.getMinBufferSize(RECORDING_RATE, CHANNELS_OUT, FORMAT);
+
+        mPlayingAsyncTask = new AsyncTask<Void, Void, Void>() {
+
+            private AudioTrack mAudioTrack;
+
+            @Override
+            protected void onPreExecute() {
+                mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+                        mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0 /* flags */);
+                mState = State.PLAYING;
+            }
+
+            @Override
+            protected Void doInBackground(Void... params) {
+                try {
+                    mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, RECORDING_RATE,
+                            CHANNELS_OUT, FORMAT, intSize, AudioTrack.MODE_STREAM);
+                    byte[] buffer = new byte[intSize * 2];
+                    FileInputStream in = null;
+                    BufferedInputStream bis = null;
+                    mAudioTrack.setVolume(AudioTrack.getMaxVolume());
+                    mAudioTrack.play();
+                    try {
+                        in = mContext.openFileInput(mOutputFileName);
+                        bis = new BufferedInputStream(in);
+                        int read;
+                        while (!isCancelled() && (read = bis.read(buffer, 0, buffer.length)) > 0) {
+                            mAudioTrack.write(buffer, 0, read);
+                        }
+                    } catch (IOException e) {
+                        Log.e(TAG, "Failed to read the sound file into a byte array", e);
+                    } finally {
+                        try {
+                            if (in != null) {
+                                in.close();
+                            }
+                            if (bis != null) {
+                                bis.close();
+                            }
+                        } catch (IOException e) { /* ignore */}
+
+                        mAudioTrack.release();
+                    }
+                } catch (IllegalStateException e) {
+                    Log.e(TAG, "Failed to start playback", e);
+                }
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Void aVoid) {
+                cleanup();
+            }
+
+            @Override
+            protected void onCancelled() {
+                cleanup();
+            }
+
+            private void cleanup() {
+                if (mListener != null) {
+                    mListener.onPlaybackStopped();
+                }
+                mState = State.IDLE;
+                mPlayingAsyncTask = null;
+            }
+        };
+
+        mPlayingAsyncTask.execute();
+    }
+
+    public interface OnVoicePlaybackStateChangedListener {
+
+        /**
+         * Called when the playback of the audio file ends. This should be called on the UI thread.
+         */
+        void onPlaybackStopped();
+    }
+
+    /**
+     * Cleans up some resources related to {@link AudioTrack} and {@link AudioRecord}
+     */
+    public void cleanup() {
+        Log.d(TAG, "cleanup() is called");
+        stopPlaying();
+        stopRecording();
+    }
+}
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/UIAnimation.java b/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/UIAnimation.java
new file mode 100644
index 0000000..7ce2fd5
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/java/com/example/android/wearable/speaker/UIAnimation.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015 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.speaker;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.ImageView;
+
+/**
+ * A helper class to provide a simple animation when user selects any of the three icons on the
+ * main UI.
+ */
+public class UIAnimation {
+
+    private AnimatorSet mCurrentAnimator;
+    private final int[] mLargeDrawables = new int[]{R.drawable.ic_mic_120dp,
+            R.drawable.ic_play_arrow_120dp, R.drawable.ic_audiotrack_120dp};
+    private final ImageView[] mThumbs;
+    private ImageView expandedImageView;
+    private final View mContainerView;
+    private final int mAnimationDurationTime;
+
+    private UIStateListener mListener;
+    private UIState mState = UIState.HOME;
+
+    public UIAnimation(View containerView, ImageView[] thumbs, ImageView expandedView,
+            int animationDuration, UIStateListener listener) {
+        mContainerView = containerView;
+        mThumbs = thumbs;
+        expandedImageView = expandedView;
+        mAnimationDurationTime = animationDuration;
+        mListener = listener;
+
+        mThumbs[0].setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                zoomImageFromThumb(0);
+            }
+        });
+
+        mThumbs[1].setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                zoomImageFromThumb(1);
+            }
+        });
+
+        mThumbs[2].setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                zoomImageFromThumb(2);
+            }
+        });
+    }
+
+    private void zoomImageFromThumb(final int index) {
+        int imageResId = mLargeDrawables[index];
+        final ImageView thumbView = mThumbs[index];
+        if (mCurrentAnimator != null) {
+            return;
+        }
+
+        expandedImageView.setImageResource(imageResId);
+
+        final Rect startBounds = new Rect();
+        final Rect finalBounds = new Rect();
+        final Point globalOffset = new Point();
+        thumbView.getGlobalVisibleRect(startBounds);
+        mContainerView.getGlobalVisibleRect(finalBounds, globalOffset);
+        startBounds.offset(-globalOffset.x, -globalOffset.y);
+        finalBounds.offset(-globalOffset.x, -globalOffset.y);
+        float startScale;
+        if ((float) finalBounds.width() / finalBounds.height()
+                > (float) startBounds.width() / startBounds.height()) {
+            startScale = (float) startBounds.height() / finalBounds.height();
+            float startWidth = startScale * finalBounds.width();
+            float deltaWidth = (startWidth - startBounds.width()) / 2;
+            startBounds.left -= deltaWidth;
+            startBounds.right += deltaWidth;
+        } else {
+            startScale = (float) startBounds.width() / finalBounds.width();
+            float startHeight = startScale * finalBounds.height();
+            float deltaHeight = (startHeight - startBounds.height()) / 2;
+            startBounds.top -= deltaHeight;
+            startBounds.bottom += deltaHeight;
+        }
+
+        for(int k=0; k < 3; k++) {
+            mThumbs[k].setAlpha(0f);
+        }
+        expandedImageView.setVisibility(View.VISIBLE);
+
+        expandedImageView.setPivotX(0f);
+        expandedImageView.setPivotY(0f);
+
+        AnimatorSet zommInAnimator = new AnimatorSet();
+        zommInAnimator.play(ObjectAnimator
+                .ofFloat(expandedImageView, View.X, startBounds.left, finalBounds.left)).with(
+                ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top, finalBounds
+                        .top)).with(
+                ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f))
+                .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f));
+        zommInAnimator.setDuration(mAnimationDurationTime);
+        zommInAnimator.setInterpolator(new DecelerateInterpolator());
+        zommInAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCurrentAnimator = null;
+                if (mListener != null) {
+                    mState = UIState.getUIState(index);
+                    mListener.onUIStateChanged(mState);
+                }
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCurrentAnimator = null;
+            }
+        });
+        zommInAnimator.start();
+        mCurrentAnimator = zommInAnimator;
+
+        final float startScaleFinal = startScale;
+        expandedImageView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if (mCurrentAnimator != null) {
+                    return;
+                }
+                AnimatorSet zoomOutAnimator = new AnimatorSet();
+                zoomOutAnimator.play(ObjectAnimator
+                        .ofFloat(expandedImageView, View.X, startBounds.left))
+                        .with(ObjectAnimator
+                                .ofFloat(expandedImageView,
+                                        View.Y, startBounds.top))
+                        .with(ObjectAnimator
+                                .ofFloat(expandedImageView,
+                                        View.SCALE_X, startScaleFinal))
+                        .with(ObjectAnimator
+                                .ofFloat(expandedImageView,
+                                        View.SCALE_Y, startScaleFinal));
+                zoomOutAnimator.setDuration(mAnimationDurationTime);
+                zoomOutAnimator.setInterpolator(new DecelerateInterpolator());
+                zoomOutAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        for (int k = 0; k < 3; k++) {
+                            mThumbs[k].setAlpha(1f);
+                        }
+                        expandedImageView.setVisibility(View.GONE);
+                        mCurrentAnimator = null;
+                        if (mListener != null) {
+                            mState = UIState.HOME;
+                            mListener.onUIStateChanged(mState);
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        thumbView.setAlpha(1f);
+                        expandedImageView.setVisibility(View.GONE);
+                        mCurrentAnimator = null;
+                    }
+                });
+                zoomOutAnimator.start();
+                mCurrentAnimator = zoomOutAnimator;
+            }
+        });
+    }
+
+    public enum UIState {
+        MIC_UP(0), SOUND_UP(1), MUSIC_UP(2), HOME(3);
+        private int mState;
+
+        UIState(int state) {
+            mState = state;
+        }
+
+        static UIState getUIState(int state) {
+            for(UIState uiState : values()) {
+                if (uiState.mState == state) {
+                    return uiState;
+                }
+            }
+           return null;
+        }
+    }
+
+    public interface UIStateListener {
+        void onUIStateChanged(UIState state);
+    }
+
+    public void transitionToHome() {
+        if (mState == UIState.HOME) {
+            return;
+        }
+        expandedImageView.callOnClick();
+
+    }
+}
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/circle.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/circle.xml
new file mode 100644
index 0000000..df4abe5
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/circle.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
+    <size android:width="100dp"
+        android:height="100dp"/>
+    <stroke
+        android:width="3dp"
+        android:color="@color/circle_color"/>
+    <solid android:color="@color/circle_color"/>
+</shape>
\ No newline at end of file
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_audiotrack_120dp.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_audiotrack_120dp.xml
new file mode 100644
index 0000000..0971d96
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_audiotrack_120dp.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<vector android:height="120dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="120dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/large_icons_color" android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
+</vector>
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_audiotrack_32dp.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_audiotrack_32dp.xml
new file mode 100644
index 0000000..70de799
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_audiotrack_32dp.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<vector android:height="32dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/small_icons_color" android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
+</vector>
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_mic_120dp.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_mic_120dp.xml
new file mode 100644
index 0000000..15e798a
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_mic_120dp.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<vector android:height="120dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="120dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/large_icons_color" android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zm5.3,-3c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_mic_32dp.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_mic_32dp.xml
new file mode 100644
index 0000000..c9417dd
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_mic_32dp.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<vector android:height="32dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/small_icons_color" android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zm5.3,-3c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_play_arrow_120dp.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_play_arrow_120dp.xml
new file mode 100644
index 0000000..e87660d
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_play_arrow_120dp.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<vector android:height="120dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="120dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/large_icons_color" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_play_arrow_32dp.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_play_arrow_32dp.xml
new file mode 100644
index 0000000..9dd8678
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/drawable/ic_play_arrow_32dp.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<vector android:height="32dp" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/small_icons_color" android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/layout/main_activity.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/layout/main_activity.xml
new file mode 100644
index 0000000..7e004ad
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/layout/main_activity.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+
+    <RelativeLayout
+        android:id="@+id/container2"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/background_color">
+
+        <View
+            android:id="@+id/circle"
+            android:layout_width="140dp"
+            android:layout_height="140dp"
+            android:layout_centerInParent="true"
+            android:background="@drawable/circle" />
+
+        <View
+            android:id="@+id/center"
+            android:layout_width="1dp"
+            android:layout_height="1dp"
+            android:layout_centerInParent="true"
+            android:visibility="invisible" />
+
+        <ImageView
+            android:id="@+id/mic"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_above="@+id/center"
+            android:layout_centerHorizontal="true"
+            android:layout_marginBottom="13dp"
+            android:src="@drawable/ic_mic_32dp" />
+
+        <ImageView
+            android:id="@+id/play"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/center"
+            android:layout_marginRight="13dp"
+            android:layout_marginTop="12dp"
+            android:layout_toLeftOf="@+id/center"
+            android:src="@drawable/ic_play_arrow_32dp" />
+
+        <ImageView
+            android:id="@+id/music"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/center"
+            android:layout_marginLeft="13dp"
+            android:layout_marginTop="12dp"
+            android:layout_toRightOf="@+id/center"
+            android:src="@drawable/ic_audiotrack_32dp" />
+
+        <ProgressBar
+            android:id="@+id/progress"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@+id/circle"
+            android:layout_alignEnd="@+id/circle"
+            android:layout_below="@+id/circle"
+            android:progressTint="@color/progressbar_tint"
+            android:progressBackgroundTint="@color/progressbar_background_tint"
+            android:layout_marginTop="5dp"
+            android:visibility="invisible" />
+    </RelativeLayout>
+
+    <ImageView
+        android:id="@+id/expanded"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scaleType="center"
+        android:visibility="invisible" />
+</FrameLayout>
\ No newline at end of file
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-hdpi/ic_launcher.png b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-mdpi/ic_launcher.png b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-xhdpi/ic_launcher.png b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/raw/sound.mp3 b/wearable/wear/WearSpeakerSample/wear/src/main/res/raw/sound.mp3
new file mode 100644
index 0000000..94e3d0e
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/raw/sound.mp3
Binary files differ
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/values/colors.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/values/colors.xml
new file mode 100644
index 0000000..e9b8605
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<resources>
+    <color name="small_icons_color">#FFF3E0</color>
+    <color name="large_icons_color">#FFF3E0</color>
+    <color name="background_color">#FF9100</color>
+    <color name="circle_color">#E65100</color>
+    <color name="progressbar_tint">#FFD180</color>
+    <color name="progressbar_background_tint">#E65100</color>
+</resources>
\ No newline at end of file
diff --git a/wearable/wear/WearSpeakerSample/wear/src/main/res/values/strings.xml b/wearable/wear/WearSpeakerSample/wear/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cc342b5
--- /dev/null
+++ b/wearable/wear/WearSpeakerSample/wear/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2015 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.
+  -->
+<resources>
+    <string name="app_name">Wear Speaker Sample</string>
+    <string name="exiting_for_permissions">Recording Audio permission is required, exiting now!</string>
+    <string name="no_speaker_supported">Speaker is not supported</string>
+</resources>
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml
index 76f0198..9d88b39 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml
@@ -16,17 +16,21 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.xyztouristattractions" >
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.example.android.xyztouristattractions">
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />
 
     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:theme="@style/XYZAppTheme" >
+        android:theme="@style/XYZAppTheme"
+        android:fullBackupContent="true">
 
         <activity
             android:name=".ui.AttractionListActivity"
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java
index 50be362..62ddbf9 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java
@@ -18,7 +18,6 @@
 
 import android.net.Uri;
 
-import com.example.android.xyztouristattractions.BuildConfig;
 import com.example.android.xyztouristattractions.common.Attraction;
 import com.google.android.gms.location.Geofence;
 import com.google.android.gms.maps.model.LatLng;
@@ -126,11 +125,8 @@
 
     public static String getClosestCity(LatLng curLatLng) {
         if (curLatLng == null) {
-            // In debug build still return a city so some data is displayed
-            if (BuildConfig.DEBUG) {
-                return TEST_CITY;
-            }
-            return null;
+            // If location is unknown return test city so some data is shown
+            return TEST_CITY;
         }
 
         double minDistance = 0;
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java
index 4112a65..3122d56 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java
@@ -150,6 +150,11 @@
      */
     private void addGeofencesInternal() {
         Log.v(TAG, ACTION_ADD_GEOFENCES);
+
+        if (!Utils.checkFineLocationPermission(this)) {
+            return;
+        }
+
         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
                 .addApi(LocationServices.API)
                 .build();
@@ -202,6 +207,11 @@
      */
     private void requestLocationInternal() {
         Log.v(TAG, ACTION_REQUEST_LOCATION);
+
+        if (!Utils.checkFineLocationPermission(this)) {
+            return;
+        }
+
         GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
                 .addApi(LocationServices.API)
                 .build();
@@ -358,7 +368,7 @@
                 .setSmallIcon(R.drawable.ic_stat_maps_pin_drop)
                 .setContentIntent(pendingIntent)
                 .setDeleteIntent(deletePendingIntent)
-                .setColor(getResources().getColor(R.color.colorPrimary))
+                .setColor(getResources().getColor(R.color.colorPrimary, getTheme()))
                 .setCategory(Notification.CATEGORY_RECOMMENDATION)
                 .setAutoCancel(true);
 
@@ -466,6 +476,7 @@
             dataMap.getDataMap().putDataMapArrayList(Constants.EXTRA_ATTRACTIONS, attractionsData);
             dataMap.getDataMap().putLong(Constants.EXTRA_TIMESTAMP, new Date().getTime());
             PutDataRequest request = dataMap.asPutDataRequest();
+            request.setUrgent();
 
             // Send the data over
             DataApi.DataItemResult result =
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java
index 8d2908c..8c23f3d 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java
@@ -16,11 +16,16 @@
 
 package com.example.android.xyztouristattractions.ui;
 
+import android.Manifest;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.View;
 import android.widget.Toast;
 
 import com.example.android.xyztouristattractions.R;
@@ -31,7 +36,10 @@
  * The main tourist attraction activity screen which contains a list of
  * attractions sorted by distance.
  */
-public class AttractionListActivity extends AppCompatActivity {
+public class AttractionListActivity extends AppCompatActivity implements
+        ActivityCompat.OnRequestPermissionsResultCallback {
+
+    private static final int PERMISSION_REQ = 0;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -44,7 +52,23 @@
                     .commit();
         }
 
-        UtilityService.addGeofences(this);
+        // Check fine location permission has been granted
+        if (!Utils.checkFineLocationPermission(this)) {
+            // See if user has denied permission in the past
+            if (ActivityCompat.shouldShowRequestPermissionRationale(
+                    this, Manifest.permission.ACCESS_FINE_LOCATION)) {
+                // Show a simple snackbar explaining the request instead
+                showPermissionSnackbar();
+            } else {
+                // Otherwise request permission from user
+                if (savedInstanceState == null) {
+                    requestFineLocationPermission();
+                }
+            }
+        } else {
+            // Otherwise permission is granted (which is always the case on pre-M devices)
+            fineLocationPermissionGranted();
+        }
     }
 
     @Override
@@ -88,6 +112,51 @@
     }
 
     /**
+     * Permissions request result callback
+     */
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, String[] permissions, int[] grantResults) {
+        switch (requestCode) {
+            case PERMISSION_REQ:
+                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    fineLocationPermissionGranted();
+                }
+        }
+    }
+
+    /**
+     * Request the fine location permission from the user
+     */
+    private void requestFineLocationPermission() {
+        ActivityCompat.requestPermissions(this,
+                new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQ);
+    }
+
+    /**
+     * Run when fine location permission has been granted
+     */
+    private void fineLocationPermissionGranted() {
+        UtilityService.addGeofences(this);
+        UtilityService.requestLocation(this);
+    }
+
+    /**
+     * Show a permission explanation snackbar
+     */
+    private void showPermissionSnackbar() {
+        Snackbar.make(
+                findViewById(R.id.container), R.string.permission_explanation, Snackbar.LENGTH_LONG)
+                .setAction(R.string.permission_explanation_action, new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        requestFineLocationPermission();
+                    }
+                })
+                .show();
+    }
+
+    /**
      * Show a basic debug dialog to provide more info on the built-in debug
      * options.
      */
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java
index 28f9127..71439b1 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java
@@ -23,7 +23,6 @@
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.support.v4.content.LocalBroadcastManager;
-import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -60,6 +59,7 @@
     private AttractionAdapter mAdapter;
     private LatLng mLatestLocation;
     private int mImageSize;
+    private boolean mItemClicked;
 
     public AttractionListFragment() {}
 
@@ -79,8 +79,6 @@
                 (AttractionsRecyclerView) view.findViewById(android.R.id.list);
         recyclerView.setEmptyView(view.findViewById(android.R.id.empty));
         recyclerView.setHasFixedSize(true);
-        recyclerView.setLayoutManager(new GridLayoutManager(
-                getActivity(), getResources().getInteger(R.integer.list_columns)));
         recyclerView.setAdapter(mAdapter);
 
         return view;
@@ -89,6 +87,7 @@
     @Override
     public void onResume() {
         super.onResume();
+        mItemClicked = false;
         LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
                 mBroadcastReceiver, UtilityService.getLocationUpdatedIntentFilter());
     }
@@ -189,9 +188,12 @@
 
         @Override
         public void onItemClick(View view, int position) {
-            View heroView = view.findViewById(android.R.id.icon);
-            DetailActivity.launch(
-                    getActivity(), mAdapter.mAttractionList.get(position).name, heroView);
+            if (!mItemClicked) {
+                mItemClicked = true;
+                View heroView = view.findViewById(android.R.id.icon);
+                DetailActivity.launch(
+                        getActivity(), mAdapter.mAttractionList.get(position).name, heroView);
+            }
         }
     }
 
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml
index 8661610..17c9f66 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml
@@ -14,12 +14,13 @@
   limitations under the License.
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.design.widget.CoordinatorLayout
+
+    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"
-    android:orientation="vertical"
     tools:context=".MainActivity">
 
-</LinearLayout>
\ No newline at end of file
+</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml
index 94d110a..c92a2a8 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/fragment_main.xml
@@ -16,6 +16,7 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
@@ -25,7 +26,9 @@
         android:id="@android:id/list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:scrollbars="vertical" />
+        android:scrollbars="vertical"
+        app:layoutManager="GridLayoutManager"
+        app:spanCount="@integer/list_columns" />
 
     <TextView
         android:id="@android:id/empty"
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml
index 5f3ee14..fede01e 100644
--- a/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml
+++ b/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml
@@ -45,5 +45,7 @@
     </string>
     <string name="action_test_toggle_geofence">Toggle Geofence Trigger</string>
     <string name="action_map">Show on Map</string>
+    <string name="permission_explanation">Allow this app to use your location to show distance to attractions?</string>
+    <string name="permission_explanation_action">Let\'s do it!</string>
 
 </resources>
diff --git a/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java b/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java
index 70e05bf..6fa5129 100644
--- a/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java
+++ b/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java
@@ -16,13 +16,16 @@
 
 package com.example.android.xyztouristattractions.common;
 
+import android.Manifest;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.preference.PreferenceManager;
+import android.support.v4.content.ContextCompat;
 import android.util.Log;
 import android.view.Display;
 
@@ -54,6 +57,15 @@
     private static final String DISTANCE_M_POSTFIX = "m";
 
     /**
+     * Check if the app has access to fine location permission. On pre-M
+     * devices this will always return true.
+     */
+    public static boolean checkFineLocationPermission(Context context) {
+        return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(
+                context, Manifest.permission.ACCESS_FINE_LOCATION);
+    }
+
+    /**
      * Calculate distance between two LatLng points and format it nicely for
      * display. As this is a sample, it only statically supports metric units.
      * A production app should check locale and support the correct units.
@@ -90,6 +102,10 @@
      * Fetch the location from app preferences.
      */
     public static LatLng getLocation(Context context) {
+        if (!checkFineLocationPermission(context)) {
+            return null;
+        }
+
         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
         Long lat = prefs.getLong(PREFERENCES_LAT, Long.MAX_VALUE);
         Long lng = prefs.getLong(PREFERENCES_LNG, Long.MAX_VALUE);
diff --git a/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml b/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml
index 80d0c92..d353b29 100644
--- a/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml
+++ b/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml
@@ -16,10 +16,14 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.example.android.xyztouristattractions" >
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.example.android.xyztouristattractions">
 
     <uses-feature android:name="android.hardware.type.watch" />
 
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />
+
     <uses-sdk
         android:minSdkVersion="21"
         android:targetSdkVersion="22" />
diff --git a/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java b/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java
index d228251..ea071f0 100644
--- a/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java
+++ b/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java
@@ -17,11 +17,12 @@
 package com.example.android.xyztouristattractions.service;
 
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
 import android.util.Log;
 
 import com.example.android.xyztouristattractions.R;
@@ -39,7 +40,6 @@
 import com.google.android.gms.wearable.WearableListenerService;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -106,21 +106,20 @@
         PendingIntent deletePendingIntent = PendingIntent.getService(
                 this, 0, UtilityService.getClearRemoteNotificationsIntent(this), 0);
 
-        Notification notification = new Notification.Builder(this)
+        Notification notification = new NotificationCompat.Builder(this)
                 .setContentText(getResources().getQuantityString(
                         R.plurals.attractions_found, count, count))
                 .setSmallIcon(R.mipmap.ic_launcher)
                 .setDeleteIntent(deletePendingIntent)
-                .addAction(R.drawable.ic_full_explore,
+                .addAction(new NotificationCompat.Action.Builder(R.drawable.ic_full_explore,
                         getString(R.string.action_explore),
-                        pendingIntent)
-                .extend(new Notification.WearableExtender()
+                        pendingIntent).build())
+                .extend(new NotificationCompat.WearableExtender()
                         .setBackground(bitmap)
                 )
                 .build();
 
-        NotificationManager notificationManager =
-                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
         notificationManager.notify(Constants.WEAR_NOTIFICATION_ID, notification);
 
         googleApiClient.disconnect();
diff --git a/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml b/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml
index ac01509..45495ad 100644
--- a/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml
+++ b/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml
@@ -26,4 +26,4 @@
     android:text="@string/action_open"
     android:maxLines="1"
     android:color="@color/colorPrimary"
-    app:rippleColor="@color/colorAccent" />
+    app:buttonRippleColor="@color/colorAccent" />
diff --git a/wearable/wear/XYZTouristAttractions/gradle/wrapper/gradle-wrapper.properties b/wearable/wear/XYZTouristAttractions/gradle/wrapper/gradle-wrapper.properties
index 7d3b483..4364027 100644
--- a/wearable/wear/XYZTouristAttractions/gradle/wrapper/gradle-wrapper.properties
+++ b/wearable/wear/XYZTouristAttractions/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-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
diff --git a/wearable/wear/XYZTouristAttractions/template-params.xml b/wearable/wear/XYZTouristAttractions/template-params.xml
index cfa368f..69f2f77 100644
--- a/wearable/wear/XYZTouristAttractions/template-params.xml
+++ b/wearable/wear/XYZTouristAttractions/template-params.xml
@@ -19,7 +19,8 @@
     <group>Wearable</group>
     <package>com.example.android.xyztouristattractions</package>
     <minSdk>18</minSdk>
-    <targetSdkVersion>22</targetSdkVersion>
+    <targetSdkVersion>23</targetSdkVersion>
+    <targetSdkVersionWear>22</targetSdkVersionWear>
 
     <wearable>
         <has_handheld_app>true</has_handheld_app>
@@ -27,11 +28,12 @@
 
     <dependency>com.google.android.gms:play-services-location</dependency>
     <dependency>com.google.maps.android:android-maps-utils:0.3.4</dependency>
-    <dependency>com.github.bumptech.glide:glide:3.6.0</dependency>
-    <dependency>com.android.support:appcompat-v7:22.2.0</dependency>
-    <dependency>com.android.support:recyclerview-v7:22.2.0</dependency>
-    <dependency>com.android.support:design:22.2.0</dependency>
+    <dependency>com.github.bumptech.glide:glide:3.6.1</dependency>
+    <dependency>com.android.support:appcompat-v7:23.0.0</dependency>
+    <dependency>com.android.support:recyclerview-v7:23.0.0</dependency>
+    <dependency>com.android.support:design:23.0.0</dependency>
     <dependency_wearable>com.google.android.gms:play-services-location</dependency_wearable>
+    <dependency_shared>com.android.support:support-v13:23.0.0</dependency_shared>
     <dependency_shared>com.google.android.gms:play-services-wearable</dependency_shared>
     <dependency_shared>com.google.android.gms:play-services-location</dependency_shared>
     <dependency_shared>com.google.maps.android:android-maps-utils:0.3.4</dependency_shared>
@@ -83,6 +85,8 @@
             <android>android.support.v7.appcompat</android>
             <android>android.support.v7.widget.RecyclerView</android>
             <android>android.support.v7.widget.GridLayoutManager</android>
+            <android>android.support.v4.content.ContextCompat</android>
+            <android>android.support.v4.app.ActivityCompat</android>
             <android>android.support.design.widget.FloatingActionButton</android>
             <android>android.support.design.widget.CoordinatorLayout</android>
             <android>com.google.android.gms.common.api.GoogleApiClient</android>