am c39b832b: am 95318487: Merge "Enable ARCH_ARM_USE_INTRINSICS for support lib."

* commit 'c39b832b00a63bfb1baabd8f5e439f05d688d440':
  Enable ARCH_ARM_USE_INTRINSICS for support lib.
diff --git a/build.gradle b/build.gradle
index ae2f294..da14cf5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,8 +9,8 @@
     }
 }
 
-ext.supportVersion = '19.2.0'
-ext.extraVersion = 6
+ext.supportVersion = '20.0.0'
+ext.extraVersion = 7
 ext.supportRepoOut = ''
 
 /*
diff --git a/settings.gradle b/settings.gradle
index 2104257..15055f8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -13,8 +13,17 @@
 include ':support-mediarouter-v7'
 project(':support-mediarouter-v7').projectDir = new File(rootDir, 'v7/mediarouter')
 
+include ':support-palette-v7'
+project(':support-palette-v7').projectDir = new File(rootDir, 'v7/palette')
+
 include ':support-recyclerview-v7'
 project(':support-recyclerview-v7').projectDir = new File(rootDir, 'v7/recyclerview')
 
 include ':support-v13'
 project(':support-v13').projectDir = new File(rootDir, 'v13')
+
+include ':support-appcompat-v14'
+project(':support-appcompat-v14').projectDir = new File(rootDir, 'v14/appcompat')
+
+include ':support-leanback-v17'
+project(':support-leanback-v17').projectDir = new File(rootDir, 'v17/leanback')
diff --git a/tests/Android.mk b/tests/Android.mk
index b14ca93..7bd3b00 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, java)
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target android-support-v4
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := AndroidSupportTests
 
diff --git a/tests/java/android/support/v4/speech/tts/TTSImplementationV1Test.java b/tests/java/android/support/v4/speech/tts/TTSImplementationV1Test.java
new file mode 100644
index 0000000..f006042
--- /dev/null
+++ b/tests/java/android/support/v4/speech/tts/TTSImplementationV1Test.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.UtteranceProgressListener;
+import android.support.v4.speech.tts.TextToSpeechClient.ConnectionCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.EngineStatus;
+import android.support.v4.speech.tts.TextToSpeechClient.RequestCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.UtteranceId;
+import android.test.InstrumentationTestCase;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+
+/**
+ * Tests for {@link TTSImplementationV1} class.
+ */
+public class TTSImplementationV1Test extends InstrumentationTestCase {
+    @Mock TextToSpeech mOldClientMock;
+    @Mock RequestCallbacks mRequestCallbacks;
+    @Mock ConnectionCallbacks mConnectionCallbacks;
+
+    TTSImplementationV1 mImplementationV1;
+    TextToSpeechClient mClient;
+
+    final String mUtterance = "text";
+    final UtteranceId mUtteranceId = new UtteranceId();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        System.setProperty("dexmaker.dexcache", getInstrumentation().getTargetContext().
+                getCacheDir().getPath());
+        MockitoAnnotations.initMocks(this);
+
+        mImplementationV1 = new TTSImplementationV1(mOldClientMock);
+        mClient = new TextToSpeechClient(mImplementationV1,
+            getInstrumentation().getContext(), "test", false, mRequestCallbacks,
+            mConnectionCallbacks);
+    }
+
+    /** Test basic setup success */
+    public void testSetupSuccess() {
+        mClient.connect();
+        mImplementationV1.mOnInitListener.onInit(TextToSpeech.SUCCESS);
+
+        Mockito.verify(mConnectionCallbacks).onConnectionSuccess();
+    }
+
+    /** Test disconnect */
+    public void testDisconnect() {
+        assertTrue(!mClient.isConnected());
+        mClient.connect();
+        mImplementationV1.mOnInitListener.onInit(TextToSpeech.SUCCESS);
+
+        assertTrue(mClient.isConnected());
+
+        mClient.disconnect();
+        Mockito.verify(mOldClientMock).shutdown();
+
+        assertTrue(!mClient.isConnected());
+    }
+
+    /** Test basic setup failure */
+    public void testSetupFailure() {
+        mClient.connect();
+        mImplementationV1.mOnInitListener.onInit(TextToSpeech.ERROR);
+
+        Mockito.verify(mConnectionCallbacks).onConnectionFailure();
+    }
+
+    /** Mock V1 client to support a set of locales
+     *
+     * - en-US embedded and network
+     * - en-GB network
+     * - en-IN embedded
+     * - en embedded
+     * - de embedded
+     */
+    void mockFewVoices() {
+        // Mock the support for set of locales
+        Mockito.when(mOldClientMock.isLanguageAvailable(Mockito.any(Locale.class))).thenReturn(
+                TextToSpeech.LANG_NOT_SUPPORTED);
+        Mockito.when(mOldClientMock.isLanguageAvailable(Locale.US)).thenReturn(
+                TextToSpeech.LANG_COUNTRY_AVAILABLE);
+        Mockito.when(mOldClientMock.isLanguageAvailable(Locale.UK)).thenReturn(
+                TextToSpeech.LANG_COUNTRY_AVAILABLE);
+        Mockito.when(mOldClientMock.isLanguageAvailable(new Locale("en", "IN"))).thenReturn(
+                TextToSpeech.LANG_COUNTRY_AVAILABLE);
+        Mockito.when(mOldClientMock.isLanguageAvailable(Locale.ENGLISH)).thenReturn(
+                TextToSpeech.LANG_AVAILABLE);
+        Mockito.when(mOldClientMock.isLanguageAvailable(Locale.GERMANY)).thenReturn(
+                TextToSpeech.LANG_AVAILABLE); // "de-DE" not supported, only "de".
+        Mockito.when(mOldClientMock.isLanguageAvailable(Locale.GERMAN)).thenReturn(
+                TextToSpeech.LANG_AVAILABLE);
+
+        HashSet<String> featuresEmbedded = new HashSet<String>();
+        featuresEmbedded.add(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+        HashSet<String> featuresNetwork = new HashSet<String>();
+        featuresNetwork.add(TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+        HashSet<String> featuresBoth = new HashSet<String>();
+        featuresBoth.add(TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+        featuresBoth.add(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+
+        Mockito.when(mOldClientMock.getFeatures(Locale.US)).thenReturn(featuresBoth);
+        Mockito.when(mOldClientMock.getFeatures(Locale.UK)).thenReturn(featuresNetwork);
+        Mockito.when(mOldClientMock.getFeatures(new Locale("en", "IN"))).thenReturn(
+                featuresEmbedded);
+        Mockito.when(mOldClientMock.getFeatures(Locale.ENGLISH)).thenReturn(featuresEmbedded);
+        Mockito.when(mOldClientMock.getFeatures(Locale.GERMAN)).thenReturn(null);
+        Mockito.when(mOldClientMock.getFeatures(Locale.GERMANY)).thenReturn(null);
+    }
+
+    /** Connect V2 client and caputure {@link UtteranceProgressListener} instance */
+    UtteranceProgressListener connectSuccessfuly() {
+        // Capture UtteranceProgressListener
+        final ArgumentCaptor<UtteranceProgressListener> listenerCaptor =
+                ArgumentCaptor.forClass(UtteranceProgressListener.class);
+        Mockito.when(mOldClientMock.setOnUtteranceProgressListener(listenerCaptor.capture()))
+            .thenReturn(TextToSpeech.SUCCESS);
+
+        // Connect and get status
+        mClient.connect();
+        mImplementationV1.mOnInitListener.onInit(TextToSpeech.SUCCESS);
+
+        return listenerCaptor.getValue();
+    }
+
+    /** Test generation of a {@link EngineStatus} */
+    public void testGetEngineData() {
+        mockFewVoices();
+
+        // Connect and get status
+        mClient.connect();
+        mImplementationV1.mOnInitListener.onInit(TextToSpeech.SUCCESS);
+
+        EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+
+        // Create hash-map with expected voices
+        final HashMap<Locale, ArrayList<String>> expectedVoices =
+                new HashMap<Locale, ArrayList<String>>();
+
+        ArrayList<String> l = new ArrayList<String>();
+        l.add(TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+        l.add(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+        expectedVoices.put(Locale.US, l);
+
+        l = new ArrayList<String>();
+        l.add(TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
+        expectedVoices.put(Locale.UK, l);
+
+        l = new ArrayList<String>();
+        l.add(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+        expectedVoices.put(new Locale("en", "IN"), l);
+
+        l = new ArrayList<String>();
+        l.add(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+        expectedVoices.put(Locale.ENGLISH, l);
+
+        l = new ArrayList<String>();
+        l.add(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
+        expectedVoices.put(Locale.GERMAN, l);
+
+        assertEquals(6, engineStatus.getVoices().size());
+        for (VoiceInfo info : engineStatus.getVoices()) {
+            ArrayList<String> features = expectedVoices.get(info.getLocale());
+            assertNotNull(features);
+            if (info.getRequiresNetworkConnection()) {
+                assertTrue(features.remove(TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS));
+            } else {
+                assertTrue(info.getName(),
+                        features.remove(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS));
+            }
+            if (features.isEmpty()) {
+                assertTrue(expectedVoices.remove(info.getLocale()) != null);
+            }
+        }
+
+        // Make sure all expected voices were found
+        assertTrue(expectedVoices.isEmpty());
+    }
+
+    /** Test successful {@link TextToSpeechClient#queueSpeak} call */
+    public void testQueueSpeakSuccess() {
+        mockFewVoices();
+        UtteranceProgressListener listener = connectSuccessfuly();
+        assertNotNull(listener);
+
+        // Mock the speak call
+        Mockito.when(mOldClientMock.speak(Mockito.anyString(), Mockito.anyInt(),
+                (HashMap<String, String>) Mockito.any()))
+                .thenReturn(TextToSpeech.SUCCESS);
+
+        final EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+        final RequestConfig rc = RequestConfigHelper.highestQuality(engineStatus, true,
+                new RequestConfigHelper.ExactLocaleMatcher(Locale.US));
+
+        // Call speak
+        mClient.queueSpeak(mUtterance, mUtteranceId, rc, null);
+
+        // Call back to the listener
+        listener.onStart(mUtteranceId.toUniqueString());
+        listener.onDone(mUtteranceId.toUniqueString());
+
+        // Verify V1 client calls
+        Mockito.verify(mOldClientMock).setLanguage(Locale.US);
+        Mockito.verify(mOldClientMock).setPitch(1.0f);
+        // We never set speed, so make sure we use value from settings
+        Mockito.verify(mOldClientMock).setSpeechRate(mImplementationV1.getDefaultSpeechRate());
+
+        ArgumentCaptor<HashMap> paramsCaptor =
+                ArgumentCaptor.forClass(HashMap.class);
+        Mockito.verify(mOldClientMock).speak(Mockito.eq(mUtterance),
+                Mockito.eq(TextToSpeech.QUEUE_ADD), paramsCaptor.capture());
+
+        HashMap<String, String> params = paramsCaptor.getValue();
+        assertEquals(mUtteranceId.toUniqueString(), params.get(
+                TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID));
+        assertEquals("true", params.get(
+                TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS));
+
+
+        // Verify callback calls
+        Mockito.verify(mRequestCallbacks).onSynthesisStart(mUtteranceId);
+        Mockito.verify(mRequestCallbacks).onSynthesisSuccess(mUtteranceId);
+    }
+
+    /** Test successful {@link TextToSpeechClient#queueSpeak} call, with synthesis failure */
+    public void testQueueSpeakSynthesisFailure() {
+        mockFewVoices();
+        UtteranceProgressListener listener = connectSuccessfuly();
+        assertNotNull(listener);
+
+        // Mock the speak call
+        Mockito.when(mOldClientMock.speak(Mockito.anyString(), Mockito.anyInt(),
+                (HashMap<String, String>) Mockito.any()))
+                .thenReturn(TextToSpeech.SUCCESS);
+
+        final EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+        final RequestConfig rcBase = RequestConfigHelper.highestQuality(engineStatus, true,
+                new RequestConfigHelper.ExactLocaleMatcher(Locale.US));
+
+        // Set speed and pitch to 0.1f
+        final RequestConfig rc = RequestConfig.Builder.newBuilder(rcBase)
+                .setVoiceParam(TextToSpeechClient.Params.SPEECH_SPEED, 0.1f)
+                .setVoiceParam(TextToSpeechClient.Params.SPEECH_PITCH, 0.1f)
+                .build();
+
+        // Call speak
+         mClient.queueSpeak(mUtterance, mUtteranceId, rc, null);
+
+        // Call back to the listener
+        listener.onStart(mUtteranceId.toUniqueString());
+        listener.onError(mUtteranceId.toUniqueString());
+
+        // Verify V1 client calls
+        Mockito.verify(mOldClientMock).setLanguage(Locale.US);
+        Mockito.verify(mOldClientMock).setPitch(0.1f);
+        Mockito.verify(mOldClientMock).setSpeechRate(0.1f);
+        Mockito.verify(mOldClientMock).speak(Mockito.eq(mUtterance),
+                Mockito.eq(TextToSpeech.QUEUE_ADD),
+                (HashMap<String, String>) Mockito.any());
+
+        // Verify callback calls
+        Mockito.verify(mRequestCallbacks).onSynthesisStart(mUtteranceId);
+        Mockito.verify(mRequestCallbacks).onSynthesisFailure(mUtteranceId,
+                TextToSpeechClient.Status.ERROR_UNKNOWN);
+    }
+
+    /** Test failing {@link TextToSpeechClient#queueSpeak} call */
+    public void testQueueSpeakFailure() {
+        mockFewVoices();
+        connectSuccessfuly();
+
+        // Mock the speak call with error
+        Mockito.when(mOldClientMock.speak(Mockito.anyString(), Mockito.anyInt(),
+                (HashMap<String, String>) Mockito.any()))
+                .thenReturn(TextToSpeech.ERROR);
+
+        final EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+        final RequestConfig rc = RequestConfigHelper.highestQuality(engineStatus, true,
+                new RequestConfigHelper.ExactLocaleMatcher(Locale.US));
+
+        // Call speak
+        mClient.queueSpeak(mUtterance, mUtteranceId, rc, null);
+
+        // Verify V1 client calls
+        Mockito.verify(mOldClientMock).speak(Mockito.eq(mUtterance),
+                Mockito.eq(TextToSpeech.QUEUE_ADD),
+                (HashMap<String, String>) Mockito.any());
+
+        // Verify callback calls
+        Mockito.verify(mRequestCallbacks).onSynthesisFailure(mUtteranceId,
+                TextToSpeechClient.Status.ERROR_UNKNOWN);
+    }
+
+    /** Test successful {@link TextToSpeechClient#queueSynthesizeToFile} call */
+    public void testQueueSynthesizeToFileSucces() {
+        mockFewVoices();
+        UtteranceProgressListener listener = connectSuccessfuly();
+        assertNotNull(listener);
+
+        // Mock the speak call
+        Mockito.when(mOldClientMock.synthesizeToFile(Mockito.anyString(),
+                (HashMap<String, String>) Mockito.any(), Mockito.anyString()))
+                .thenReturn(TextToSpeech.SUCCESS);
+
+        final EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+        final RequestConfig rc = RequestConfigHelper.highestQuality(engineStatus, true,
+                new RequestConfigHelper.ExactLocaleMatcher(Locale.US));
+
+        // Call speak
+        File output = new File("test");
+        mClient.queueSynthesizeToFile(mUtterance, mUtteranceId, output, rc, null);
+
+        // Call back to the listener
+        listener.onStart(mUtteranceId.toUniqueString());
+        listener.onDone(mUtteranceId.toUniqueString());
+
+        // Verify V1 client calls
+        Mockito.verify(mOldClientMock).setLanguage(Locale.US);
+        Mockito.verify(mOldClientMock).setPitch(1.0f);
+        // We never set speed, so make sure we use value from settings
+        Mockito.verify(mOldClientMock).setSpeechRate(mImplementationV1.getDefaultSpeechRate());
+
+        ArgumentCaptor<HashMap> paramsCaptor =
+                ArgumentCaptor.forClass(HashMap.class);
+        Mockito.verify(mOldClientMock).synthesizeToFile(Mockito.eq(mUtterance),
+                paramsCaptor.capture(), Mockito.eq(output.getAbsolutePath()));
+
+        HashMap<String, String> params = paramsCaptor.getValue();
+        assertEquals(mUtteranceId.toUniqueString(), params.get(
+                TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID));
+        assertEquals("true", params.get(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS));
+
+        // Verify callback calls
+        Mockito.verify(mRequestCallbacks).onSynthesisStart(mUtteranceId);
+        Mockito.verify(mRequestCallbacks).onSynthesisSuccess(mUtteranceId);
+    }
+
+    /** Test successful {@link TextToSpeechClient#queueSynthesizeToFile} call, with synthesis
+     * failure */
+    public void testQueueSynthesizeToFileSynthesisFailure() {
+        mockFewVoices();
+        UtteranceProgressListener listener = connectSuccessfuly();
+        assertNotNull(listener);
+
+        // Mock the speak call
+        Mockito.when(mOldClientMock.synthesizeToFile(Mockito.anyString(),
+                (HashMap<String, String>) Mockito.any(), Mockito.anyString()))
+                .thenReturn(TextToSpeech.SUCCESS);
+
+        final EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+        final RequestConfig rcBase = RequestConfigHelper.highestQuality(engineStatus, true,
+                new RequestConfigHelper.ExactLocaleMatcher(Locale.US));
+
+        // Set speed and pitch to 0.1f
+        final RequestConfig rc = RequestConfig.Builder.newBuilder(rcBase)
+                .setVoiceParam(TextToSpeechClient.Params.SPEECH_SPEED, 0.1f)
+                .setVoiceParam(TextToSpeechClient.Params.SPEECH_PITCH, 0.1f)
+                .build();
+
+        // Call synthesizeToFile
+        File output = new File("test");
+        mClient.queueSynthesizeToFile(mUtterance, mUtteranceId, output, rc, null);
+
+        // Call back to the listener
+        listener.onStart(mUtteranceId.toUniqueString());
+        listener.onError(mUtteranceId.toUniqueString());
+
+        // Verify V1 client calls
+        Mockito.verify(mOldClientMock).setLanguage(Locale.US);
+        Mockito.verify(mOldClientMock).setPitch(0.1f);
+        Mockito.verify(mOldClientMock).setSpeechRate(0.1f);
+        Mockito.verify(mOldClientMock).synthesizeToFile(Mockito.eq(mUtterance),
+                (HashMap<String, String>) Mockito.any(), Mockito.eq(output.getAbsolutePath()));
+
+        // Verify callback calls
+        Mockito.verify(mRequestCallbacks).onSynthesisStart(mUtteranceId);
+        Mockito.verify(mRequestCallbacks).onSynthesisFailure(mUtteranceId,
+                TextToSpeechClient.Status.ERROR_UNKNOWN);
+    }
+
+    /** Test failing {@link TextToSpeechClient#queueSynthesizeToFile} call */
+    public void testQueueSynthesizeToFileFailure() {
+        mockFewVoices();
+        connectSuccessfuly();
+
+        // Mock the synthesizeToFile call with error
+        Mockito.when(mOldClientMock.synthesizeToFile(Mockito.anyString(),
+                (HashMap<String, String>) Mockito.any(), Mockito.anyString()))
+                .thenReturn(TextToSpeech.ERROR);
+
+        final EngineStatus engineStatus = mClient.getEngineStatus();
+        assertNotNull(engineStatus);
+        final RequestConfig rc = RequestConfigHelper.highestQuality(engineStatus, true,
+                new RequestConfigHelper.ExactLocaleMatcher(Locale.US));
+
+        // Call speak
+        File output = new File("test");
+        mClient.queueSynthesizeToFile(mUtterance, mUtteranceId, output, rc, null);
+
+        // Verify V1 client calls
+        Mockito.verify(mOldClientMock).synthesizeToFile(Mockito.eq(mUtterance),
+                (HashMap<String, String>) Mockito.any(), Mockito.eq(output.getAbsolutePath()));
+
+        // Verify callback calls
+        Mockito.verify(mRequestCallbacks).onSynthesisFailure(mUtteranceId,
+                TextToSpeechClient.Status.ERROR_UNKNOWN);
+    }
+
+}
diff --git a/tests/java/android/support/v4/speech/tts/TTSImplementationV2Test.java b/tests/java/android/support/v4/speech/tts/TTSImplementationV2Test.java
new file mode 100644
index 0000000..26e8dfd
--- /dev/null
+++ b/tests/java/android/support/v4/speech/tts/TTSImplementationV2Test.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.speech.tts.TextToSpeech;
+import android.support.v4.speech.tts.TextToSpeechClient.ConnectionCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.RequestCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.UtteranceId;
+import android.test.InstrumentationTestCase;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+/**
+ * Tests for {@link TTSImplementationV2} class.
+ */
+public class TTSImplementationV2Test extends InstrumentationTestCase {
+    @Mock android.speech.tts.TextToSpeechClient mRealClientMock;
+    @Mock RequestCallbacks mRequestCallbacksMock;
+    @Mock ConnectionCallbacks mConnectionCallbacksMock;
+
+    @Mock TTSImplementationV2.TextToSpeechClientConstructor mClientConstructorMock;
+    TTSImplementationV2 mImplementationV2;
+    TextToSpeechClient mClient;
+
+    final String mUtterance = "text";
+    final UtteranceId mUtteranceId = new UtteranceId();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        System.setProperty("dexmaker.dexcache", getInstrumentation().getTargetContext().
+                getCacheDir().getPath());
+        MockitoAnnotations.initMocks(this);
+
+        Mockito.when(mClientConstructorMock.newClient(
+                (Context) Mockito.any(),
+                Mockito.anyString(), Mockito.anyBoolean(),
+                (android.speech.tts.TextToSpeechClient.RequestCallbacks)Mockito.any(),
+                (android.speech.tts.TextToSpeechClient.ConnectionCallbacks)Mockito.any()))
+                .thenReturn(mRealClientMock);
+
+        mImplementationV2 = new TTSImplementationV2(mClientConstructorMock);
+        mClient = new TextToSpeechClient(mImplementationV2,
+            getInstrumentation().getContext(), "test", false, mRequestCallbacksMock,
+            mConnectionCallbacksMock);
+    }
+
+    private class Callbacks {
+        android.speech.tts.TextToSpeechClient.RequestCallbacks requestCallback;
+        android.speech.tts.TextToSpeechClient.ConnectionCallbacks connectionCallbacks;
+    }
+
+    public Callbacks connect() {
+        ArgumentCaptor<android.speech.tts.TextToSpeechClient.RequestCallbacks>
+            requestCallbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.speech.tts.TextToSpeechClient.RequestCallbacks.class);
+
+        ArgumentCaptor<android.speech.tts.TextToSpeechClient.ConnectionCallbacks>
+        connectionCallbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.speech.tts.TextToSpeechClient.ConnectionCallbacks.class);
+
+        Mockito.verify(mClientConstructorMock).newClient(
+                Mockito.eq(getInstrumentation().getContext()),
+                Mockito.eq("test"), Mockito.eq(false),
+                requestCallbackCaptor.capture(),
+                connectionCallbackCaptor.capture());
+
+        mClient.connect();
+
+        Callbacks callbacks = new Callbacks();
+        callbacks.requestCallback = requestCallbackCaptor.getValue();
+        callbacks.connectionCallbacks = connectionCallbackCaptor.getValue();
+        assertNotNull(callbacks.requestCallback);
+        assertNotNull(callbacks.connectionCallbacks);
+        return callbacks;
+    }
+
+    public void testSetupSuccess() {
+        Callbacks callbacks = connect();
+        callbacks.connectionCallbacks.onConnectionSuccess();
+        Mockito.verify(mConnectionCallbacksMock).onConnectionSuccess();
+    }
+
+    public void testSetupFailure() {
+        Callbacks callbacks = connect();
+        callbacks.connectionCallbacks.onConnectionFailure();
+        Mockito.verify(mConnectionCallbacksMock).onConnectionFailure();
+    }
+
+    private static void assertEquals(Bundle expeced, Bundle value) {
+        if (expeced.size() != value.size()) {
+            fail("Received bundle has different number of mappings: " + value.size() +
+                    ", expected: " + expeced.size());
+        }
+        for (String key : value.keySet()) {
+            if (!expeced.containsKey(key)) {
+                fail("received bundle is missing key: " + key);
+            }
+            Object e = expeced.get(key);
+            Object v = value.get(key);
+            if (!e.equals(v)) {
+                fail("received bundle has wrong value for key " + key + " value: " + v +
+                        ", expected: " + e);
+            }
+        }
+    }
+
+    public android.speech.tts.VoiceInfo createFrameworkVoiceInfo() {
+        Bundle params = new Bundle();
+        params.putInt("foo1", 1);
+        params.putInt("foo2", 1);
+        Bundle features = new Bundle();
+        features.putInt("bar", 2);
+        return (new android.speech.tts.VoiceInfo.Builder())
+                .setName("name")
+                .setQuality(123)
+                .setQuality(321)
+                .setRequiresNetworkConnection(false)
+                .setLocale(Locale.GERMANY)
+                .setParamsWithDefaults(params).setAdditionalFeatures(features).build();
+    }
+
+    public void testVoiceInfoConvert() {
+        android.speech.tts.VoiceInfo frameworkVoiceInfo = createFrameworkVoiceInfo();
+
+        VoiceInfo supportVoiceInfo =
+                TTSImplementationV2.convert(frameworkVoiceInfo);
+
+        assertEquals(frameworkVoiceInfo.getName(), supportVoiceInfo.getName());
+        assertEquals(frameworkVoiceInfo.getLatency(), supportVoiceInfo.getLatency());
+        assertEquals(frameworkVoiceInfo.getQuality(), supportVoiceInfo.getQuality());
+        assertEquals(frameworkVoiceInfo.getLocale(), supportVoiceInfo.getLocale());
+        assertEquals(frameworkVoiceInfo.getRequiresNetworkConnection(),
+                supportVoiceInfo.getRequiresNetworkConnection());
+        assertEquals(frameworkVoiceInfo.getAdditionalFeatures(),
+                supportVoiceInfo.getAdditionalFeatures());
+        assertEquals(frameworkVoiceInfo.getParamsWithDefaults(),
+                supportVoiceInfo.getParamsWithDefaults());
+
+        android.speech.tts.VoiceInfo frameworkVoiceInfo2 =
+                TTSImplementationV2.convert(supportVoiceInfo);
+
+        assertEquals(frameworkVoiceInfo, frameworkVoiceInfo2);
+    }
+
+    public void testRequestInfoConvert() {
+        android.speech.tts.VoiceInfo frameworkVoiceInfo = createFrameworkVoiceInfo();
+        VoiceInfo supportVoiceInfo = TTSImplementationV2.convert(frameworkVoiceInfo);
+
+        RequestConfig.Builder builder = RequestConfig.Builder.newBuilder();
+        builder.setVoice(supportVoiceInfo);
+        builder.setAudioParamVolume(0.5f);
+        builder.setVoiceParam("foo1", 5);
+        RequestConfig supportRequestConfig = builder.build();
+
+        android.speech.tts.RequestConfig frameworkRequestConfig =
+                TTSImplementationV2.convert(supportRequestConfig);
+
+        assertEquals(frameworkRequestConfig.getVoice(), frameworkVoiceInfo);
+        assertEquals(frameworkRequestConfig.getAudioParams(),
+                supportRequestConfig.getAudioParams());
+        assertEquals(frameworkRequestConfig.getVoiceParams(),
+                supportRequestConfig.getVoiceParams());
+    }
+
+    public void testRequestCallbacksConvert() {
+        android.speech.tts.TextToSpeechClient.RequestCallbacks frameworkRequestCallbacks =
+                TTSImplementationV2.convert(mRequestCallbacksMock);
+
+        frameworkRequestCallbacks.onSynthesisFailure(TTSImplementationV2.convert(mUtteranceId), 29);
+        Mockito.verify(mRequestCallbacksMock).onSynthesisFailure(mUtteranceId, 29);
+
+        frameworkRequestCallbacks.onSynthesisSuccess(TTSImplementationV2.convert(mUtteranceId));
+        Mockito.verify(mRequestCallbacksMock).onSynthesisSuccess(mUtteranceId);
+
+        frameworkRequestCallbacks.onSynthesisStop(TTSImplementationV2.convert(mUtteranceId));
+        Mockito.verify(mRequestCallbacksMock).onSynthesisStop(mUtteranceId);
+
+        frameworkRequestCallbacks.onSynthesisStart(TTSImplementationV2.convert(mUtteranceId));
+        Mockito.verify(mRequestCallbacksMock).onSynthesisStart(mUtteranceId);
+
+        frameworkRequestCallbacks.onSynthesisFallback(TTSImplementationV2.convert(mUtteranceId));
+        Mockito.verify(mRequestCallbacksMock).onSynthesisFallback(mUtteranceId);
+
+        frameworkRequestCallbacks.onSynthesisProgress(TTSImplementationV2.convert(mUtteranceId),
+                1, 2);
+        Mockito.verify(mRequestCallbacksMock).onSynthesisProgress(mUtteranceId, 1, 2);
+    }
+}
diff --git a/v13/AndroidManifest.xml b/v13/AndroidManifest.xml
index 08dbb61..c862c99 100644
--- a/v13/AndroidManifest.xml
+++ b/v13/AndroidManifest.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.v4">
+          package="android.support.v13">
     <application />
 </manifest>
diff --git a/v14/appcompat/.classpath b/v14/appcompat/.classpath
new file mode 100644
index 0000000..a4763d1
--- /dev/null
+++ b/v14/appcompat/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/v14/appcompat/.gitignore b/v14/appcompat/.gitignore
new file mode 100644
index 0000000..c3c25e0
--- /dev/null
+++ b/v14/appcompat/.gitignore
@@ -0,0 +1,4 @@
+.settings
+bin
+libs
+gen
diff --git a/v14/appcompat/.project b/v14/appcompat/.project
new file mode 100644
index 0000000..224f486
--- /dev/null
+++ b/v14/appcompat/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>android-support-v14-appcompat</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/v14/appcompat/Android.mk b/v14/appcompat/Android.mk
new file mode 100644
index 0000000..b687289
--- /dev/null
+++ b/v14/appcompat/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# The R class is automatically excluded from the generated library.
+# Applications that use this library must specify LOCAL_RESOURCE_DIR
+# in their makefiles to include the resources in their package.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v14-appcompat
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES += android-support-v4
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v14/appcompat/AndroidManifest.xml
similarity index 68%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v14/appcompat/AndroidManifest.xml
index 36688bc..c83f8d9 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v14/appcompat/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <?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.
@@ -14,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v7.appcompat">
+    <uses-sdk android:minSdkVersion="14"/>
+    <application />
+</manifest>
diff --git a/v14/appcompat/build.gradle b/v14/appcompat/build.gradle
new file mode 100644
index 0000000..d3a554f
--- /dev/null
+++ b/v14/appcompat/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'android-library'
+
+archivesBaseName = 'appcompat-v14'
+
+dependencies {
+    compile project(':support-v4')
+}
+
+android {
+    compileSdkVersion 'current'
+    buildToolsVersion '19.0.3'
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDir 'src'
+        main.res.srcDir 'res'
+        main.assets.srcDir 'assets'
+        main.resources.srcDir 'src'
+
+        // this moves src/instrumentTest to tests so all folders follow:
+        // tests/java, tests/res, tests/assets, ...
+        // This is a *reset* so it replaces the default paths
+        androidTest.setRoot('tests')
+        androidTest.java.srcDir 'tests/src'
+    }
+
+    lintOptions {
+        // TODO: fix errors and reenable.
+        abortOnError false
+    }
+}
diff --git a/v14/appcompat/project.properties b/v14/appcompat/project.properties
new file mode 100644
index 0000000..91d2b02
--- /dev/null
+++ b/v14/appcompat/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v14/appcompat/res/values-land/dimens.xml
similarity index 75%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v14/appcompat/res/values-land/dimens.xml
index 36688bc..8d8e2e5 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v14/appcompat/res/values-land/dimens.xml
@@ -14,8 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<resources>
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+    <!-- Default height of an action bar. -->
+    <dimen name="action_bar_default_height_quantum">48dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values-v19/themes.xml b/v14/appcompat/res/values-v19/themes.xml
new file mode 100644
index 0000000..5fa3fa1
--- /dev/null
+++ b/v14/appcompat/res/values-v19/themes.xml
@@ -0,0 +1,42 @@
+<?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>
+    <!--
+        TODO: Update comment
+
+        These theme declarations contain any version-independent specification. Items
+        that need to vary based on platform version should be defined in the corresponding
+        "Theme.Base" theme.
+    -->
+
+    <!-- Variant of the appcompat (dark) theme that has no title bar and translucent
+         system decor (if available) -->
+    <style name="Theme.AppCompat.NoActionBar.TranslucentDecor">
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Variant of the appcompat (light) theme that has no title bar and translucent
+         system decor (if available) -->
+    <style name="Theme.AppCompat.Light.NoActionBar.TranslucentDecor">
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values-v21/styles_base.xml b/v14/appcompat/res/values-v21/styles_base.xml
new file mode 100644
index 0000000..8bf68eb
--- /dev/null
+++ b/v14/appcompat/res/values-v21/styles_base.xml
@@ -0,0 +1,126 @@
+<?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>
+
+    <!--
+        Themes in the "Base.Theme.*" family vary based on the current platform
+        version to provide the correct basis on each device. You probably don't
+        want to use them directly in your apps.
+
+        Themes in the "Theme.AppCompat" family are meant to be extended or used
+        directly by apps.
+    -->
+    <eat-comment />
+
+    <style name="Base.Widget.AppCompat.ActionBar" parent="android:Widget.Quantum.ActionBar" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar" parent="android:Widget.Quantum.Light.ActionBar" />
+
+    <style name="Base.Widget.AppCompat.ActionBar.Solid" parent="android:Widget.Quantum.ActionBar.Solid" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.Solid" parent="android:Widget.Quantum.Light.ActionBar.Solid" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.Solid.Inverse" parent="android:Widget.Quantum.Light.ActionBar.Solid" />
+
+    <style name="Base.Widget.AppCompat.ActionBar.TabBar" parent="android:Widget.Quantum.ActionBar.TabBar" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabBar" parent="android:Widget.Quantum.Light.ActionBar.TabBar" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabBar.Inverse" parent="android:Widget.Quantum.Light.ActionBar.TabBar" />
+
+    <style name="Base.Widget.AppCompat.ActionBar.TabView" parent="android:Widget.Quantum.ActionBar.TabView" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabView" parent="android:Widget.Quantum.Light.ActionBar.TabView" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabView.Inverse" parent="android:Widget.Quantum.Light.ActionBar.TabView" />
+
+    <style name="Base.Widget.AppCompat.ActionBar.TabText" parent="android:Widget.Quantum.ActionBar.TabText" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabText" parent="android:Widget.Quantum.Light.ActionBar.TabText" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabText.Inverse" parent="android:Widget.Quantum.Light.ActionBar.TabText" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionMode.Inverse" parent="android:Widget.Quantum.Light.ActionMode" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Menu" parent="android:TextAppearance.Quantum.Widget.ActionBar.Menu" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Title" parent="android:TextAppearance.Quantum.Widget.ActionBar.Title" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle" parent="android:TextAppearance.Quantum.Widget.ActionBar.Subtitle" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" parent="android:TextAppearance.Quantum.Widget.ActionBar.Title.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" parent="android:TextAppearance.Quantum.Widget.ActionBar.Subtitle.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Title" parent="android:TextAppearance.Quantum.Widget.ActionMode.Title" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Subtitle" parent="android:TextAppearance.Quantum.Widget.ActionMode.Subtitle" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Title.Inverse" parent="android:TextAppearance.Quantum.Widget.ActionMode.Title.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Subtitle.Inverse" parent="android:TextAppearance.Quantum.Widget.ActionMode.Subtitle.Inverse" />
+
+    <!-- Action Button Styles -->
+
+    <style name="Base.Widget.AppCompat.ActionButton" parent="android:Widget.Quantum.ActionButton" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionButton" parent="android:Widget.Quantum.Light.ActionButton" />
+
+    <style name="Base.Widget.AppCompat.ActionButton.CloseMode" parent="android:Widget.Quantum.ActionButton.CloseMode" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionButton.CloseMode" parent="android:Widget.Quantum.Light.ActionButton.CloseMode" />
+
+    <style name="Base.Widget.AppCompat.ActionButton.Overflow" parent="android:Widget.Quantum.ActionButton.Overflow" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionButton.Overflow" parent="android:Widget.Quantum.Light.ActionButton.Overflow" />
+
+    <!-- Spinner Widgets -->
+
+    <style name="Base.Widget.AppCompat.ListView.DropDown" parent="android:Widget.Quantum.ListView.DropDown" />
+
+    <style name="Base.Widget.AppCompat.Light.ListView.DropDown" parent="android:Widget.Quantum.ListView.DropDown" />
+
+    <style name="Base.Widget.AppCompat.DropDownItem.Spinner" parent="android:Widget.Quantum.DropDownItem.Spinner" />
+
+    <style name="Base.Widget.AppCompat.Light.DropDownItem.Spinner" parent="android:Widget.Quantum.Light.DropDownItem.Spinner" />
+
+    <style name="Base.Widget.AppCompat.Spinner" parent="android:Widget.Quantum.Spinner" />
+
+    <style name="Base.Widget.AppCompat.Light.Spinner" parent="android:Widget.Quantum.Light.Spinner" />
+
+    <style name="Base.Widget.AppCompat.ListView.Menu" parent="android:Widget.ListView.Menu" />
+
+    <!-- Popup Menu -->
+
+    <style name="Base.Widget.AppCompat.ListPopupWindow" parent="android:Widget.Quantum.ListPopupWindow" />
+
+    <style name="Base.Widget.AppCompat.Light.ListPopupWindow" parent="android:Widget.Quantum.Light.ListPopupWindow" />
+
+    <style name="Base.Widget.AppCompat.PopupMenu" parent="android:Widget.Quantum.PopupMenu" />
+
+    <style name="Base.Widget.AppCompat.Light.PopupMenu" parent="android:Widget.Quantum.Light.PopupMenu" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.PopupMenu.Large" parent="android:TextAppearance.Quantum.Widget.PopupMenu.Large" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.PopupMenu.Small" parent="android:TextAppearance.Quantum.Widget.PopupMenu.Small" />
+
+    <style name="Base.TextAppearance.AppCompat.Light.Widget.PopupMenu.Large" parent="android:TextAppearance.Quantum.Widget.PopupMenu.Large" />
+
+    <style name="Base.TextAppearance.AppCompat.Light.Widget.PopupMenu.Small" parent="android:TextAppearance.Quantum.Widget.PopupMenu.Small" />
+
+</resources>
diff --git a/v14/appcompat/res/values-v21/styles_base_text.xml b/v14/appcompat/res/values-v21/styles_base_text.xml
new file mode 100644
index 0000000..4fd3612
--- /dev/null
+++ b/v14/appcompat/res/values-v21/styles_base_text.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="Base.TextAppearance.AppCompat" parent="android:TextAppearance.Quantum" />
+
+    <style name="Base.TextAppearance.AppCompat.Display4" parent="android:TextAppearance.Quantum.Display4" />
+
+    <style name="Base.TextAppearance.AppCompat.Display3" parent="android:TextAppearance.Quantum.Display3" />
+
+    <style name="Base.TextAppearance.AppCompat.Display2" parent="android:TextAppearance.Quantum.Display2" />
+
+    <style name="Base.TextAppearance.AppCompat.Display1" parent="android:TextAppearance.Quantum.Display1" />
+
+    <style name="Base.TextAppearance.AppCompat.Headline" parent="android:TextAppearance.Quantum.Headline" />
+
+    <style name="Base.TextAppearance.AppCompat.Title" parent="android:TextAppearance.Quantum.Title" />
+
+    <style name="Base.TextAppearance.AppCompat.Subhead" parent="android:TextAppearance.Quantum.Subhead" />
+
+    <style name="Base.TextAppearance.AppCompat.Body2" parent="android:TextAppearance.Quantum.Body2" />
+
+    <style name="Base.TextAppearance.AppCompat.Body1" parent="android:TextAppearance.Quantum.Body1" />
+
+    <style name="Base.TextAppearance.AppCompat.Caption" parent="android:TextAppearance.Quantum.Caption" />
+
+    <style name="Base.TextAppearance.AppCompat.Menu" parent="android:TextAppearance.Quantum.Menu" />
+
+    <!-- Now deprecated styles -->
+
+    <style name="Base.TextAppearance.AppCompat.Inverse" parent="android:TextAppearance.Quantum.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Large" parent="android:TextAppearance.Quantum.Large" />
+
+    <style name="Base.TextAppearance.AppCompat.Large.Inverse" parent="android:TextAppearance.Quantum.Large.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Medium" parent="android:TextAppearance.Quantum.Medium" />
+
+    <style name="Base.TextAppearance.AppCompat.Medium.Inverse" parent="android:TextAppearance.Quantum.Medium.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Small" parent="android:TextAppearance.Quantum.Small" />
+
+    <style name="Base.TextAppearance.AppCompat.Small.Inverse" parent="android:TextAppearance.Quantum.Small.Inverse" />
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values-v21/themes_base.xml b/v14/appcompat/res/values-v21/themes_base.xml
new file mode 100644
index 0000000..acf6ed4
--- /dev/null
+++ b/v14/appcompat/res/values-v21/themes_base.xml
@@ -0,0 +1,63 @@
+<?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>
+
+    <!--
+        Themes in the "Base.Theme.*" family vary based on the current platform
+        version to provide the correct basis on each device. You probably don't
+        want to use them directly in your apps.
+
+        Themes in the "Theme.AppCompat" family are meant to be extended or used
+        directly by apps.
+    -->
+    <eat-comment />
+
+    <style name="Base.Theme.AppCompat" parent="android:Theme.Quantum">
+        <!-- Copy our color palette attribute values to the framework's -->
+        <item name="android:colorPrimary">?attr/colorPrimary</item>
+        <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
+        <item name="android:colorPrimaryLight">?attr/colorPrimaryLight</item>
+        <item name="android:colorAccent">?attr/colorAccent</item>
+        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
+        <item name="android:colorControlActivated">?attr/colorControlActivated</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
+    </style>
+
+    <style name="Base.Theme.AppCompat.Light" parent="android:Theme.Quantum.Light">
+        <!-- Copy our color palette attribute values to the framework's -->
+        <item name="android:colorPrimary">?attr/colorPrimary</item>
+        <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
+        <item name="android:colorPrimaryLight">?attr/colorPrimaryLight</item>
+        <item name="android:colorAccent">?attr/colorAccent</item>
+        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
+        <item name="android:colorControlActivated">?attr/colorControlActivated</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
+    </style>
+
+    <style name="Base.Theme.AppCompat.Light.DarkActionBar" parent="android:Theme.Quantum.Light.DarkActionBar">
+        <!-- Copy our color palette attribute values to the framework's -->
+        <item name="android:colorPrimary">?attr/colorPrimary</item>
+        <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
+        <item name="android:colorPrimaryLight">?attr/colorPrimaryLight</item>
+        <item name="android:colorAccent">?attr/colorAccent</item>
+        <item name="android:colorControlNormal">?attr/colorControlNormal</item>
+        <item name="android:colorControlActivated">?attr/colorControlActivated</item>
+        <item name="android:colorButtonNormal">?attr/colorButtonNormal</item>
+    </style>
+
+</resources>
diff --git a/v14/appcompat/res/values/attrs.xml b/v14/appcompat/res/values/attrs.xml
new file mode 100644
index 0000000..03c4b70
--- /dev/null
+++ b/v14/appcompat/res/values/attrs.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+
+    <!-- ============= -->
+    <!-- Color palette -->
+    <!-- ============= -->
+    <eat-comment />
+
+    <!-- The primary branding color for the app. By default, this is the color applied to the
+     action bar background and framework controls (via colorControlActivated). -->
+    <attr name="colorPrimary" format="color" />
+
+    <!-- Dark variant of the primary branding color. By default, this is the color applied to
+     the status bar (via statusBarColor) and navigation bar (via navigationBarColor). -->
+    <attr name="colorPrimaryDark" format="color" />
+
+    <!-- Light variant of the primary branding color. TODO: Not used? -->
+    <attr name="colorPrimaryLight" format="color" />
+
+    <!-- Bright complement to the primary branding color. TODO: Not used? -->
+    <attr name="colorAccent" format="color" />
+
+    <!-- The color applied to framework controls in their normal state. -->
+    <attr name="colorControlNormal" format="color" />
+
+    <!-- The color applied to framework controls in their activated (ex. checked) state. -->
+    <attr name="colorControlActivated" format="color" />
+
+    <!-- The color applied to framework control highlights (ex. ripples, selection). -->
+    <attr name="colorControlHighlight" format="color" />
+
+    <!-- The color applied to framework buttons in their normal state. -->
+    <attr name="colorButtonNormal" format="color" />
+
+</resources>
diff --git a/v14/appcompat/res/values/colors.xml b/v14/appcompat/res/values/colors.xml
new file mode 100644
index 0000000..47c4f2d
--- /dev/null
+++ b/v14/appcompat/res/values/colors.xml
@@ -0,0 +1,137 @@
+<?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.
+-->
+
+<!-- Colors specific to Quantum themes. -->
+<resources>
+    <color name="background_quantum_dark">#ff303030</color>
+    <color name="background_quantum_light">@android:color/white</color>
+
+    <color name="bright_foreground_quantum_dark">@android:color/white</color>
+    <color name="bright_foreground_quantum_light">@android:color/black</color>
+    <!-- TODO: This is 50% alpha black -->
+    <color name="bright_foreground_disabled_quantum_dark">#80000000</color>
+    <!-- TODO: This is 50% alpha white -->
+    <color name="bright_foreground_disabled_quantum_light">#80ffffff</color>
+    <color name="bright_foreground_inverse_quantum_dark">@color/bright_foreground_quantum_light
+    </color>
+    <color name="bright_foreground_inverse_quantum_light">@color/bright_foreground_quantum_dark
+    </color>
+
+    <color name="dim_foreground_quantum_dark">#ffbebebe</color>
+    <color name="dim_foreground_quantum_light">#ff323232</color>
+    <color name="dim_foreground_disabled_quantum_dark">#80bebebe</color>
+    <color name="dim_foreground_disabled_quantum_light">#80323232</color>
+
+    <color name="hint_foreground_quantum_dark">@color/bright_foreground_disabled_quantum_dark
+    </color>
+    <color name="hint_foreground_quantum_light">@color/bright_foreground_disabled_quantum_light
+    </color>
+    <!-- TODO: This is 40% alpha teal_A200 -->
+    <color name="highlighted_text_quantum_dark">#660097a7</color>
+    <!-- TODO: This is 40% alpha teal_A200 -->
+    <color name="highlighted_text_quantum_light">#660097a7</color>
+
+    <!-- Primary & accent colors -->
+    <eat-comment />
+
+    <color name="quantum_red_100">#fff4c7c3</color>
+    <color name="quantum_red_300">#ffe67c73</color>
+    <color name="quantum_red_500">#ffdb4437</color>
+    <color name="quantum_red_700">#ffc53929</color>
+    <color name="quantum_red_A200">#ffff5252</color>
+    <color name="quantum_red_A400">#ffff1744</color>
+
+    <color name="quantum_blue_100">#ffc6dafc</color>
+    <color name="quantum_blue_300">#ff7baaf7</color>
+    <color name="quantum_blue_500">#ff4285f4</color>
+    <color name="quantum_blue_700">#ff3367d6</color>
+    <color name="quantum_blue_A200">#ff448aff</color>
+    <color name="quantum_blue_A400">#ff2979ff</color>
+
+    <color name="quantum_teal_100">#ffb2ebf2</color>
+    <color name="quantum_teal_300">#ff4dd0e1</color>
+    <color name="quantum_teal_500">#ff00bcd4</color>
+    <color name="quantum_teal_700">#ff0097a7</color>
+    <color name="quantum_teal_A200">#ff18ffff</color>
+    <color name="quantum_teal_A400">#ff00e5ff</color>
+
+    <color name="quantum_green_100">#ffb7e1cd</color>
+    <color name="quantum_green_300">#ff57bb8a</color>
+    <color name="quantum_green_500">#ff0f9d58</color>
+    <color name="quantum_green_700">#ff0b8043</color>
+    <color name="quantum_green_A200">#ff69f0ae</color>
+    <color name="quantum_green_A400">#ff00e676</color>
+
+    <color name="quantum_lime_100">#fff0f4c3</color>
+    <color name="quantum_lime_300">#ffdce775</color>
+    <color name="quantum_lime_500">#ffcddc39</color>
+    <color name="quantum_lime_700">#ffafb42b</color>
+    <color name="quantum_lime_A200">#ffeeff41</color>
+    <color name="quantum_lime_A400">#ffc6ff00</color>
+
+    <color name="quantum_yellow_100">#fffce8b2</color>
+    <color name="quantum_yellow_300">#fff7cb4d</color>
+    <color name="quantum_yellow_500">#fff4b400</color>
+    <color name="quantum_yellow_700">#fff09300</color>
+    <color name="quantum_yellow_A200">#ffffcd40</color>
+    <color name="quantum_yellow_A400">#ffffbc00</color>
+
+    <color name="quantum_orange_100">#ffffe0b2</color>
+    <color name="quantum_orange_300">#ffffb74d</color>
+    <color name="quantum_orange_500">#ffff9800</color>
+    <color name="quantum_orange_700">#fff57c00</color>
+    <color name="quantum_orange_A200">#ffffab40</color>
+    <color name="quantum_orange_A400">#ffff9100</color>
+
+    <color name="quantum_deep_orange_100">#fff4c7c3</color>
+    <color name="quantum_deep_orange_300">#ffe67c73</color>
+    <color name="quantum_deep_orange_500">#ffff5722</color>
+    <color name="quantum_deep_orange_700">#ffc53929</color>
+    <color name="quantum_deep_orange_A200">#ffff5252</color>
+    <color name="quantum_deep_orange_A400">#ffff1744</color>
+
+    <!-- Neutral colors -->
+    <eat-comment />
+
+    <color name="quantum_grey_50">#fffafafa</color>
+    <color name="quantum_grey_100">#fff5f5f5</color>
+    <color name="quantum_grey_300">#ffeeeeee</color>
+    <color name="quantum_grey_500">#ffa3a3a3</color>
+    <color name="quantum_grey_700">#ff717171</color>
+
+    <color name="quantum_blue_grey_50">#ffeceff1</color>
+    <color name="quantum_blue_grey_100">#ffcfd8dc</color>
+    <color name="quantum_blue_grey_300">#ff90a4ae</color>
+    <color name="quantum_blue_grey_500">#ff607d8b</color>
+    <color name="quantum_blue_grey_700">#ff455a64</color>
+
+    <color name="quantum_brown_100">#ffd7ccc8</color>
+    <color name="quantum_brown_300">#ffa1887f</color>
+    <color name="quantum_brown_500">#ff795548</color>
+    <color name="quantum_brown_700">#ff5d4037</color>
+
+    <!-- Text & foreground colors -->
+    <eat-comment />
+
+    <color name="primary_text_default_quantum_light">#de000000</color>
+    <color name="secondary_text_quantum_light">#8a000000</color>
+    <color name="tertiary_text_quantum_light">#4d000000</color>
+
+    <color name="primary_text_default_quantum_dark">#deffffff</color>
+    <color name="secondary_text_quantum_dark">#8affffff</color>
+    <color name="tertiary_text_quantum_dark">#4dffffff</color>
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values/dimens.xml b/v14/appcompat/res/values/dimens.xml
new file mode 100644
index 0000000..56e09ac
--- /dev/null
+++ b/v14/appcompat/res/values/dimens.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+
+    <!-- Default height of an action bar. -->
+    <dimen name="action_bar_default_height_quantum">56dp</dimen>
+
+    <dimen name="text_size_display_4_quantum">112sp</dimen>
+    <dimen name="text_size_display_3_quantum">56sp</dimen>
+    <dimen name="text_size_display_2_quantum">45sp</dimen>
+    <dimen name="text_size_display_1_quantum">34sp</dimen>
+    <dimen name="text_size_headline_quantum">24sp</dimen>
+    <dimen name="text_size_title_quantum">20sp</dimen>
+    <dimen name="text_size_subhead_quantum">16sp</dimen>
+    <dimen name="text_size_body_2_quantum">14sp</dimen>
+    <dimen name="text_size_body_1_quantum">14sp</dimen>
+    <dimen name="text_size_caption_quantum">12sp</dimen>
+    <dimen name="text_size_menu_quantum">14sp</dimen>
+    <dimen name="text_size_button_quantum">14sp</dimen>
+
+    <!-- Text size for action bar titles -->
+    <dimen name="action_bar_title_text_size_quantum">20sp</dimen>
+    <!-- Text size for action bar subtitles -->
+    <dimen name="action_bar_subtitle_text_size_quantum">16sp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values/styles.xml b/v14/appcompat/res/values/styles.xml
new file mode 100644
index 0000000..955d291
--- /dev/null
+++ b/v14/appcompat/res/values/styles.xml
@@ -0,0 +1,165 @@
+<?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>
+
+    <!--
+        Styles in here can be extended for customisation in your application. Each utilises
+        one of the Base styles. If Quantum themes are available on the current platform version
+        they will be used instead of the compat styles.
+    -->
+    <eat-comment />
+
+    <style name="Widget.AppCompat.ActionBar" parent="Base.Widget.AppCompat.ActionBar" />
+
+    <style name="Widget.AppCompat.Light.ActionBar" parent="Base.Widget.AppCompat.Light.ActionBar" />
+
+    <style name="Widget.AppCompat.ActionBar.Solid" parent="Base.Widget.AppCompat.ActionBar.Solid" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.Solid" parent="Base.Widget.AppCompat.Light.ActionBar.Solid" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.Solid.Inverse" parent="Base.Widget.AppCompat.Light.ActionBar.Solid.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionBar.Title" parent="Base.TextAppearance.AppCompat.Widget.ActionBar.Title" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionBar.Subtitle" parent="Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" parent="Base.TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" parent="Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" />
+
+    <style name="Widget.AppCompat.ProgressBar.Horizontal" parent="Base.Widget.AppCompat.ProgressBar.Horizontal" />
+
+    <style name="Widget.AppCompat.ProgressBar" parent="Base.Widget.AppCompat.ProgressBar" />
+
+    <style name="Widget.AppCompat.ActionButton" parent="Base.Widget.AppCompat.ActionButton" />
+
+    <style name="Widget.AppCompat.Light.ActionButton" parent="Base.Widget.AppCompat.Light.ActionButton" />
+
+    <style name="Widget.AppCompat.ActionButton.CloseMode" parent="Base.Widget.AppCompat.ActionButton.CloseMode" />
+
+    <style name="Widget.AppCompat.Light.ActionButton.CloseMode" parent="Base.Widget.AppCompat.Light.ActionButton.CloseMode" />
+
+    <style name="Widget.AppCompat.ActionButton.Overflow" parent="Base.Widget.AppCompat.ActionButton.Overflow" />
+
+    <style name="Widget.AppCompat.Light.ActionButton.Overflow" parent="Base.Widget.AppCompat.Light.ActionButton.Overflow" />
+
+    <style name="Widget.AppCompat.ActionBar.TabBar" parent="Base.Widget.AppCompat.ActionBar.TabBar" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.TabBar" parent="Base.Widget.AppCompat.Light.ActionBar.TabBar" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.TabBar.Inverse" parent="Base.Widget.AppCompat.Light.ActionBar.TabBar.Inverse" />
+
+    <style name="Widget.AppCompat.ActionBar.TabView" parent="Base.Widget.AppCompat.ActionBar.TabView" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.TabView" parent="Base.Widget.AppCompat.Light.ActionBar.TabView" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.TabView.Inverse" parent="Base.Widget.AppCompat.Light.ActionBar.TabView.Inverse" />
+
+    <style name="Widget.AppCompat.ActionBar.TabText" parent="Base.Widget.AppCompat.ActionBar.TabText" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.TabText" parent="Base.Widget.AppCompat.Light.ActionBar.TabText" />
+
+    <style name="Widget.AppCompat.Light.ActionBar.TabText.Inverse" parent="Base.Widget.AppCompat.Light.ActionBar.TabText.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionBar.Menu" parent="Base.TextAppearance.AppCompat.Widget.ActionBar.Menu" />
+
+    <style name="Widget.AppCompat.ActionMode" parent="Base.Widget.AppCompat.ActionMode" />
+
+    <style name="Widget.AppCompat.Light.ActionMode" parent="Base.Widget.AppCompat.Light.ActionMode" />
+
+    <style name="Widget.AppCompat.Light.ActionMode.Inverse" parent="Base.Widget.AppCompat.Light.ActionMode.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionMode.Title" parent="Base.TextAppearance.AppCompat.Widget.ActionMode.Title" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionMode.Subtitle" parent="Base.TextAppearance.AppCompat.Widget.ActionMode.Subtitle" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionMode.Title.Inverse" parent="Base.TextAppearance.AppCompat.Widget.ActionMode.Title.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Widget.ActionMode.Subtitle.Inverse" parent="Base.TextAppearance.AppCompat.Widget.ActionMode.Subtitle.Inverse" />
+
+    <style name="Widget.AppCompat.Spinner.DropDown.ActionBar" parent="Base.Widget.AppCompat.Spinner" />
+
+    <style name="Widget.AppCompat.Light.Spinner.DropDown.ActionBar" parent="Base.Widget.AppCompat.Light.Spinner" />
+
+    <style name="Widget.AppCompat.DropDownItem.Spinner" parent="Base.Widget.AppCompat.DropDownItem.Spinner" />
+
+    <style name="Widget.AppCompat.Light.DropDownItem.Spinner" parent="Base.Widget.AppCompat.Light.DropDownItem.Spinner" />
+
+    <style name="Widget.AppCompat.ListView.DropDown" parent="Base.Widget.AppCompat.ListView.DropDown" />
+
+    <style name="Widget.AppCompat.Light.ListView.DropDown" parent="Base.Widget.AppCompat.Light.ListView.DropDown" />
+
+    <style name="Widget.AppCompat.ListPopupWindow" parent="Base.Widget.AppCompat.ListPopupWindow" />
+
+    <style name="Widget.AppCompat.Light.ListPopupWindow" parent="Base.Widget.AppCompat.Light.ListPopupWindow" />
+
+    <style name="Widget.AppCompat.PopupMenu" parent="Base.Widget.AppCompat.PopupMenu" />
+
+    <style name="Widget.AppCompat.Light.PopupMenu" parent="Base.Widget.AppCompat.Light.PopupMenu" />
+
+    <style name="Widget.AppCompat.ListView.Menu" parent="Base.Widget.AppCompat.ListView.Menu" />
+
+    <style name="TextAppearance.AppCompat.Widget.PopupMenu.Large" parent="Base.TextAppearance.AppCompat.Widget.PopupMenu.Large" />
+
+    <style name="TextAppearance.AppCompat.Widget.PopupMenu.Small" parent="Base.TextAppearance.AppCompat.Widget.PopupMenu.Small" />
+
+    <style name="TextAppearance.AppCompat.Light.Widget.PopupMenu.Large" parent="Base.TextAppearance.AppCompat.Light.Widget.PopupMenu.Large" />
+
+    <style name="TextAppearance.AppCompat.Light.Widget.PopupMenu.Small" parent="Base.TextAppearance.AppCompat.Light.Widget.PopupMenu.Small" />
+
+    <eat-comment />
+    <!-- Text styles -->
+
+    <style name="TextAppearance.AppCompat" parent="Base.TextAppearance.AppCompat" />
+
+    <style name="TextAppearance.AppCompat.Display4" parent="Base.TextAppearance.AppCompat.Display4" />
+
+    <style name="TextAppearance.AppCompat.Display3" parent="Base.TextAppearance.AppCompat.Display3" />
+
+    <style name="TextAppearance.AppCompat.Display2" parent="Base.TextAppearance.AppCompat.Display2" />
+
+    <style name="TextAppearance.AppCompat.Display1" parent="Base.TextAppearance.AppCompat.Display1" />
+
+    <style name="TextAppearance.AppCompat.Headline" parent="Base.TextAppearance.AppCompat.Headline" />
+
+    <style name="TextAppearance.AppCompat.Title" parent="Base.TextAppearance.AppCompat.Title" />
+
+    <style name="TextAppearance.AppCompat.Subhead" parent="Base.TextAppearance.AppCompat.Subhead" />
+
+    <style name="TextAppearance.AppCompat.Body2" parent="Base.TextAppearance.AppCompat.Body2" />
+
+    <style name="TextAppearance.AppCompat.Body1" parent="Base.TextAppearance.AppCompat.Body1" />
+
+    <style name="TextAppearance.AppCompat.Caption" parent="Base.TextAppearance.AppCompat.Caption" />
+
+    <style name="TextAppearance.AppCompat.Menu" parent="Base.TextAppearance.AppCompat.Menu" />
+
+    <style name="TextAppearance.AppCompat.Inverse" parent="Base.TextAppearance.AppCompat.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Large" parent="Base.TextAppearance.AppCompat.Large" />
+
+    <style name="TextAppearance.AppCompat.Large.Inverse" parent="Base.TextAppearance.AppCompat.Large.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Medium" parent="Base.TextAppearance.AppCompat.Medium" />
+
+    <style name="TextAppearance.AppCompat.Medium.Inverse" parent="Base.TextAppearance.AppCompat.Medium.Inverse" />
+
+    <style name="TextAppearance.AppCompat.Small" parent="Base.TextAppearance.AppCompat.Small" />
+
+    <style name="TextAppearance.AppCompat.Small.Inverse" parent="Base.TextAppearance.AppCompat.Small.Inverse" />
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values/styles_base.xml b/v14/appcompat/res/values/styles_base.xml
new file mode 100644
index 0000000..7e42de7
--- /dev/null
+++ b/v14/appcompat/res/values/styles_base.xml
@@ -0,0 +1,213 @@
+<?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>
+
+    <!--
+        Like in themes_base.xml, the namespace "Base.*.AppCompat" is used to
+        define base styles for the platform version. The "Widget.AppCompat"
+        variants are for direct use or use as parent styles by the app.
+    -->
+    <eat-comment />
+
+    <style name="Base.Widget.AppCompat.ActionBar" parent="android:Widget.Holo.ActionBar">
+        <item name="android:background">@null</item>
+        <item name="android:backgroundStacked">@null</item>
+        <item name="android:backgroundSplit">@null</item>
+
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:progressBarPadding">32dip</item>
+        <item name="android:itemPadding">8dip</item>
+
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar" parent="android:Widget.Holo.Light.ActionBar">
+        <item name="android:background">@null</item>
+        <item name="android:backgroundStacked">@null</item>
+        <item name="android:backgroundSplit">@null</item>
+
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:progressBarPadding">32dip</item>
+        <item name="android:itemPadding">8dip</item>
+
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.ActionBar.Solid" parent="android:Widget.Holo.ActionBar.Solid">
+        <item name="android:background">?attr/colorPrimary</item>
+        <item name="android:backgroundStacked">?attr/colorPrimary</item>
+        <item name="android:backgroundSplit">?attr/colorPrimary</item>
+
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:progressBarPadding">32dip</item>
+        <item name="android:itemPadding">8dip</item>
+
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.Solid" parent="android:Widget.Holo.Light.ActionBar.Solid">
+        <item name="android:background">?attr/colorPrimary</item>
+        <item name="android:backgroundStacked">?attr/colorPrimary</item>
+        <item name="android:backgroundSplit">?attr/colorPrimary</item>
+
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:progressBarPadding">32dip</item>
+        <item name="android:itemPadding">8dip</item>
+
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.Solid.Inverse" parent="android:Widget.Holo.Light.ActionBar.Solid.Inverse">
+        <item name="android:background">?attr/colorPrimary</item>
+        <item name="android:backgroundStacked">?attr/colorPrimary</item>
+        <item name="android:backgroundSplit">?attr/colorPrimary</item>
+
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:progressBarPadding">32dip</item>
+        <item name="android:itemPadding">8dip</item>
+
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.ActionMode" parent="android:Widget.Holo.ActionMode">
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.Light.ActionMode" parent="android:Widget.Holo.Light.ActionMode">
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.Light.ActionMode.Inverse" parent="android:Widget.Holo.Light.ActionMode.Inverse">
+        <item name="android:titleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionMode.Title.Inverse</item>
+        <item name="android:subtitleTextStyle">@style/TextAppearance.AppCompat.Widget.ActionMode.Subtitle.Inverse</item>
+    </style>
+
+    <style name="Base.Widget.AppCompat.ActionBar.TabBar" parent="android:Widget.Holo.ActionBar.TabBar" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabBar" parent="android:Widget.Holo.Light.ActionBar.TabBar" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabBar.Inverse" parent="android:Widget.Holo.Light.ActionBar.TabBar.Inverse" />
+
+    <style name="Base.Widget.AppCompat.ActionBar.TabView" parent="android:Widget.Holo.ActionBar.TabView" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabView" parent="android:Widget.Holo.Light.ActionBar.TabView" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabView.Inverse" parent="android:Widget.Holo.Light.ActionBar.TabView.Inverse" />
+
+    <style name="Base.Widget.AppCompat.ActionBar.TabText" parent="android:Widget.Holo.ActionBar.TabText" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabText" parent="android:Widget.Holo.Light.ActionBar.TabText" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionBar.TabText.Inverse" parent="android:Widget.Holo.Light.ActionBar.TabText.Inverse" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Menu" parent="android:TextAppearance.Holo.Widget.ActionBar.Menu" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Title" parent="Base.TextAppearance.AppCompat.Medium">
+        <item name="android:textSize">@dimen/action_bar_title_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle" parent="Base.TextAppearance.AppCompat.Small">
+        <item name="android:textSize">@dimen/action_bar_title_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse" parent="Base.TextAppearance.AppCompat.Medium.Inverse">
+        <item name="android:textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse" parent="Base.TextAppearance.AppCompat.Small.Inverse">
+        <item name="android:textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Title" parent="Base.TextAppearance.AppCompat.Medium">
+        <item name="android:textSize">@dimen/action_bar_title_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Subtitle" parent="Base.TextAppearance.AppCompat.Small">
+        <item name="android:textSize">@dimen/action_bar_title_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Title.Inverse" parent="Base.TextAppearance.AppCompat.Medium.Inverse">
+        <item name="android:textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Widget.ActionMode.Subtitle.Inverse" parent="Base.TextAppearance.AppCompat.Small.Inverse">
+        <item name="android:textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
+    </style>
+
+    <!-- Progress Bar -->
+
+    <style name="Base.Widget.AppCompat.ProgressBar.Horizontal" parent="android:Widget.Holo.ProgressBar.Horizontal" />
+
+    <style name="Base.Widget.AppCompat.ProgressBar" parent="android:Widget.Holo.ProgressBar" />
+
+    <!-- Action Button Styles -->
+
+    <style name="Base.Widget.AppCompat.ActionButton" parent="android:Widget.Holo.ActionButton" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionButton" parent="android:Widget.Holo.Light.ActionButton" />
+
+    <style name="Base.Widget.AppCompat.ActionButton.CloseMode" parent="android:Widget.Holo.ActionButton.CloseMode" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionButton.CloseMode" parent="android:Widget.Holo.Light.ActionButton.CloseMode" />
+
+    <style name="Base.Widget.AppCompat.ActionButton.Overflow" parent="android:Widget.Holo.ActionButton.Overflow" />
+
+    <style name="Base.Widget.AppCompat.Light.ActionButton.Overflow" parent="android:Widget.Holo.Light.ActionButton.Overflow" />
+
+    <!-- Spinner Widgets -->
+
+    <style name="Base.Widget.AppCompat.ListView.DropDown" parent="android:Widget.Holo.ListView.DropDown" />
+
+    <style name="Base.Widget.AppCompat.Light.ListView.DropDown" parent="android:Widget.Holo.ListView.DropDown" />
+
+    <style name="Base.Widget.AppCompat.DropDownItem.Spinner" parent="android:Widget.Holo.DropDownItem.Spinner" />
+
+    <style name="Base.Widget.AppCompat.Light.DropDownItem.Spinner" parent="android:Widget.Holo.Light.DropDownItem.Spinner" />
+
+    <style name="Base.Widget.AppCompat.Spinner" parent="android:Widget.Holo.Spinner" />
+
+    <style name="Base.Widget.AppCompat.Light.Spinner" parent="android:Widget.Holo.Light.Spinner" />
+
+    <style name="Base.Widget.AppCompat.ListView.Menu" parent="android:Widget.ListView.Menu" />
+
+    <!-- Popup Menu -->
+
+    <style name="Base.Widget.AppCompat.ListPopupWindow" parent="android:Widget.Holo.ListPopupWindow" />
+
+    <style name="Base.Widget.AppCompat.Light.ListPopupWindow" parent="android:Widget.Holo.Light.ListPopupWindow" />
+
+    <style name="Base.Widget.AppCompat.PopupMenu" parent="android:Widget.Holo.PopupMenu" />
+
+    <style name="Base.Widget.AppCompat.Light.PopupMenu" parent="android:Widget.Holo.Light.PopupMenu" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.PopupMenu.Large" parent="android:TextAppearance.Holo.Widget.PopupMenu.Large" />
+
+    <style name="Base.TextAppearance.AppCompat.Widget.PopupMenu.Small" parent="android:TextAppearance.Holo.Widget.PopupMenu.Small" />
+
+    <style name="Base.TextAppearance.AppCompat.Light.Widget.PopupMenu.Large" parent="android:TextAppearance.Holo.Widget.PopupMenu.Large" />
+
+    <style name="Base.TextAppearance.AppCompat.Light.Widget.PopupMenu.Small" parent="android:TextAppearance.Holo.Widget.PopupMenu.Small" />
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values/styles_base_text.xml b/v14/appcompat/res/values/styles_base_text.xml
new file mode 100644
index 0000000..0a156bc
--- /dev/null
+++ b/v14/appcompat/res/values/styles_base_text.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="Base.TextAppearance.AppCompat" parent="android:TextAppearance.Holo">
+        <item name="android:textColor">?android:textColorPrimary</item>
+        <item name="android:textColorHint">?android:textColorHint</item>
+        <item name="android:textColorHighlight">?android:textColorHighlight</item>
+        <item name="android:textColorLink">?android:textColorLink</item>
+        <item name="android:textSize">@dimen/text_size_body_1_quantum</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Display4">
+        <item name="android:textSize">@dimen/text_size_display_4_quantum</item>
+        <item name="android:textColor">?android:textColorSecondary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Display3">
+        <item name="android:textSize">@dimen/text_size_display_3_quantum</item>
+        <item name="android:textColor">?android:textColorSecondary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Display2">
+        <item name="android:textSize">@dimen/text_size_display_2_quantum</item>
+        <item name="android:textColor">?android:textColorSecondary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Display1">
+        <item name="android:textSize">@dimen/text_size_display_1_quantum</item>
+        <item name="android:textColor">?android:textColorSecondary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Headline">
+        <item name="android:textSize">@dimen/text_size_headline_quantum</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Title">
+        <item name="android:textSize">@dimen/text_size_title_quantum</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Subhead">
+        <item name="android:textSize">@dimen/text_size_subhead_quantum</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Body2">
+        <item name="android:textSize">@dimen/text_size_body_2_quantum</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Body1">
+        <item name="android:textSize">@dimen/text_size_body_1_quantum</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Caption">
+        <item name="android:textSize">@dimen/text_size_caption_quantum</item>
+        <item name="android:textColor">?android:textColorSecondary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Menu">
+        <item name="android:textSize">@dimen/text_size_menu_quantum</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Button">
+        <item name="android:textSize">@dimen/text_size_button_quantum</item>
+        <item name="android:textAllCaps">true</item>
+        <item name="android:textColor">?android:textColorPrimary</item>
+    </style>
+
+    <!-- Deprecated text styles -->
+
+    <style name="Base.TextAppearance.AppCompat.Inverse">
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?android:attr/textColorHintInverse</item>
+        <item name="android:textColorHighlight">?android:attr/textColorHighlightInverse</item>
+        <item name="android:textColorLink">?android:attr/textColorLinkInverse</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Large" parent="TextAppearance.AppCompat.Headline" />
+
+    <style name="Base.TextAppearance.AppCompat.Large.Inverse">
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?android:attr/textColorHintInverse</item>
+        <item name="android:textColorHighlight">?android:attr/textColorHighlightInverse</item>
+        <item name="android:textColorLink">?android:attr/textColorLinkInverse</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Medium" parent="TextAppearance.AppCompat.Body1" />
+
+    <style name="Base.TextAppearance.AppCompat.Medium.Inverse">
+        <item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
+        <item name="android:textColorHint">?android:attr/textColorHintInverse</item>
+        <item name="android:textColorHighlight">?android:attr/textColorHighlightInverse</item>
+        <item name="android:textColorLink">?android:attr/textColorLinkInverse</item>
+    </style>
+
+    <style name="Base.TextAppearance.AppCompat.Small" parent="TextAppearance.AppCompat.Caption" />
+
+    <style name="Base.TextAppearance.AppCompat.Small.Inverse">
+        <item name="android:textColor">?android:attr/textColorTertiaryInverse</item>
+        <item name="android:textColorHint">?android:attr/textColorHintInverse</item>
+        <item name="android:textColorHighlight">?android:attr/textColorHighlightInverse</item>
+        <item name="android:textColorLink">?android:attr/textColorLinkInverse</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values/themes.xml b/v14/appcompat/res/values/themes.xml
new file mode 100644
index 0000000..72df717
--- /dev/null
+++ b/v14/appcompat/res/values/themes.xml
@@ -0,0 +1,67 @@
+<?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>
+    <!--
+        These theme declarations contain any version-independent specification. Items
+        that need to vary based on platform version should be defined in the corresponding
+        "Theme.Base" theme.
+    -->
+
+    <!-- Platform-independent theme providing an action bar in a dark-themed activity. -->
+    <style name="Theme.AppCompat" parent="Base.Theme.AppCompat" />
+
+    <!-- Variant of the appcompat (dark) theme with no action bar. -->
+    <style name="Theme.AppCompat.NoActionBar">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <!-- Variant of the appcompat (dark) theme that has no title bar and fills
+         the entire screen. This theme sets {@link android.R.attr#windowFullscreen} to true. -->
+    <style name="Theme.AppCompat.NoActionBar.Fullscreen">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Variant of the appcompat (dark) theme that has no title bar and translucent
+         system decor (if available) -->
+    <style name="Theme.AppCompat.NoActionBar.TranslucentDecor" />
+
+    <!-- Platform-independent theme providing an action bar in a light-themed activity. -->
+    <style name="Theme.AppCompat.Light" parent="Base.Theme.AppCompat.Light" />
+
+    <!-- Platform-independent theme providing an action bar in a dark-themed activity. -->
+    <style name="Theme.AppCompat.Light.DarkActionBar" parent="Base.Theme.AppCompat.Light.DarkActionBar" />
+
+    <!-- Variant of the appcompat (light) theme with no action bar. -->
+    <style name="Theme.AppCompat.Light.NoActionBar">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <!-- Variant of the appcompat (light) theme that has no title bar and fills
+         the entire screen. This theme sets {@link android.R.attr#windowFullscreen} to true. -->
+    <style name="Theme.AppCompat.Light.NoActionBar.Fullscreen">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Variant of the quantum (light) theme that has no title bar and translucent
+         system decor (if available) -->
+    <style name="Theme.AppCompat.Light.NoActionBar.TranslucentDecor" />
+
+</resources>
\ No newline at end of file
diff --git a/v14/appcompat/res/values/themes_base.xml b/v14/appcompat/res/values/themes_base.xml
new file mode 100644
index 0000000..f0f87c9
--- /dev/null
+++ b/v14/appcompat/res/values/themes_base.xml
@@ -0,0 +1,161 @@
+<?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>
+
+    <!--
+        Themes in the "Base.Theme.*" family vary based on the current platform
+        version to provide the correct basis on each device. You probably don't
+        want to use them directly in your apps.
+
+        Themes in the "Theme.AppCompat" family are meant to be extended or used
+        directly by apps.
+    -->
+    <eat-comment />
+
+    <!-- Base platform-dependent theme providing an action bar in a dark-themed activity. -->
+    <style name="Base.Theme.AppCompat" parent="android:Theme.Holo">
+        <!-- Color palette -->
+        <item name="colorPrimaryDark">@color/quantum_blue_700</item>
+        <item name="colorPrimary">@color/quantum_blue_500</item>
+        <item name="colorPrimaryLight">@color/quantum_blue_100</item>
+        <item name="colorAccent">@color/quantum_teal_A200</item>
+
+        <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+        <item name="colorControlActivated">?attr/colorPrimary</item>
+        <item name="colorControlHighlight">#30ffffff</item>
+
+        <item name="android:actionBarSize">@dimen/action_bar_default_height_quantum</item>
+        <item name="android:actionBarStyle">@style/Widget.AppCompat.ActionBar.Solid</item>
+        <item name="android:actionModeBackground">?attr/colorPrimaryDark</item>
+        <item name="android:actionModeSplitBackground">?attr/colorPrimaryDark</item>
+        <item name="android:actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
+
+        <!-- Text styles -->
+        <item name="android:textAppearance">@style/TextAppearance.AppCompat</item>
+        <item name="android:textAppearanceInverse">@style/TextAppearance.AppCompat.Inverse</item>
+
+        <item name="android:textAppearanceLarge">@style/TextAppearance.AppCompat.Large</item>
+        <item name="android:textAppearanceLargeInverse">
+            @style/TextAppearance.AppCompat.Large.Inverse
+        </item>
+        <item name="android:textAppearanceMedium">@style/TextAppearance.AppCompat.Medium</item>
+        <item name="android:textAppearanceMediumInverse">
+            @style/TextAppearance.AppCompat.Medium.Inverse
+        </item>
+        <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
+        <item name="android:textAppearanceSmallInverse">
+            @style/TextAppearance.AppCompat.Small.Inverse
+        </item>
+
+        <item name="android:textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
+        <item name="android:textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead
+        </item>
+        <!-- TODO <item name="android:textAppearanceListItemSecondary">@style/TextAppearance.AppCompat.Body1</item> -->
+        <item name="android:listPreferredItemPaddingLeft">16dip</item>
+        <item name="android:listPreferredItemPaddingRight">16dip</item>
+    </style>
+
+    <!-- Base platform-dependent theme providing an action bar in a light-themed activity. -->
+    <style name="Base.Theme.AppCompat.Light" parent="android:Theme.Holo.Light">
+        <!-- Color palette -->
+        <item name="colorPrimaryDark">@color/quantum_blue_700</item>
+        <item name="colorPrimary">@color/quantum_blue_500</item>
+        <item name="colorPrimaryLight">@color/quantum_blue_100</item>
+        <item name="colorAccent">@color/quantum_teal_A200</item>
+
+        <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+        <item name="colorControlActivated">?attr/colorPrimary</item>
+        <item name="colorControlHighlight">#30000000</item>
+
+        <item name="android:actionBarSize">@dimen/action_bar_default_height_quantum</item>
+        <item name="android:actionBarStyle">@style/Widget.AppCompat.Light.ActionBar.Solid</item>
+        <item name="android:actionModeBackground">?attr/colorPrimaryDark</item>
+        <item name="android:actionModeSplitBackground">?attr/colorPrimaryDark</item>
+        <item name="android:actionModeStyle">@style/Widget.AppCompat.Light.ActionMode</item>
+
+        <!-- Text styles -->
+        <item name="android:textAppearance">@style/TextAppearance.AppCompat</item>
+        <item name="android:textAppearanceInverse">@style/TextAppearance.AppCompat.Inverse</item>
+
+        <item name="android:textAppearanceLarge">@style/TextAppearance.AppCompat.Large</item>
+        <item name="android:textAppearanceLargeInverse">
+            @style/TextAppearance.AppCompat.Large.Inverse
+        </item>
+        <item name="android:textAppearanceMedium">@style/TextAppearance.AppCompat.Medium</item>
+        <item name="android:textAppearanceMediumInverse">
+            @style/TextAppearance.AppCompat.Medium.Inverse
+        </item>
+        <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
+        <item name="android:textAppearanceSmallInverse">
+            @style/TextAppearance.AppCompat.Small.Inverse
+        </item>
+
+        <item name="android:textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
+        <item name="android:textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead
+        </item>
+        <!-- TODO <item name="android:textAppearanceListItemSecondary">@style/TextAppearance.AppCompat.Body1</item> -->
+        <item name="android:listPreferredItemPaddingLeft">16dip</item>
+        <item name="android:listPreferredItemPaddingRight">16dip</item>
+    </style>
+
+    <!-- Base platform-dependent theme providing a dark action bar in a light-themed activity. -->
+    <style name="Base.Theme.AppCompat.Light.DarkActionBar" parent="android:Theme.Holo.Light.DarkActionBar">
+        <!-- Color palette -->
+        <item name="colorPrimaryDark">@color/quantum_blue_700</item>
+        <item name="colorPrimary">@color/quantum_blue_500</item>
+        <item name="colorPrimaryLight">@color/quantum_blue_100</item>
+        <item name="colorAccent">@color/quantum_teal_A200</item>
+
+        <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+        <item name="colorControlActivated">?attr/colorPrimary</item>
+        <item name="colorControlHighlight">#30000000</item>
+
+        <item name="android:actionBarSize">@dimen/action_bar_default_height_quantum</item>
+        <item name="android:actionBarStyle">@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse
+        </item>
+        <item name="android:actionModeBackground">?attr/colorPrimaryDark</item>
+        <item name="android:actionModeSplitBackground">?attr/colorPrimaryDark</item>
+        <item name="android:actionModeStyle">@style/Widget.AppCompat.Light.ActionMode.Inverse</item>
+
+        <!-- Text styles -->
+        <item name="android:textAppearance">@style/TextAppearance.AppCompat</item>
+        <item name="android:textAppearanceInverse">@style/TextAppearance.AppCompat.Inverse</item>
+
+        <item name="android:textAppearanceLarge">@style/TextAppearance.AppCompat.Large</item>
+        <item name="android:textAppearanceLargeInverse">
+            @style/TextAppearance.AppCompat.Large.Inverse
+        </item>
+        <item name="android:textAppearanceMedium">@style/TextAppearance.AppCompat.Medium</item>
+        <item name="android:textAppearanceMediumInverse">
+            @style/TextAppearance.AppCompat.Medium.Inverse
+        </item>
+        <item name="android:textAppearanceSmall">@style/TextAppearance.AppCompat.Small</item>
+        <item name="android:textAppearanceSmallInverse">
+            @style/TextAppearance.AppCompat.Small.Inverse
+        </item>
+
+        <item name="android:textAppearanceListItem">@style/TextAppearance.AppCompat.Subhead</item>
+        <item name="android:textAppearanceListItemSmall">@style/TextAppearance.AppCompat.Subhead
+        </item>
+        <!-- TODO <item name="android:textAppearanceListItemSecondary">@style/TextAppearance.AppCompat.Body1</item> -->
+        <item name="android:listPreferredItemPaddingLeft">16dip</item>
+        <item name="android:listPreferredItemPaddingRight">16dip</item>
+
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/v17/leanback/Android.mk b/v17/leanback/Android.mk
index 01a8ae3..f64ca8e 100644
--- a/v17/leanback/Android.mk
+++ b/v17/leanback/Android.mk
@@ -30,12 +30,32 @@
 
 # -----------------------------------------------------------------------
 
+#  Base sub-library contains classes both needed by api-level specific libraries
+#  (e.g. KitKat) and final static library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v17-leanback-common
+LOCAL_SDK_VERSION := 17
+LOCAL_SRC_FILES := $(call all-java-files-under, common)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# -----------------------------------------------------------------------
+
+#  A helper sub-library that makes direct use of API 21.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v17-leanback-api21
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api21)
+LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# -----------------------------------------------------------------------
+
 #  A helper sub-library that makes direct use of KitKat APIs.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v17-leanback-kitkat
 LOCAL_SDK_VERSION := 19
 LOCAL_SRC_FILES := $(call all-java-files-under, kitkat)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res
+LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # -----------------------------------------------------------------------
@@ -43,9 +63,9 @@
 #  A helper sub-library that makes direct use of JBMR2 APIs.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v17-leanback-jbmr2
-LOCAL_SDK_VERSION := 19
+LOCAL_SDK_VERSION := 18
 LOCAL_SRC_FILES := $(call all-java-files-under, jbmr2)
-LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res
+LOCAL_JAVA_LIBRARIES := android-support-v17-leanback-res android-support-v17-leanback-common
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # -----------------------------------------------------------------------
@@ -58,7 +78,8 @@
 LOCAL_MODULE := android-support-v17-leanback
 LOCAL_SDK_VERSION := 17
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v17-leanback-kitkat android-support-v17-leanback-jbmr2
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v17-leanback-kitkat android-support-v17-leanback-jbmr2 \
+        android-support-v17-leanback-api21 android-support-v17-leanback-common
 LOCAL_JAVA_LIBRARIES := \
         android-support-v4 \
         android-support-v7-recyclerview \
@@ -119,6 +140,7 @@
 
 LOCAL_SRC_FILES := $(leanback.docs.src_files)
 LOCAL_JAVA_LIBRARIES := $(leanback.docs.java_libraries)
+LOCAL_SDK_VERSION := current
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
 LOCAL_UNINSTALLABLE_MODULE := true
diff --git a/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
new file mode 100644
index 0000000..83e784f
--- /dev/null
+++ b/v17/leanback/api21/android/support/v17/leanback/widget/ShadowHelperApi21.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import android.support.v17.leanback.R;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.ViewGroup;
+
+class ShadowHelperApi21 {
+
+    static int sNormalZ;
+    static int sFocusedZ;
+    static ColorDrawable sColorDrawable;
+
+    private static void initializeResources(Resources res) {
+        if (sColorDrawable == null) {
+            sNormalZ = (int) res.getDimension(R.dimen.lb_quantum_shadow_normal_z);
+            sFocusedZ = (int) res.getDimension(R.dimen.lb_quantum_shadow_focused_z);
+            sColorDrawable = new ColorDrawable(Color.TRANSPARENT);
+        }
+    }
+
+    /* add shadows and return a implementation detail object */
+    public static Object addShadow(ViewGroup shadowContainer) {
+        initializeResources(shadowContainer.getResources());
+        shadowContainer.setBackground(sColorDrawable);
+        shadowContainer.setZ(sNormalZ);
+        return shadowContainer;
+    }
+
+    /* set shadow focus level 0 for unfocused 1 for fully focused */
+    public static void setShadowFocusLevel(Object impl, float level) {
+        ViewGroup shadowContainer = (ViewGroup) impl;
+        shadowContainer.setZ(sNormalZ + level * (sFocusedZ - sNormalZ));
+    }
+}
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index 89d5f3b..0c8c413 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -8,21 +8,96 @@
 }
 
 android {
+    // WARNING: should be 17
     compileSdkVersion 'current'
+
     buildToolsVersion "19.0.1"
 
+    defaultConfig {
+        minSdkVersion 17
+        // TODO: get target from branch
+        //targetSdkVersion 19
+    }
+
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
-        main.java.srcDirs = [
-            'src',
-            'kitkat',
-            'jbmr2'
-        ]
-        main.res.srcDir 'res'
+        main.java.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'src']
+        main.aidl.srcDirs = ['common', 'jbmr2', 'kitkat', 'api21', 'src']
+        main.res.srcDirs = ['res']
+
+        androidTest.setRoot('tests')
+        androidTest.java.srcDir 'tests/java'
     }
 
     lintOptions {
         // TODO: fix errors and reenable.
         abortOnError false
     }
-}
\ No newline at end of file
+}
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+
+    if (name.equals(com.android.builder.BuilderConstants.DEBUG)) {
+        return; // Skip debug builds.
+    }
+    def suffix = name.capitalize()
+
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        from 'LICENSE.txt'
+    }
+    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
+        source android.sourceSets.main.allJava
+        classpath = files(variant.javaCompile.classpath.files) + files(
+                "${android.plugin.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
+    }
+
+    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
+        classifier = 'javadoc'
+        from 'build/docs/javadoc'
+    }
+
+    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
+        classifier = 'sources'
+        from android.sourceSets.main.allSource
+    }
+
+    artifacts.add('archives', javadocJarTask);
+    artifacts.add('archives', sourcesJarTask);
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: uri(rootProject.ext.supportRepoOut)) {
+            }
+
+            pom.project {
+                name 'Android Support Leanback v17'
+                description "Android Support Leanback v17"
+                url 'http://developer.android.com/tools/extras/support-library.html'
+                inceptionYear '2011'
+
+                licenses {
+                    license {
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                        distribution 'repo'
+                    }
+                }
+
+                scm {
+                    url "http://source.android.com"
+                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
+                }
+                developers {
+                    developer {
+                        name 'The Android Open Source Project'
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/v17/leanback/common/android/support/v17/leanback/app/SlideCallback.java b/v17/leanback/common/android/support/v17/leanback/app/SlideCallback.java
new file mode 100644
index 0000000..0693541
--- /dev/null
+++ b/v17/leanback/common/android/support/v17/leanback/app/SlideCallback.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.view.View;
+
+/**
+ * Used by Slide to determine the slide edge and distance when it is about to
+ * create animator.
+ */
+interface SlideCallback {
+
+    /**
+     * Called when Slide is about to create animator for an appearing/disappearing view.
+     * Callback returns true to ask Slide to create animator, edge is returned
+     * in edge[0], distance in pixels is returned in distance[0].  Slide will not
+     * create animator if callback returns false.
+     */
+    public boolean getSlide(View view, boolean appear, int[] edge, float[] distance);
+
+}
diff --git a/v17/leanback/common/android/support/v17/leanback/app/TransitionListener.java b/v17/leanback/common/android/support/v17/leanback/app/TransitionListener.java
new file mode 100644
index 0000000..bcda031
--- /dev/null
+++ b/v17/leanback/common/android/support/v17/leanback/app/TransitionListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+/**
+ * Listeners for transition start and stop.
+ */
+class TransitionListener {
+
+    public void onTransitionStart(Object transition) {
+    }
+
+    public void onTransitionEnd(Object transition) {
+    }
+
+}
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/app/Slide.java b/v17/leanback/kitkat/android/support/v17/leanback/app/Slide.java
new file mode 100644
index 0000000..d9c156d
--- /dev/null
+++ b/v17/leanback/kitkat/android/support/v17/leanback/app/Slide.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.util.Log;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.transition.Visibility;
+import android.transition.Transition;
+import android.transition.TransitionValues;
+
+/**
+ * Slide distance toward/from a edge.  The direction and distance are determined by
+ * {@link SlideCallback}.
+ */
+class Slide extends Visibility {
+    private static final String TAG = "Slide";
+
+    /**
+     * Move Views in or out of the left edge of the scene.
+     * @see #setSlideEdge(int)
+     */
+    public static final int LEFT = 0;
+
+    /**
+     * Move Views in or out of the top edge of the scene.
+     * @see #setSlideEdge(int)
+     */
+    public static final int TOP = 1;
+
+    /**
+     * Move Views in or out of the right edge of the scene.
+     * @see #setSlideEdge(int)
+     */
+    public static final int RIGHT = 2;
+
+    /**
+     * Move Views in or out of the bottom edge of the scene. This is the
+     * default slide direction.
+     * @see #setSlideEdge(int)
+     */
+    public static final int BOTTOM = 3;
+
+    private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
+    private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
+
+    private int[] mTempLoc = new int[2];
+    SlideCallback mCallback;
+    private int[] mTempEdge = new int[1];
+    private float[] mTempDistance = new float[1];
+
+    private interface CalculateSlide {
+        /** Returns the translation value for view when it out of the scene */
+        float getGone(float slide, View view);
+
+        /** Returns the translation value for view when it is in the scene */
+        float getHere(View view);
+
+        /** Returns the property to animate translation */
+        Property<View, Float> getProperty();
+    }
+
+    private static abstract class CalculateSlideHorizontal implements CalculateSlide {
+        @Override
+        public float getHere(View view) {
+            return view.getTranslationX();
+        }
+
+        @Override
+        public Property<View, Float> getProperty() {
+            return View.TRANSLATION_X;
+        }
+    }
+
+    private static abstract class CalculateSlideVertical implements CalculateSlide {
+        @Override
+        public float getHere(View view) {
+            return view.getTranslationY();
+        }
+
+        @Override
+        public Property<View, Float> getProperty() {
+            return View.TRANSLATION_Y;
+        }
+    }
+
+    private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
+        @Override
+        public float getGone(float distance, View view) {
+            return view.getTranslationX() - distance;
+        }
+    };
+
+    private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
+        @Override
+        public float getGone(float distance, View view) {
+            return view.getTranslationY() - distance;
+        }
+    };
+
+    private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
+        @Override
+        public float getGone(float distance, View view) {
+            return view.getTranslationX() + distance;
+        }
+    };
+
+    private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
+        @Override
+        public float getGone(float distance, View view) {
+            return view.getTranslationY() + distance;
+        }
+    };
+
+    public Slide() {
+    }
+
+    public void setCallback(SlideCallback callback) {
+        mCallback = callback;
+    }
+
+    private CalculateSlide getSlideEdge(int slideEdge) {
+        switch (slideEdge) {
+            case LEFT:
+                return sCalculateLeft;
+            case TOP:
+                return sCalculateTop;
+            case RIGHT:
+                return sCalculateRight;
+            case BOTTOM:
+                return sCalculateBottom;
+            default:
+                throw new IllegalArgumentException("Invalid slide direction");
+        }
+    }
+
+    private Animator createAnimation(final View view, Property<View, Float> property,
+            float start, float end, float terminalValue, TimeInterpolator interpolator) {
+        view.setTranslationY(start);
+        if (start == end) {
+            return null;
+        }
+        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);
+
+        SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end);
+        anim.addListener(listener);
+        anim.addPauseListener(listener);
+        anim.setInterpolator(interpolator);
+        return anim;
+    }
+
+    @Override
+    public Animator onAppear(ViewGroup sceneRoot,
+            TransitionValues startValues, int startVisibility,
+            TransitionValues endValues, int endVisibility) {
+        View view = (endValues != null) ? endValues.view : null;
+        if (view == null) {
+            return null;
+        }
+        if (mCallback == null || !mCallback.getSlide(view, true, mTempEdge, mTempDistance)) {
+            return null;
+        }
+        final CalculateSlide slideCalculator = getSlideEdge(mTempEdge[0]);
+        float end = slideCalculator.getHere(view);
+        float start = slideCalculator.getGone(mTempDistance[0], view);
+        return createAnimation(view, slideCalculator.getProperty(), start, end, end, sDecelerate);
+    }
+
+    @Override
+    public Animator onDisappear(ViewGroup sceneRoot,
+            TransitionValues startValues, int startVisibility,
+            TransitionValues endValues, int endVisibility) {
+        View view = (startValues != null) ? startValues.view : null;
+        if (view == null) {
+            return null;
+        }
+        if (mCallback == null || !mCallback.getSlide(view, false, mTempEdge, mTempDistance)) {
+            return null;
+        }
+        final CalculateSlide slideCalculator = getSlideEdge(mTempEdge[0]);
+        float start = slideCalculator.getHere(view);
+        float end = slideCalculator.getGone(mTempDistance[0], view);
+
+        return createAnimation(view, slideCalculator.getProperty(), start, end, start,
+                sAccelerate);
+    }
+
+    private static class SlideAnimatorListener extends AnimatorListenerAdapter {
+        private boolean mCanceled = false;
+        private float mPausedY;
+        private final View mView;
+        private final float mEndY;
+        private final float mTerminalY;
+
+        public SlideAnimatorListener(View view, float terminalY, float endY) {
+            mView = view;
+            mTerminalY = terminalY;
+            mEndY = endY;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animator) {
+            mView.setTranslationY(mTerminalY);
+            mCanceled = true;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animator) {
+            if (!mCanceled) {
+                mView.setTranslationY(mTerminalY);
+            }
+        }
+
+        @Override
+        public void onAnimationPause(Animator animator) {
+            mPausedY = mView.getTranslationY();
+            mView.setTranslationY(mEndY);
+        }
+
+        @Override
+        public void onAnimationResume(Animator animator) {
+            mView.setTranslationY(mPausedY);
+        }
+    }
+}
\ No newline at end of file
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/app/TransitionHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/app/TransitionHelperKitkat.java
index 4ad20b0..07a3995 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/app/TransitionHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/app/TransitionHelperKitkat.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.app;
 
 import android.animation.Animator;
+import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.transition.AutoTransition;
 import android.transition.ChangeBounds;
@@ -33,10 +34,7 @@
 
 class TransitionHelperKitkat {
 
-    private final Context mContext;
-
-    TransitionHelperKitkat(Context context) {
-        mContext = context;
+    TransitionHelperKitkat() {
     }
 
     Object createScene(ViewGroup sceneRoot, Runnable enterAction) {
@@ -60,6 +58,12 @@
         return new AutoTransition();
     }
 
+    Object createSlide(SlideCallback callback) {
+        Slide slide = new Slide();
+        slide.setCallback(callback);
+        return slide;
+    }
+
     Object createFadeTransition(int fadingMode) {
         Fade fade = new Fade(fadingMode);
         return fade;
@@ -143,6 +147,14 @@
         ((CustomChangeBounds) changeBounds).setDefaultStartDelay(startDelay);
     }
 
+    void setStartDelay(Object transition, long startDelay) {
+        ((Transition)transition).setStartDelay(startDelay);
+    }
+
+    void setDuration(Object transition, long duration) {
+        ((Transition)transition).setDuration(duration);
+    }
+
     void exclude(Object transition, int targetId, boolean exclude) {
         ((Transition) transition).excludeTarget(targetId, exclude);
     }
@@ -167,13 +179,13 @@
         ((Transition) transition).addTarget(targetView);
     }
 
-    public void setTransitionCompleteListener(Object transition, Runnable listener) {
+    public void setTransitionListener(Object transition, final TransitionListener listener) {
         Transition t = (Transition) transition;
-        final Runnable completeListener = listener;
         t.addListener(new Transition.TransitionListener() {
 
             @Override
             public void onTransitionStart(Transition transition) {
+                listener.onTransitionStart(transition);
             }
 
             @Override
@@ -186,7 +198,7 @@
 
             @Override
             public void onTransitionEnd(Transition transition) {
-                completeListener.run();
+                listener.onTransitionEnd(transition);
             }
 
             @Override
@@ -198,4 +210,8 @@
     void runTransition(Object scene, Object transition) {
         TransitionManager.go((Scene) scene, (Transition) transition);
     }
+
+    void setInterpolator(Object transition, Object timeInterpolator) {
+        ((Transition) transition).setInterpolator((TimeInterpolator) timeInterpolator);
+    }
 }
diff --git a/v17/leanback/res/drawable-hdpi/ic_action_search.png b/v17/leanback/res/drawable-hdpi/ic_action_search.png
deleted file mode 100644
index a70393b..0000000
--- a/v17/leanback/res/drawable-hdpi/ic_action_search.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/ic_circle_g_bg.png b/v17/leanback/res/drawable-hdpi/ic_circle_g_bg.png
deleted file mode 100644
index 6777e19..0000000
--- a/v17/leanback/res/drawable-hdpi/ic_circle_g_bg.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/ic_circle_shadow.9.png b/v17/leanback/res/drawable-hdpi/ic_circle_shadow.9.png
deleted file mode 100644
index f556eac..0000000
--- a/v17/leanback/res/drawable-hdpi/ic_circle_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_bg_protection.png b/v17/leanback/res/drawable-hdpi/lb_bg_protection.png
new file mode 100644
index 0000000..34fb6a2
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_bg_protection.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png
new file mode 100644
index 0000000..e737a66
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png
new file mode 100644
index 0000000..d58ac6c
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png
new file mode 100644
index 0000000..913e404
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png
new file mode 100644
index 0000000..b097ef9
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png
new file mode 100644
index 0000000..16c1b81
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png
new file mode 100644
index 0000000..d5c5dc1
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png
new file mode 100644
index 0000000..562ae9d
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png
new file mode 100644
index 0000000..db44754
--- /dev/null
+++ b/v17/leanback/res/drawable-hdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/ic_action_search.png b/v17/leanback/res/drawable-mdpi/ic_action_search.png
deleted file mode 100644
index dea3962..0000000
--- a/v17/leanback/res/drawable-mdpi/ic_action_search.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/ic_circle_g_bg.png b/v17/leanback/res/drawable-mdpi/ic_circle_g_bg.png
deleted file mode 100644
index 641f096..0000000
--- a/v17/leanback/res/drawable-mdpi/ic_circle_g_bg.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/ic_circle_shadow.9.png b/v17/leanback/res/drawable-mdpi/ic_circle_shadow.9.png
deleted file mode 100644
index f45b76c..0000000
--- a/v17/leanback/res/drawable-mdpi/ic_circle_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_bg_protection.png b/v17/leanback/res/drawable-mdpi/lb_bg_protection.png
new file mode 100644
index 0000000..369e36a
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_bg_protection.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png
new file mode 100644
index 0000000..aa6b44f
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png
new file mode 100644
index 0000000..74f439d
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png
new file mode 100644
index 0000000..98dc39a
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png
new file mode 100644
index 0000000..1b40609
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png
new file mode 100644
index 0000000..5fdd381
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png
new file mode 100644
index 0000000..be062cc
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png
new file mode 100644
index 0000000..b63a57f
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png
new file mode 100644
index 0000000..d91acee
--- /dev/null
+++ b/v17/leanback/res/drawable-mdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/ic_action_search.png b/v17/leanback/res/drawable-xhdpi/ic_action_search.png
deleted file mode 100644
index 19658e4..0000000
--- a/v17/leanback/res/drawable-xhdpi/ic_action_search.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/ic_circle_g_bg.png b/v17/leanback/res/drawable-xhdpi/ic_circle_g_bg.png
deleted file mode 100644
index acb7c79..0000000
--- a/v17/leanback/res/drawable-xhdpi/ic_circle_g_bg.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/ic_circle_shadow.9.png b/v17/leanback/res/drawable-xhdpi/ic_circle_shadow.9.png
deleted file mode 100644
index 60930f4..0000000
--- a/v17/leanback/res/drawable-xhdpi/ic_circle_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_bg_protection.png b/v17/leanback/res/drawable-xhdpi/lb_bg_protection.png
new file mode 100644
index 0000000..5e9f150
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_bg_protection.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png
new file mode 100644
index 0000000..9796171
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png
new file mode 100644
index 0000000..ca78f0d
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png
new file mode 100644
index 0000000..1db0c04
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png
new file mode 100644
index 0000000..d4eaff1
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png
new file mode 100644
index 0000000..0558e8a
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png
new file mode 100644
index 0000000..8cc5438
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png
new file mode 100644
index 0000000..f913c0f
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png
new file mode 100644
index 0000000..791ffd7
--- /dev/null
+++ b/v17/leanback/res/drawable-xhdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/ic_action_search.png b/v17/leanback/res/drawable-xxhdpi/ic_action_search.png
deleted file mode 100644
index a108638..0000000
--- a/v17/leanback/res/drawable-xxhdpi/ic_action_search.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/ic_circle_g_bg.png b/v17/leanback/res/drawable-xxhdpi/ic_circle_g_bg.png
deleted file mode 100644
index 2bd8ab7..0000000
--- a/v17/leanback/res/drawable-xxhdpi/ic_circle_g_bg.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/ic_circle_shadow.9.png b/v17/leanback/res/drawable-xxhdpi/ic_circle_shadow.9.png
deleted file mode 100644
index 619ce81..0000000
--- a/v17/leanback/res/drawable-xxhdpi/ic_circle_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png b/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png
index 48f82a4..d2227b9 100644
--- a/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png
+++ b/v17/leanback/res/drawable-xxhdpi/lb_action_bg_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png
new file mode 100644
index 0000000..6539869
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_actions_right_arrow.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png
new file mode 100644
index 0000000..dae6e51
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_in_app_search.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png
new file mode 100644
index 0000000..fac4c29
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_sad_cloud.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png
new file mode 100644
index 0000000..9ea80aa
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png
new file mode 100644
index 0000000..ff03bb9
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_ic_search_mic_out.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png
new file mode 100644
index 0000000..b9e372e
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_bg.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png
new file mode 100644
index 0000000..65f3a9e
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_focused.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png
new file mode 100644
index 0000000..9bc3f6f
--- /dev/null
+++ b/v17/leanback/res/drawable-xxhdpi/lb_in_app_search_shadow_normal.9.png
Binary files differ
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v17/leanback/res/drawable/lb_search_orb.xml
similarity index 76%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v17/leanback/res/drawable/lb_search_orb.xml
index 36688bc..438099c 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v17/leanback/res/drawable/lb_search_orb.xml
@@ -15,7 +15,6 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+</shape>
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v17/leanback/res/drawable/lb_selector_action_bg.xml
similarity index 80%
rename from v17/leanback/res/drawable/lb_transition_action_bg.xml
rename to v17/leanback/res/drawable/lb_selector_action_bg.xml
index 36688bc..e02a49e 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v17/leanback/res/drawable/lb_selector_action_bg.xml
@@ -15,7 +15,7 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/lb_action_bg_focused" android:state_focused="true" />
     <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+</selector>
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v17/leanback/res/drawable/lb_speech_orb.xml
similarity index 76%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v17/leanback/res/drawable/lb_speech_orb.xml
index 36688bc..3b66f12 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v17/leanback/res/drawable/lb_speech_orb.xml
@@ -15,7 +15,6 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+</shape>
diff --git a/v17/leanback/res/layout/lb_action_1_line.xml b/v17/leanback/res/layout/lb_action_1_line.xml
index 2374cf3..52d89e0 100644
--- a/v17/leanback/res/layout/lb_action_1_line.xml
+++ b/v17/leanback/res/layout/lb_action_1_line.xml
@@ -15,17 +15,10 @@
      limitations under the License.
 -->
 
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/lb_action_text"
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/lb_action_button"
     android:layout_width="wrap_content"
     android:layout_height="@dimen/lb_action_1_line_height"
-    android:background="@drawable/lb_transition_action_bg"
-    android:focusable="true"
-    android:focusableInTouchMode="true"
-    android:gravity="center_vertical"
+    style="?attr/detailsActionButtonStyle"
     android:lines="1"
-    android:paddingLeft="@dimen/lb_action_1_line_padding_left"
-    android:paddingRight="@dimen/lb_action_padding_right"
-    android:textAllCaps="true"
-    android:textColor="@color/lb_action_text_color"
-    android:textSize="@dimen/lb_action_text_size" />
+    />
diff --git a/v17/leanback/res/layout/lb_action_2_lines.xml b/v17/leanback/res/layout/lb_action_2_lines.xml
index 15cff81..697074a 100644
--- a/v17/leanback/res/layout/lb_action_2_lines.xml
+++ b/v17/leanback/res/layout/lb_action_2_lines.xml
@@ -15,32 +15,10 @@
      limitations under the License.
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/lb_action_button"
     android:layout_width="wrap_content"
     android:layout_height="@dimen/lb_action_2_lines_height"
-    android:background="@drawable/lb_transition_action_bg"
-    android:focusable="true"
-    android:focusableInTouchMode="true" >
-
-    <ImageView
-        android:id="@+id/lb_action_icon"
-        android:layout_width="@dimen/lb_action_icon_width"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_centerVertical="true"
-        android:layout_marginLeft="@dimen/lb_action_icon_margin"
-        android:layout_marginRight="@dimen/lb_action_icon_margin" />
-
-    <TextView
-        android:id="@+id/lb_action_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_toRightOf="@id/lb_action_icon"
-        android:gravity="left|center_vertical"
-        android:lines="2"
-        android:textAllCaps="true"
-        android:textColor="@color/lb_action_text_color"
-        android:textSize="@dimen/lb_action_text_size" />
-
-</RelativeLayout>
\ No newline at end of file
+    style="?attr/detailsActionButtonStyle"
+    android:lines="2"
+    />
diff --git a/v17/leanback/res/layout/lb_browse_fragment.xml b/v17/leanback/res/layout/lb_browse_fragment.xml
index 0315daa..0ca87b7 100644
--- a/v17/leanback/res/layout/lb_browse_fragment.xml
+++ b/v17/leanback/res/layout/lb_browse_fragment.xml
@@ -28,14 +28,14 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         >
-        <include layout="@layout/lb_browse_title" />
         <android.support.v17.leanback.app.BrowseRowsFrameLayout
             android:id="@+id/browse_container_dock"
-            android:layout_width="wrap_content"
+            android:layout_width="match_parent"
             android:layout_height="match_parent" />
         <FrameLayout
             android:id="@+id/browse_headers_dock"
             android:layout_width="wrap_content"
             android:layout_height="match_parent" />
+        <include layout="@layout/lb_browse_title" />
     </android.support.v17.leanback.app.BrowseFrameLayout>
 </FrameLayout>
diff --git a/v17/leanback/res/layout/lb_browse_title.xml b/v17/leanback/res/layout/lb_browse_title.xml
index 77bd35c..26bce3f 100644
--- a/v17/leanback/res/layout/lb_browse_title.xml
+++ b/v17/leanback/res/layout/lb_browse_title.xml
@@ -14,43 +14,37 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/browse_title_group"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:paddingTop="?attr/browsePaddingTop"
-    android:paddingLeft="?attr/browsePaddingLeft">
+    android:paddingRight="?attr/browsePaddingRight"
+    android:paddingLeft="?attr/browsePaddingLeft"
+    android:paddingBottom="?attr/browsePaddingTop"
+    android:clipToPadding="false"
+    android:clipChildren="false">
 
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
+    <ImageView
+        android:id="@+id/browse_badge"
+        android:layout_width="@dimen/lb_browse_title_text_width"
+        android:layout_height="@dimen/lb_browse_title_height"
+        android:layout_gravity="center_vertical|right"
+        android:src="@null"
+        android:visibility="gone"
+        style="?attr/browseTitleIconStyle"/>
 
-        <ImageView
-            android:id="@+id/browse_badge"
-            android:layout_width="@dimen/lb_browse_title_icon_width"
-            android:layout_height="@dimen/lb_browse_title_icon_height"
-            android:layout_marginRight="@dimen/lb_browse_title_icon_margin_right"
-            android:layout_alignParentLeft="true"
-            android:layout_centerVertical="true"
-            android:src="@null"
-            android:visibility="gone"
-            style="?attr/browseTitleIconStyle"/>
+    <TextView
+        android:id="@+id/browse_title"
+        android:layout_width="@dimen/lb_browse_title_text_width"
+        android:layout_height="@dimen/lb_browse_title_height"
+        android:layout_gravity="center_vertical|right"
+        style="?attr/browseTitleTextStyle"/>
 
-        <TextView
-            android:id="@+id/browse_title"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/lb_browse_title_height"
-            android:layout_toRightOf="@id/browse_badge"
-            android:layout_centerVertical="true"
-            style="?attr/browseTitleTextStyle"/>
+    <android.support.v17.leanback.widget.SearchOrbView
+        android:id="@+id/browse_orb"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical|left" />
 
-        <android.support.v17.leanback.widget.SearchOrbView
-            android:id="@+id/browse_orb"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_alignParentTop="true"
-            android:layout_alignParentRight="true"
-            android:layout_marginRight="48dip"
-            android:layout_marginBottom="4dip"/>
-    </RelativeLayout>
-</LinearLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/v17/leanback/res/layout/lb_details_description.xml b/v17/leanback/res/layout/lb_details_description.xml
index 1cd26cd..798504f 100644
--- a/v17/leanback/res/layout/lb_details_description.xml
+++ b/v17/leanback/res/layout/lb_details_description.xml
@@ -22,6 +22,7 @@
     android:layout_height="wrap_content"
     >
 
+    <!-- Top margins set programatically -->
     <TextView
         android:id="@+id/lb_details_description_title"
         android:layout_width="wrap_content"
diff --git a/v17/leanback/res/layout/lb_details_overview.xml b/v17/leanback/res/layout/lb_details_overview.xml
index ea8220e..beca042 100644
--- a/v17/leanback/res/layout/lb_details_overview.xml
+++ b/v17/leanback/res/layout/lb_details_overview.xml
@@ -15,45 +15,70 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lb="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical" >
+    android:layout_height="wrap_content" >
 
-    <LinearLayout
+    <!-- Background is applied to this inner layout -->
+    <RelativeLayout
+        android:id="@+id/details_overview"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
+        android:layout_height="@dimen/lb_details_overview_height_large"
+        android:layout_marginLeft="@dimen/lb_details_overview_margin_left"
+        android:layout_marginRight="@dimen/lb_details_overview_margin_right"
+        android:layout_marginBottom="@dimen/lb_details_overview_margin_bottom"
+         >
 
         <ImageView
             android:id="@+id/details_overview_image"
-            android:layout_width="@dimen/lb_details_overview_image_width"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/lb_details_overview_image_margin_left"
-            android:gravity="top|left" />
+            android:layout_alignParentLeft="true"
+            android:adjustViewBounds="true"
+            />
+
+       <ImageView
+            android:id="@+id/details_overview_actions_more"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/lb_ic_actions_right_arrow"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginRight="@dimen/lb_details_overview_actions_more_margin_right"
+            android:layout_marginBottom="@dimen/lb_details_overview_actions_more_margin_bottom"
+            />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_toRightOf="@id/details_overview_image"
+            android:layout_marginLeft="@dimen/lb_details_overview_description_margin_left"
+            android:layout_marginRight="@dimen/lb_details_overview_description_margin_right"
+            android:layout_marginBottom="@dimen/lb_details_overview_description_margin_bottom"
+            android:layout_marginTop="@dimen/lb_details_overview_description_margin_top"
+            android:orientation="vertical" >
 
         <FrameLayout
             android:id="@+id/details_overview_description"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/lb_details_overview_description_margin_left"
-            android:layout_marginRight="@dimen/lb_details_overview_description_margin_right"
+            android:layout_height="0dp"
+            android:layout_weight="1"
             android:gravity="top" />
-    </LinearLayout>
 
-    <android.support.v17.leanback.widget.HorizontalGridView
-        android:id="@+id/details_overview_actions"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/lb_details_overiew_actions_margin_top"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:paddingLeft="@dimen/lb_details_overview_actions_padding_left"
-        android:paddingRight="@dimen/lb_details_overview_actions_padding_right"
-        lb:horizontalMargin="@dimen/lb_details_overview_action_items_margin"
-        lb:rowHeight="@dimen/lb_details_overview_actions_height" />
+        <android.support.v17.leanback.widget.HorizontalGridView
+            android:id="@+id/details_overview_actions"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            lb:horizontalMargin="@dimen/lb_details_overview_action_items_margin"
+            lb:rowHeight="@dimen/lb_details_overview_actions_height" />
 
-</LinearLayout>
\ No newline at end of file
+        </LinearLayout>
+    </RelativeLayout>
+
+</FrameLayout>
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v17/leanback/res/layout/lb_header.xml
similarity index 71%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v17/leanback/res/layout/lb_header.xml
index 36688bc..7437cf3 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v17/leanback/res/layout/lb_header.xml
@@ -15,7 +15,10 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<android.support.v17.leanback.widget.RowHeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/row_header"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    style="?headerStyle"
+    />
diff --git a/v17/leanback/res/layout/lb_headers_fragment.xml b/v17/leanback/res/layout/lb_headers_fragment.xml
index c72cd06..dbfbb8d 100644
--- a/v17/leanback/res/layout/lb_headers_fragment.xml
+++ b/v17/leanback/res/layout/lb_headers_fragment.xml
@@ -18,6 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:lb="http://schemas.android.com/apk/res-auto"
     android:id="@+id/browse_headers"
-    android:layout_width="match_parent"
+    android:layout_width="@dimen/lb_browse_headers_width"
     android:layout_height="match_parent"
     style="?attr/headersVerticalGridStyle"/>
diff --git a/v17/leanback/res/layout/lb_list_row.xml b/v17/leanback/res/layout/lb_list_row.xml
index a432518..80d7bef 100644
--- a/v17/leanback/res/layout/lb_list_row.xml
+++ b/v17/leanback/res/layout/lb_list_row.xml
@@ -19,5 +19,5 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/row_content"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/lb_browse_row_list_height"
+    android:layout_height="wrap_content"
     style="?attr/rowHorizontalGridStyle" />
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v17/leanback/res/layout/lb_row_header.xml
similarity index 71%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v17/leanback/res/layout/lb_row_header.xml
index 36688bc..69fac46 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v17/leanback/res/layout/lb_row_header.xml
@@ -15,7 +15,10 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<android.support.v17.leanback.widget.RowHeaderView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/row_header"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    style="?rowHeaderStyle"
+    />
diff --git a/v17/leanback/res/layout/lb_rows_fragment.xml b/v17/leanback/res/layout/lb_rows_fragment.xml
index 5b147c5..c4ffdc3 100644
--- a/v17/leanback/res/layout/lb_rows_fragment.xml
+++ b/v17/leanback/res/layout/lb_rows_fragment.xml
@@ -16,8 +16,8 @@
 -->
 <android.support.v17.leanback.widget.VerticalGridView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:lb="http://schemas.android.com/apk/res-auto"
     android:id="@+id/container_list"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     style="?attr/rowsVerticalGridStyle" />
+
diff --git a/v17/leanback/res/layout/lb_search_bar.xml b/v17/leanback/res/layout/lb_search_bar.xml
index 8e1a1b7..0298643 100644
--- a/v17/leanback/res/layout/lb_search_bar.xml
+++ b/v17/leanback/res/layout/lb_search_bar.xml
@@ -14,52 +14,59 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<android.support.v17.leanback.widget.SearchBar
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/lb_search_bar"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" >
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <android.support.v17.leanback.widget.SpeechOrbView
+            android:id="@+id/lb_search_bar_speech_orb"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="@dimen/lb_search_bar_speech_orb_margin_left"
+            >
+        <requestFocus/>
+    </android.support.v17.leanback.widget.SpeechOrbView>
 
     <RelativeLayout
-            android:id="@+id/lb_search_bar_layout"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/lb_search_bar_height"
-            android:paddingLeft="@dimen/lb_search_bar_padding_left"
-            android:clipChildren="false"
-            android:layout_alignParentTop="true"
-            android:layout_gravity="top"
-            android:background="@android:color/transparent" >
+            android:id="@+id/lb_search_bar_items"
+            android:layout_width="@dimen/lb_search_bar_items_width"
+            android:layout_height="@dimen/lb_search_bar_items_height"
+            android:layout_toRightOf="@+id/lb_search_bar_speech_orb"
+            android:layout_centerVertical="true"
+            android:layout_marginLeft="@dimen/lb_search_bar_items_margin_left"
+            android:orientation="horizontal"
+            android:background="@drawable/lb_in_app_search_bg"
+            >
 
+        <ImageView
+                android:id="@+id/lb_search_bar_badge"
+                android:layout_width="@dimen/lb_search_bar_icon_width"
+                android:layout_height="@dimen/lb_search_bar_icon_height"
+                android:layout_gravity="center_vertical|left"
+                android:layout_centerVertical="true"
+                android:layout_marginLeft="@dimen/lb_search_bar_icon_margin_left"
+                android:src="@null"
+                android:visibility="gone"
+                style="?attr/browseTitleIconStyle"/>
 
-        <FrameLayout
-                android:id="@+id/lb_search_bar_items"
-                android:layout_width="@dimen/lb_search_bar_items_width"
-                android:layout_height="wrap_content"
-                android:layout_gravity="top"
-                android:layout_marginLeft="@dimen/lb_search_browse_row_padding_left"
-                android:layout_marginTop="@dimen/lb_search_bar_items_layout_margin_top"
-                android:layout_alignParentLeft="true"
-                android:layout_alignParentTop="true"
-                android:orientation="horizontal"
-                android:background="@android:color/transparent"
-                android:layout_weight="1">
-                <android.support.v17.leanback.widget.SearchEditText
-                        android:id="@+id/lb_search_text_editor"
-                        android:layout_width="match_parent"
-                        android:layout_height="match_parent"
-                        android:cursorVisible="true"
-                        android:editable="true"
-                        android:background="@null"
-                        android:fontFamily="sans-serif"
-                        android:focusable="true"
-                        android:imeOptions="normal|flagNoExtractUi|actionSearch"
-                        android:inputType="text|textAutoComplete"
-                        android:singleLine="true"
-                        android:textColor="@color/lb_search_bar_text_color"
-                        android:textColorHint="@color/lb_search_bar_hint_color"
-                        android:textCursorDrawable="@null"
-                        android:hint="@string/lb_search_bar_hint"
-                        android:textSize="@dimen/lb_search_bar_text_size"/>
-        </FrameLayout>
+        <android.support.v17.leanback.widget.SearchEditText
+                    android:id="@+id/lb_search_text_editor"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical|right"
+                    android:layout_marginLeft="@dimen/lb_search_bar_edit_text_margin_left"
+                    android:layout_centerVertical="true"
+                    android:cursorVisible="true"
+                    android:layout_toRightOf="@+id/lb_search_bar_badge"
+                    android:editable="true"
+                    android:background="@null"
+                    android:fontFamily="sans-serif"
+                    android:focusable="true"
+                    android:imeOptions="normal|flagNoExtractUi|actionSearch"
+                    android:inputType="text|textAutoComplete"
+                    android:singleLine="true"
+                    android:textColor="@color/lb_search_bar_text_color"
+                    android:textColorHint="@color/lb_search_bar_hint_color"
+                    android:textCursorDrawable="@null"
+                    android:hint="@string/lb_search_bar_hint"
+                    android:textSize="@dimen/lb_search_bar_text_size"/>
     </RelativeLayout>
-</android.support.v17.leanback.widget.SearchBar>
+</merge>
diff --git a/v17/leanback/res/layout/lb_search_fragment.xml b/v17/leanback/res/layout/lb_search_fragment.xml
index 985ab21..57a46b4 100644
--- a/v17/leanback/res/layout/lb_search_fragment.xml
+++ b/v17/leanback/res/layout/lb_search_fragment.xml
@@ -18,10 +18,18 @@
              android:id="@+id/lb_search_frame"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
-             android:paddingTop="@dimen/lb_search_bar_padding_top">
+             android:paddingTop="@dimen/lb_search_bar_padding_top"
+             android:clipToPadding="false"
+             android:clipChildren="false"
+        >
         <FrameLayout
                 android:id="@+id/lb_results_frame"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"/>
-        <include layout="@layout/lb_search_bar" />
+    <android.support.v17.leanback.widget.SearchBar
+            android:id="@+id/lb_search_bar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:clipChildren="false"
+            />
 </FrameLayout>
diff --git a/v17/leanback/res/layout/lb_search_orb.xml b/v17/leanback/res/layout/lb_search_orb.xml
index c6bf690..0eff71e 100644
--- a/v17/leanback/res/layout/lb_search_orb.xml
+++ b/v17/leanback/res/layout/lb_search_orb.xml
@@ -14,45 +14,21 @@
      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_height="match_parent"
-    android:layout_width="wrap_content"
-    android:orientation="horizontal"
-    android:focusable="true">
 
-    <TextView
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:id="@+id/title"
-        android:text="@string/orb_search_label"
-        android:layout_gravity="center_vertical"
-        android:textAppearance="@style/TextAppearance.Leanback.SearchLabel" />
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
 
-    <FrameLayout
+    <View
         android:id="@+id/search_orb"
         android:layout_width="@dimen/lb_search_orb_size"
         android:layout_height="@dimen/lb_search_orb_size"
-        android:layout_gravity="top|start"
-        android:clipChildren="false"
-        android:layout_marginBottom="@dimen/lb_search_orb_margin_bottom"
-        android:layout_marginLeft="@dimen/lb_search_orb_margin_left"
-        android:layout_marginRight="@dimen/lb_search_orb_margin_right"
-        android:layout_marginTop="@dimen/lb_search_orb_margin_top" >
+        android:background="@drawable/lb_search_orb" />
 
-        <ImageView
-            android:id="@+id/orb"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:src="@drawable/ic_circle_g_bg"
-            android:background="@drawable/ic_circle_shadow"
-            android:contentDescription="@null" />
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:src="@drawable/lb_ic_in_app_search"
+        android:contentDescription="@string/orb_search_action" />
 
-        <ImageView
-            android:id="@+id/icon"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:src="@drawable/ic_action_search"
-            android:contentDescription="@string/orb_search_action" />
-    </FrameLayout>
-</LinearLayout>
\ No newline at end of file
+</merge>
diff --git a/v17/leanback/res/layout/lb_speech_orb.xml b/v17/leanback/res/layout/lb_speech_orb.xml
new file mode 100644
index 0000000..ff6129b
--- /dev/null
+++ b/v17/leanback/res/layout/lb_speech_orb.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <View
+            android:id="@+id/lb_speech_orb"
+            android:layout_width="@dimen/lb_search_bar_speech_orb_size"
+            android:layout_height="@dimen/lb_search_bar_speech_orb_size"
+            android:background="@drawable/lb_speech_orb" />
+
+    <ImageView
+            android:id="@+id/lb_speech_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:src="@drawable/lb_ic_search_mic_out"
+            android:contentDescription="@string/orb_search_action" />
+
+</merge>
diff --git a/v17/leanback/res/layout/lb_vertical_grid_fragment.xml b/v17/leanback/res/layout/lb_vertical_grid_fragment.xml
index 902c483..0720501 100644
--- a/v17/leanback/res/layout/lb_vertical_grid_fragment.xml
+++ b/v17/leanback/res/layout/lb_vertical_grid_fragment.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -27,4 +27,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-</LinearLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 78456c6..f31cd96 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -26,18 +26,24 @@
         <attr name="horizontalMargin" format="dimension" />
         <!-- Defining margin between two items vertically -->
         <attr name="verticalMargin" format="dimension" />
+        <!-- Defining gravity of child view -->
+        <attr name="android:gravity" />
     </declare-styleable>
 
     <declare-styleable name="lbHorizontalGridView">
         <!-- Defining height of each row of HorizontalGridView -->
-        <attr name="rowHeight" format="dimension" />
+        <attr name="rowHeight" format="dimension" >
+            <enum name="wrap_content" value="-2" />
+        </attr>
         <!-- Defining number of rows -->
         <attr name="numberOfRows" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="lbVerticalGridView">
         <!-- Defining width of each column of VerticalGridView -->
-        <attr name="columnWidth" format="dimension" />
+        <attr name="columnWidth" format="dimension" >
+            <enum name="wrap_content" value="-2" />
+        </attr>
         <!-- Defining number of columns -->
         <attr name="numberOfColumns" format="integer" />
     </declare-styleable>
@@ -134,6 +140,8 @@
 
         <!-- vertical grid style inside HeadersFragment -->
         <attr name="headersVerticalGridStyle" format="reference" />
+        <!-- header style inside HeadersFragment -->
+        <attr name="headerStyle" format="reference" />
 
         <!-- vertical grid style inside RowsFragment -->
         <attr name="rowsVerticalGridStyle" format="reference" />
@@ -156,6 +164,7 @@
         <attr name="detailsDescriptionTitleStyle" format="reference" />
         <attr name="detailsDescriptionSubtitleStyle" format="reference" />
         <attr name="detailsDescriptionBodyStyle" format="reference" />
+        <attr name="detailsActionButtonStyle" format="reference" />
 
         <!-- style for a vertical grid of items -->
         <attr name="itemsVerticalGridStyle" format="reference" />
diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml
index 0eeb825..f78d9f3 100644
--- a/v17/leanback/res/values/colors.xml
+++ b/v17/leanback/res/values/colors.xml
@@ -19,7 +19,7 @@
     <color name="lb_grey">#888888</color>
 
     <color name="lb_browse_title_color">#EEEEEE</color>
-    <color name="lb_browse_header_color">#EEEEEE</color>
+    <color name="lb_browse_header_color">#FFFFFF</color>
 
     <color name="lb_list_item_unselected_text_color">#FFF1F1F1</color>
     <color name="lb_background_protection">#A0333333</color>
@@ -27,13 +27,18 @@
     <color name="lb_view_dim_mask_color">#000000</color>
     <item name="lb_view_dimmed_level" type="dimen">60%</item>
 
+    <color name="lb_details_overview_bg_color">#1B1B1B</color>
     <color name="lb_details_description_color">#EEEEEE</color>
 
     <color name="lb_action_text_color">#EEEEEE</color>
     <color name="lb_action_bg_color">#3D3D3D</color>
 
     <color name="lb_search_bar_text_color">#FFEEEEEE</color>
-    <color name="lb_search_bar_hint_color">#33EEEEEE</color>
+    <color name="lb_search_bar_text_speech_color">#FF444444</color>
+    <color name="lb_search_bar_hint_color">#66222222</color>
+
+    <color name="lb_speech_orb_not_recording">#33EEEEEE</color>
+    <color name="lb_speech_orb_recording">#33EE0000</color>
 
     <color name="lb_basic_card_bg_color">#FF1B1B1B</color>
     <color name="lb_basic_card_info_bg_color">#FF1B1B1B</color>
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 61792fa..b3ec3ee 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -15,96 +15,129 @@
      limitations under the License.
 -->
 <resources>
+    <dimen name="lb_list_row_height">224dp</dimen>
+
     <dimen name="lb_browse_padding_left">56dp</dimen>
     <dimen name="lb_browse_padding_top">27dp</dimen>
     <dimen name="lb_browse_padding_right">56dp</dimen>
     <dimen name="lb_browse_padding_bottom">48dp</dimen>
     <dimen name="lb_browse_rows_margin_start">238dp</dimen>
-    <dimen name="lb_browse_rows_margin_top">120dp</dimen>
+    <dimen name="lb_browse_rows_margin_top">167dp</dimen>
     <dimen name="lb_browse_rows_fading_edge">16dp</dimen>
 
     <dimen name="lb_browse_title_height">60dp</dimen>
     <dimen name="lb_browse_title_icon_height">52dp</dimen>
     <dimen name="lb_browse_title_icon_width">52dp</dimen>
-    <dimen name="lb_browse_title_icon_margin_right">24dp</dimen>
-    <dimen name="lb_browse_title_text_size">28sp</dimen>
+    <dimen name="lb_browse_title_icon_margin_right">52dp</dimen>
+    <dimen name="lb_browse_title_text_size">44sp</dimen>
+    <dimen name="lb_browse_title_text_width">584dp</dimen>
 
-    <integer name="lb_browse_headers_transition_delay">250</integer>
+    <dimen name="lb_browse_headers_width">270dp</dimen>
+    <integer name="lb_browse_headers_transition_delay">150</integer>
+    <integer name="lb_browse_headers_transition_duration">250</integer>
 
     <integer name="lb_browse_rows_anim_duration">250</integer>
 
-    <dimen name="lb_browse_headers_vertical_margin">12dp</dimen>
-    <dimen name="lb_browse_header_height">48dp</dimen>
-    <dimen name="lb_browse_header_half_height">24dp</dimen>
-    <dimen name="lb_browse_header_text_size">22sp</dimen>
-    <item name="lb_browse_header_select_duration" format="integer" type="dimen">150</item>
-    <item name="lb_browse_header_unselect_alpha" format="float" type="dimen">0.5</item>
-    <item name="lb_browse_header_select_scale" format="float" type="dimen">1.1</item>
+    <!-- Derived from the ux spec of 48dp baseline to baseline -->
+    <dimen name="lb_browse_headers_vertical_margin">21dp</dimen>
+    <dimen name="lb_browse_header_text_size">20sp</dimen>
+    <dimen name="lb_browse_header_padding_right">24dp</dimen>
+    <dimen name="lb_browse_header_height">24dp</dimen>
 
-    <dimen name="lb_browse_row_list_height">224dp</dimen>
-    <dimen name="lb_browse_row_title_height">24dp</dimen>
-    <dimen name="lb_browse_row_hovercard_max_width">420dp</dimen>
+    <item name="lb_browse_header_select_duration" format="integer" type="dimen">150</item>
+    <item name="lb_browse_header_unselect_alpha" type="fraction">50%</item>
+    <item name="lb_browse_header_select_scale" format="float" type="dimen">1.2</item>
+
+    <dimen name="lb_browse_row_hovercard_max_width">400dp</dimen>
     <dimen name="lb_browse_row_hovercard_title_font_size">18sp</dimen>
-    <dimen name="lb_browse_row_hovercard_description_font_size">12sp</dimen>
-    <dimen name="lb_browse_row_header_text_size">18sp</dimen>
-    <dimen name="lb_browse_item_margin">12dp</dimen>
-    <dimen name="lb_browse_item_margin_vertical">12dp</dimen>
-    <dimen name="lb_browse_item_margin_horizontal">12dp</dimen>
+    <dimen name="lb_browse_row_hovercard_description_font_size">14sp</dimen>
+    <dimen name="lb_browse_item_horizontal_margin">8dp</dimen>
+    <dimen name="lb_browse_item_vertical_margin">10dp</dimen>
 
     <item name="lb_focus_zoom_factor_small" type="fraction">106%</item>
     <item name="lb_focus_zoom_factor_medium" type="fraction">110%</item>
     <item name="lb_focus_zoom_factor_large" type="fraction">114%</item>
 
-    <dimen name="lb_details_overview_image_width">132dp</dimen>
-    <dimen name="lb_details_overview_image_margin_left">132dp</dimen>
-    <dimen name="lb_details_overview_description_intertext_spacing">16dp</dimen>
-    <dimen name="lb_details_overview_description_margin_left">30dp</dimen>
-    <dimen name="lb_details_overview_description_margin_right">132dp</dimen>
-    <dimen name="lb_details_overiew_actions_margin_top">19dp</dimen>
-    <dimen name="lb_details_overview_action_items_margin">32dp</dimen>
+    <dimen name="lb_details_overview_height_large">274dp</dimen>
+    <dimen name="lb_details_overview_height_small">159dp</dimen>
+    <dimen name="lb_details_overview_margin_left">132dp</dimen>
+    <dimen name="lb_details_overview_margin_right">132dp</dimen>
+    <dimen name="lb_details_overview_margin_bottom">40dp</dimen>
+
+    <dimen name="lb_details_overview_description_margin_top">24dp</dimen>
+    <dimen name="lb_details_overview_description_margin_left">24dp</dimen>
+    <dimen name="lb_details_overview_description_margin_right">24dp</dimen>
+    <dimen name="lb_details_overview_description_margin_bottom">12dp</dimen>
+    <dimen name="lb_details_overview_action_items_margin">0dp</dimen>
     <item name="lb_details_overview_action_select_duration" format="integer" type="dimen">150</item>
     <dimen name="lb_details_overview_actions_padding_left">294dp</dimen>
     <dimen name="lb_details_overview_actions_padding_right">132dp</dimen>
     <dimen name="lb_details_overview_actions_height">56dp</dimen>
-    <dimen name="lb_details_rows_align_top">120dp</dimen>
+    <dimen name="lb_details_overview_actions_more_margin_right">12dp</dimen>
+    <dimen name="lb_details_overview_actions_more_margin_bottom">28dp</dimen>
+    <dimen name="lb_details_overview_actions_fade_size">16dp</dimen>
+    <dimen name="lb_details_rows_align_top">167dp</dimen>
 
     <dimen name="lb_details_description_title_text_size">34sp</dimen>
-    <dimen name="lb_details_description_title_leading_space">42sp</dimen>
-    <dimen name="lb_details_description_subtitle_text_size">14sp</dimen>
+    <dimen name="lb_details_description_subtitle_text_size">16sp</dimen>
     <dimen name="lb_details_description_body_text_size">14sp</dimen>
+    <dimen name="lb_details_description_title_line_spacing">40dp</dimen>
+    <dimen name="lb_details_description_body_line_spacing">20dp</dimen>
+    <dimen name="lb_details_description_title_baseline">26dp</dimen>
+    <dimen name="lb_details_description_under_title_baseline_margin">32dp</dimen>
+    <dimen name="lb_details_description_under_subtitle_baseline_margin">32dp</dimen>
+
     <integer name="lb_details_description_title_max_lines">2</integer>
     <integer name="lb_details_description_subtitle_max_lines">1</integer>
     <integer name="lb_details_description_body_max_lines">5</integer>
+    <integer name="lb_details_description_body_min_lines">3</integer>
 
     <dimen name="lb_action_1_line_height">36dp</dimen>
-    <dimen name="lb_action_1_line_padding_left">32dp</dimen>
-    <dimen name="lb_action_2_lines_height">54dp</dimen>
-    <dimen name="lb_action_padding_right">32dp</dimen>
+    <dimen name="lb_action_2_lines_height">56dp</dimen>
+    <dimen name="lb_action_padding_horizontal">34dp</dimen>
+    <dimen name="lb_action_with_icon_padding_left">14dp</dimen>
+    <dimen name="lb_action_with_icon_padding_right">20dp</dimen>
     <dimen name="lb_action_icon_margin">12dp</dimen>
-    <dimen name="lb_action_icon_width">30dp</dimen>
     <dimen name="lb_action_text_size">16sp</dimen>
-    <dimen name="lb_action_text_spacing">2sp</dimen>
 
     <!-- Search bar -->
     <dimen name="lb_search_bar_height">60dp</dimen>
     <dimen name="lb_search_bar_padding_left">56dp</dimen>
     <dimen name="lb_search_bar_padding_top">27dp</dimen>
 
-    <dimen name="lb_search_bar_text_size">28sp</dimen>
+    <dimen name="lb_search_bar_text_size">22sp</dimen>
+    <dimen name="lb_search_bar_unfocused_text_size">18sp</dimen>
     <dimen name="lb_search_bar_items_layout_margin_top">27dp</dimen>
-    <dimen name="lb_search_bar_items_width">660dp</dimen>
+    <dimen name="lb_search_bar_items_width">600dp</dimen>
+    <dimen name="lb_search_bar_items_height">56dp</dimen>
+    <dimen name="lb_search_bar_items_margin_left">56dp</dimen>
+    <dimen name="lb_search_bar_icon_height">32dp</dimen>
+    <dimen name="lb_search_bar_icon_width">32dp</dimen>
+    <dimen name="lb_search_bar_icon_margin_left">16dp</dimen>
+    <dimen name="lb_search_bar_edit_text_margin_left">24dp</dimen>
+    <dimen name="lb_search_bar_hint_margin_left">52dp</dimen>
+
 
     <!-- Search Fragment -->
     <dimen name="lb_search_browse_rows_align_top">120dp</dimen>
     <dimen name="lb_search_browse_row_padding_left">56dp</dimen>
 
     <dimen name="lb_search_orb_size">52dp</dimen>
+    <item name="lb_search_orb_focused_zoom" type="fraction">120%</item>
+    <item name="lb_search_orb_brightness_alpha" type="fraction">30%</item>
+    <item name="lb_search_orb_pulse_duration_ms" type="integer">1000</item>
+    <item name="lb_search_orb_scale_down_duration_ms" type="integer">100</item>
 
     <dimen name="lb_search_orb_margin_top">4dp</dimen>
     <dimen name="lb_search_orb_margin_bottom">4dp</dimen>
     <dimen name="lb_search_orb_margin_left">4dp</dimen>
     <dimen name="lb_search_orb_margin_right">4dp</dimen>
 
+    <dimen name="lb_search_bar_speech_orb_size">52dp</dimen>
+    <item name="lb_search_bar_speech_orb_focused_zoom" type="fraction">120%</item>
+    <item name="lb_search_bar_speech_orb_max_level_zoom" type="fraction">144%</item>
+    <dimen name="lb_search_bar_speech_orb_margin_left">56dp</dimen>
+
     <!-- BasicCardView -->
     <dimen name="lb_basic_card_main_width">140dp</dimen>
     <dimen name="lb_basic_card_main_height">188dp</dimen>
@@ -114,4 +147,9 @@
     <dimen name="lb_basic_card_title_text_size">14sp</dimen>
     <dimen name="lb_basic_card_content_text_size">10sp</dimen>
     <dimen name="lb_basic_card_info_badge_size">16dp</dimen>
+
+    <!-- z based shadow -->
+    <dimen name="lb_quantum_shadow_normal_z">2dp</dimen>
+    <dimen name="lb_quantum_shadow_focused_z">12dp</dimen>
+
 </resources>
diff --git a/v17/leanback/res/values/ids.xml b/v17/leanback/res/values/ids.xml
index 4010b2c..05a6304 100644
--- a/v17/leanback/res/values/ids.xml
+++ b/v17/leanback/res/values/ids.xml
@@ -16,4 +16,5 @@
 -->
  <resources>
      <item type="id" name="lb_focus_animator" />
+     <item type="id" name="lb_header_transition_position" />
  </resources>
\ No newline at end of file
diff --git a/v17/leanback/res/values/integers.xml b/v17/leanback/res/values/integers.xml
index 4fbe83f..ff2cf89 100644
--- a/v17/leanback/res/values/integers.xml
+++ b/v17/leanback/res/values/integers.xml
@@ -17,4 +17,6 @@
     <integer name="lb_card_selected_animation_delay">400</integer>
     <integer name="lb_card_selected_animation_duration">150</integer>
     <integer name="lb_card_activated_animation_duration">150</integer>
+    <integer name="lb_search_bar_text_mode_background_alpha">51</integer>
+    <integer name="lb_search_bar_speech_mode_background_alpha">179</integer>
 </resources>
diff --git a/v17/leanback/res/values/strings.xml b/v17/leanback/res/values/strings.xml
index 333c06a..4cd08f2 100644
--- a/v17/leanback/res/values/strings.xml
+++ b/v17/leanback/res/values/strings.xml
@@ -18,4 +18,7 @@
     <string name="orb_search_label">Search</string>
     <string name="orb_search_action">Search Action</string>
     <string name="lb_search_bar_hint">Search</string>
+    <string name="lb_search_bar_hint_speech">Speak to search</string>
+    <string name="lb_search_bar_hint_with_title">Search %1$s</string>
+    <string name="lb_search_bar_hint_with_title_speech">Speak to search %1$s</string>
 </resources>
\ No newline at end of file
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 955e9fe..535f7e3 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -22,24 +22,18 @@
     </style>
 
     <style name="TextAppearance.Leanback.Title" parent="TextAppearance.Leanback">
+        <item name="android:fontFamily">sans-serif-light</item>
         <item name="android:textSize">@dimen/lb_browse_title_text_size</item>
         <item name="android:textColor">@color/lb_browse_title_color</item>
     </style>
 
-    <style name="TextAppearance.Leanback.Row.Header" parent="TextAppearance.Leanback">
-        <item name="android:textAllCaps">true</item>
-        <item name="android:textSize">@dimen/lb_browse_row_header_text_size</item>
-        <item name="android:textColor">@color/lb_browse_header_color</item>
-        </style>
-
     <style name="TextAppearance.Leanback.Header" parent="TextAppearance.Leanback">
-        <item name="android:textAllCaps">true</item>
         <item name="android:textSize">@dimen/lb_browse_header_text_size</item>
+        <item name="android:textColor">@color/lb_browse_header_color</item>
     </style>
 
-    <style name="TextAppearance.Leanback.SearchLabel" parent="TextAppearance.Leanback">
-        <item name="android:textSize">@dimen/lb_browse_header_text_size</item>
-        <item name="android:textColor">@color/lb_list_item_unselected_text_color</item>
+    <style name="TextAppearance.Leanback.Row.Header" parent="TextAppearance.Leanback.Header">
+        <item name="android:fontFamily">sans-serif</item>
     </style>
 
     <style name="TextAppearance.Leanback.SearchTextEdit" parent="TextAppearance.Leanback">
@@ -49,13 +43,13 @@
     <style name="TextAppearance.Leanback.DetailsDescriptionTitle">
         <item name="android:textSize">@dimen/lb_details_description_title_text_size</item>
         <item name="android:textColor">@color/lb_details_description_color</item>
-        <item name="android:fontFamily">sans-serif-condensed</item>
+        <item name="android:fontFamily">sans-serif</item>
     </style>
 
     <style name="TextAppearance.Leanback.DetailsDescriptionSubtitle">
         <item name="android:textSize">@dimen/lb_details_description_subtitle_text_size</item>
         <item name="android:textColor">@color/lb_details_description_color</item>
-        <item name="android:fontFamily">sans-serif-condensed</item>
+        <item name="android:fontFamily">sans-serif</item>
     </style>
 
     <style name="TextAppearance.Leanback.DetailsDescriptionBody">
@@ -64,6 +58,12 @@
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
+    <style name="TextAppearance.Leanback.DetailsActionButton">
+        <item name="android:textSize">@dimen/lb_action_text_size</item>
+        <item name="android:textColor">@color/lb_action_text_color</item>
+        <item name="android:textAllCaps">true</item>
+    </style>
+
     <style name="Widget.Leanback" parent="android:Widget.Holo" />
 
     <style name="Widget.Leanback.BaseCardViewStyle" />
@@ -77,16 +77,17 @@
     <style name="Widget.Leanback.Title" />
 
     <style name="Widget.Leanback.Title.Text">
-        <item name="android:gravity">center_vertical</item>
         <item name="android:singleLine">true</item>
+        <item name="android:gravity">right</item>
+        <item name="android:ellipsize">end</item>
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Title</item>
     </style>
 
     <style name="Widget.Leanback.Title.Icon">
-        <item name="android:scaleType">centerInside</item>
+        <item name="android:scaleType">fitEnd</item>
     </style>
 
-    <!-- HeadersFragment (fast lane) -->
+    <!-- HeadersFragment -->
     <style name="Widget.Leanback.Headers" />
 
     <!-- RowsFragment -->
@@ -110,11 +111,12 @@
     </style>
 
     <style name="Widget.Leanback.Header" >
-        <item name="android:gravity">center_vertical</item>
+        <item name="android:minHeight">@dimen/lb_browse_header_height</item>
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Header</item>
         <item name="android:singleLine">true</item>
-        <item name="android:focusable">true</item>
-        <item name="android:focusableInTouchMode">true</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:marqueeRepeatLimit">0</item>
+        <item name="android:paddingRight">@dimen/lb_browse_header_padding_right</item>
     </style>
 
     <style name="Widget.Leanback.Rows.VerticalGridView" >
@@ -132,11 +134,12 @@
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:paddingLeft">?attr/browsePaddingLeft</item>
         <item name="android:paddingRight">?attr/browsePaddingRight</item>
-        <item name="android:paddingBottom">@dimen/lb_browse_item_margin_vertical</item>
-        <item name="android:paddingTop">@dimen/lb_browse_item_margin_vertical</item>
-        <item name="horizontalMargin">@dimen/lb_browse_item_margin</item>
-        <item name="verticalMargin">@dimen/lb_browse_item_margin</item>
+        <item name="android:paddingBottom">@dimen/lb_browse_item_vertical_margin</item>
+        <item name="android:paddingTop">@dimen/lb_browse_item_vertical_margin</item>
+        <item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
+        <item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
         <item name="focusOutFront">true</item>
+        <item name="rowHeight">wrap_content</item>
     </style>
 
     <style name="Widget.Leanback.GridItems.VerticalGridView">
@@ -145,15 +148,15 @@
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:paddingLeft">?attr/browsePaddingLeft</item>
         <item name="android:paddingRight">?attr/browsePaddingRight</item>
-        <item name="android:paddingBottom">@dimen/lb_browse_item_margin_vertical</item>
-        <item name="android:paddingTop">@dimen/lb_browse_item_margin_vertical</item>
-        <item name="horizontalMargin">@dimen/lb_browse_item_margin</item>
-        <item name="verticalMargin">@dimen/lb_browse_item_margin</item>
+        <item name="android:paddingBottom">@dimen/lb_browse_item_vertical_margin</item>
+        <item name="android:paddingTop">?attr/browseRowsMarginTop</item>
+        <item name="android:gravity">center_horizontal</item>
+        <item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
+        <item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
         <item name="focusOutFront">true</item>
     </style>
 
-    <style name="Widget.Leanback.Row.Header">
-        <item name="android:minHeight">@dimen/lb_browse_row_title_height</item>
+    <style name="Widget.Leanback.Row.Header" parent="Widget.Leanback.Header">
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Row.Header</item>
     </style>
 
@@ -176,21 +179,38 @@
         <item name="android:textAppearance">@style/TextAppearance.Leanback.Row.HoverCardDescription</item>
         <item name="android:maxWidth">@dimen/lb_browse_row_hovercard_max_width</item>
         <item name="android:ellipsize">end</item>
-        <item name="android:maxLines">2</item>
+        <item name="android:maxLines">4</item>
     </style>
 
     <style name="Widget.Leanback.DetailsDescriptionTitleStyle">
         <item name="android:textAppearance">@style/TextAppearance.Leanback.DetailsDescriptionTitle</item>
         <item name="android:maxLines">@integer/lb_details_description_title_max_lines</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:ellipsize">end</item>
     </style>
 
     <style name="Widget.Leanback.DetailsDescriptionSubtitleStyle">
         <item name="android:textAppearance">@style/TextAppearance.Leanback.DetailsDescriptionSubtitle</item>
         <item name="android:maxLines">@integer/lb_details_description_subtitle_max_lines</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:ellipsize">end</item>
     </style>
 
     <style name="Widget.Leanback.DetailsDescriptionBodyStyle">
         <item name="android:textAppearance">@style/TextAppearance.Leanback.DetailsDescriptionBody</item>
-        <item name="android:maxLines">@integer/lb_details_description_body_max_lines</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:ellipsize">end</item>
     </style>
+
+    <style name="Widget.Leanback.DetailsActionButtonStyle" parent="android:Widget.Holo.Button.Borderless">
+        <item name="android:textAppearance">@style/TextAppearance.Leanback.DetailsActionButton</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:background">@drawable/lb_selector_action_bg</item>
+        <item name="android:drawablePadding">@dimen/lb_action_icon_margin</item>
+        <item name="android:focusable">true</item>
+        <item name="android:focusableInTouchMode">true</item>
+        <item name="android:paddingLeft">@dimen/lb_action_padding_horizontal</item>
+        <item name="android:paddingRight">@dimen/lb_action_padding_horizontal</item>
+    </style>
+
 </resources>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index b683b28..3024054 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -33,6 +33,8 @@
         <item name="browseRowsFadingEdgeLength">@dimen/lb_browse_rows_fading_edge</item>
 
         <item name="headersVerticalGridStyle">@style/Widget.Leanback.Headers.VerticalGridView</item>
+        <item name="headerStyle">@style/Widget.Leanback.Header</item>
+
         <item name="rowsVerticalGridStyle">@style/Widget.Leanback.Rows.VerticalGridView</item>
         <item name="rowHorizontalGridStyle">@style/Widget.Leanback.Row.HorizontalGridView</item>
         <item name="itemsVerticalGridStyle">@style/Widget.Leanback.GridItems.VerticalGridView</item>
@@ -46,6 +48,7 @@
         <item name="detailsDescriptionTitleStyle">@style/Widget.Leanback.DetailsDescriptionTitleStyle</item>
         <item name="detailsDescriptionSubtitleStyle">@style/Widget.Leanback.DetailsDescriptionSubtitleStyle</item>
         <item name="detailsDescriptionBodyStyle">@style/Widget.Leanback.DetailsDescriptionBodyStyle</item>
+        <item name="detailsActionButtonStyle">@style/Widget.Leanback.DetailsActionButtonStyle</item>
     </style>
 
 </resources>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 8b56775..9d267e5 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -17,17 +17,19 @@
 import android.animation.ObjectAnimator;
 import android.app.Activity;
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.Matrix;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.Paint;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.Handler;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -85,15 +87,6 @@
      */
     private static final boolean USE_SEPARATE_WINDOW = false;
 
-    /**
-     * If true, bitmaps will be scaled to the exact display size.
-     * Small bitmaps will be scaled up, using more memory but improving display quality.
-     * Large bitmaps will be scaled down to use less memory.
-     * Introduces an allocation overhead.
-     * TODO: support a leanback configuration option.
-     */
-    private static final boolean SCALE_BITMAPS_TO_FIT = true;
-
     private static final String WINDOW_NAME = "BackgroundManager";
     private static final String FRAGMENT_TAG = BackgroundManager.class.getCanonicalName();
 
@@ -111,7 +104,55 @@
     private int mBackgroundColor;
     private boolean mAttached;
 
-    private class DrawableWrapper {
+    private static class BitmapDrawable extends Drawable {
+
+        Bitmap mBitmap;
+        Matrix mMatrix;
+        Paint mPaint;
+
+        BitmapDrawable(Resources resources, Bitmap bitmap) {
+            this(resources, bitmap, null);
+        }
+
+        BitmapDrawable(Resources resources, Bitmap bitmap, Matrix matrix) {
+            mBitmap = bitmap;
+            mMatrix = matrix != null ? matrix : new Matrix();
+            mPaint = new Paint();
+            mPaint.setFilterBitmap(true);
+        }
+
+        Bitmap getBitmap() {
+            return mBitmap;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (mBitmap == null) {
+                return;
+            }
+            canvas.drawBitmap(mBitmap, mMatrix, mPaint);
+        }
+
+        @Override
+        public int getOpacity() {
+            return android.graphics.PixelFormat.OPAQUE;
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            if (mPaint.getAlpha() != alpha) {
+                mPaint.setAlpha(alpha);
+                invalidateSelf();
+            }
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+            // Abstract in Drawable, not implemented
+        }
+    }
+
+    private static class DrawableWrapper {
         protected int mAlpha;
         protected Drawable mDrawable;
         protected ObjectAnimator mAnimator;
@@ -319,10 +360,6 @@
         if (DEBUG) Log.v(TAG, "syncWithService color " + Integer.toHexString(color)
                 + " drawable " + drawable);
 
-        if (drawable != null) {
-            drawable = drawable.getConstantState().newDrawable(mContext.getResources()).mutate();
-        }
-
         mBackgroundColor = color;
         mBackgroundDrawable = drawable;
 
@@ -531,52 +568,32 @@
             return;
         }
 
-        if (mBackgroundDrawable instanceof BitmapDrawable &&
-                ((BitmapDrawable) mBackgroundDrawable).getBitmap() == bitmap) {
-            if (DEBUG) {
-                Log.v(TAG, "same bitmap detected");
-            }
-            mService.setDrawable(mBackgroundDrawable);
-            return;
-        }
+        Matrix matrix = null;
 
-        if (SCALE_BITMAPS_TO_FIT &&
-                (bitmap.getWidth() != mWidthPx || bitmap.getHeight() != mHeightPx)) {
-            // Scale proportionately to fit width and height.
-
-            Matrix matrix = new Matrix();
-
+        if ((bitmap.getWidth() != mWidthPx || bitmap.getHeight() != mHeightPx)) {
             int dwidth = bitmap.getWidth();
             int dheight = bitmap.getHeight();
             float scale;
-            int dx;
 
-            if (DEBUG) {
-                Log.v(TAG, "original image size " + dwidth + "x" + dheight);
-            }
-
+            // Scale proportionately to fit width and height.
             if (dwidth * mHeightPx > mWidthPx * dheight) {
                 scale = (float) mHeightPx / (float) dheight;
             } else {
                 scale = (float) mWidthPx / (float) dwidth;
             }
 
-            matrix.setScale(scale, scale);
-
-            if (DEBUG) {
-                Log.v(TAG, "original image size " + bitmap.getWidth() + "x" + bitmap.getHeight());
-            }
             int subX = Math.min((int) (mWidthPx / scale), dwidth);
-            int subY = Math.min((int) (mHeightPx / scale), dheight);
-            dx = Math.max(0, (dwidth - subX) / 2);
+            int dx = Math.max(0, (dwidth - subX) / 2);
 
-            bitmap = Bitmap.createBitmap(bitmap, dx, 0, subX, subY, matrix, true);
-            if (DEBUG) {
-                Log.v(TAG, "new image size " + bitmap.getWidth() + "x" + bitmap.getHeight());
-            }
+            matrix = new Matrix();
+            matrix.setScale(scale, scale);
+            matrix.preTranslate(-dx, 0);
+
+            if (DEBUG) Log.v(TAG, "original image size " + bitmap.getWidth() + "x" + bitmap.getHeight() +
+                    " scale " + scale + " dx " + dx);
         }
 
-        BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
+        BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap, matrix);
 
         setDrawableInternal(bitmapDrawable);
     }
@@ -629,6 +646,21 @@
         return mBackgroundDrawable;
     }
 
+    private boolean sameDrawable(Drawable first, Drawable second) {
+        if (first == null || second == null) {
+            return false;
+        }
+        if (first == second) {
+            return true;
+        }
+        if (first instanceof BitmapDrawable && second instanceof BitmapDrawable) {
+            if (((BitmapDrawable) first).getBitmap().sameAs(((BitmapDrawable) second).getBitmap())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Task which changes the background.
      */
@@ -652,22 +684,10 @@
         }
 
         private void runTask() {
-            boolean newBackground = false;
             lazyInit();
 
-            if (mDrawable != mBackgroundDrawable) {
-                newBackground = true;
-                if (mDrawable instanceof BitmapDrawable &&
-                        mBackgroundDrawable instanceof BitmapDrawable) {
-                    if (((BitmapDrawable) mDrawable).getBitmap() ==
-                            ((BitmapDrawable) mBackgroundDrawable).getBitmap()) {
-                        if (DEBUG) Log.v(TAG, "same underlying bitmap detected");
-                        newBackground = false;
-                    }
-                }
-            }
-
-            if (!newBackground) {
+            if (sameDrawable(mDrawable, mBackgroundDrawable)) {
+                if (DEBUG) Log.v(TAG, "same bitmap detected");
                 return;
             }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
index 2be3e54..2336690 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseRowFragment.java
@@ -35,6 +35,8 @@
     private PresenterSelector mPresenterSelector;
     private ItemBridgeAdapter mBridgeAdapter;
     private int mSelectedPosition = -1;
+    protected int mReparentHeaderId;
+    protected boolean mInTransition;
 
     abstract protected int getLayoutResourceId();
 
@@ -144,4 +146,45 @@
             return null;
         }
     }
+
+    void setReparentHeaderId(int reparentId) {
+        mReparentHeaderId = reparentId;
+    }
+
+    void onTransitionStart() {
+        mInTransition = true;
+        if (mVerticalGridView != null) {
+            mVerticalGridView.setAnimateChildLayout(false);
+            mVerticalGridView.setPruneChild(false);
+            mVerticalGridView.setFocusSearchDisabled(true);
+        }
+    }
+
+    void onTransitionEnd() {
+        if (mVerticalGridView != null) {
+            mVerticalGridView.setAnimateChildLayout(true);
+            mVerticalGridView.setPruneChild(true);
+            mVerticalGridView.setFocusSearchDisabled(false);
+        }
+        mInTransition = false;
+    }
+
+    void setItemAlignment() {
+        if (mVerticalGridView != null) {
+            // align the top edge of item
+            mVerticalGridView.setItemAlignmentOffset(0);
+            mVerticalGridView.setItemAlignmentOffsetPercent(
+                    VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
+        }
+    }
+
+    void setWindowAlignmentFromTop(int alignedTop) {
+        if (mVerticalGridView != null) {
+            // align to a fixed position from top
+            mVerticalGridView.setWindowAlignmentOffset(alignedTop);
+            mVerticalGridView.setWindowAlignmentOffsetPercent(
+                    VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
+            mVerticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
+        }
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index 0df07f4..05c3749 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -16,15 +16,21 @@
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.HorizontalGridView;
 import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.OnItemSelectedListener;
 import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.SearchOrbView;
+import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.util.SparseIntArray;
+import android.util.TypedValue;
+import android.app.Activity;
 import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentManager.BackStackEntry;
 import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -32,8 +38,10 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 
 import java.util.ArrayList;
@@ -43,74 +51,14 @@
 /**
  * Wrapper fragment for leanback browse screens. Composed of a
  * RowsFragment and a HeadersFragment.
- *
+ * <p>
+ * The fragment comes with default back key support to show headers.
+ * For app customized {@link Activity#onBackPressed()}, app must disable
+ * BrowseFragment's default back key support by calling
+ * {@link #setHeadersTransitionOnBackEnabled(boolean)} with false and use
+ * {@link BrowseFragment.BrowseTransitionListener} and {@link #startHeadersTransition(boolean)}.
  */
 public class BrowseFragment extends Fragment {
-    private static final String TAG = "BrowseFragment";
-    private static boolean DEBUG = false;
-
-    /** The fastlane navigation panel is enabled and shown by default. */
-    public static final int HEADERS_ENABLED = 1;
-
-    /** The fastlane navigation panel is enabled and hidden by default. */
-    public static final int HEADERS_HIDDEN = 2;
-
-    /** The fastlane navigation panel is disabled and will never be shown. */
-    public static final int HEADERS_DISABLED = 3;
-
-    private RowsFragment mRowsFragment;
-    private HeadersFragment mHeadersFragment;
-
-    private ObjectAdapter mAdapter;
-
-    private Params mParams;
-    private BrowseFrameLayout mBrowseFrame;
-    private ImageView mBadgeView;
-    private TextView mTitleView;
-    private ViewGroup mBrowseTitle;
-    private SearchOrbView mSearchOrbView;
-    private boolean mShowingTitle = true;
-    private boolean mShowingHeaders = true;
-    private boolean mCanShowHeaders = true;
-    private int mContainerListMarginLeft;
-    private int mContainerListAlignTop;
-    private TransitionHelper mTransitionHelper;
-    private OnItemSelectedListener mExternalOnItemSelectedListener;
-    private OnClickListener mExternalOnSearchClickedListener;
-    private OnItemClickedListener mOnItemClickedListener;
-    private int mSelectedPosition = -1;
-
-    // transition related:
-    private static int sReparentHeaderId = View.generateViewId();
-    private Object mSceneWithTitle;
-    private Object mSceneWithoutTitle;
-    private Object mSceneWithHeaders;
-    private Object mSceneWithoutHeaders;
-    private Object mTitleTransition;
-    private Object mHeadersTransition;
-    private int mHeadersTransitionStartDelay;
-
-    private static final String ARG_TITLE = BrowseFragment.class.getCanonicalName() + ".title";
-    private static final String ARG_BADGE_URI = BrowseFragment.class.getCanonicalName() + ".badge";
-    private static final String ARG_HEADERS_STATE =
-        BrowseFragment.class.getCanonicalName() + ".headersState";
-
-    /**
-     * @param args Bundle to use for the arguments, if null a new Bundle will be created.
-     */
-    public static Bundle createArgs(Bundle args, String title, String badgeUri) {
-        return createArgs(args, title, badgeUri, HEADERS_ENABLED);
-    }
-
-    public static Bundle createArgs(Bundle args, String title, String badgeUri, int headersState) {
-        if (args == null) {
-            args = new Bundle();
-        }
-        args.putString(ARG_TITLE, title);
-        args.putString(ARG_BADGE_URI, badgeUri);
-        args.putInt(ARG_HEADERS_STATE, headersState);
-        return args;
-    }
 
     public static class Params {
         private String mTitle;
@@ -166,6 +114,141 @@
         }
     }
 
+    final class BackStackListener implements FragmentManager.OnBackStackChangedListener {
+        int mLastEntryCount;
+        int mIndexOfHeadersBackStack;
+
+        BackStackListener() {
+            reset();
+        }
+
+        void reset() {
+            mLastEntryCount = getFragmentManager().getBackStackEntryCount();
+            mIndexOfHeadersBackStack = -1;
+        }
+
+        @Override
+        public void onBackStackChanged() {
+            int count = getFragmentManager().getBackStackEntryCount();
+            // if backstack is growing and last pushed entry is "headers" backstack,
+            // remember the index of the entry.
+            if (count > mLastEntryCount) {
+                BackStackEntry entry = getFragmentManager().getBackStackEntryAt(count - 1);
+                if (mWithHeadersBackStackName.equals(entry.getName())) {
+                    mIndexOfHeadersBackStack = count - 1;
+                }
+            } else if (count < mLastEntryCount) {
+                // if popped "headers" backstack, initiate the show header transition if needed
+                if (mIndexOfHeadersBackStack >= count) {
+                    if (!mShowingHeaders) {
+                        startHeadersTransitionInternal(true);
+                    }
+                }
+            }
+            mLastEntryCount = count;
+        }
+    }
+
+    /**
+     * Listener for browse transitions.
+     */
+    public static class BrowseTransitionListener {
+        /**
+         * Callback when headers transition starts.
+         */
+        public void onHeadersTransitionStart(boolean withHeaders) {
+        }
+        /**
+         * Callback when headers transition stops.
+         */
+        public void onHeadersTransitionStop(boolean withHeaders) {
+        }
+    }
+
+    private static final String TAG = "BrowseFragment";
+
+    private static final String LB_HEADERS_BACKSTACK = "lbHeadersBackStack_";
+
+    private static boolean DEBUG = false;
+
+    /** The headers fragment is enabled and shown by default. */
+    public static final int HEADERS_ENABLED = 1;
+
+    /** The headers fragment is enabled and hidden by default. */
+    public static final int HEADERS_HIDDEN = 2;
+
+    /** The headers fragment is disabled and will never be shown. */
+    public static final int HEADERS_DISABLED = 3;
+
+    private static final float SLIDE_DISTANCE_FACTOR = 2;
+
+    private RowsFragment mRowsFragment;
+    private HeadersFragment mHeadersFragment;
+
+    private ObjectAdapter mAdapter;
+
+    private Params mParams;
+    private int mBrandColor = Color.TRANSPARENT;
+    private boolean mBrandColorSet;
+
+    private BrowseFrameLayout mBrowseFrame;
+    private ImageView mBadgeView;
+    private TextView mTitleView;
+    private ViewGroup mBrowseTitle;
+    private SearchOrbView mSearchOrbView;
+    private boolean mShowingTitle = true;
+    private boolean mHeadersBackStackEnabled = true;
+    private String mWithHeadersBackStackName;
+    private boolean mShowingHeaders = true;
+    private boolean mCanShowHeaders = true;
+    private int mContainerListMarginLeft;
+    private int mContainerListAlignTop;
+    private int mSearchAffordanceColor;
+    private boolean mSearchAffordanceColorSet;
+    private OnItemSelectedListener mExternalOnItemSelectedListener;
+    private OnClickListener mExternalOnSearchClickedListener;
+    private OnItemClickedListener mOnItemClickedListener;
+    private int mSelectedPosition = -1;
+
+    private PresenterSelector mHeaderPresenterSelector;
+
+    // transition related:
+    private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
+    private int mReparentHeaderId = View.generateViewId();
+    private Object mSceneWithTitle;
+    private Object mSceneWithoutTitle;
+    private Object mSceneWithHeaders;
+    private Object mSceneWithoutHeaders;
+    private Object mTitleUpTransition;
+    private Object mTitleDownTransition;
+    private Object mHeadersTransition;
+    private int mHeadersTransitionStartDelay;
+    private int mHeadersTransitionDuration;
+    private BackStackListener mBackStackChangedListener;
+    private BrowseTransitionListener mBrowseTransitionListener;
+
+    private static final String ARG_TITLE = BrowseFragment.class.getCanonicalName() + ".title";
+    private static final String ARG_BADGE_URI = BrowseFragment.class.getCanonicalName() + ".badge";
+    private static final String ARG_HEADERS_STATE =
+        BrowseFragment.class.getCanonicalName() + ".headersState";
+
+    /**
+     * @param args Bundle to use for the arguments, if null a new Bundle will be created.
+     */
+    public static Bundle createArgs(Bundle args, String title, String badgeUri) {
+        return createArgs(args, title, badgeUri, HEADERS_ENABLED);
+    }
+
+    public static Bundle createArgs(Bundle args, String title, String badgeUri, int headersState) {
+        if (args == null) {
+            args = new Bundle();
+        }
+        args.putString(ARG_TITLE, title);
+        args.putString(ARG_BADGE_URI, badgeUri);
+        args.putInt(ARG_HEADERS_STATE, headersState);
+        return args;
+    }
+
     /**
      * Set browse parameters.
      */
@@ -184,6 +267,26 @@
     }
 
     /**
+     * Sets the brand color for the browse fragment.
+     */
+    public void setBrandColor(int color) {
+        mBrandColor = color;
+        mBrandColorSet = true;
+
+        if (mHeadersFragment != null) {
+            mHeadersFragment.setBackgroundColor(mBrandColor);
+        }
+    }
+
+    /**
+     * Returns the brand color for the browse fragment.
+     * The default is transparent.
+     */
+    public int getBrandColor() {
+        return mBrandColor;
+    }
+
+    /**
      * Sets the list of rows for the fragment.
      */
     public void setAdapter(ObjectAdapter adapter) {
@@ -245,11 +348,94 @@
         }
     }
 
-    private void onHeadersTransitionStart(boolean withHeaders) {
-        mRowsFragment.getVerticalGridView().setAnimateChildLayout(false);
-        mRowsFragment.getVerticalGridView().setFocusSearchDisabled(true);
-        mHeadersFragment.getVerticalGridView().setFocusSearchDisabled(true);
-        createHeadersTransition(withHeaders);
+    /**
+     * Sets the color used to draw the search affordance.
+     */
+    public void setSearchAffordanceColor(int color) {
+        mSearchAffordanceColor = color;
+        mSearchAffordanceColorSet = true;
+
+        if (mSearchOrbView != null) {
+            mSearchOrbView.setOrbColor(mSearchAffordanceColor);
+        }
+    }
+
+    /**
+     * Returns the color used to draw the search affordance.
+     * Can be called only after an activity has been attached.
+     */
+    public int getSearchAffordanceColor() {
+        if (getActivity() == null) {
+            throw new IllegalStateException("Activity must be attached");
+        }
+
+        if (mSearchAffordanceColorSet) {
+            return mSearchAffordanceColor;
+        }
+
+        TypedValue outValue = new TypedValue();
+        getActivity().getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
+        return getResources().getColor(outValue.resourceId);
+    }
+
+    /**
+     * Start headers transition.
+     */
+    public void startHeadersTransition(boolean withHeaders) {
+        if (!mCanShowHeaders) {
+            throw new IllegalStateException("Cannot start headers transition");
+        }
+        if (isInHeadersTransition() || mShowingHeaders == withHeaders) {
+            return;
+        }
+        startHeadersTransitionInternal(withHeaders);
+    }
+
+    /**
+     * Returns true if headers transition is currently running.
+     */
+    public boolean isInHeadersTransition() {
+        return mHeadersTransition != null;
+    }
+
+    /**
+     * Returns true if headers is showing.
+     */
+    public boolean isShowingHeaders() {
+        return mShowingHeaders;
+    }
+
+    /**
+     * Set listener for browse fragment transitions.
+     */
+    public void setBrowseTransitionListener(BrowseTransitionListener listener) {
+        mBrowseTransitionListener = listener;
+    }
+
+    private void startHeadersTransitionInternal(boolean withHeaders) {
+        mShowingHeaders = withHeaders;
+        mRowsFragment.onTransitionStart();
+        mHeadersFragment.onTransitionStart();
+        createHeadersTransition();
+        if (mBrowseTransitionListener != null) {
+            mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
+        }
+        sTransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders,
+                mHeadersTransition);
+        if (mHeadersBackStackEnabled) {
+            if (!withHeaders) {
+                getFragmentManager().beginTransaction()
+                        .addToBackStack(mWithHeadersBackStackName).commit();
+            } else {
+                int count = getFragmentManager().getBackStackEntryCount();
+                if (count > 0) {
+                    BackStackEntry entry = getFragmentManager().getBackStackEntryAt(count - 1);
+                    if (mWithHeadersBackStackName.equals(entry.getName())) {
+                        getFragmentManager().popBackStack();
+                    }
+                }
+            }
+        }
     }
 
     private boolean isVerticalScrolling() {
@@ -264,11 +450,11 @@
             new BrowseFrameLayout.OnFocusSearchListener() {
         @Override
         public View onFocusSearch(View focused, int direction) {
-            // If fastlane is disabled, just return null.
+            // If headers fragment is disabled, just return null.
             if (!mCanShowHeaders) return null;
 
-            // if fast lane is running transition,  focus stays
-            if (mHeadersTransition != null) return focused;
+            // if headers is running transition,  focus stays
+            if (isInHeadersTransition()) return focused;
             if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
             if (direction == View.FOCUS_LEFT) {
                 if (isVerticalScrolling() || mShowingHeaders) {
@@ -299,16 +485,11 @@
         @Override
         public void onRequestChildFocus(View child, View focused) {
             int childId = child.getId();
-            if (mHeadersTransition != null) return;
+            if (!mCanShowHeaders || isInHeadersTransition()) return;
             if (childId == R.id.browse_container_dock && mShowingHeaders) {
-                mShowingHeaders = false;
-                onHeadersTransitionStart(false);
-                mTransitionHelper.runTransition(mSceneWithoutHeaders, mHeadersTransition);
+                startHeadersTransitionInternal(false);
             } else if (childId == R.id.browse_headers_dock && !mShowingHeaders) {
-                mShowingHeaders = true;
-                //mHeadersFragment.getView().setAlpha(1f);
-                onHeadersTransitionStart(true);
-                mTransitionHelper.runTransition(mSceneWithHeaders, mHeadersTransition);
+                startHeadersTransitionInternal(true);
             }
         }
     };
@@ -322,8 +503,23 @@
         mContainerListAlignTop = (int) ta.getDimension(
                 R.styleable.LeanbackTheme_browseRowsMarginTop, 0);
         ta.recycle();
+
         mHeadersTransitionStartDelay = getResources()
                 .getInteger(R.integer.lb_browse_headers_transition_delay);
+        mHeadersTransitionDuration = getResources()
+                .getInteger(R.integer.lb_browse_headers_transition_duration);
+
+        readArguments(getArguments());
+        if (mCanShowHeaders && mHeadersBackStackEnabled) {
+            mWithHeadersBackStackName = LB_HEADERS_BACKSTACK + this;
+            mBackStackChangedListener = new BackStackListener();
+            getFragmentManager().addOnBackStackChangedListener(mBackStackChangedListener);
+            if (!mShowingHeaders) {
+                getFragmentManager().beginTransaction()
+                        .addToBackStack(mWithHeadersBackStackName).commit();
+            }
+        }
+
     }
 
     @Override
@@ -341,7 +537,13 @@
             mRowsFragment = (RowsFragment) getChildFragmentManager()
                     .findFragmentById(R.id.browse_container_dock);
         }
+
+        mHeadersFragment.setHeadersGone(!mCanShowHeaders);
+
         mRowsFragment.setAdapter(mAdapter);
+        if (mHeaderPresenterSelector != null) {
+            mHeadersFragment.setPresenterSelector(mHeaderPresenterSelector);
+        }
         mHeadersFragment.setAdapter(mAdapter);
 
         mRowsFragment.setOnItemSelectedListener(mRowSelectedListener);
@@ -359,135 +561,131 @@
         mBadgeView = (ImageView) mBrowseTitle.findViewById(R.id.browse_badge);
         mTitleView = (TextView) mBrowseTitle.findViewById(R.id.browse_title);
         mSearchOrbView = (SearchOrbView) mBrowseTitle.findViewById(R.id.browse_orb);
+        mSearchOrbView.setOrbColor(getSearchAffordanceColor());
         if (mExternalOnSearchClickedListener != null) {
             mSearchOrbView.setOnOrbClickedListener(mExternalOnSearchClickedListener);
         }
 
-        readArguments(getArguments());
         if (mParams != null) {
             setBadgeDrawable(mParams.mBadgeDrawable);
             setTitle(mParams.mTitle);
             setHeadersState(mParams.mHeadersState);
+            if (mBrandColorSet) {
+                mHeadersFragment.setBackgroundColor(mBrandColor);
+            }
         }
 
-        mTransitionHelper = new TransitionHelper(getActivity());
-        mSceneWithTitle = mTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showTitle(true);
             }
         });
-        mSceneWithoutTitle = mTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithoutTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showTitle(false);
             }
         });
-        mSceneWithHeaders = mTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showHeaders(true);
             }
         });
-        mSceneWithoutHeaders =  mTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithoutHeaders =  sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showHeaders(false);
             }
         });
-        mTitleTransition = mTransitionHelper.createAutoTransition();
-        mTransitionHelper.excludeChildren(mTitleTransition, R.id.browse_headers, true);
-        mTransitionHelper.excludeChildren(mTitleTransition, R.id.container_list, true);
+        mTitleUpTransition = sTransitionHelper.createChangeBounds(false);
+        sTransitionHelper.setInterpolator(mTitleUpTransition, new DecelerateInterpolator(4));
+        mTitleDownTransition = sTransitionHelper.createChangeBounds(false);
+        sTransitionHelper.setInterpolator(mTitleDownTransition, new DecelerateInterpolator());
+
+        sTransitionHelper.excludeChildren(mTitleUpTransition, R.id.browse_headers, true);
+        sTransitionHelper.excludeChildren(mTitleDownTransition, R.id.browse_headers, true);
+        sTransitionHelper.excludeChildren(mTitleUpTransition, R.id.container_list, true);
+        sTransitionHelper.excludeChildren(mTitleDownTransition, R.id.container_list, true);
 
         return root;
     }
 
-    private void createHeadersTransition(boolean withHeaders) {
-        ArrayList<View> fastHeaders = new ArrayList<View>();
-        ArrayList<Integer> fastHeaderPositions = new ArrayList<Integer>();
-        ArrayList<View> headers = new ArrayList<View>();
-        ArrayList<Integer> headerPositions = new ArrayList<Integer>();
+    private void createHeadersTransition() {
+        mHeadersTransition = sTransitionHelper.createTransitionSet(false);
+        sTransitionHelper.excludeChildren(mHeadersTransition, R.id.browse_title_group, true);
+        Object changeBounds = sTransitionHelper.createChangeBounds(false);
+        Object fadeIn = sTransitionHelper.createFadeTransition(TransitionHelper.FADE_IN);
+        Object fadeOut = sTransitionHelper.createFadeTransition(TransitionHelper.FADE_OUT);
 
-        mHeadersFragment.getHeaderViews(fastHeaders, fastHeaderPositions);
-        mRowsFragment.getHeaderViews(headers, headerPositions);
-
-        mHeadersTransition = mTransitionHelper.createTransitionSet(true);
-        mTransitionHelper.excludeChildren(mHeadersTransition, R.id.browse_title_group, true);
-        Object changeBounds = mTransitionHelper.createChangeBounds(true);
-        Object fadeIn = mTransitionHelper.createFadeTransition(TransitionHelper.FADE_IN);
-        Object fadeOut = mTransitionHelper.createFadeTransition(TransitionHelper.FADE_OUT);
-        if (!withHeaders) {
-            mTransitionHelper.setChangeBoundsDefaultStartDelay(changeBounds,
-                    mHeadersTransitionStartDelay);
+        sTransitionHelper.setDuration(fadeOut, mHeadersTransitionDuration);
+        sTransitionHelper.addTransition(mHeadersTransition, fadeOut);
+        if (mShowingHeaders) {
+            sTransitionHelper.setStartDelay(changeBounds, mHeadersTransitionStartDelay);
         }
+        sTransitionHelper.setDuration(changeBounds, mHeadersTransitionDuration);
+        sTransitionHelper.addTransition(mHeadersTransition, changeBounds);
+        sTransitionHelper.setDuration(fadeIn, mHeadersTransitionDuration);
+        sTransitionHelper.setStartDelay(fadeIn, mHeadersTransitionStartDelay);
+        sTransitionHelper.addTransition(mHeadersTransition, fadeIn);
 
-        for (int i = 0; i < headerPositions.size(); i++) {
-            Integer position = headerPositions.get(i);
-            if (position == mSelectedPosition) {
-                headers.get(i).setId(sReparentHeaderId);
-                mTransitionHelper.setChangeBoundsStartDelay(changeBounds, sReparentHeaderId,
-                        withHeaders ? mHeadersTransitionStartDelay : 0);
-                mTransitionHelper.exclude(fadeIn, headers.get(i), true);
-                mTransitionHelper.exclude(fadeOut, headers.get(i), true);
-            } else {
-                headers.get(i).setId(View.NO_ID);
-            }
-        }
-        for (int i = 0; i < fastHeaderPositions.size(); i++) {
-            Integer position = fastHeaderPositions.get(i);
-            if (position == mSelectedPosition) {
-                fastHeaders.get(i).setId(sReparentHeaderId);
-                mTransitionHelper.setChangeBoundsStartDelay(changeBounds, sReparentHeaderId,
-                        withHeaders ? mHeadersTransitionStartDelay : 0);
-                mTransitionHelper.exclude(fadeIn, fastHeaders.get(i), true);
-                mTransitionHelper.exclude(fadeOut, fastHeaders.get(i), true);
-            } else {
-                fastHeaders.get(i).setId(View.NO_ID);
-            }
-        }
-
-        mTransitionHelper.addTransition(mHeadersTransition, fadeOut);
-        mTransitionHelper.addTransition(mHeadersTransition, changeBounds);
-        mTransitionHelper.addTransition(mHeadersTransition, fadeIn);
-
-        mTransitionHelper.setTransitionCompleteListener(mHeadersTransition, new Runnable() {
+        sTransitionHelper.setTransitionListener(mHeadersTransition, new TransitionListener() {
             @Override
-            public void run() {
+            public void onTransitionStart(Object transition) {
+            }
+            @Override
+            public void onTransitionEnd(Object transition) {
                 mHeadersTransition = null;
-                // TODO: deal fragment destroy view properly
-                VerticalGridView rowsGridView = mRowsFragment.getVerticalGridView();
-                if (rowsGridView != null) {
-                    rowsGridView.setAnimateChildLayout(true);
-                    rowsGridView.setFocusSearchDisabled(false);
-                    if (!mShowingHeaders && !rowsGridView.hasFocus()) {
+                mRowsFragment.onTransitionEnd();
+                mHeadersFragment.onTransitionEnd();
+                if (mShowingHeaders) {
+                    VerticalGridView headerGridView = mHeadersFragment.getVerticalGridView();
+                    if (headerGridView != null && !headerGridView.hasFocus()) {
+                        headerGridView.requestFocus();
+                    }
+                } else {
+                    VerticalGridView rowsGridView = mRowsFragment.getVerticalGridView();
+                    if (rowsGridView != null && !rowsGridView.hasFocus()) {
                         rowsGridView.requestFocus();
                     }
                 }
-                VerticalGridView headerGridView = mHeadersFragment.getVerticalGridView();
-                if (headerGridView != null) {
-                    headerGridView.setFocusSearchDisabled(false);
-                    headerGridView.invalidate();
-                    if (mShowingHeaders && !headerGridView.hasFocus()) {
-                        headerGridView.requestFocus();
-                    }
+                if (mBrowseTransitionListener != null) {
+                    mBrowseTransitionListener.onHeadersTransitionStop(mShowingHeaders);
                 }
             }
         });
     }
 
+    public void setHeaderPresenterSelector(PresenterSelector headerPresenterSelector) {
+        mHeaderPresenterSelector = headerPresenterSelector;
+        if (mHeadersFragment != null) {
+            mHeadersFragment.setPresenterSelector(mHeaderPresenterSelector);
+        }
+    }
+
     private void showTitle(boolean show) {
-        mBrowseTitle.setVisibility(show ? View.VISIBLE : View.GONE);
+        MarginLayoutParams lp = (MarginLayoutParams) mBrowseTitle.getLayoutParams();
+        lp.topMargin = show ? 0 : -mBrowseTitle.getHeight();
+        mBrowseTitle.setLayoutParams(lp);
     }
 
     private void showHeaders(boolean show) {
         if (DEBUG) Log.v(TAG, "showHeaders " + show);
-        mHeadersFragment.setHeadersVisiblity(show);
-
-        View containerList = mRowsFragment.getView();
+        mHeadersFragment.setHeadersEnabled(show);
         MarginLayoutParams lp;
+        View containerList;
+
+        containerList = mRowsFragment.getView();
         lp = (MarginLayoutParams) containerList.getLayoutParams();
         lp.leftMargin = show ? mContainerListMarginLeft : 0;
         containerList.setLayoutParams(lp);
+
+        containerList = mHeadersFragment.getView();
+        lp = (MarginLayoutParams) containerList.getLayoutParams();
+        lp.leftMargin = show ? 0 : -mContainerListMarginLeft;
+        containerList.setLayoutParams(lp);
+
         mRowsFragment.setExpand(!show);
     }
 
@@ -495,14 +693,10 @@
         new HeadersFragment.OnHeaderClickedListener() {
             @Override
             public void onHeaderClicked() {
-                if (!mCanShowHeaders || !mShowingHeaders) return;
-
-                if (mHeadersTransition != null) {
+                if (!mCanShowHeaders || !mShowingHeaders || isInHeadersTransition()) {
                     return;
                 }
-                mShowingHeaders = false;
-                onHeadersTransitionStart(false);
-                mTransitionHelper.runTransition(mSceneWithoutHeaders, mHeadersTransition);
+                startHeadersTransitionInternal(false);
                 mRowsFragment.getVerticalGridView().requestFocus();
             }
         };
@@ -533,13 +727,13 @@
             mSetSelectionRunnable.mPosition = position;
             mBrowseFrame.getHandler().post(mSetSelectionRunnable);
 
-            if (position == 0) {
+            if (getAdapter() == null || getAdapter().size() == 0 || position == 0) {
                 if (!mShowingTitle) {
-                    mTransitionHelper.runTransition(mSceneWithTitle, mTitleTransition);
+                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
                     mShowingTitle = true;
                 }
             } else if (mShowingTitle) {
-                mTransitionHelper.runTransition(mSceneWithoutTitle, mTitleTransition);
+                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
                 mShowingTitle = false;
             }
         }
@@ -563,39 +757,44 @@
         mSelectedPosition = position;
     }
 
-    private void setVerticalVerticalGridViewLayout(VerticalGridView listview, int extraOffset) {
-        // align the top edge of item to a fixed position
-        listview.setItemAlignmentOffset(0);
-        listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
-        listview.setWindowAlignmentOffset(mContainerListAlignTop + extraOffset);
-        listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
-        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
-    }
-
-    /**
-     * Setup dimensions that are only meaningful when the child Fragments are inside
-     * BrowseFragment.
-     */
-    private void setupChildFragmentsLayout() {
-        VerticalGridView headerList = mHeadersFragment.getVerticalGridView();
-        VerticalGridView containerList = mRowsFragment.getVerticalGridView();
-
-        // Both fragments list view has the same alignment
-        setVerticalVerticalGridViewLayout(headerList, 16);
-        setVerticalVerticalGridViewLayout(containerList, 0);
-    }
-
     @Override
     public void onStart() {
         super.onStart();
-        setupChildFragmentsLayout();
+        mHeadersFragment.setWindowAlignmentFromTop(mContainerListAlignTop);
+        mHeadersFragment.setItemAlignment();
+        mRowsFragment.setWindowAlignmentFromTop(mContainerListAlignTop);
+        mRowsFragment.setItemAlignment();
+
         if (mCanShowHeaders && mShowingHeaders && mHeadersFragment.getView() != null) {
             mHeadersFragment.getView().requestFocus();
         } else if ((!mCanShowHeaders || !mShowingHeaders)
                 && mRowsFragment.getView() != null) {
             mRowsFragment.getView().requestFocus();
         }
-        showHeaders(mCanShowHeaders && mShowingHeaders);
+        if (mCanShowHeaders) {
+            showHeaders(mShowingHeaders);
+        }
+    }
+
+    /**
+     * Enable/disable headers transition on back key support.  This is enabled by default.
+     * BrowseFragment will add a back stack entry when headers are showing.
+     * Headers transition on back key only works for {@link #HEADERS_ENABLED}
+     * or {@link #HEADERS_HIDDEN}.
+     * <p>
+     * NOTE: If app has its own onBackPressed() handling,
+     * app must disable this feature, app may use {@link #startHeadersTransition(boolean)}
+     * and {@link BrowseTransitionListener} in its own back stack handling.
+     */
+    public final void setHeadersTransitionOnBackEnabled(boolean headersBackStackEnabled) {
+        mHeadersBackStackEnabled = headersBackStackEnabled;
+    }
+
+    /**
+     * Returns true if headers transition on back key support is enabled.
+     */
+    public final boolean isHeadersTransitionOnBackEnabled() {
+        return mHeadersBackStackEnabled;
     }
 
     private void readArguments(Bundle args) {
@@ -626,8 +825,10 @@
         mBadgeView.setImageDrawable(drawable);
         if (drawable != null) {
             mBadgeView.setVisibility(View.VISIBLE);
+            mTitleView.setVisibility(View.GONE);
         } else {
             mBadgeView.setVisibility(View.GONE);
+            mTitleView.setVisibility(View.VISIBLE);
         }
     }
 
@@ -656,5 +857,8 @@
                 Log.w(TAG, "Unknown headers state: " + headersState);
                 break;
         }
+        if (mHeadersFragment != null) {
+            mHeadersFragment.setHeadersGone(!mCanShowHeaders);
+        }
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
index 7cb5ce1..22c1ec6 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
@@ -14,6 +14,7 @@
 
 package android.support.v17.leanback.app;
 
+import android.graphics.Color;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.FocusHighlightHelper;
@@ -23,12 +24,16 @@
 import android.support.v17.leanback.widget.OnItemSelectedListener;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowHeaderPresenter;
+import android.support.v17.leanback.widget.SinglePresenterSelector;
 import android.support.v17.leanback.widget.VerticalGridView;
 import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.View.OnLayoutChangeListener;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -42,17 +47,16 @@
 
     private OnItemSelectedListener mOnItemSelectedListener;
     private OnHeaderClickedListener mOnHeaderClickedListener;
-    private boolean mShow = true;
+    private boolean mHeadersEnabled = true;
+    private boolean mHeadersGone = false;
+    private int mBackgroundColor;
+    private boolean mBackgroundColorSet;
 
-    private static final Presenter sHeaderPresenter = new RowHeaderPresenter();
+    private static final PresenterSelector sHeaderPresenter = new SinglePresenterSelector(
+            new RowHeaderPresenter(R.layout.lb_header));
 
     public HeadersFragment() {
-        setPresenterSelector(new PresenterSelector() {
-            @Override
-            public Presenter getPresenter(Object item) {
-                return sHeaderPresenter;
-            }
-        });
+        setPresenterSelector(sHeaderPresenter);
     }
 
     public void setOnHeaderClickedListener(OnHeaderClickedListener listener) {
@@ -69,6 +73,8 @@
             if (position >= 0) {
                 Row row = (Row) getAdapter().get(position);
                 mOnItemSelectedListener.onItemSelected(null, row);
+            } else {
+                mOnItemSelectedListener.onItemSelected(null, null);
             }
         }
     }
@@ -88,12 +94,17 @@
             });
             headerView.setFocusable(true);
             headerView.setFocusableInTouchMode(true);
+            headerView.addOnLayoutChangeListener(sLayoutChangeListener);
         }
 
+    };
+
+    private static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() {
         @Override
-        public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
-            View headerView = viewHolder.getViewHolder().view;
-            headerView.setVisibility(mShow ? View.VISIBLE : View.INVISIBLE);
+        public void onLayoutChange(View v, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+            v.setPivotX(0);
+            v.setPivotY(v.getMeasuredHeight() / 2);
         }
     };
 
@@ -105,47 +116,31 @@
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        if (getBridgeAdapter() != null && getVerticalGridView() != null) {
-            FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView());
-        }
-    }
-
-    void getHeaderViews(List<View> headers, List<Integer> positions) {
         final VerticalGridView listView = getVerticalGridView();
         if (listView == null) {
             return;
         }
-        final int count = listView.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = listView.getChildAt(i);
-            headers.add(child);
-            positions.add(listView.getChildViewHolder(child).getPosition());
+        if (getBridgeAdapter() != null) {
+            FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
+        }
+        listView.setBackgroundColor(getBackgroundColor());
+        listView.setVisibility(mHeadersGone ? View.GONE : View.VISIBLE);
+        listView.setLayoutEnabled(mHeadersEnabled);
+    }
+
+    void setHeadersEnabled(boolean enabled) {
+        mHeadersEnabled = enabled;
+        final VerticalGridView listView = getVerticalGridView();
+        if (listView != null) {
+            listView.setLayoutEnabled(mHeadersEnabled);
         }
     }
 
-    void setHeadersVisiblity(boolean show) {
-        mShow = show;
+    void setHeadersGone(boolean gone) {
+        mHeadersGone = gone;
         final VerticalGridView listView = getVerticalGridView();
-        if (listView == null) {
-            return;
-        }
-        final int count = listView.getChildCount();
-        final int visibility = mShow ? View.VISIBLE : View.INVISIBLE;
-
-        // we should set visibility of selected view first so that it can
-        // regain the focus from parent (which is FOCUS_AFTER_DESCENDANT)
-        final int selectedPosition = listView.getSelectedPosition();
-        if (selectedPosition >= 0) {
-            RecyclerView.ViewHolder vh = listView.findViewHolderForPosition(selectedPosition);
-            if (vh != null) {
-                vh.itemView.setVisibility(visibility);
-            }
-        }
-        for (int i = 0; i < count; i++) {
-            View child = listView.getChildAt(i);
-            if (listView.getChildPosition(child) != selectedPosition) {
-                child.setVisibility(visibility);
-            }
+        if (listView != null) {
+            listView.setVisibility(mHeadersGone ? View.GONE : View.VISIBLE);
         }
     }
 
@@ -160,4 +155,27 @@
             FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView());
         }
     }
+
+    void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+        mBackgroundColorSet = true;
+
+        if (getVerticalGridView() != null) {
+            getVerticalGridView().setBackgroundColor(mBackgroundColor);
+        }
+    }
+
+    int getBackgroundColor() {
+        if (getActivity() == null) {
+            throw new IllegalStateException("Activity must be attached");
+        }
+
+        if (mBackgroundColorSet) {
+            return mBackgroundColor;
+        }
+
+        TypedValue outValue = new TypedValue();
+        getActivity().getTheme().resolveAttribute(android.R.attr.colorBackground, outValue, true);
+        return getResources().getColor(outValue.resourceId);
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
index c5c1d1e..de911e1 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
@@ -33,8 +33,6 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
-import java.util.List;
-
 /**
  * An ordered set of rows of leanback widgets.
  */
@@ -50,6 +48,7 @@
 
         final TimeAnimator mSelectAnimator = new TimeAnimator();
         final ColorOverlayDimmer mColorDimmer;
+
         int mSelectAnimatorDurationInUse;
         Interpolator mSelectAnimatorInterpolatorInUse;
         float mSelectLevelAnimStart;
@@ -69,6 +68,12 @@
 
         @Override
         public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+            if (mSelectAnimator.isRunning()) {
+                updateSelect(totalTime, deltaTime);
+            }
+        }
+
+        void updateSelect(long totalTime, long deltaTime) {
             float fraction;
             if (totalTime >= mSelectAnimatorDurationInUse) {
                 fraction = 1;
@@ -87,7 +92,7 @@
         }
 
         void animateSelect(boolean select, boolean immediate) {
-            endAnimation();
+            endSelectAnimation();
             final float end = select ? 1 : 0;
             if (immediate) {
                 mRowPresenter.setSelectLevel(mRowViewHolder, end);
@@ -103,7 +108,11 @@
             }
         }
 
-        void endAnimation() {
+        void endAnimations() {
+            endSelectAnimation();
+        }
+
+        void endSelectAnimation() {
             mSelectAnimator.end();
         }
 
@@ -124,8 +133,9 @@
     private OnItemSelectedListener mOnItemSelectedListener;
     private OnItemClickedListener mOnItemClickedListener;
 
-    // Select animation and interpolator are not intended to exposed at this moment.
-    // They might be synced with vertical scroll animation later.
+    // Select animation and interpolator are not intended to be
+    // exposed at this moment. They might be synced with vertical scroll
+    // animation later.
     int mSelectAnimatorDuration;
     Interpolator mSelectAnimatorInterpolator = new DecelerateInterpolator(2);
 
@@ -157,7 +167,6 @@
         mExpand = expand;
         VerticalGridView listView = getVerticalGridView();
         if (listView != null) {
-            listView.setActivated(expand);
             final int count = listView.getChildCount();
             if (DEBUG) Log.v(TAG, "setExpand " + expand + " count " + count);
             for (int i = 0; i < count; i++) {
@@ -222,10 +231,20 @@
     public void onViewCreated(View view, Bundle savedInstanceState) {
         if (DEBUG) Log.v(TAG, "onViewCreated");
         super.onViewCreated(view, savedInstanceState);
+        // Align the top edge of child with id row_content.
+        // Need set this for directly using RowsFragment.
         getVerticalGridView().setItemAlignmentViewId(R.id.row_content);
         getVerticalGridView().addItemDecoration(mItemDecoration);
     }
 
+    @Override
+    void setItemAlignment() {
+        super.setItemAlignment();
+        if (getVerticalGridView() != null) {
+            getVerticalGridView().setItemAlignmentOffsetWithPadding(true);
+        }
+    }
+
     private RecyclerView.ItemDecoration mItemDecoration = new RecyclerView.ItemDecoration() {
         @Override
         public void onDrawOver(Canvas c, RecyclerView parent) {
@@ -289,7 +308,7 @@
         @Override
         public void onUnbind(ItemBridgeAdapter.ViewHolder vh) {
             RowViewHolderExtra extra = (RowViewHolderExtra) vh.getExtraObject();
-            extra.endAnimation();
+            extra.endAnimations();
         }
     };
 
@@ -305,22 +324,4 @@
         }
     }
 
-    void getHeaderViews(List<View> headers, List<Integer> positions) {
-        final VerticalGridView listView = getVerticalGridView();
-        if (listView == null) {
-            return;
-        }
-        final int count = listView.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = listView.getChildAt(i);
-            ItemBridgeAdapter.ViewHolder viewHolder = (ItemBridgeAdapter.ViewHolder)
-                    listView.getChildViewHolder(child);
-            RowPresenter presenter = (RowPresenter) viewHolder.getPresenter();
-            RowPresenter.ViewHolder rowViewHolder = presenter.getRowViewHolder(
-                    viewHolder.getViewHolder());
-            headers.add(rowViewHolder.getHeaderViewHolder().view);
-            positions.add(viewHolder.getPosition());
-        }
-    }
-
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
index 9720634..53d6011 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/SearchFragment.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.app;
 
 import android.app.Fragment;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.support.v17.leanback.widget.ObjectAdapter;
@@ -35,7 +36,10 @@
 public class SearchFragment extends Fragment {
     private static final String TAG = SearchFragment.class.getSimpleName();
     private static final boolean DEBUG = false;
-    private static final String ARG_QUERY = SearchFragment.class.getCanonicalName() + ".query";
+
+    private static final String ARG_PREFIX = SearchFragment.class.getCanonicalName();
+    private static final String ARG_QUERY =  ARG_PREFIX + ".query";
+    private static final String ARG_TITLE = ARG_PREFIX  + ".title";
 
     /**
      * Search API exposed to application
@@ -84,14 +88,22 @@
     private OnItemClickedListener mOnItemClickedListener;
     private ObjectAdapter mResultAdapter;
 
+    private String mTitle;
+    private Drawable mBadgeDrawable;
+
     /**
      * @param args Bundle to use for the arguments, if null a new Bundle will be created.
      */
     public static Bundle createArgs(Bundle args, String query) {
+        return createArgs(args, query, null);
+    }
+
+    public static Bundle createArgs(Bundle args, String query, String title)  {
         if (args == null) {
             args = new Bundle();
         }
         args.putString(ARG_QUERY, query);
+        args.putString(ARG_TITLE, title);
         return args;
     }
 
@@ -145,15 +157,18 @@
 
             @Override
             public void onKeyboardDismiss(String query) {
+                if (DEBUG) Log.v(TAG, String.format("onKeyboardDismiss %s", query));
                 mRowsFragment.setSelectedPosition(0);
                 mRowsFragment.getVerticalGridView().requestFocus();
             }
         });
 
-        Bundle args = getArguments();
-        if (null != args) {
-            String query = args.getString(ARG_QUERY, "");
-            mSearchBar.setSearchQuery(query);
+        readArguments(getArguments());
+        if (null != mBadgeDrawable) {
+            setBadgeDrawable(mBadgeDrawable);
+        }
+        if (null != mTitle) {
+            setTitle(mTitle);
         }
 
         // Inject the RowsFragment in the results container
@@ -179,6 +194,8 @@
         mRowsFragment.setOnItemClickedListener(new OnItemClickedListener() {
             @Override
             public void onItemClicked(Object item, Row row) {
+                int position = mRowsFragment.getVerticalGridView().getSelectedPosition();
+                if (DEBUG) Log.v(TAG, String.format("onItemClicked %d", position));
                 if (null != mOnItemClickedListener) {
                     mOnItemClickedListener.onItemClicked(item, row);
                 }
@@ -231,6 +248,47 @@
         mOnItemClickedListener = listener;
     }
 
+    /**
+     * Sets the title string to be be shown in an empty search bar
+     */
+    public void setTitle(String title) {
+        mTitle = title;
+        if (null != mSearchBar) {
+            mSearchBar.setTitle(title);
+        }
+    }
+
+    /**
+     * Returns the title set
+     */
+    public String getTitle() {
+        if (null != mSearchBar) {
+            return mSearchBar.getTitle();
+        }
+        return null;
+    }
+
+    /**
+     * Sets the badge drawable that will be shown inside the search bar, next to the hint
+     */
+    public void setBadgeDrawable(Drawable drawable) {
+        mBadgeDrawable = drawable;
+        if (null != mSearchBar) {
+            mSearchBar.setBadgeDrawable(drawable);
+        }
+    }
+
+    /**
+     * Returns the badge drawable
+     */
+    public Drawable getBadgeDrawable() {
+        if (null != mSearchBar) {
+            return mSearchBar.getBadgeDrawable();
+        }
+        return null;
+    }
+
+
     private void retrieveResults(String searchQuery) {
         if (DEBUG) Log.v(TAG, String.format("retrieveResults %s", searchQuery));
         mProvider.onQueryTextChange(searchQuery);
@@ -258,4 +316,23 @@
         }
     }
 
+    private void readArguments(Bundle args) {
+        if (null == args) {
+            return;
+        }
+        if (args.containsKey(ARG_QUERY)) {
+            setSearchQuery(args.getString(ARG_QUERY));
+        }
+
+        if (args.containsKey(ARG_TITLE)) {
+            setTitle(args.getString(ARG_TITLE));
+        }
+    }
+
+    private void setSearchQuery(String query) {
+        mSearchBar.setSearchQuery(query);
+    }
+
+
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/app/TransitionHelper.java
index 78c2766..9a5a8fe 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/TransitionHelper.java
@@ -13,9 +13,7 @@
  */
 package android.support.v17.leanback.app;
 
-import android.content.Context;
 import android.os.Build;
-import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -27,6 +25,12 @@
     public static final int FADE_IN = 0x1;
     public static final int FADE_OUT = 0x2;
 
+    public static final int SLIDE_LEFT = 0;
+    public static final int SLIDE_TOP = 1;
+    public static final int SLIDE_RIGHT = 2;
+    public static final int SLIDE_BOTTOM = 3;
+
+    private final static TransitionHelper sHelper = new TransitionHelper();
     TransitionHelperVersionImpl mImpl;
 
     /**
@@ -51,6 +55,8 @@
 
         public Object createAutoTransition();
 
+        public Object createSlide(SlideCallback callback);
+
         public Object createFadeTransition(int fadingMode);
 
         public Object createChangeBounds(boolean reparent);
@@ -68,7 +74,7 @@
 
         public void addTransition(Object transitionSet, Object transition);
 
-        public void setTransitionCompleteListener(Object transition, Runnable listener);
+        public void setTransitionListener(Object transition, TransitionListener listener);
 
         public void runTransition(Object scene, Object transition);
 
@@ -84,6 +90,12 @@
 
         public void include(Object transition, View targetView);
 
+        public void setStartDelay(Object transition, long startDelay);
+
+        public void setDuration(Object transition, long duration);
+
+        public void setInterpolator(Object transition, Object timeInterpolator);
+
     }
 
     /**
@@ -92,7 +104,7 @@
     private static final class TransitionHelperStubImpl implements TransitionHelperVersionImpl {
 
         private static class TransitionStub {
-            Runnable mCompleteListener;
+            TransitionListener mTransitionListener;
         }
 
         @Override
@@ -116,6 +128,11 @@
         }
 
         @Override
+        public Object createSlide(SlideCallback callback) {
+            return new TransitionStub();
+        }
+
+        @Override
         public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
         }
 
@@ -166,21 +183,36 @@
         }
 
         @Override
-        public void setTransitionCompleteListener(Object transition, Runnable listener) {
-            ((TransitionStub) transition).mCompleteListener = listener;
+        public void setStartDelay(Object transition, long startDelay) {
+        }
+
+        @Override
+        public void setDuration(Object transition, long duration) {
+        }
+
+        @Override
+        public void setTransitionListener(Object transition, TransitionListener listener) {
+            ((TransitionStub) transition).mTransitionListener = listener;
         }
 
         @Override
         public void runTransition(Object scene, Object transition) {
+            TransitionStub transitionStub = (TransitionStub) transition;
+            if (transitionStub != null && transitionStub.mTransitionListener != null) {
+                transitionStub.mTransitionListener.onTransitionStart(transition);
+            }
             Runnable r = ((Runnable) scene);
             if (r != null) {
                 r.run();
             }
-            TransitionStub transitionStub = (TransitionStub) transition;
-            if (transitionStub != null && transitionStub.mCompleteListener != null) {
-                transitionStub.mCompleteListener.run();
+            if (transitionStub != null && transitionStub.mTransitionListener != null) {
+                transitionStub.mTransitionListener.onTransitionEnd(transition);
             }
         }
+
+        @Override
+        public void setInterpolator(Object transition, Object timeInterpolator) {
+        }
     }
 
     /**
@@ -189,8 +221,8 @@
     private static final class TransitionHelperKitkatImpl implements TransitionHelperVersionImpl {
         private final TransitionHelperKitkat mTransitionHelper;
 
-        TransitionHelperKitkatImpl(Context context) {
-            mTransitionHelper = new TransitionHelperKitkat(context);
+        TransitionHelperKitkatImpl() {
+            mTransitionHelper = new TransitionHelperKitkat();
         }
 
         @Override
@@ -214,6 +246,11 @@
         }
 
         @Override
+        public Object createSlide(SlideCallback callback) {
+            return mTransitionHelper.createSlide(callback);
+        }
+
+        @Override
         public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
             mTransitionHelper.setChangeBoundsStartDelay(changeBounds, view, startDelay);
         }
@@ -275,25 +312,42 @@
         }
 
         @Override
-        public void setTransitionCompleteListener(Object transition, Runnable listener) {
-            mTransitionHelper.setTransitionCompleteListener(transition, listener);
+        public void setStartDelay(Object transition, long startDelay) {
+            mTransitionHelper.setStartDelay(transition, startDelay);
+        }
+
+        @Override
+        public void setDuration(Object transition, long duration) {
+            mTransitionHelper.setDuration(transition, duration);
+        }
+
+        @Override
+        public void setTransitionListener(Object transition, TransitionListener listener) {
+            mTransitionHelper.setTransitionListener(transition, listener);
         }
 
         @Override
         public void runTransition(Object scene, Object transition) {
             mTransitionHelper.runTransition(scene, transition);
         }
+
+        @Override
+        public void setInterpolator(Object transition, Object timeInterpolator) {
+            mTransitionHelper.setInterpolator(transition, timeInterpolator);
+        }
     }
 
     /**
      * Returns the TransitionHelper that can be used to perform Transition
      * animations.
-     *
-     * @param context A context for accessing system resources.
      */
-    public TransitionHelper(Context context) {
+    public static TransitionHelper getInstance() {
+        return sHelper;
+    }
+
+    private TransitionHelper() {
         if (systemSupportsTransitions()) {
-            mImpl = new TransitionHelperKitkatImpl(context);
+            mImpl = new TransitionHelperKitkatImpl();
         } else {
             mImpl = new TransitionHelperStubImpl();
         }
@@ -327,6 +381,10 @@
         return mImpl.createTransitionSet(sequential);
     }
 
+    public Object createSlide(SlideCallback callback) {
+        return mImpl.createSlide(callback);
+    }
+
     public void addTransition(Object transitionSet, Object transition) {
         mImpl.addTransition(transitionSet, transition);
     }
@@ -355,6 +413,14 @@
         mImpl.include(transition, targetView);
     }
 
+    public void setStartDelay(Object transition, long startDelay) {
+        mImpl.setStartDelay(transition, startDelay);
+    }
+
+    public void setDuration(Object transition, long duration) {
+        mImpl.setDuration(transition, duration);
+    }
+
     public Object createAutoTransition() {
         return mImpl.createAutoTransition();
     }
@@ -363,11 +429,15 @@
         return mImpl.createFadeTransition(fadeMode);
     }
 
-    public void setTransitionCompleteListener(Object transition, Runnable listener) {
-        mImpl.setTransitionCompleteListener(transition, listener);
+    public void setTransitionListener(Object transition, TransitionListener listener) {
+        mImpl.setTransitionListener(transition, listener);
     }
 
     public void runTransition(Object scene, Object transition) {
         mImpl.runTransition(scene, transition);
     }
+
+    public void setInterpolator(Object transition, Object timeInterpolator) {
+        mImpl.setInterpolator(transition, timeInterpolator);
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
index ff4cddf..4de12cf 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -14,19 +14,16 @@
 package android.support.v17.leanback.app;
 
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.VerticalGridPresenter;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.OnItemClickedListener;
 import android.support.v17.leanback.widget.OnItemSelectedListener;
-import android.support.v17.leanback.widget.Presenter;
-import android.support.v17.leanback.widget.PresenterSelector;
-import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SearchOrbView;
-import android.support.v17.leanback.widget.VerticalGridView;
-import android.util.Log;
 import android.app.Fragment;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -56,6 +53,13 @@
     private TextView mTitleView;
     private ViewGroup mBrowseTitle;
     private SearchOrbView mSearchOrbView;
+    private boolean mShowingTitle = true;
+
+    // transition related
+    private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
+    private Object mTitleTransition;
+    private Object mSceneWithTitle;
+    private Object mSceneWithoutTitle;
 
     public static class Params {
         private String mTitle;
@@ -114,9 +118,7 @@
             throw new IllegalArgumentException("Grid presenter may not be null");
         }
         mGridPresenter = gridPresenter;
-        if (mOnItemSelectedListener != null) {
-            mGridPresenter.setOnItemSelectedListener(mOnItemSelectedListener);
-        }
+        mGridPresenter.setOnItemSelectedListener(mRowSelectedListener);
         if (mOnItemClickedListener != null) {
             mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
         }
@@ -144,17 +146,40 @@
         return mAdapter;
     }
 
+    final private OnItemSelectedListener mRowSelectedListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(Object item, Row row) {
+            int position = mGridViewHolder.getGridView().getSelectedPosition();
+            if (DEBUG) Log.v(TAG, "row selected position " + position);
+            onRowSelected(position);
+            if (mOnItemSelectedListener != null) {
+                mOnItemSelectedListener.onItemSelected(item, row);
+            }
+        }
+    };
+
     /**
      * Sets an item selection listener.
      */
     public void setOnItemSelectedListener(OnItemSelectedListener listener) {
         mOnItemSelectedListener = listener;
-        if (mGridPresenter != null) {
-            mGridPresenter.setOnItemSelectedListener(mOnItemSelectedListener);
-        }
     }
 
-    // TODO: getitemselectedlistener?
+    private void onRowSelected(int position) {
+        if (position != mSelectedPosition) {
+            if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(position)) {
+                // if has no sibling in front of it,  show title
+                if (!mShowingTitle) {
+                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleTransition);
+                    mShowingTitle = true;
+                }
+            } else if (mShowingTitle) {
+                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleTransition);
+                mShowingTitle = false;
+            }
+            mSelectedPosition = position;
+        }
+    }
 
     /**
      * Sets an item clicked listener.
@@ -211,7 +236,8 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
-        View root = inflater.inflate(R.layout.lb_vertical_grid_fragment, container, false);
+        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
+                container, false);
 
         mBrowseTitle = (ViewGroup) root.findViewById(R.id.browse_title_group);
         mBadgeView = (ImageView) mBrowseTitle.findViewById(R.id.browse_badge);
@@ -226,6 +252,25 @@
             setTitle(mParams.mTitle);
         }
 
+        mSceneWithTitle = sTransitionHelper.createScene(root, new Runnable() {
+            @Override
+            public void run() {
+                mBrowseTitle.setVisibility(View.VISIBLE);
+            }
+        });
+        mSceneWithoutTitle = sTransitionHelper.createScene(root, new Runnable() {
+            @Override
+            public void run() {
+                mBrowseTitle.setVisibility(View.GONE);
+            }
+        });
+        mTitleTransition = sTransitionHelper.createTransitionSet(false);
+        Object fade = sTransitionHelper.createFadeTransition(
+                TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
+        Object changeBounds = sTransitionHelper.createChangeBounds(false);
+        sTransitionHelper.addTransition(mTitleTransition, fade);
+        sTransitionHelper.addTransition(mTitleTransition, changeBounds);
+        sTransitionHelper.excludeChildren(mTitleTransition, R.id.browse_grid_dock, true);
         return root;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
index c42f3e0..ea72cb7 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractDetailsDescriptionPresenter.java
@@ -35,20 +35,55 @@
         private final TextView mTitle;
         private final TextView mSubtitle;
         private final TextView mBody;
-        private final int mUnderTitleSpacing;
-        private final int mUnderSubtitleSpacing;
+        private final int mTitleMargin;
+        private final int mUnderTitleBaselineMargin;
+        private final int mUnderSubtitleBaselineMargin;
+        private final int mTitleLineSpacing;
+        private final int mBodyLineSpacing;
+        private final int mBodyMaxLines;
+        private final int mBodyMinLines;
+        private final FontMetricsInt mTitleFontMetricsInt;
+        private final FontMetricsInt mSubtitleFontMetricsInt;
+        private final FontMetricsInt mBodyFontMetricsInt;
 
         public ViewHolder(View view) {
             super(view);
             mTitle = (TextView) view.findViewById(R.id.lb_details_description_title);
             mSubtitle = (TextView) view.findViewById(R.id.lb_details_description_subtitle);
             mBody = (TextView) view.findViewById(R.id.lb_details_description_body);
-            int interTextSpacing = view.getContext().getResources().getDimensionPixelSize(
-                    R.dimen.lb_details_overview_description_intertext_spacing);
+
             FontMetricsInt titleFontMetricsInt = getFontMetricsInt(mTitle);
-            mUnderTitleSpacing = interTextSpacing - titleFontMetricsInt.descent;
-            FontMetricsInt subtitleFontMetricsInt = getFontMetricsInt(mSubtitle);
-            mUnderSubtitleSpacing = interTextSpacing - subtitleFontMetricsInt.descent;
+            final int titleAscent = view.getResources().getDimensionPixelSize(
+                    R.dimen.lb_details_description_title_baseline);
+            // Ascent is negative
+            mTitleMargin = titleAscent + titleFontMetricsInt.ascent;
+
+            mUnderTitleBaselineMargin = view.getResources().getDimensionPixelSize(
+                    R.dimen.lb_details_description_under_title_baseline_margin);
+            mUnderSubtitleBaselineMargin = view.getResources().getDimensionPixelSize(
+                    R.dimen.lb_details_description_under_subtitle_baseline_margin);
+
+            mTitleLineSpacing = view.getResources().getDimensionPixelSize(
+                    R.dimen.lb_details_description_title_line_spacing);
+            mBodyLineSpacing = view.getResources().getDimensionPixelSize(
+                    R.dimen.lb_details_description_body_line_spacing);
+
+            mBodyMaxLines = view.getResources().getInteger(
+                    R.integer.lb_details_description_body_max_lines);
+            mBodyMinLines = view.getResources().getInteger(
+                    R.integer.lb_details_description_body_min_lines);
+
+            mTitleFontMetricsInt = getFontMetricsInt(mTitle);
+            mSubtitleFontMetricsInt = getFontMetricsInt(mSubtitle);
+            mBodyFontMetricsInt = getFontMetricsInt(mBody);
+
+            mTitle.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right,
+                        int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    mBody.setMaxLines(mTitle.getLineCount() > 1 ? mBodyMinLines : mBodyMaxLines);
+                }
+            });
         }
 
         public TextView getTitle() {
@@ -90,7 +125,10 @@
             hasTitle = false;
         } else {
             vh.mTitle.setVisibility(View.VISIBLE);
+            vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight() -
+                    vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier());
         }
+        setTopMargin(vh.mTitle, vh.mTitleMargin);
 
         boolean hasSubtitle = true;
         if (TextUtils.isEmpty(vh.mSubtitle.getText())) {
@@ -99,7 +137,8 @@
         } else {
             vh.mSubtitle.setVisibility(View.VISIBLE);
             if (hasTitle) {
-                setTopMargin(vh.mSubtitle, vh.mUnderTitleSpacing);
+                setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin +
+                        vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
             } else {
                 setTopMargin(vh.mSubtitle, 0);
             }
@@ -109,10 +148,15 @@
             vh.mBody.setVisibility(View.GONE);
         } else {
             vh.mBody.setVisibility(View.VISIBLE);
+            vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight() -
+                    vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier());
+
             if (hasSubtitle) {
-                setTopMargin(vh.mBody, vh.mUnderSubtitleSpacing);
+                setTopMargin(vh.mBody, vh.mUnderSubtitleBaselineMargin +
+                        vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent);
             } else if (hasTitle) {
-                setTopMargin(vh.mBody, vh.mUnderTitleSpacing);
+                setTopMargin(vh.mBody, vh.mUnderTitleBaselineMargin +
+                        vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent);
             } else {
                 setTopMargin(vh.mBody, 0);
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java b/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java
index 7698872..a229ecc 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ActionPresenterSelector.java
@@ -18,6 +18,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -47,13 +48,11 @@
 
     static class ActionViewHolder extends Presenter.ViewHolder {
         Action mAction;
-        ImageView mIconView;
-        TextView mLabel;
+        Button mButton;
 
         public ActionViewHolder(View view) {
             super(view);
-            mIconView = (ImageView) view.findViewById(R.id.lb_action_icon);
-            mLabel = (TextView) view.findViewById(R.id.lb_action_text);
+            mButton = (Button) view.findViewById(R.id.lb_action_button);
         }
     }
 
@@ -80,7 +79,7 @@
             Action action = (Action) item;
             ActionViewHolder vh = (ActionViewHolder) viewHolder;
             vh.mAction = action;
-            vh.mLabel.setText(action.getLabel1());
+            vh.mButton.setText(action.getLabel1());
         }
 
         @Override
@@ -113,32 +112,34 @@
             ActionViewHolder vh = (ActionViewHolder) viewHolder;
             vh.mAction = action;
 
-            int horizontalPadding = vh.view.getContext().getResources()
-                    .getDimensionPixelSize(R.dimen.lb_action_1_line_padding_left);
             if (action.getIcon() != null) {
-                vh.view.setPadding(0, 0, horizontalPadding, 0);
-                vh.mIconView.setVisibility(View.VISIBLE);
-                // TODO: scale this?
-                vh.mIconView.setImageDrawable(action.getIcon());
+                final int leftPadding = vh.view.getResources()
+                        .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_left);
+                final int rightPadding = vh.view.getResources()
+                        .getDimensionPixelSize(R.dimen.lb_action_with_icon_padding_right);
+                vh.view.setPadding(leftPadding, 0, rightPadding, 0);
             } else {
-                vh.view.setPadding(horizontalPadding, 0, horizontalPadding, 0);
-                vh.mIconView.setVisibility(View.GONE);
+                final int padding = vh.view.getResources()
+                        .getDimensionPixelSize(R.dimen.lb_action_padding_horizontal);
+                vh.view.setPadding(padding, 0, padding, 0);
             }
+            vh.mButton.setCompoundDrawablesWithIntrinsicBounds(action.getIcon(), null, null, null);
+
             CharSequence line1 = action.getLabel1();
             CharSequence line2 = action.getLabel2();
             if (TextUtils.isEmpty(line1)) {
-                vh.mLabel.setText(line2);
+                vh.mButton.setText(line2);
             } else if (TextUtils.isEmpty(line2)) {
-                vh.mLabel.setText(line1);
+                vh.mButton.setText(line1);
             } else {
-                vh.mLabel.setText(line1 + "\n" + line2);
+                vh.mButton.setText(line1 + "\n" + line2);
             }
         }
 
         @Override
         public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
             ActionViewHolder vh = (ActionViewHolder) viewHolder;
-            vh.mIconView.setVisibility(View.GONE);
+            vh.mButton.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
             vh.view.setPadding(0, 0, 0, 0);
             vh.mAction = null;
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
index 74bb038..79b6226 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ArrayObjectAdapter.java
@@ -46,6 +46,25 @@
     }
 
     /**
+     * Search first occurrence of item in the list; return -1 if not found.
+     * @param item  The item to search in the list.
+     * @return First occurrence of item in the list or -1 if not found.
+     */
+    public int indexOf(Object item) {
+        return mItems.indexOf(item);
+    }
+
+    /**
+     * Notify content of range of items changed.  Note that this is not same
+     * as add or remove items.
+     * @param positionStart The position of first item that has changed.
+     * @param itemCount The count of how many items has changed.
+     */
+    public void notifyArrayItemRangeChanged(int positionStart, int itemCount) {
+        notifyItemRangeChanged(positionStart, itemCount);
+    }
+
+    /**
      * Adds an item to the end of the list.
      *
      * @param item The item to add to the end of the list.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
index 854f5de..90d834e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/BaseGridView.java
@@ -19,9 +19,8 @@
 import android.support.v17.leanback.R;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Interpolator;
 
 /**
  * Base class for vertically and horizontally scrolling lists. The items come
@@ -122,6 +121,9 @@
                 a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0));
         mLayoutManager.setHorizontalMargin(
                 a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0));
+        if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
+            setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
+        }
         a.recycle();
     }
 
@@ -248,6 +250,24 @@
     }
 
     /**
+     * Set to true if include padding in calculating item align offset.
+     *
+     * @param withPadding When it is true: we include left/top padding for positive
+     *          item offset, include right/bottom padding for negative item offset.
+     */
+    public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
+        mLayoutManager.setItemAlignmentOffsetWithPadding(withPadding);
+        requestLayout();
+    }
+
+    /**
+     * Returns true if include padding in calculating item align offset.
+     */
+    public boolean isItemAlignmentOffsetWithPadding() {
+        return mLayoutManager.isItemAlignmentOffsetWithPadding();
+    }
+
+    /**
      * Set offset percent for item alignment in addition to {@link
      * #getItemAlignmentOffset()}.
      *
@@ -374,42 +394,6 @@
     }
 
     /**
-     * Set an interpolator for the animation when a child changes size or when 
-     * adding or removing a child.
-     * <p><i>Unstable API, might change later.</i>
-     */
-    public void setChildLayoutAnimationInterpolator(Interpolator interpolator) {
-        mLayoutManager.setChildLayoutAnimationInterpolator(interpolator);
-    }
-
-    /**
-     * Get the interpolator for the animation when a child changes size or when
-     * adding or removing a child.
-     * <p><i>Unstable API, might change later.</i>
-     */
-    public Interpolator getChildLayoutAnimationInterpolator() {
-        return mLayoutManager.getChildLayoutAnimationInterpolator();
-    }
-
-    /**
-     * Set the duration of the animation when a child changes size or when 
-     * adding or removing a child.
-     * <p><i>Unstable API, might change later.</i>
-     */
-    public void setChildLayoutAnimationDuration(long duration) {
-        mLayoutManager.setChildLayoutAnimationDuration(duration);
-    }
-
-    /**
-     * Get the duration of the animation when a child changes size or when 
-     * adding or removing a child.
-     * <p><i>Unstable API, might change later.</i>
-     */
-    public long getChildLayoutAnimationDuration() {
-        return mLayoutManager.getChildLayoutAnimationDuration();
-    }
-
-    /**
      * Describes how the child views are positioned. Defaults to
      * GRAVITY_TOP|GRAVITY_LEFT.
      *
@@ -467,4 +451,36 @@
         return mLayoutManager.isFocusSearchDisabled();
     }
 
+    /**
+     * Enable or disable layout.  All children will be removed when layout is
+     * disabled.
+     */
+    public void setLayoutEnabled(boolean layoutEnabled) {
+        mLayoutManager.setLayoutEnabled(layoutEnabled);
+    }
+
+    /**
+     * Enable or disable pruning child.  Disable is useful during transition.
+     */
+    public void setPruneChild(boolean pruneChild) {
+        mLayoutManager.setPruneChild(pruneChild);
+    }
+
+    /**
+     * Get if view has same row sibling next to it.
+     *
+     * @param position Position in adapter.
+     */
+    public boolean hasNextViewInSameRow(int position) {
+        return mLayoutManager.hasNextViewInSameRow(position);
+    }
+
+    /**
+     * Get if view has same row sibling in front of it.
+     *
+     * @param position Position in adapter.
+     */
+    public boolean hasPreviousViewInSameRow(int position) {
+        return mLayoutManager.hasPreviousViewInSameRow(position);
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
index 3504581..6b08b29 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRowPresenter.java
@@ -13,7 +13,12 @@
  */
 package android.support.v17.leanback.widget;
 
+import android.content.Context;
+import android.graphics.Color;
 import android.support.v17.leanback.R;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,11 +40,102 @@
     private static final String TAG = "DetailsOverviewRowPresenter";
     private static final boolean DEBUG = false;
 
+    private static final int MORE_ACTIONS_FADE_MS = 100;
+
     public static class ViewHolder extends RowPresenter.ViewHolder {
         final ImageView mImageView;
         final FrameLayout mDetailsDescriptionFrame;
         final HorizontalGridView mActionsRow;
+        final View mMoreActionsView;
         Presenter.ViewHolder mDetailsDescriptionViewHolder;
+        int mNumItems;
+        boolean mShowMoreRight;
+        boolean mShowMoreLeft;
+        boolean mShowMoreActions;
+
+        void bind(ItemBridgeAdapter bridgeAdapter) {
+            mNumItems = bridgeAdapter.getItemCount();
+            bridgeAdapter.setAdapterListener(mAdapterListener);
+
+            mMoreActionsView.setAlpha(0f);
+            mShowMoreRight = false;
+            mShowMoreActions = false;
+
+            mShowMoreLeft = true;
+            showMoreLeft(false);
+        }
+
+        final ItemBridgeAdapter.AdapterListener mAdapterListener =
+                new ItemBridgeAdapter.AdapterListener() {
+
+            @Override
+            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
+                checkFirstAndLastPosition(false);
+            }
+            @Override
+            public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
+                checkFirstAndLastPosition(false);
+            }
+        };
+
+        final RecyclerView.OnScrollListener mScrollListener =
+                new RecyclerView.OnScrollListener() {
+
+            @Override
+            public void onScrollStateChanged(int newState) {
+            }
+            @Override
+            public void onScrolled(int dx, int dy) {
+                checkFirstAndLastPosition(true);
+            }
+        };
+
+        private int getViewCenter(View view) {
+            return (view.getRight() - view.getLeft()) / 2;
+        }
+
+        private void checkFirstAndLastPosition(boolean fromScroll) {
+            RecyclerView.ViewHolder viewHolder;
+
+            viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
+            boolean showRight = (viewHolder == null ||
+                    viewHolder.itemView.getRight() > mActionsRow.getWidth());
+
+            boolean showMore = (viewHolder == null ||
+                    getViewCenter(viewHolder.itemView) > mActionsRow.getWidth());
+
+            viewHolder = mActionsRow.findViewHolderForPosition(0);
+            boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
+
+            if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll +
+                    " showRight " + showRight + " showLeft " + showLeft);
+
+            showMoreActions(showMore);
+            showMoreRight(showRight);
+            showMoreLeft(showLeft);
+        }
+
+        private void showMoreLeft(boolean show) {
+            if (show != mShowMoreLeft) {
+                mActionsRow.setFadingLeftEdge(show);
+                mShowMoreLeft = show;
+            }
+        }
+
+        private void showMoreRight(boolean show) {
+            if (show != mShowMoreRight) {
+                mActionsRow.setFadingRightEdge(show);
+                mShowMoreRight = show;
+            }
+        }
+
+        private void showMoreActions(boolean show) {
+            if (show != mShowMoreActions) {
+                mMoreActionsView.animate().alpha(show ? 1f : 0).setDuration(
+                        MORE_ACTIONS_FADE_MS).start();
+                mShowMoreActions = show;
+            }
+        }
 
         public ViewHolder(View rootView) {
             super(rootView);
@@ -48,51 +144,116 @@
                     (FrameLayout) rootView.findViewById(R.id.details_overview_description);
             mActionsRow =
                     (HorizontalGridView) rootView.findViewById(R.id.details_overview_actions);
+            mActionsRow.setOnScrollListener(mScrollListener);
+
+            final int fadeLength = rootView.getResources().getDimensionPixelSize(
+                    R.dimen.lb_details_overview_actions_fade_size);
+            mActionsRow.setFadingRightEdgeLength(fadeLength);
+            mActionsRow.setFadingRightEdgeOffset(-fadeLength);
+            mActionsRow.setFadingLeftEdgeLength(fadeLength);
+            mActionsRow.setFadingLeftEdgeOffset(-fadeLength);
+
+            mMoreActionsView = rootView.findViewById(R.id.details_overview_actions_more);
         }
     }
 
     private final Presenter mDetailsPresenter;
     private final ActionPresenterSelector mActionPresenterSelector;
     private final ItemBridgeAdapter mActionBridgeAdapter;
+    private int mBackgroundColor = Color.TRANSPARENT;
+    private boolean mBackgroundColorSet;
+    private boolean mIsStyleLarge = true;
 
     /**
      * Constructor that uses the given {@link Presenter} to render the detailed
      * description for the row.
      */
     public DetailsOverviewRowPresenter(Presenter detailsPresenter) {
+        setHeaderPresenter(null);
         setSelectEffectEnabled(false);
         mDetailsPresenter = detailsPresenter;
         mActionPresenterSelector = new ActionPresenterSelector();
         mActionBridgeAdapter = new ItemBridgeAdapter();
-        FocusHighlightHelper.setupActionItemFocusHighlight(mActionBridgeAdapter);
     }
 
     /**
-     * Set the listener for action click events.
+     * Sets the listener for action click events.
      */
     public void setOnActionClickedListener(OnActionClickedListener listener) {
         mActionPresenterSelector.setOnActionClickedListener(listener);
     }
 
     /**
-     * Get the listener for action click events.
+     * Gets the listener for action click events.
      */
     public OnActionClickedListener getOnActionClickedListener() {
         return mActionPresenterSelector.getOnActionClickedListener();
     }
 
+    /**
+     * Sets the background color.  If not set a default from the theme will be used.
+     */
+    public void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+        mBackgroundColorSet = true;
+    }
+
+    /**
+     * Returns the background color.  If no background color was set, returns transparent.
+     */
+    public int getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    /**
+     * Sets the layout style to be large or small.
+     * The default is large.
+     */
+    public void setStyleLarge(boolean large) {
+        mIsStyleLarge = large;
+    }
+
+    /**
+     * Returns true if the layout style is large.
+     */
+    public boolean isStyleLarge() {
+        return mIsStyleLarge;
+    }
+
+    private int getDefaultBackgroundColor(Context context) {
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.colorBackground, outValue, true);
+        return context.getResources().getColor(outValue.resourceId);
+    }
+
     @Override
     protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
         View v = LayoutInflater.from(parent.getContext())
             .inflate(R.layout.lb_details_overview, parent, false);
         ViewHolder vh = new ViewHolder(v);
+
         vh.mDetailsDescriptionViewHolder =
             mDetailsPresenter.onCreateViewHolder(vh.mDetailsDescriptionFrame);
         vh.mDetailsDescriptionFrame.addView(vh.mDetailsDescriptionViewHolder.view);
 
+        initDetailsOverview(v.findViewById(R.id.details_overview));
+
         return vh;
     }
 
+    private void initDetailsOverview(View view) {
+        int resId = mIsStyleLarge ? R.dimen.lb_details_overview_height_large :
+            R.dimen.lb_details_overview_height_small;
+
+        ViewGroup.LayoutParams lp = view.getLayoutParams();
+        lp.height = view.getResources().getDimensionPixelSize(resId);
+        view.setLayoutParams(lp);
+
+        view.setBackgroundColor(mBackgroundColorSet ?
+                mBackgroundColor : getDefaultBackgroundColor(view.getContext()));
+
+    }
+
     @Override
     protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
         super.onBindRowViewHolder(holder, item);
@@ -109,8 +270,11 @@
         mActionBridgeAdapter.clear();
         ArrayObjectAdapter aoa = new ArrayObjectAdapter(mActionPresenterSelector);
         aoa.addAll(0, (Collection)row.getActions());
+
         mActionBridgeAdapter.setAdapter(aoa);
         vh.mActionsRow.setAdapter(mActionBridgeAdapter);
+
+        vh.bind(mActionBridgeAdapter);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
index d027ede..569e264 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FocusHighlightHelper.java
@@ -147,9 +147,6 @@
         }
     }
 
-    private static ActionItemFocusHighlight sActionItemFocusHighlight =
-            new ActionItemFocusHighlight();
-
     /**
      * Setup the focus highlight behavior of a focused item in browse list row.
      * @param adapter  adapter of the list row.
@@ -169,14 +166,6 @@
         }
     }
 
-    /**
-     * Setup the focus highlight behavior of a focused item in an action list.
-     * @param adapter  adapter of the action list.
-     */
-    public static void setupActionItemFocusHighlight(ItemBridgeAdapter adapter) {
-        adapter.setFocusHighlight(sActionItemFocusHighlight);
-    }
-
     static class HeaderItemFocusHighlight implements FocusHighlight {
         private static boolean sInitialized;
         private static float sSelectScale;
@@ -233,26 +222,4 @@
             viewFocused(view, hasFocus);
         }
     }
-
-    private static class ActionItemFocusHighlight implements FocusHighlight {
-        private boolean mInitialized;
-        private int mDuration;
-
-        private void initializeDimensions(Resources res) {
-            if (!mInitialized) {
-                mDuration = Integer.parseInt(res.getString(R.dimen.lb_details_overview_action_select_duration));
-            }
-        }
-
-        @Override
-        public void onItemFocused(View view, boolean hasFocus) {
-            initializeDimensions(view.getResources());
-            TransitionDrawable td = (TransitionDrawable) view.getBackground();
-            if (hasFocus) {
-                td.startTransition(mDuration);
-            } else {
-                td.reverseTransition(mDuration);
-            }
-        }
-    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 8143767..85e77f4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -15,10 +15,12 @@
 
 import android.animation.TimeAnimator;
 import android.content.Context;
+import android.graphics.PointF;
 import android.graphics.Rect;
+import android.support.v7.widget.LinearSmoothScroller;
 import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Adapter;
 import android.support.v7.widget.RecyclerView.Recycler;
+import android.support.v7.widget.RecyclerView.State;
 
 import static android.support.v7.widget.RecyclerView.NO_ID;
 import static android.support.v7.widget.RecyclerView.NO_POSITION;
@@ -46,10 +48,9 @@
 
      /*
       * LayoutParams for {@link HorizontalGridView} and {@link VerticalGridView}.
-      * The class currently does three internal jobs:
+      * The class currently does two internal jobs:
       * - Saves optical bounds insets.
       * - Caches focus align view center.
-      * - Manages child view layout animation.
       */
     static class LayoutParams extends RecyclerView.LayoutParams {
 
@@ -66,15 +67,6 @@
         private int mAlignX;
         private int mAlignY;
 
-        // For animations
-        private TimeAnimator mAnimator;
-        private long mDuration;
-        private boolean mFirstAttached;
-        // current virtual view position (scrollOffset + left/top) in the GridLayoutManager
-        private int mViewX, mViewY;
-        // animation start value of translation x and y
-        private float mAnimationStartTranslationX, mAnimationStartTranslationY;
-
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
         }
@@ -99,15 +91,6 @@
             super(source);
         }
 
-        void onViewAttached() {
-            endAnimate();
-            mFirstAttached = true;
-        }
-
-        void onViewDetached() {
-            endAnimate();
-        }
-
         int getAlignX() {
             return mAlignX;
         }
@@ -140,6 +123,22 @@
             return view.getHeight() - mTopInset - mBottomInset;
         }
 
+        int getOpticalLeftInset() {
+            return mLeftInset;
+        }
+
+        int getOpticalRightInset() {
+            return mRighInset;
+        }
+
+        int getOpticalTopInset() {
+            return mTopInset;
+        }
+
+        int getOpticalBottomInset() {
+            return mBottomInset;
+        }
+
         void setAlignX(int alignX) {
             mAlignX = alignX;
         }
@@ -155,68 +154,6 @@
             mBottomInset = bottomInset;
         }
 
-        private TimeAnimator.TimeListener mTimeListener = new TimeAnimator.TimeListener() {
-            @Override
-            public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-                if (mView == null) {
-                    return;
-                }
-                if (totalTime >= mDuration) {
-                    endAnimate();
-                } else {
-                    float fraction = (float) (totalTime / (double)mDuration);
-                    float fractionToEnd = 1 - mAnimator
-                        .getInterpolator().getInterpolation(fraction);
-                    mView.setTranslationX(fractionToEnd * mAnimationStartTranslationX);
-                    mView.setTranslationY(fractionToEnd * mAnimationStartTranslationY);
-                    invalidateItemDecoration();
-                }
-            }
-        };
-
-        void startAnimate(GridLayoutManager layout, View view, long startDelay) {
-            if (mAnimator == null) {
-                mAnimator = new TimeAnimator();
-                mAnimator.setTimeListener(mTimeListener);
-            }
-            if (mFirstAttached) {
-                // first time record the initial location and return without animation
-                // TODO do we need initial animation?
-                mViewX = layout.getScrollOffsetX() + getOpticalLeft(view);
-                mViewY = layout.getScrollOffsetY() + getOpticalTop(view);
-                mFirstAttached = false;
-                return;
-            }
-            mView = view;
-            int newViewX = layout.getScrollOffsetX() + getOpticalLeft(mView);
-            int newViewY = layout.getScrollOffsetY() + getOpticalTop(mView);
-            if (newViewX != mViewX || newViewY != mViewY) {
-                mAnimator.cancel();
-                mAnimationStartTranslationX = mView.getTranslationX();
-                mAnimationStartTranslationY = mView.getTranslationY();
-                mAnimationStartTranslationX += mViewX - newViewX;
-                mAnimationStartTranslationY += mViewY - newViewY;
-                mDuration = layout.getChildLayoutAnimationDuration();
-                mAnimator.setDuration(mDuration);
-                mAnimator.setInterpolator(layout.getChildLayoutAnimationInterpolator());
-                mAnimator.setStartDelay(startDelay);
-                mAnimator.start();
-                mViewX = newViewX;
-                mViewY = newViewY;
-            }
-        }
-
-        void endAnimate() {
-            if (mAnimator != null) {
-                mAnimator.end();
-            }
-            if (mView != null) {
-                mView.setTranslationX(0);
-                mView.setTranslationY(0);
-                mView = null;
-            }
-        }
-
         private void invalidateItemDecoration() {
             ViewParent parent = mView.getParent();
             if (parent instanceof RecyclerView) {
@@ -245,7 +182,7 @@
      */
     private int mOrientation = HORIZONTAL;
 
-    private RecyclerView.Adapter mAdapter;
+    private RecyclerView.State mState;
     private RecyclerView.Recycler mRecycler;
 
     private boolean mInLayout = false;
@@ -265,23 +202,43 @@
     private boolean mForceFullLayout;
 
     /**
+     * True if layout is enabled.
+     */
+    private boolean mLayoutEnabled = true;
+
+    /**
      * The scroll offsets of the viewport relative to the entire view.
      */
     private int mScrollOffsetPrimary;
     private int mScrollOffsetSecondary;
 
     /**
-     * User-specified fixed size of each grid item in the secondary direction, can be
-     * 0 to be determined by parent size and number of rows.
+     * User-specified row height/column width.  Can be WRAP_CONTENT.
      */
-    private int mItemLengthSecondaryRequested;
+    private int mRowSizeSecondaryRequested;
+
     /**
      * The fixed size of each grid item in the secondary direction. This corresponds to
      * the row height, equal for all rows. Grid items may have variable length
      * in the primary direction.
-     *
      */
-    private int mItemLengthSecondary;
+    private int mFixedRowSizeSecondary;
+
+    /**
+     * Tracks the secondary size of each row.
+     */
+    private int[] mRowSizeSecondary;
+
+    /**
+     * Flag controlling whether the current/next layout should
+     * be updating the secondary size of rows.
+     */
+    private boolean mRowSecondarySizeRefresh;
+
+    /**
+     * The maximum measured size of the view.
+     */
+    private int mMaxSizeSecondary;
 
     /**
      * Margin between items.
@@ -372,14 +329,20 @@
     private boolean mAnimateChildLayout = true;
 
     /**
-     * Interpolator used to animate layout of children.
+     * True if prune child,  might be disabled during transition.
      */
-    private Interpolator mAnimateLayoutChildInterpolator = sDefaultAnimationChildLayoutInterpolator;
+    private boolean mPruneChild = true;
+
+    private int[] mTempDeltas = new int[2];
+
+    private boolean mUseDeltaInPreLayout;
+
+    private int mDeltaInPreLayout, mDeltaSecondaryInPreLayout;
 
     /**
-     * Duration used to animate layout of children.
+     * Temporaries used for measuring.
      */
-    private long mAnimateLayoutChildDuration = DEFAULT_CHILD_ANIMATION_DURATION_MS;
+    private int[] mMeasuredDimension = new int[2];
 
     public GridLayoutManager(BaseGridView baseGridView) {
         mBaseGridView = baseGridView;
@@ -438,6 +401,15 @@
         return mItemAlignment.mainAxis().getItemAlignmentOffset();
     }
 
+    public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
+        mItemAlignment.mainAxis().setItemAlignmentOffsetWithPadding(withPadding);
+        updateChildAlignments();
+    }
+
+    public boolean isItemAlignmentOffsetWithPadding() {
+        return mItemAlignment.mainAxis().isItemAlignmentOffsetWithPadding();
+    }
+
     public void setItemAlignmentOffsetPercent(float offsetPercent) {
         mItemAlignment.mainAxis().setItemAlignmentOffsetPercent(offsetPercent);
         updateChildAlignments();
@@ -467,9 +439,15 @@
         mForceFullLayout = true;
     }
 
+    /**
+     * Set the row height. May be WRAP_CONTENT, or a size in pixels.
+     */
     public void setRowHeight(int height) {
-        if (height < 0) throw new IllegalArgumentException();
-        mItemLengthSecondaryRequested = height;
+        if (height >= 0 || height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+            mRowSizeSecondaryRequested = height;
+        } else {
+            throw new IllegalArgumentException("Invalid row height: " + height);
+        }
     }
 
     public void setItemMargin(int margin) {
@@ -514,45 +492,34 @@
     }
 
     private int getPositionByView(View view) {
-        return getPositionByIndex(mBaseGridView.indexOfChild(view));
+        if (view == null) {
+            return NO_POSITION;
+        }
+        RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
+        if (vh == null) {
+            return NO_POSITION;
+        }
+        return vh.getPosition();
     }
 
     private int getPositionByIndex(int index) {
-        if (index < 0) {
-            return NO_POSITION;
-        }
-        return mFirstVisiblePos + index;
-    }
-
-    private View getViewByPosition(int position) {
-        int index = getIndexByPosition(position);
-        if (index < 0) {
-            return null;
-        }
-        return getChildAt(index);
-    }
-
-    private int getIndexByPosition(int position) {
-        if (mFirstVisiblePos < 0 ||
-                position < mFirstVisiblePos || position > mLastVisiblePos) {
-            return NO_POSITION;
-        }
-        return position - mFirstVisiblePos;
+        return getPositionByView(getChildAt(index));
     }
 
     private void dispatchChildSelected() {
         if (mChildSelectedListener == null) {
             return;
         }
-
-        View view = getViewByPosition(mFocusPosition);
-
         if (mFocusPosition != NO_POSITION) {
-            mChildSelectedListener.onChildSelected(mBaseGridView, view, mFocusPosition,
-                    mAdapter.getItemId(mFocusPosition));
-        } else {
-            mChildSelectedListener.onChildSelected(mBaseGridView, null, NO_POSITION, NO_ID);
+            View view = findViewByPosition(mFocusPosition);
+            if (view != null) {
+                RecyclerView.ViewHolder vh = mBaseGridView.getChildViewHolder(view);
+                mChildSelectedListener.onChildSelected(mBaseGridView, view, mFocusPosition,
+                        vh == null? NO_ID: vh.getItemId());
+                return;
+            }
         }
+        mChildSelectedListener.onChildSelected(mBaseGridView, null, NO_POSITION, NO_ID);
     }
 
     @Override
@@ -594,11 +561,7 @@
     }
 
     protected View getViewForPosition(int position) {
-        View v = mRecycler.getViewForPosition(position);
-        if (v != null) {
-            ((LayoutParams) v.getLayoutParams()).onViewAttached();
-        }
-        return v;
+        return mRecycler.getViewForPosition(position);
     }
 
     final int getOpticalLeft(View v) {
@@ -644,6 +607,25 @@
     }
 
     /**
+     * Save Recycler and State for convenience.  Must be paired with leaveContext().
+     */
+    private void saveContext(Recycler recycler, State state) {
+        if (mRecycler != null || mState != null) {
+            Log.e(TAG, "Recycler information was not released, bug!");
+        }
+        mRecycler = recycler;
+        mState = state;
+    }
+
+    /**
+     * Discard saved Recycler and State.
+     */
+    private void leaveContext() {
+        mRecycler = null;
+        mState = null;
+    }
+
+    /**
      * Re-initialize data structures for a data change or handling invisible
      * selection. The method tries its best to preserve position information so
      * that staggered grid looks same before and after re-initialize.
@@ -651,10 +633,9 @@
      *        focus on.
      * @return Actual position that can be focused on.
      */
-    private int init(RecyclerView.Adapter adapter, RecyclerView.Recycler recycler,
-            int focusPosition) {
+    private int init(int focusPosition) {
 
-        final int newItemCount = adapter.getItemCount();
+        final int newItemCount = mState.getItemCount();
 
         if (focusPosition == NO_POSITION && newItemCount > 0) {
             // if focus position is never set before,  initialize it to 0
@@ -705,7 +686,7 @@
                 }
                 // fill rows with minimal view positions of the subset
                 for (int i = firstIndex; i <= lastIndex; i++) {
-                    View v = getViewByPosition(i);
+                    View v = findViewByPosition(i);
                     if (v == null) {
                         continue;
                     }
@@ -715,24 +696,34 @@
                         mRows[row].low = mRows[row].high = low;
                     }
                 }
-                // fill other rows that does not include the subset using first item
                 int firstItemRowPosition = mRows[mGrid.getLocation(firstIndex).row].low;
                 if (firstItemRowPosition == Integer.MAX_VALUE) {
                     firstItemRowPosition = 0;
                 }
-                for (int i = 0; i < mNumRows; i++) {
-                    if (mRows[i].low == Integer.MAX_VALUE) {
-                        mRows[i].low = mRows[i].high = firstItemRowPosition;
+                if (mState.didStructureChange()) {
+                    // if there is structure change, the removed item might be in the
+                    // subset,  so it is meaningless to maintain the low locations.
+                    for (int i = 0; i < mNumRows; i++) {
+                        mRows[i].low = firstItemRowPosition;
+                        mRows[i].high = firstItemRowPosition;
+                    }
+                } else {
+                    // fill other rows that does not include the subset using first item
+                    for (int i = 0; i < mNumRows; i++) {
+                        if (mRows[i].low == Integer.MAX_VALUE) {
+                            mRows[i].low = mRows[i].high = firstItemRowPosition;
+                        }
                     }
                 }
             }
 
             // Same adapter, we can reuse any attached views
-            detachAndScrapAttachedViews(recycler);
+            detachAndScrapAttachedViews(mRecycler);
 
         } else {
             // otherwise recreate data structure
             mRows = new StaggeredGrid.Row[mNumRows];
+
             for (int i = 0; i < mNumRows; i++) {
                 mRows[i] = new StaggeredGrid.Row();
             }
@@ -751,20 +742,149 @@
             mWindowAlignment.reset();
         }
 
-        mAdapter = adapter;
-        mRecycler = recycler;
         mGrid.setProvider(mGridProvider);
         // mGrid share the same Row array information
         mGrid.setRows(mRows);
         mFirstVisiblePos = mLastVisiblePos = NO_POSITION;
 
         initScrollController();
+        updateScrollSecondAxis();
 
         return focusPosition;
     }
 
+    private int getRowSizeSecondary(int rowIndex) {
+        if (mFixedRowSizeSecondary != 0) {
+            return mFixedRowSizeSecondary;
+        }
+        if (mRowSizeSecondary == null) {
+            return 0;
+        }
+        return mRowSizeSecondary[rowIndex];
+    }
+
+    private int getRowStartSecondary(int rowIndex) {
+        int start = 0;
+        for (int i = 0; i < rowIndex; i++) {
+            start += getRowSizeSecondary(i) + mMarginSecondary;
+        }
+        return start;
+    }
+
+    private int getSizeSecondary() {
+        return getRowStartSecondary(mNumRows - 1) + getRowSizeSecondary(mNumRows - 1);
+    }
+
+    private void measureScrapChild(int position, int widthSpec, int heightSpec,
+            int[] measuredDimension) {
+        View view = mRecycler.getViewForPosition(position);
+        if (view != null) {
+            LayoutParams p = (LayoutParams) view.getLayoutParams();
+            int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
+                    getPaddingLeft() + getPaddingRight(), p.width);
+            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
+                    getPaddingTop() + getPaddingBottom(), p.height);
+            view.measure(childWidthSpec, childHeightSpec);
+            measuredDimension[0] = view.getMeasuredWidth();
+            measuredDimension[1] = view.getMeasuredHeight();
+            mRecycler.recycleView(view);
+        }
+    }
+
+    private boolean processRowSizeSecondary(boolean measure) {
+        if (mFixedRowSizeSecondary != 0) {
+            return false;
+        }
+
+        if (mGrid == null) {
+            measureScrapChild(mFocusPosition == NO_POSITION ? 0 : mFocusPosition,
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                    mMeasuredDimension);
+            if (DEBUG) Log.v(TAG, "measured scrap child: " + mMeasuredDimension[0] +
+                    " " + mMeasuredDimension[1]);
+        }
+
+        List<Integer>[] rows = mGrid == null ? null :
+            mGrid.getItemPositionsInRows(mFirstVisiblePos, mLastVisiblePos);
+        boolean changed = false;
+
+        for (int rowIndex = 0; rowIndex < mNumRows; rowIndex++) {
+            int rowSize = 0;
+
+            final int rowItemCount = rows == null ? 1 : rows[rowIndex].size();
+            if (DEBUG) Log.v(getTag(), "processRowSizeSecondary row " + rowIndex +
+                    " rowItemCount " + rowItemCount);
+
+            for (int i = 0; i < rowItemCount; i++) {
+                if (rows != null) {
+                    final int position = rows[rowIndex].get(i);
+                    final View view = findViewByPosition(position);
+                    if (measure && view.isLayoutRequested()) {
+                        measureChild(view);
+                    }
+                    // If this view is outside the primary axis bounds, we ignore it.
+                    if (getViewMax(view) < 0 || getViewMin(view) > mSizePrimary) {
+                        continue;
+                    }
+                    mMeasuredDimension[0] = view.getWidth();
+                    mMeasuredDimension[1] = view.getHeight();
+                }
+                final int secondarySize = mOrientation == HORIZONTAL ?
+                        mMeasuredDimension[1] : mMeasuredDimension[0];
+                if (secondarySize > rowSize) {
+                    rowSize = secondarySize;
+                }
+            }
+            if (DEBUG) Log.v(getTag(), "row " + rowIndex + " rowItemCount " + rowItemCount +
+                    " rowSize " + rowSize);
+
+            if (mRowSizeSecondary[rowIndex] != rowSize) {
+                if (DEBUG) Log.v(getTag(), "row size secondary changed: " + mRowSizeSecondary[rowIndex] +
+                        ", " + rowSize);
+
+                mRowSizeSecondary[rowIndex] = rowSize;
+                changed = true;
+            }
+        }
+
+        return changed;
+    }
+
+    /**
+     * Checks if we need to update row secondary sizes.
+     */
+    private void updateRowSecondarySizeRefresh() {
+        mRowSecondarySizeRefresh = processRowSizeSecondary(false);
+        if (mRowSecondarySizeRefresh) {
+            if (DEBUG) Log.v(getTag(), "mRowSecondarySizeRefresh now set");
+            forceRequestLayout();
+        }
+    }
+
+    private void forceRequestLayout() {
+        if (DEBUG) Log.v(getTag(), "forceRequestLayout");
+        // RecyclerView prevents us from requesting layout in many cases
+        // (during layout, during scroll, etc.)
+        // For secondary row size wrap_content support we currently need a
+        // second layout pass to update the measured size after having measured
+        // and added child views in layoutChildren.
+        // Force the second layout by posting a delayed runnable.
+        // TODO: investigate allowing a second layout pass,
+        // or move child add/measure logic to the measure phase.
+        mBaseGridView.getHandler().post(new Runnable() {
+           @Override
+           public void run() {
+               if (DEBUG) Log.v(getTag(), "request Layout from runnable");
+               requestLayout();
+           }
+        });
+    }
+
     @Override
-    public void onMeasure(int widthSpec, int heightSpec) {
+    public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
+        saveContext(recycler, state);
+
         int sizePrimary, sizeSecondary, modeSecondary, paddingSecondary;
         int measuredSizeSecondary;
         if (mOrientation == HORIZONTAL) {
@@ -778,52 +898,83 @@
             modeSecondary = MeasureSpec.getMode(widthSpec);
             paddingSecondary = getPaddingLeft() + getPaddingRight();
         }
-        switch (modeSecondary) {
-        case MeasureSpec.UNSPECIFIED:
-            if (mItemLengthSecondaryRequested == 0) {
-                if (mOrientation == HORIZONTAL) {
-                    throw new IllegalStateException("Must specify rowHeight or view height");
-                } else {
-                    throw new IllegalStateException("Must specify columnWidth or view width");
+        if (DEBUG) Log.v(getTag(), "onMeasure widthSpec " + Integer.toHexString(widthSpec) +
+                " heightSpec " + Integer.toHexString(heightSpec) +
+                " modeSecondary " + Integer.toHexString(modeSecondary) +
+                " sizeSecondary " + sizeSecondary + " " + this);
+
+        mMaxSizeSecondary = sizeSecondary;
+
+        if (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT) {
+            mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested;
+            mFixedRowSizeSecondary = 0;
+
+            if (mRowSizeSecondary == null || mRowSizeSecondary.length != mNumRows) {
+                mRowSizeSecondary = new int[mNumRows];
+            }
+
+            // Measure all current children and update cached row heights
+            processRowSizeSecondary(true);
+
+            switch (modeSecondary) {
+            case MeasureSpec.UNSPECIFIED:
+                measuredSizeSecondary = getSizeSecondary() + paddingSecondary;
+                break;
+            case MeasureSpec.AT_MOST:
+                measuredSizeSecondary = Math.min(getSizeSecondary() + paddingSecondary,
+                        mMaxSizeSecondary);
+                break;
+            case MeasureSpec.EXACTLY:
+                measuredSizeSecondary = mMaxSizeSecondary;
+                break;
+            default:
+                throw new IllegalStateException("wrong spec");
+            }
+
+        } else {
+            switch (modeSecondary) {
+            case MeasureSpec.UNSPECIFIED:
+                if (mRowSizeSecondaryRequested == 0) {
+                    if (mOrientation == HORIZONTAL) {
+                        throw new IllegalStateException("Must specify rowHeight or view height");
+                    } else {
+                        throw new IllegalStateException("Must specify columnWidth or view width");
+                    }
                 }
-            }
-            mItemLengthSecondary = mItemLengthSecondaryRequested;
-            if (mNumRowsRequested == 0) {
-                mNumRows = 1;
-            } else {
-                mNumRows = mNumRowsRequested;
-            }
-            measuredSizeSecondary = mItemLengthSecondary * mNumRows + mMarginSecondary
-                * (mNumRows - 1) + paddingSecondary;
-            break;
-        case MeasureSpec.AT_MOST:
-        case MeasureSpec.EXACTLY:
-            if (mNumRowsRequested == 0 && mItemLengthSecondaryRequested == 0) {
-                mNumRows = 1;
-                mItemLengthSecondary = sizeSecondary - paddingSecondary;
-            } else if (mNumRowsRequested == 0) {
-                mItemLengthSecondary = mItemLengthSecondaryRequested;
-                mNumRows = (sizeSecondary + mMarginSecondary)
-                    / (mItemLengthSecondaryRequested + mMarginSecondary);
-            } else if (mItemLengthSecondaryRequested == 0) {
-                mNumRows = mNumRowsRequested;
-                mItemLengthSecondary = (sizeSecondary - paddingSecondary - mMarginSecondary
-                        * (mNumRows - 1)) / mNumRows;
-            } else {
-                mNumRows = mNumRowsRequested;
-                mItemLengthSecondary = mItemLengthSecondaryRequested;
-            }
-            measuredSizeSecondary = sizeSecondary;
-            if (modeSecondary == MeasureSpec.AT_MOST) {
-                int childrenSize = mItemLengthSecondary * mNumRows + mMarginSecondary
+                mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
+                mNumRows = mNumRowsRequested == 0 ? 1 : mNumRowsRequested;
+                measuredSizeSecondary = mFixedRowSizeSecondary * mNumRows + mMarginSecondary
                     * (mNumRows - 1) + paddingSecondary;
-                if (childrenSize < measuredSizeSecondary) {
-                    measuredSizeSecondary = childrenSize;
+                break;
+            case MeasureSpec.AT_MOST:
+            case MeasureSpec.EXACTLY:
+                if (mNumRowsRequested == 0 && mRowSizeSecondaryRequested == 0) {
+                    mNumRows = 1;
+                    mFixedRowSizeSecondary = sizeSecondary - paddingSecondary;
+                } else if (mNumRowsRequested == 0) {
+                    mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
+                    mNumRows = (sizeSecondary + mMarginSecondary)
+                        / (mRowSizeSecondaryRequested + mMarginSecondary);
+                } else if (mRowSizeSecondaryRequested == 0) {
+                    mNumRows = mNumRowsRequested;
+                    mFixedRowSizeSecondary = (sizeSecondary - paddingSecondary - mMarginSecondary
+                            * (mNumRows - 1)) / mNumRows;
+                } else {
+                    mNumRows = mNumRowsRequested;
+                    mFixedRowSizeSecondary = mRowSizeSecondaryRequested;
                 }
+                measuredSizeSecondary = sizeSecondary;
+                if (modeSecondary == MeasureSpec.AT_MOST) {
+                    int childrenSize = mFixedRowSizeSecondary * mNumRows + mMarginSecondary
+                        * (mNumRows - 1) + paddingSecondary;
+                    if (childrenSize < measuredSizeSecondary) {
+                        measuredSizeSecondary = childrenSize;
+                    }
+                }
+                break;
+            default:
+                throw new IllegalStateException("wrong spec");
             }
-            break;
-        default:
-            throw new IllegalStateException("wrong spec");
         }
         if (mOrientation == HORIZONTAL) {
             setMeasuredDimension(sizePrimary, measuredSizeSecondary);
@@ -833,35 +984,47 @@
         if (DEBUG) {
             Log.v(getTag(), "onMeasure sizePrimary " + sizePrimary +
                     " measuredSizeSecondary " + measuredSizeSecondary +
-                    " mItemLengthSecondary " + mItemLengthSecondary +
+                    " mFixedRowSizeSecondary " + mFixedRowSizeSecondary +
                     " mNumRows " + mNumRows);
         }
+
+        leaveContext();
     }
 
     private void measureChild(View child) {
         final ViewGroup.LayoutParams lp = child.getLayoutParams();
-
+        final int secondarySpec = (mRowSizeSecondaryRequested == ViewGroup.LayoutParams.WRAP_CONTENT) ?
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) :
+                MeasureSpec.makeMeasureSpec(mFixedRowSizeSecondary, MeasureSpec.EXACTLY);
         int widthSpec, heightSpec;
+
         if (mOrientation == HORIZONTAL) {
             widthSpec = ViewGroup.getChildMeasureSpec(
-                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, lp.width);
-            heightSpec = ViewGroup.getChildMeasureSpec(MeasureSpec.makeMeasureSpec(
-                    mItemLengthSecondary, MeasureSpec.EXACTLY), 0, lp.height);
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                    0, lp.width);
+            heightSpec = ViewGroup.getChildMeasureSpec(secondarySpec, 0, lp.height);
         } else {
             heightSpec = ViewGroup.getChildMeasureSpec(
-                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, lp.height);
-            widthSpec = ViewGroup.getChildMeasureSpec(MeasureSpec.makeMeasureSpec(
-                    mItemLengthSecondary, MeasureSpec.EXACTLY), 0, lp.width);
+                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                    0, lp.height);
+            widthSpec = ViewGroup.getChildMeasureSpec(secondarySpec, 0, lp.width);
         }
 
         child.measure(widthSpec, heightSpec);
+
+        if (DEBUG) Log.v(getTag(), "measureChild secondarySpec " + Integer.toHexString(secondarySpec) +
+                " widthSpec " + Integer.toHexString(widthSpec) +
+                " heightSpec " + Integer.toHexString(heightSpec) +
+                " measuredWidth " + child.getMeasuredWidth() +
+                " measuredHeight " + child.getMeasuredHeight());
+        if (DEBUG) Log.v(getTag(), "child lp width " + lp.width + " height " + lp.height);
     }
 
     private StaggeredGrid.Provider mGridProvider = new StaggeredGrid.Provider() {
 
         @Override
         public int getCount() {
-            return mAdapter.getItemCount();
+            return mState.getItemCount();
         }
 
         @Override
@@ -877,14 +1040,16 @@
                 }
             }
 
-            if (append) {
-                addView(v);
-            } else {
-                addView(v, 0);
+            // See recyclerView docs:  we don't need re-add scraped view if it was removed.
+            if (!((RecyclerView.LayoutParams) v.getLayoutParams()).isItemRemoved()) {
+                if (append) {
+                    addView(v);
+                } else {
+                    addView(v, 0);
+                }
+                measureChild(v);
             }
 
-            measureChild(v);
-
             int length = mOrientation == HORIZONTAL ? v.getMeasuredWidth() : v.getMeasuredHeight();
             int start, end;
             if (append) {
@@ -923,9 +1088,10 @@
                     mFirstVisiblePos--;
                 }
             }
-            int startSecondary = rowIndex * (mItemLengthSecondary + mMarginSecondary);
-            layoutChild(v, start - mScrollOffsetPrimary, end - mScrollOffsetPrimary,
-                    startSecondary - mScrollOffsetSecondary);
+            if (DEBUG) Log.v(getTag(), "start " + start + " end " + end);
+            int startSecondary = getRowStartSecondary(rowIndex) - mScrollOffsetSecondary;
+            layoutChild(rowIndex, v, start - mScrollOffsetPrimary, end - mScrollOffsetPrimary,
+                    startSecondary);
             if (DEBUG) {
                 Log.d(getTag(), "addView " + index + " " + v);
             }
@@ -934,11 +1100,11 @@
         }
     };
 
-    private void layoutChild(View v, int start, int end, int startSecondary) {
-         int measuredSecondary = mOrientation == HORIZONTAL ? v.getMeasuredHeight()
-                 : v.getMeasuredWidth();
-        if (measuredSecondary > mItemLengthSecondary) {
-           measuredSecondary = mItemLengthSecondary;
+    private void layoutChild(int rowIndex, View v, int start, int end, int startSecondary) {
+        int sizeSecondary = mOrientation == HORIZONTAL ? v.getMeasuredHeight()
+                : v.getMeasuredWidth();
+        if (mFixedRowSizeSecondary > 0) {
+            sizeSecondary = Math.min(sizeSecondary, mFixedRowSizeSecondary);
         }
         final int verticalGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
         final int horizontalGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
@@ -947,22 +1113,22 @@
             // do nothing
         } else if (mOrientation == HORIZONTAL && verticalGravity == Gravity.BOTTOM
                 || mOrientation == VERTICAL && horizontalGravity == Gravity.RIGHT) {
-            startSecondary += mItemLengthSecondary - measuredSecondary;
+            startSecondary += getRowSizeSecondary(rowIndex) - sizeSecondary;
         } else if (mOrientation == HORIZONTAL && verticalGravity == Gravity.CENTER_VERTICAL
                 || mOrientation == VERTICAL && horizontalGravity == Gravity.CENTER_HORIZONTAL) {
-            startSecondary += (mItemLengthSecondary - measuredSecondary) / 2;
+            startSecondary += (getRowSizeSecondary(rowIndex) - sizeSecondary) / 2;
         }
         int left, top, right, bottom;
         if (mOrientation == HORIZONTAL) {
             left = start;
             top = startSecondary;
             right = end;
-            bottom = startSecondary + measuredSecondary;
+            bottom = startSecondary + sizeSecondary;
         } else {
             top = start;
             left = startSecondary;
             bottom = end;
-            right = startSecondary + measuredSecondary;
+            right = startSecondary + sizeSecondary;
         }
         v.layout(left, top, right, bottom);
         updateChildOpticalInsets(v, left, top, right, bottom);
@@ -1023,7 +1189,7 @@
     // Append one column if possible and return true if reach end.
     private boolean appendOneVisibleItem() {
         while (true) {
-            if (mLastVisiblePos != NO_POSITION && mLastVisiblePos < mAdapter.getItemCount() -1 &&
+            if (mLastVisiblePos != NO_POSITION && mLastVisiblePos < mState.getItemCount() -1 &&
                     mLastVisiblePos < mGrid.getLastIndex()) {
                 // append invisible view of saved location till last row
                 final int index = mLastVisiblePos + 1;
@@ -1032,9 +1198,9 @@
                 if (row == mNumRows - 1) {
                     return false;
                 }
-            } else if ((mLastVisiblePos == NO_POSITION && mAdapter.getItemCount() > 0) ||
+            } else if ((mLastVisiblePos == NO_POSITION && mState.getItemCount() > 0) ||
                     (mLastVisiblePos != NO_POSITION &&
-                            mLastVisiblePos < mAdapter.getItemCount() - 1)) {
+                            mLastVisiblePos < mState.getItemCount() - 1)) {
                 mGrid.appendItems(mScrollOffsetPrimary + mSizePrimary);
                 return false;
             } else {
@@ -1082,20 +1248,22 @@
     }
 
     private void removeChildAt(int position) {
-        View v = getViewByPosition(position);
+        View v = findViewByPosition(position);
         if (v != null) {
             if (DEBUG) {
                 Log.d(getTag(), "removeAndRecycleViewAt " + position);
             }
-            ((LayoutParams) v.getLayoutParams()).onViewDetached();
-            removeAndRecycleViewAt(getIndexByPosition(position), mRecycler);
+            removeAndRecycleView(v, mRecycler);
         }
     }
 
     private void removeInvisibleViewsAtEnd() {
+        if (!mPruneChild) {
+            return;
+        }
         boolean update = false;
         while(mLastVisiblePos > mFirstVisiblePos && mLastVisiblePos > mFocusPosition) {
-            View view = getViewByPosition(mLastVisiblePos);
+            View view = findViewByPosition(mLastVisiblePos);
             if (getViewMin(view) > mSizePrimary) {
                 removeChildAt(mLastVisiblePos);
                 mLastVisiblePos--;
@@ -1110,9 +1278,12 @@
     }
 
     private void removeInvisibleViewsAtFront() {
+        if (!mPruneChild) {
+            return;
+        }
         boolean update = false;
         while(mLastVisiblePos > mFirstVisiblePos && mFirstVisiblePos < mFocusPosition) {
-            View view = getViewByPosition(mFirstVisiblePos);
+            View view = findViewByPosition(mFirstVisiblePos);
             if (getViewMax(view) < 0) {
                 removeChildAt(mFirstVisiblePos);
                 mFirstVisiblePos++;
@@ -1135,7 +1306,7 @@
             mRows[i].high = Integer.MIN_VALUE;
         }
         for (int i = mFirstVisiblePos; i <= mLastVisiblePos; i++) {
-            View view = getViewByPosition(i);
+            View view = findViewByPosition(i);
             int row = mGrid.getLocation(i).row;
             int low = getViewMin(view) + mScrollOffsetPrimary;
             if (low < mRows[row].low) {
@@ -1148,35 +1319,6 @@
         }
     }
 
-    /**
-     * Relayout and re-positioning child for a possible new size and/or a new
-     * start.
-     *
-     * @param view View to measure and layout.
-     * @param start New start of the view or Integer.MIN_VALUE for not change.
-     * @return New start of next view.
-     */
-    private int updateChildView(View view, int start, int startSecondary) {
-        if (start == Integer.MIN_VALUE) {
-            start = getViewMin(view);
-        }
-        int end;
-        if (mOrientation == HORIZONTAL) {
-            if (view.isLayoutRequested() || view.getMeasuredHeight() != mItemLengthSecondary) {
-                measureChild(view);
-            }
-            end = start + view.getMeasuredWidth();
-        } else {
-            if (view.isLayoutRequested() || view.getMeasuredWidth() != mItemLengthSecondary) {
-                measureChild(view);
-            }
-            end = start + view.getMeasuredHeight();
-        }
-
-        layoutChild(view, start, end, startSecondary);
-        return end + mMarginPrimary;
-    }
-
     // Fast layout when there is no structure change, adapter change, etc.
     protected void fastRelayout() {
         initScrollController();
@@ -1186,35 +1328,71 @@
         // relayout and repositioning views on each row
         for (int i = 0; i < mNumRows; i++) {
             List<Integer> row = rows[i];
-            int start = Integer.MIN_VALUE;
-            int startSecondary =
-                i * (mItemLengthSecondary + mMarginSecondary) - mScrollOffsetSecondary;
+            final int startSecondary = getRowStartSecondary(i) - mScrollOffsetSecondary;
             for (int j = 0, size = row.size(); j < size; j++) {
-                int position = row.get(j);
-                start = updateChildView(getViewByPosition(position), start, startSecondary);
+                final int position = row.get(j);
+                final View view = findViewByPosition(position);
+                int primaryDelta, start, end;
+
+                if (mOrientation == HORIZONTAL) {
+                    final int primarySize = view.getMeasuredWidth();
+                    if (view.isLayoutRequested()) {
+                        measureChild(view);
+                    }
+                    start = getViewMin(view);
+                    end = start + view.getMeasuredWidth();
+                    primaryDelta = view.getMeasuredWidth() - primarySize;
+                    if (primaryDelta != 0) {
+                        for (int k = j + 1; k < size; k++) {
+                            findViewByPosition(row.get(k)).offsetLeftAndRight(primaryDelta);
+                        }
+                    }
+                } else {
+                    final int primarySize = view.getMeasuredHeight();
+                    if (view.isLayoutRequested()) {
+                        measureChild(view);
+                    }
+                    start = getViewMin(view);
+                    end = start + view.getMeasuredHeight();
+                    primaryDelta = view.getMeasuredHeight() - primarySize;
+                    if (primaryDelta != 0) {
+                        for (int k = j + 1; k < size; k++) {
+                            findViewByPosition(row.get(k)).offsetTopAndBottom(primaryDelta);
+                        }
+                    }
+                }
+                layoutChild(i, view, start, end, startSecondary);
             }
         }
 
+        updateRowsMinMax();
         appendVisibleItems();
         prependVisibleItems();
 
         updateRowsMinMax();
         updateScrollMin();
         updateScrollMax();
+        updateScrollSecondAxis();
 
         if (mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED) {
-            View focusView = getViewByPosition(mFocusPosition == NO_POSITION ? 0 : mFocusPosition);
+            View focusView = findViewByPosition(mFocusPosition == NO_POSITION ? 0 : mFocusPosition);
             scrollToView(focusView, false);
         }
     }
 
+    @Override
+    public boolean supportsItemAnimations() {
+        return mAnimateChildLayout;
+    }
+
     // Lays out items based on the current scroll position
-    public void onLayoutChildren(RecyclerView.Adapter adapter, RecyclerView.Recycler recycler,
-            boolean structureChanged, RecyclerView.State state) {
+    @Override
+    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
         if (DEBUG) {
             Log.v(getTag(), "layoutChildren start numRows " + mNumRows + " mScrollOffsetSecondary "
                     + mScrollOffsetSecondary + " mScrollOffsetPrimary " + mScrollOffsetPrimary
-                    + " structureChanged " + structureChanged
+                    + " inPreLayout " + state.isPreLayout()
+                    + " didStructureChange " + state.didStructureChange()
                     + " mForceFullLayout " + mForceFullLayout);
             Log.v(getTag(), "width " + getWidth() + " height " + getHeight());
         }
@@ -1223,36 +1401,53 @@
             // haven't done measure yet
             return;
         }
-        final int itemCount = adapter.getItemCount();
+        final int itemCount = state.getItemCount();
         if (itemCount < 0) {
             return;
         }
 
+        if (!mLayoutEnabled) {
+            discardLayoutInfo();
+            removeAllViews();
+            return;
+        }
         mInLayout = true;
 
+        saveContext(recycler, state);
         // Track the old focus view so we can adjust our system scroll position
         // so that any scroll animations happening now will remain valid.
+        // We must use same delta in Pre Layout (if prelayout exists) and second layout.
+        // So we cache the deltas in PreLayout and use it in second layout.
         int delta = 0, deltaSecondary = 0;
-        if (mFocusPosition != NO_POSITION
-                && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED) {
-            View focusView = getViewByPosition(mFocusPosition);
-            if (focusView != null) {
-                delta = mWindowAlignment.mainAxis().getSystemScrollPos(
-                        getViewCenter(focusView) + mScrollOffsetPrimary) - mScrollOffsetPrimary;
-                deltaSecondary =
-                    mWindowAlignment.secondAxis().getSystemScrollPos(
-                            getViewCenterSecondary(focusView) + mScrollOffsetSecondary)
-                    - mScrollOffsetSecondary;
+        if (!state.isPreLayout() && mUseDeltaInPreLayout) {
+            delta = mDeltaInPreLayout;
+            deltaSecondary = mDeltaSecondaryInPreLayout;
+        } else {
+            if (mFocusPosition != NO_POSITION
+                    && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED) {
+                View focusView = findViewByPosition(mFocusPosition);
+                if (focusView != null) {
+                    delta = mWindowAlignment.mainAxis().getSystemScrollPos(
+                            getViewCenter(focusView) + mScrollOffsetPrimary) - mScrollOffsetPrimary;
+                    deltaSecondary =
+                        mWindowAlignment.secondAxis().getSystemScrollPos(
+                                getViewCenterSecondary(focusView) + mScrollOffsetSecondary)
+                        - mScrollOffsetSecondary;
+                    if (mUseDeltaInPreLayout = state.isPreLayout()) {
+                        mDeltaInPreLayout = delta;
+                        mDeltaSecondaryInPreLayout = deltaSecondary;
+                    }
+                }
             }
         }
 
-        boolean hasDoneFirstLayout = hasDoneFirstLayout();
-        if (!structureChanged && !mForceFullLayout && hasDoneFirstLayout) {
+        final boolean hasDoneFirstLayout = hasDoneFirstLayout();
+        if (!mState.didStructureChange() && !mForceFullLayout && hasDoneFirstLayout) {
             fastRelayout();
         } else {
             boolean hadFocus = mBaseGridView.hasFocus();
 
-            int newFocusPosition = init(adapter, recycler, mFocusPosition);
+            int newFocusPosition = init(mFocusPosition);
             if (DEBUG) {
                 Log.v(getTag(), "mFocusPosition " + mFocusPosition + " newFocusPosition "
                     + newFocusPosition);
@@ -1290,7 +1485,7 @@
             do {
                 oldFirstVisible = mFirstVisiblePos;
                 oldLastVisible = mLastVisiblePos;
-                View focusView = getViewByPosition(newFocusPosition);
+                View focusView = findViewByPosition(newFocusPosition);
                 // we need force to initialize the child view's position
                 scrollToView(focusView, false);
                 if (focusView != null && hadFocus) {
@@ -1320,12 +1515,20 @@
             Log.d(getTag(), sw.toString());
         }
 
-        attemptAnimateLayoutChild();
+        if (mRowSecondarySizeRefresh) {
+            mRowSecondarySizeRefresh = false;
+        } else {
+            updateRowSecondarySizeRefresh();
+        }
 
-        if (!hasDoneFirstLayout) {
-            dispatchChildSelected();
+        if (!state.isPreLayout()) {
+            mUseDeltaInPreLayout = false;
+            if (!hasDoneFirstLayout) {
+                dispatchChildSelected();
+            }
         }
         mInLayout = false;
+        leaveContext();
         if (DEBUG) Log.v(getTag(), "layoutChildren end");
     }
 
@@ -1359,52 +1562,86 @@
 
     @Override
     public int scrollHorizontallyBy(int dx, Recycler recycler, RecyclerView.State state) {
-        if (DEBUG) Log.v(TAG, "scrollHorizontallyBy " + dx);
-
-        if (mOrientation == HORIZONTAL) {
-            return scrollDirectionPrimary(dx);
-        } else {
-            return scrollDirectionSecondary(dx);
+        if (DEBUG) Log.v(getTag(), "scrollHorizontallyBy " + dx);
+        if (!mLayoutEnabled) {
+            return 0;
         }
+        saveContext(recycler, state);
+        int result;
+        if (mOrientation == HORIZONTAL) {
+            result = scrollDirectionPrimary(dx);
+        } else {
+            result = scrollDirectionSecondary(dx);
+        }
+        leaveContext();
+        return result;
     }
 
     @Override
     public int scrollVerticallyBy(int dy, Recycler recycler, RecyclerView.State state) {
-        if (DEBUG) Log.v(TAG, "scrollVerticallyBy " + dy);
-        if (mOrientation == VERTICAL) {
-            return scrollDirectionPrimary(dy);
-        } else {
-            return scrollDirectionSecondary(dy);
+        if (DEBUG) Log.v(getTag(), "scrollVerticallyBy " + dy);
+        if (!mLayoutEnabled) {
+            return 0;
         }
+        saveContext(recycler, state);
+        int result;
+        if (mOrientation == VERTICAL) {
+            result = scrollDirectionPrimary(dy);
+        } else {
+            result = scrollDirectionSecondary(dy);
+        }
+        leaveContext();
+        return result;
     }
 
     // scroll in main direction may add/prune views
     private int scrollDirectionPrimary(int da) {
+        if (da == 0) {
+            return 0;
+        }
         offsetChildrenPrimary(-da);
         if (mInLayout) {
             return da;
         }
+
+        int childCount = getChildCount();
+        boolean updated;
+
         if (da > 0) {
             appendVisibleItems();
-            removeInvisibleViewsAtFront();
         } else if (da < 0) {
             prependVisibleItems();
+        }
+        updated = getChildCount() > childCount;
+        childCount = getChildCount();
+
+        if (da > 0) {
+            removeInvisibleViewsAtFront();
+        } else if (da < 0) {
             removeInvisibleViewsAtEnd();
         }
-        attemptAnimateLayoutChild();
+        updated |= getChildCount() < childCount;
+
+        if (updated) {
+            updateRowSecondarySizeRefresh();
+        }
+
         mBaseGridView.invalidate();
         return da;
     }
 
     // scroll in second direction will not add/prune views
     private int scrollDirectionSecondary(int dy) {
+        if (dy == 0) {
+            return 0;
+        }
         offsetChildrenSecondary(-dy);
         mBaseGridView.invalidate();
         return dy;
     }
 
     private void updateScrollMax() {
-        if (mLastVisiblePos >= 0 && mLastVisiblePos == mAdapter.getItemCount() - 1) {
+        if (mLastVisiblePos >= 0 && mLastVisiblePos == mState.getItemCount() - 1) {
             int maxEdge = Integer.MIN_VALUE;
             for (int i = 0; i < mRows.length; i++) {
                 if (mRows[i].high > maxEdge) {
@@ -1429,6 +1666,11 @@
         }
     }
 
+    private void updateScrollSecondAxis() {
+        mWindowAlignment.secondAxis().setMinEdge(0);
+        mWindowAlignment.secondAxis().setMaxEdge(getSizeSecondary());
+    }
+
     private void initScrollController() {
         mWindowAlignment.horizontal.setSize(getWidth());
         mWindowAlignment.horizontal.setPadding(getPaddingLeft(), getPaddingRight());
@@ -1438,15 +1680,9 @@
         mWindowAlignment.mainAxis().invalidateScrollMin();
         mWindowAlignment.mainAxis().invalidateScrollMax();
 
-        // second axis min/max is determined at initialization, the mainAxis
-        // min/max is determined later when we scroll to first or last item
-        mWindowAlignment.secondAxis().setMinEdge(0);
-        mWindowAlignment.secondAxis().setMaxEdge(mItemLengthSecondary * mNumRows + mMarginSecondary
-                * (mNumRows - 1));
-
         if (DEBUG) {
-            Log.v(getTag(), "initScrollController mSizePrimary " + mSizePrimary + " "
-                + " mItemLengthSecondary " + mItemLengthSecondary + " " + mWindowAlignment);
+            Log.v(getTag(), "initScrollController mSizePrimary " + mSizePrimary
+                    + " mWindowAlignment " + mWindowAlignment);
         }
     }
 
@@ -1466,24 +1702,58 @@
         if (mFocusPosition == position) {
             return;
         }
-        View view = getViewByPosition(position);
+        View view = findViewByPosition(position);
         if (view != null) {
             scrollToView(view, smooth);
         } else {
-            boolean right = position > mFocusPosition;
             mFocusPosition = position;
+            if (!mLayoutEnabled) {
+                return;
+            }
             if (smooth) {
                 if (!hasDoneFirstLayout()) {
                     Log.w(getTag(), "setSelectionSmooth should " +
                             "not be called before first layout pass");
                     return;
                 }
-                if (right) {
-                    appendVisibleItems();
-                } else {
-                    prependVisibleItems();
-                }
-                scrollToView(getViewByPosition(position), smooth);
+                LinearSmoothScroller linearSmoothScroller =
+                        new LinearSmoothScroller(parent.getContext()) {
+                    @Override
+                    public PointF computeScrollVectorForPosition(int targetPosition) {
+                        if (getChildCount() == 0) {
+                            return null;
+                        }
+                        final int firstChildPos = getPosition(getChildAt(0));
+                        final int direction = targetPosition < firstChildPos ? -1 : 1;
+                        if (mOrientation == HORIZONTAL) {
+                            return new PointF(direction, 0);
+                        } else {
+                            return new PointF(0, direction);
+                        }
+                    }
+                    @Override
+                    protected void onTargetFound(View targetView,
+                            RecyclerView.State state, Action action) {
+                        if (hasFocus()) {
+                            targetView.requestFocus();
+                        }
+                        if (updateScrollPosition(targetView, false, mTempDeltas)) {
+                            int dx, dy;
+                            if (mOrientation == HORIZONTAL) {
+                                dx = mTempDeltas[0];
+                                dy = mTempDeltas[1];
+                            } else {
+                                dx = mTempDeltas[1];
+                                dy = mTempDeltas[0];
+                            }
+                            final int distance = (int) Math.sqrt(dx * dx + dy * dy);
+                            final int time = calculateTimeForDeceleration(distance);
+                            action.update(dx, dy, time, mDecelerateInterpolator);
+                        }
+                    }
+                };
+                linearSmoothScroller.setTargetPosition(position);
+                startSmoothScroll(linearSmoothScroller);
             } else {
                 mForceFullLayout = true;
                 parent.requestLayout();
@@ -1549,7 +1819,9 @@
         int newFocusPosition = getPositionByView(view);
         if (mInLayout || newFocusPosition != mFocusPosition) {
             mFocusPosition = newFocusPosition;
-            dispatchChildSelected();
+            if (mState == null || !mState.isPreLayout()) {
+                dispatchChildSelected();
+            }
         }
         if (mBaseGridView.isChildrenDrawingOrderEnabledInternal()) {
             mBaseGridView.invalidate();
@@ -1562,19 +1834,23 @@
             // by setSelection())
             view.requestFocus();
         }
-        switch (mFocusScrollStrategy) {
-        case BaseGridView.FOCUS_SCROLL_ALIGNED:
-        default:
-            scrollToAlignedPosition(view, smooth);
-            break;
-        case BaseGridView.FOCUS_SCROLL_ITEM:
-        case BaseGridView.FOCUS_SCROLL_PAGE:
-            scrollItemOrPage(view, smooth);
-            break;
+        if (updateScrollPosition(view, mInLayout, mTempDeltas)) {
+            scrollGrid(mTempDeltas[0], mTempDeltas[1], smooth);
         }
     }
 
-    private void scrollItemOrPage(View view, boolean smooth) {
+    private boolean updateScrollPosition(View view, boolean force, int[] deltas) {
+        switch (mFocusScrollStrategy) {
+        case BaseGridView.FOCUS_SCROLL_ALIGNED:
+        default:
+            return updateAlignedPosition(view, force, deltas);
+        case BaseGridView.FOCUS_SCROLL_ITEM:
+        case BaseGridView.FOCUS_SCROLL_PAGE:
+            return updateNoneAlignedPosition(view, deltas);
+        }
+    }
+
+    private boolean updateNoneAlignedPosition(View view, int[] deltas) {
         int pos = getPositionByView(view);
         int viewMin = getViewMin(view);
         int viewMax = getViewMax(view);
@@ -1594,10 +1870,10 @@
                 while (!prependOneVisibleItem()) {
                     List<Integer> positions =
                             mGrid.getItemPositionsInRows(mFirstVisiblePos, pos)[row];
-                    firstView = getViewByPosition(positions.get(0));
+                    firstView = findViewByPosition(positions.get(0));
                     if (viewMax - getViewMin(firstView) > clientSize) {
                         if (positions.size() > 1) {
-                            firstView = getViewByPosition(positions.get(1));
+                            firstView = findViewByPosition(positions.get(1));
                         }
                         break;
                     }
@@ -1611,7 +1887,7 @@
                 do {
                     List<Integer> positions =
                             mGrid.getItemPositionsInRows(pos, mLastVisiblePos)[row];
-                    lastView = getViewByPosition(positions.get(positions.size() - 1));
+                    lastView = findViewByPosition(positions.get(positions.size() - 1));
                     if (getViewMax(lastView) - viewMin > clientSize) {
                         lastView = null;
                         break;
@@ -1645,33 +1921,35 @@
         mWindowAlignment.secondAxis().updateScrollCenter(viewCenterSecondary);
         scrollSecondary = mWindowAlignment.secondAxis().getSystemScrollPos();
         scrollSecondary -= mScrollOffsetSecondary;
-        scrollGrid(scrollPrimary, scrollSecondary, smooth);
+        if (scrollPrimary != 0 || scrollSecondary != 0) {
+            deltas[0] = scrollPrimary;
+            deltas[1] = scrollSecondary;
+            return true;
+        }
+        return false;
     }
 
-    private void scrollToAlignedPosition(View view, boolean smooth) {
+    private boolean updateAlignedPosition(View view, boolean force, int[] deltas) {
         int viewCenterPrimary = mScrollOffsetPrimary + getViewCenter(view);
         int viewCenterSecondary = mScrollOffsetSecondary + getViewCenterSecondary(view);
-        if (DEBUG) {
-            Log.v(getTag(), "scrollAligned smooth=" + smooth + " pos=" + mFocusPosition + " "
-                    + viewCenterPrimary +","+viewCenterSecondary + " " + mWindowAlignment);
-        }
 
-        if (mInLayout || viewCenterPrimary != mWindowAlignment.mainAxis().getScrollCenter()
+        if (force || viewCenterPrimary != mWindowAlignment.mainAxis().getScrollCenter()
                 || viewCenterSecondary != mWindowAlignment.secondAxis().getScrollCenter()) {
             mWindowAlignment.mainAxis().updateScrollCenter(viewCenterPrimary);
             mWindowAlignment.secondAxis().updateScrollCenter(viewCenterSecondary);
             int scrollPrimary = mWindowAlignment.mainAxis().getSystemScrollPos();
             int scrollSecondary = mWindowAlignment.secondAxis().getSystemScrollPos();
             if (DEBUG) {
-                Log.v(getTag(), "scrollAligned " + scrollPrimary + " " + scrollSecondary
+                Log.v(getTag(), "updateAlignedPosition " + scrollPrimary + " " + scrollSecondary
                         +" " + mWindowAlignment);
             }
-
             scrollPrimary -= mScrollOffsetPrimary;
             scrollSecondary -= mScrollOffsetSecondary;
-
-            scrollGrid(scrollPrimary, scrollSecondary, smooth);
+            deltas[0] = scrollPrimary;
+            deltas[1] = scrollSecondary;
+            return true;
         }
+        return false;
     }
 
     private void scrollGrid(int scrollPrimary, int scrollSecondary, boolean smooth) {
@@ -1698,48 +1976,23 @@
 
     public void setAnimateChildLayout(boolean animateChildLayout) {
         mAnimateChildLayout = animateChildLayout;
-        for (int i = 0, c = getChildCount(); i < c; i++) {
-            View v = getChildAt(i);
-            LayoutParams p = (LayoutParams) v.getLayoutParams();
-            if (!mAnimateChildLayout) {
-                p.endAnimate();
-            } else {
-                // record initial location values
-                p.mFirstAttached = true;
-                p.startAnimate(this, v, 0);
-            }
-        }
-    }
-
-    private void attemptAnimateLayoutChild() {
-        if (!mAnimateChildLayout) {
-            return;
-        }
-        for (int i = 0, c = getChildCount(); i < c; i++) {
-            // TODO: start delay can be staggered
-            View v = getChildAt(i);
-            ((LayoutParams) v.getLayoutParams()).startAnimate(this, v, 0);
-        }
     }
 
     public boolean isChildLayoutAnimated() {
         return mAnimateChildLayout;
     }
 
-    public void setChildLayoutAnimationInterpolator(Interpolator interpolator) {
-        mAnimateLayoutChildInterpolator = interpolator;
+    public void setPruneChild(boolean pruneChild) {
+        if (mPruneChild != pruneChild) {
+            mPruneChild = pruneChild;
+            if (mPruneChild) {
+                requestLayout();
+            }
+        }
     }
 
-    public Interpolator getChildLayoutAnimationInterpolator() {
-        return mAnimateLayoutChildInterpolator;
-    }
-
-    public void setChildLayoutAnimationDuration(long duration) {
-        mAnimateLayoutChildDuration = duration;
-    }
-
-    public long getChildLayoutAnimationDuration() {
-        return mAnimateLayoutChildDuration;
+    public boolean getPruneChild() {
+        return mPruneChild;
     }
 
     private int findImmediateChildIndex(View view) {
@@ -1769,6 +2022,40 @@
         return null;
     }
 
+    boolean hasNextViewInSameRow(int pos) {
+        if (mGrid == null || pos == NO_POSITION) {
+            return false;
+        }
+        final int focusedRow = mGrid.getLocation(pos).row;
+        for (int i = 0, count = getChildCount(); i < count; i++) {
+            int position = getPositionByIndex(i);
+            StaggeredGrid.Location loc = mGrid.getLocation(position);
+            if (loc != null && loc.row == focusedRow) {
+                if (position > pos) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean hasPreviousViewInSameRow(int pos) {
+        if (mGrid == null || pos == NO_POSITION) {
+            return false;
+        }
+        final int focusedRow = mGrid.getLocation(pos).row;
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            int position = getPositionByIndex(i);
+            StaggeredGrid.Location loc = mGrid.getLocation(position);
+            if (loc != null && loc.row == focusedRow) {
+                if (position < pos) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     @Override
     public boolean onAddFocusables(RecyclerView recyclerView,
             ArrayList<View> views, int direction, int focusableMode) {
@@ -1792,7 +2079,7 @@
             final int focusedPos = getPositionByIndex(findImmediateChildIndex(focused));
             // Add focusables of focused item.
             if (focusedPos != NO_POSITION) {
-                getViewByPosition(focusedPos).addFocusables(views,  direction, focusableMode);
+                findViewByPosition(focusedPos).addFocusables(views,  direction, focusableMode);
             }
             final int focusedRow = mGrid != null && focusedPos != NO_POSITION ?
                     mGrid.getLocation(focusedPos).row : NO_POSITION;
@@ -1861,6 +2148,7 @@
             RecyclerView.State state) {
         if (DEBUG) Log.v(getTag(), "onFocusSearchFailed direction " + direction);
 
+        saveContext(recycler, state);
         View view = null;
         int movement = getMovement(direction);
         final FocusFinder ff = FocusFinder.getInstance();
@@ -1881,6 +2169,7 @@
                 view = mFocusOutEnd ? null : focused;
             }
         }
+        leaveContext();
         if (DEBUG) Log.v(getTag(), "returning view " + view);
         return view;
     }
@@ -1901,7 +2190,7 @@
 
     private boolean gridOnRequestFocusInDescendantsAligned(RecyclerView recyclerView,
             int direction, Rect previouslyFocusedRect) {
-        View view = getViewByPosition(mFocusPosition);
+        View view = findViewByPosition(mFocusPosition);
         if (view != null) {
             boolean result = view.requestFocus(direction, previouslyFocusedRect);
             if (!result && DEBUG) {
@@ -1987,10 +2276,11 @@
     }
 
     int getChildDrawingOrder(RecyclerView recyclerView, int childCount, int i) {
-        int focusIndex = getIndexByPosition(mFocusPosition);
-        if (focusIndex == NO_POSITION) {
+        View view = findViewByPosition(mFocusPosition);
+        if (view == null) {
             return i;
         }
+        int focusIndex = recyclerView.indexOfChild(view);
         // supposely 0 1 2 3 4 5 6 7 8 9, 4 is the center item
         // drawing order is 0 1 2 3 9 8 7 6 5 4
         if (i < focusIndex) {
@@ -2005,8 +2295,23 @@
     @Override
     public void onAdapterChanged(RecyclerView.Adapter oldAdapter,
             RecyclerView.Adapter newAdapter) {
+        discardLayoutInfo();
+        super.onAdapterChanged(oldAdapter, newAdapter);
+    }
+
+    private void discardLayoutInfo() {
         mGrid = null;
         mRows = null;
-        super.onAdapterChanged(oldAdapter, newAdapter);
+        mRowSizeSecondary = null;
+        mFirstVisiblePos = -1;
+        mLastVisiblePos = -1;
+        mRowSecondarySizeRefresh = false;
+    }
+
+    public void setLayoutEnabled(boolean layoutEnabled) {
+        if (mLayoutEnabled != layoutEnabled) {
+            mLayoutEnabled = layoutEnabled;
+            requestLayout();
+        }
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
index 2024425..c4d6e4f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/HorizontalGridView.java
@@ -27,7 +27,9 @@
 import android.support.v17.leanback.R;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * A view that shows items in a horizontal scrolling list. The items come from
@@ -66,7 +68,7 @@
     protected void initAttributes(Context context, AttributeSet attrs) {
         initBaseGridViewAttributes(context, attrs);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbHorizontalGridView);
-        setRowHeight(a.getDimensionPixelSize(R.styleable.lbHorizontalGridView_rowHeight, 0));
+        setRowHeight(a);
         setNumRows(a.getInt(R.styleable.lbHorizontalGridView_numberOfRows, 1));
         a.recycle();
         setWillNotDraw(false);
@@ -74,8 +76,19 @@
         mTempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
     }
 
+    void setRowHeight(TypedArray array) {
+        TypedValue typedValue = array.peekValue(R.styleable.lbHorizontalGridView_rowHeight);
+        int size;
+        if (typedValue != null && typedValue.type == TypedValue.TYPE_DIMENSION) {
+            size = array.getDimensionPixelSize(R.styleable.lbHorizontalGridView_rowHeight, 0);
+        } else {
+            size = array.getInt(R.styleable.lbHorizontalGridView_rowHeight, 0);
+        }
+        setRowHeight(size);
+    }
+
     /**
-     * Set the number of rows.
+     * Set the number of rows.  Defaults to one.
      */
     public void setNumRows(int numRows) {
         mLayoutManager.setNumRows(numRows);
@@ -84,6 +97,9 @@
 
     /**
      * Set the row height.
+     *
+     * @param height May be WRAP_CONTENT, or a size in pixels. If zero,
+     * row height will be fixed based on number of rows and view height.
      */
     public void setRowHeight(int height) {
         mLayoutManager.setRowHeight(height);
@@ -264,17 +280,18 @@
         if (mTempBitmapHigh == null
                 || mTempBitmapHigh.getWidth() != mHighFadeShaderLength
                 || mTempBitmapHigh.getHeight() != getHeight()) {
-            if (mTempBitmapLow != null
+            // TODO: fix logic for sharing mTempBitmapLow
+            if (false && mTempBitmapLow != null
                     && mTempBitmapLow.getWidth() == mHighFadeShaderLength
                     && mTempBitmapLow.getHeight() == getHeight()) {
                 // share same bitmap for low edge fading and high edge fading.
                 mTempBitmapHigh = mTempBitmapLow;
             } else {
-                mTempBitmapLow = Bitmap.createBitmap(mHighFadeShaderLength, getHeight(),
+                mTempBitmapHigh = Bitmap.createBitmap(mHighFadeShaderLength, getHeight(),
                         Bitmap.Config.ARGB_8888);
             }
         }
-        return mTempBitmapLow;
+        return mTempBitmapHigh;
     }
 
     @Override
@@ -298,8 +315,8 @@
 
         // draw not-fade content
         int save = canvas.save();
-        canvas.clipRect(lowEdge + mLowFadeShaderLength, 0,
-                highEdge - mHighFadeShaderLength, getHeight());
+        canvas.clipRect(lowEdge + (mFadingLowEdge ? mLowFadeShaderLength : 0), 0,
+                highEdge - (mFadingHighEdge ? mHighFadeShaderLength : 0), getHeight());
         super.draw(canvas);
         canvas.restoreToCount(save);
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
index cefb431..3cc3ddf 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
@@ -36,6 +36,7 @@
         private int mOffset = 0;
         private float mOffsetPercent = 50;
         private int mViewId = 0;
+        private boolean mOffsetWithPadding = false;
         private Rect mRect = new Rect();
 
         Axis(int orientation) {
@@ -50,6 +51,14 @@
             return mOffset;
         }
 
+        public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
+            mOffsetWithPadding = withPadding;
+        }
+
+        public boolean isItemAlignmentOffsetWithPadding() {
+            return mOffsetWithPadding;
+        }
+
         public void setItemAlignmentOffsetPercent(float percent) {
             if ( (percent < 0 || percent > 100) &&
                     percent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
@@ -70,8 +79,10 @@
             return mViewId;
         }
 
+        /**
+         * get alignment position relative to optical left/top of itemView.
+         */
         public int getAlignmentPosition(View itemView) {
-
             LayoutParams p = (LayoutParams) itemView.getLayoutParams();
             View view = itemView;
             if (mViewId != 0) {
@@ -84,30 +95,46 @@
             if (mOrientation == HORIZONTAL) {
                 if (mOffset >= 0) {
                     alignPos = mOffset;
+                    if (mOffsetWithPadding) {
+                        alignPos += view.getPaddingLeft();
+                    }
                 } else {
-                    alignPos = p.getOpticalWidth(itemView) + mOffset;
+                    alignPos = view == itemView ? p.getOpticalWidth(view) : view.getWidth()
+                            + mOffset;
+                    if (mOffsetWithPadding) {
+                        alignPos -= view.getPaddingRight();
+                    }
                 }
                 if (mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
-                    alignPos += (p.getOpticalWidth(itemView) * mOffsetPercent) / 100;
+                    alignPos += ((view == itemView ? p.getOpticalWidth(view) : view.getWidth())
+                            * mOffsetPercent) / 100f;
                 }
                 if (itemView != view) {
                     mRect.left = alignPos;
                     ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, mRect);
-                    alignPos = mRect.left;
+                    alignPos = mRect.left - p.getOpticalLeftInset();
                 }
             } else {
                 if (mOffset >= 0) {
                     alignPos = mOffset;
+                    if (mOffsetWithPadding) {
+                        alignPos += view.getPaddingTop();
+                    }
                 } else {
-                    alignPos = p.getOpticalHeight(itemView) + mOffset;
+                    alignPos = view == itemView ? p.getOpticalHeight(view) : view.getHeight()
+                            + mOffset;
+                    if (mOffsetWithPadding) {
+                        alignPos += view.getPaddingBottom();
+                    }
                 }
                 if (mOffsetPercent != ITEM_ALIGN_OFFSET_PERCENT_DISABLED) {
-                    alignPos += (p.getOpticalHeight(itemView) * mOffsetPercent) / 100;
+                    alignPos += ((view == itemView ? p.getOpticalHeight(view) : view.getHeight())
+                            * mOffsetPercent) / 100f;
                 }
                 if (itemView != view) {
                     mRect.top = alignPos;
                     ((ViewGroup) itemView).offsetDescendantRectToMyCoords(view, mRect);
-                    alignPos = mRect.top;
+                    alignPos = mRect.top - p.getOpticalTopInset();
                 }
             }
             return alignPos;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
index 0de39ab..93fdd9e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -59,12 +59,20 @@
         final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter();
         final HorizontalHoverCardSwitcher mHoverCardViewSwitcher = new HorizontalHoverCardSwitcher();
         final ColorOverlayDimmer mColorDimmer;
+        final int mPaddingTop;
+        final int mPaddingBottom;
+        final int mPaddingLeft;
+        final int mPaddingRight;
 
         public ViewHolder(View rootView, HorizontalGridView gridView, ListRowPresenter p) {
             super(rootView);
             mGridView = gridView;
             mListRowPresenter = p;
             mColorDimmer = ColorOverlayDimmer.createDefault(rootView.getContext());
+            mPaddingTop = mGridView.getPaddingTop();
+            mPaddingBottom = mGridView.getPaddingBottom();
+            mPaddingLeft = mGridView.getPaddingLeft();
+            mPaddingRight = mGridView.getPaddingRight();
         }
 
         public final ListRowPresenter getListRowPresenter() {
@@ -76,6 +84,8 @@
         }
     }
 
+    private int mRowHeight;
+    private int mExpandedRowHeight;
     private PresenterSelector mHoverCardPresenterSelector;
     private int mZoomFactor;
     private boolean mShadowEnabled = true;
@@ -103,6 +113,43 @@
     }
 
     /**
+     * Sets the row height for rows created by this Presenter. Rows
+     * created before calling this method will not be updated.
+     *
+     * @param rowHeight Row height in pixels, or WRAP_CONTENT, or 0
+     * to use the default height.
+     */
+    public void setRowHeight(int rowHeight) {
+        mRowHeight = rowHeight;
+    }
+
+    /**
+     * Returns the row height for list rows created by this Presenter.
+     */
+    public int getRowHeight() {
+        return mRowHeight;
+    }
+
+    /**
+     * Sets the expanded row height for rows created by this Presenter.
+     * If not set, expanded rows have the same height as unexpanded
+     * rows.
+     *
+     * @param rowHeight The row height in to use when the row is expanded,
+     *        in pixels, or WRAP_CONTENT, or 0 to use the default.
+     */
+    public void setExpandedRowHeight(int rowHeight) {
+        mExpandedRowHeight = rowHeight;
+    }
+
+    /**
+     * Returns the expanded row height for rows created by this Presenter.
+     */
+    public int getExpandedRowHeight() {
+        return mExpandedRowHeight != 0 ? mExpandedRowHeight : mRowHeight;
+    }
+
+    /**
      * Returns the zoom factor used for focus highlighting.
      */
     public final int getZoomFactor() {
@@ -172,6 +219,7 @@
                     int dimmedColor = rowViewHolder.mColorDimmer.getPaint().getColor();
                     ((ShadowOverlayContainer) viewHolder.itemView).setOverlayColor(dimmedColor);
                 }
+                viewHolder.itemView.setActivated(rowViewHolder.mExpanded);
             }
         });
     }
@@ -225,11 +273,15 @@
     protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
         ListRowView rowView = new ListRowView(parent.getContext());
         setupFadingEffect(rowView);
+        if (mRowHeight != 0) {
+            rowView.getGridView().setRowHeight(mRowHeight);
+        }
         return new ViewHolder(rowView, rowView.getGridView(), this);
     }
 
     @Override
     protected void onRowViewSelected(RowPresenter.ViewHolder holder, boolean selected) {
+        super.onRowViewSelected(holder, selected);
         updateFooterViewSwitcher((ViewHolder) holder);
     }
 
@@ -248,7 +300,7 @@
             selectChildView(vh, ibh == null ? null : ibh.itemView);
         } else {
             if (mHoverCardPresenterSelector != null) {
-                vh.mHoverCardViewSwitcher.clear();
+                vh.mHoverCardViewSwitcher.unselect();
             }
         }
     }
@@ -270,6 +322,17 @@
     protected void onRowViewExpanded(RowPresenter.ViewHolder holder, boolean expanded) {
         super.onRowViewExpanded(holder, expanded);
         ViewHolder vh = (ViewHolder) holder;
+        if (getRowHeight() != getExpandedRowHeight()) {
+            int newHeight = expanded ? getExpandedRowHeight() : getRowHeight();
+            vh.getGridView().setRowHeight(newHeight);
+        }
+        if (expanded) {
+            vh.getGridView().setPadding(vh.mPaddingLeft, vh.mPaddingTop,
+                    vh.mPaddingRight, vh.mPaddingBottom);
+        } else {
+            vh.getGridView().setPadding(vh.mPaddingLeft, vh.mPaddingTop,
+                    vh.mPaddingRight, 0);
+        }
         vh.getGridView().setFadingLeftEdge(!expanded);
         updateFooterViewSwitcher(vh);
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java
index 946c69d..dce91d0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/OnItemSelectedListener.java
@@ -21,7 +21,7 @@
      * Called when the a row or a new item becomes selected.  The concept of current selection
      * is different than focus.  Row or item can be selected even they don't have focus.
      * Having the concept of selection will allow developer to switch background to selected
-     * item or selected row when user selects rows outside row UI (e.g. a fast lane next to
+     * item or selected row when user selects rows outside row UI (e.g. headers left of
      * rows).
      * <p>
      * For a none {@link ListRow} case,  parameter item is always null.  Event is fired when
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java b/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
index 05dff40..4fbcb6f 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowContainerView.java
@@ -62,4 +62,7 @@
         addView(view);
     }
 
+    public void showHeader(boolean show) {
+        mHeaderDock.setVisibility(show ? View.VISIBLE : View.GONE);
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
index b31d5f0..64f46ad 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowHeaderPresenter.java
@@ -13,7 +13,8 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.graphics.ColorOverlayDimmer;
+import android.support.v17.leanback.R;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -24,10 +25,24 @@
  */
 public class RowHeaderPresenter extends Presenter {
 
+    private final int mLayoutResourceId;
+
+    public RowHeaderPresenter() {
+        this(R.layout.lb_row_header);
+    }
+
+    /**
+     * @hide
+     */
+    public RowHeaderPresenter(int layoutResourceId) {
+        mLayoutResourceId = layoutResourceId;
+    }
+
     public static class ViewHolder extends Presenter.ViewHolder {
         float mSelectLevel;
         int mOriginalTextColor;
-        ColorOverlayDimmer mColorDimmer;
+        float mUnselectAlpha;
+
         public ViewHolder(View view) {
             super(view);
         }
@@ -38,9 +53,13 @@
 
     @Override
     public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
-        RowHeaderView headerView = new RowHeaderView(parent.getContext());
+        RowHeaderView headerView = (RowHeaderView) LayoutInflater.from(parent.getContext())
+                .inflate(mLayoutResourceId, parent, false);
+
         ViewHolder viewHolder = new ViewHolder(headerView);
         viewHolder.mOriginalTextColor = headerView.getCurrentTextColor();
+        viewHolder.mUnselectAlpha = parent.getResources().getFraction(
+                R.fraction.lb_browse_header_unselect_alpha, 1, 1);
         return viewHolder;
     }
 
@@ -68,11 +87,7 @@
     }
 
     protected void onSelectLevelChanged(ViewHolder holder) {
-        if (holder.mColorDimmer == null) {
-            holder.mColorDimmer = ColorOverlayDimmer.createDefault(holder.view.getContext());
-        }
-        holder.mColorDimmer.setActiveLevel(holder.mSelectLevel);
-        final RowHeaderView headerView = (RowHeaderView) holder.view;
-        headerView.setTextColor(holder.mColorDimmer.applyToColor(holder.mOriginalTextColor));
+        holder.view.setAlpha(holder.mUnselectAlpha + holder.mSelectLevel *
+                (1f - holder.mUnselectAlpha));
     }
 }
\ No newline at end of file
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
index 787597f..5c8b6ff 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RowPresenter.java
@@ -210,15 +210,8 @@
      * animation on the row view.
      */
     protected void onRowViewExpanded(ViewHolder vh, boolean expanded) {
-        if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) {
-            RowContainerView containerView = ((RowContainerView) vh.mContainerViewHolder.view);
-            View headerView = vh.mHeaderViewHolder.view;
-            if (expanded) {
-                containerView.addHeaderView(headerView);
-            } else {
-                containerView.removeHeaderView(headerView);
-            }
-        }
+        updateHeaderViewVisibility(vh);
+        vh.view.setActivated(expanded);
     }
 
     /**
@@ -230,6 +223,14 @@
         if (selected && mOnItemSelectedListener != null) {
             mOnItemSelectedListener.onItemSelected(null, vh.getRow());
         }
+        updateHeaderViewVisibility(vh);
+    }
+
+    private void updateHeaderViewVisibility(ViewHolder vh) {
+        if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) {
+            RowContainerView containerView = ((RowContainerView) vh.mContainerViewHolder.view);
+            containerView.showHeader(vh.isExpanded());
+        }
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
index 029db3e..ae717ab 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchBar.java
@@ -14,20 +14,37 @@
 package android.support.v17.leanback.widget;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.speech.RecognitionListener;
+import android.speech.RecognizerIntent;
+import android.speech.SpeechRecognizer;
 import android.text.Editable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.widget.ImageView;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.RelativeLayout;
 import android.support.v17.leanback.R;
 import android.widget.TextView;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * SearchBar is a search widget.
  */
@@ -35,6 +52,9 @@
     private static final String TAG = SearchBar.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    private SpeechRecognizer mSpeechRecognizer;
+    private boolean mListening;
+
     /**
      * Listener for search query changes
      */
@@ -64,8 +84,21 @@
 
     private SearchBarListener mSearchBarListener;
     private SearchEditText mSearchTextEditor;
+    private SpeechOrbView mSpeechOrbView;
+    private ImageView mBadgeView;
     private String mSearchQuery;
+    private String mTitle;
+    private Drawable mBadgeDrawable;
     private final Handler mHandler = new Handler();
+    private final InputMethodManager mInputMethodManager;
+    private boolean mAutoStartRecognition = false;
+    private Drawable mBarBackground;
+
+    private int mTextColor;
+    private int mTextSpeechColor;
+    private int mBackgroundAlpha;
+    private int mBackgroundSpeechAlpha;
+    private int mBarHeight;
 
     public SearchBar(Context context) {
         this(context, null);
@@ -77,21 +110,53 @@
 
     public SearchBar(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        Resources r = getResources();
+
+        LayoutInflater inflater = LayoutInflater.from(getContext());
+        inflater.inflate(R.layout.lb_search_bar, this, true);
+
+        mBarHeight = getResources().getDimensionPixelSize(R.dimen.lb_search_bar_height);
+        RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                mBarHeight);
+        params.addRule(ALIGN_PARENT_TOP, RelativeLayout.TRUE);
+        setLayoutParams(params);
+        setBackgroundColor(Color.TRANSPARENT);
+        setClipChildren(false);
+
         mSearchQuery = "";
+        mInputMethodManager =
+                (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context);
+
+
+        mTextSpeechColor = r.getColor(R.color.lb_search_bar_text_speech_color);
+        mBackgroundSpeechAlpha = r.getInteger(R.integer.lb_search_bar_speech_mode_background_alpha);
+
+        mTextColor = r.getColor(R.color.lb_search_bar_text_color);
+        mBackgroundAlpha = r.getInteger(R.integer.lb_search_bar_text_mode_background_alpha);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        RelativeLayout items = (RelativeLayout)findViewById(R.id.lb_search_bar_items);
+        mBarBackground = items.getBackground();
+
         mSearchTextEditor = (SearchEditText)findViewById(R.id.lb_search_text_editor);
+        mBadgeView = (ImageView)findViewById(R.id.lb_search_bar_badge);
+        if (null != mBadgeDrawable) {
+            mBadgeView.setImageDrawable(mBadgeDrawable);
+        }
+
         mSearchTextEditor.setOnFocusChangeListener(new OnFocusChangeListener() {
             @Override
             public void onFocusChange(View view, boolean hasFocus) {
-                if (DEBUG) Log.v(TAG, "onFocusChange " + hasFocus);
+                if (DEBUG) Log.v(TAG, "EditText.onFocusChange " + hasFocus);
                 if (hasFocus) {
                     showNativeKeyboard();
                 }
+                updateUi();
             }
         });
         mSearchTextEditor.addTextChangedListener(new TextWatcher() {
@@ -102,7 +167,9 @@
 
             @Override
             public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
-                setSearchQuery(charSequence.toString());
+                if (mSearchTextEditor.hasFocus()) {
+                    setSearchQuery(charSequence.toString());
+                }
             }
 
             @Override
@@ -119,16 +186,87 @@
                         }
                     }
                 });
+
         mSearchTextEditor.setOnEditorActionListener(new TextView.OnEditorActionListener() {
             @Override
             public boolean onEditorAction(TextView textView, int action, KeyEvent keyEvent) {
+                if (DEBUG) Log.v(TAG, "onEditorAction: " + action + " event: " + keyEvent);
+                boolean handled = true;
                 if (EditorInfo.IME_ACTION_SEARCH == action && null != mSearchBarListener) {
-                    mSearchBarListener.onSearchQuerySubmit(mSearchQuery);
-                    return true;
+                    if (DEBUG) Log.v(TAG, "Action Pressed");
+                    hideNativeKeyboard();
+                    mHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (DEBUG) Log.v(TAG, "Delayed action handling (search)");
+                            submitQuery();
+                        }
+                    }, 500);
+
+                } else if (EditorInfo.IME_ACTION_NONE == action && null != mSearchBarListener) {
+                    if (DEBUG) Log.v(TAG, "Escaped North");
+                    hideNativeKeyboard();
+                    mHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (DEBUG) Log.v(TAG, "Delayed action handling (escape_north)");
+                            mSearchBarListener.onKeyboardDismiss(mSearchQuery);
+                        }
+                    }, 500);
+                } else if (EditorInfo.IME_ACTION_GO == action) {
+                    if (DEBUG) Log.v(TAG, "Voice Clicked");
+                        hideNativeKeyboard();
+                        mHandler.postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (DEBUG) Log.v(TAG, "Delayed action handling (voice_mode)");
+                                mAutoStartRecognition = true;
+                                mSpeechOrbView.requestFocus();
+                            }
+                        }, 500);
+                } else {
+                    handled = false;
                 }
-                return false;
+
+                return handled;
             }
         });
+
+        mSearchTextEditor.setPrivateImeOptions("EscapeNorth=1;VoiceDismiss=1;");
+
+        mSpeechOrbView = (SpeechOrbView)findViewById(R.id.lb_search_bar_speech_orb);
+        mSpeechOrbView.setOnOrbClickedListener(new OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startRecognition();
+            }
+        });
+        mSpeechOrbView.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View view, boolean hasFocus) {
+                if (DEBUG) Log.v(TAG, "SpeechOrb.onFocusChange " + hasFocus);
+                if (hasFocus) {
+                    hideNativeKeyboard();
+                    if (mAutoStartRecognition) {
+                        startRecognition();
+                        mAutoStartRecognition = false;
+                    }
+                } else {
+                    stopRecognition();
+                }
+                updateUi();
+            }
+        });
+
+        updateHint();
+        // Start in voice mode
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mAutoStartRecognition = true;
+                mSpeechOrbView.requestFocus();
+            }
+        }, 200);
     }
 
     @Override
@@ -166,14 +304,50 @@
     }
 
     /**
-     * Set the hint text shown in the search bar.
-     * @param hint The hint to use.
+     * Set the title text used in the hint shown in the search bar.
+     * @param title The hint to use.
      */
-    public void setHint(String hint) {
-        mSearchTextEditor.setHint(hint);
+    public void setTitle(String title) {
+        mTitle = title;
+        updateHint();
     }
 
-    protected void showNativeKeyboard() {
+    /**
+     * Returns the current title
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Set the badge drawable showing inside the search bar.
+     * @param drawable The drawable to be used in the search bar.
+     */
+    public void setBadgeDrawable(Drawable drawable) {
+        mBadgeDrawable = drawable;
+        if (null != mBadgeView) {
+            mBadgeView.setImageDrawable(drawable);
+            if (null != drawable) {
+                mBadgeView.setVisibility(View.VISIBLE);
+            } else {
+                mBadgeView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    /**
+     * Returns the badge drawable
+     */
+    public Drawable getBadgeDrawable() {
+        return mBadgeDrawable;
+    }
+
+    private void hideNativeKeyboard() {
+        mInputMethodManager.hideSoftInputFromWindow(mSearchTextEditor.getWindowToken(),
+                InputMethodManager.RESULT_UNCHANGED_SHOWN);
+    }
+
+    private void showNativeKeyboard() {
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -188,4 +362,162 @@
         });
     }
 
+    /**
+     * This will update the hint for the search bar properly depending on state and provided title
+     */
+    private void updateHint() {
+        if (null == mSearchTextEditor) return;
+
+        String title = getResources().getString(R.string.lb_search_bar_hint);
+        if (!TextUtils.isEmpty(mTitle)) {
+            if (isVoiceMode()) {
+                title = getResources().getString(R.string.lb_search_bar_hint_with_title_speech, mTitle);
+            } else {
+                title = getResources().getString(R.string.lb_search_bar_hint_with_title, mTitle);
+            }
+        } else if (isVoiceMode()) {
+            title = getResources().getString(R.string.lb_search_bar_hint_speech);
+        }
+        mSearchTextEditor.setHint(title);
+    }
+
+    private void stopRecognition() {
+        if (DEBUG) Log.v(TAG, "stopRecognition " + mListening);
+        mSpeechOrbView.showNotListening();
+
+        if (mListening) {
+            mSpeechRecognizer.cancel();
+        }
+    }
+
+    private void startRecognition() {
+        if (DEBUG) Log.v(TAG, "startRecognition " + mListening);
+
+        mSearchTextEditor.setText("");
+
+        Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+
+        recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
+        recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
+
+        mSpeechRecognizer.setRecognitionListener(new RecognitionListener() {
+            @Override
+            public void onReadyForSpeech(Bundle bundle) {
+                if (DEBUG) Log.v(TAG, "onReadyForSpeech");
+            }
+
+            @Override
+            public void onBeginningOfSpeech() {
+                if (DEBUG) Log.v(TAG, "onBeginningOfSpeech");
+                mListening = true;
+            }
+
+            @Override
+            public void onRmsChanged(float rmsdB) {
+                if (DEBUG) Log.v(TAG, "onRmsChanged " + rmsdB);
+                int level = rmsdB < 0 ? 0 : (int)(10 * rmsdB);
+                mSpeechOrbView.setSoundLevel(level);
+            }
+
+            @Override
+            public void onBufferReceived(byte[] bytes) {
+                if (DEBUG) Log.v(TAG, "onBufferReceived " + bytes.length);
+            }
+
+            @Override
+            public void onEndOfSpeech() {
+                if (DEBUG) Log.v(TAG, "onEndOfSpeech");
+                mListening = false;
+            }
+
+            @Override
+            public void onError(int error) {
+                if (DEBUG) Log.v(TAG, "onError " + error);
+                switch (error) {
+                    case SpeechRecognizer.ERROR_NO_MATCH:
+                        Log.d(TAG, "recognizer error no match");
+                        break;
+                    case SpeechRecognizer.ERROR_SERVER:
+                        Log.d(TAG, "recognizer error server error");
+                        break;
+                    case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
+                        Log.d(TAG, "recognizer error speech timeout");
+                        break;
+                    case SpeechRecognizer.ERROR_CLIENT:
+                        Log.d(TAG, "recognizer error client error");
+                        break;
+                    default:
+                        Log.d(TAG, "recognizer other error");
+                        break;
+                }
+
+                mSpeechRecognizer.stopListening();
+                mListening = false;
+                mSpeechRecognizer.setRecognitionListener(null);
+                mSpeechOrbView.showNotListening();
+            }
+
+            @Override
+            public void onResults(Bundle bundle) {
+                if (DEBUG) Log.v(TAG, "onResults");
+                final ArrayList<String> matches =
+                        bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
+                if (matches != null) {
+                    Log.v(TAG, "Got results" + matches);
+
+                    mSearchQuery = matches.get(0);
+                    mSearchTextEditor.setText(mSearchQuery);
+                    submitQuery();
+
+                    if (mListening) {
+                        mSpeechRecognizer.stopListening();
+                    }
+                }
+                mSpeechRecognizer.setRecognitionListener(null);
+                mSpeechOrbView.showNotListening();
+            }
+
+            @Override
+            public void onPartialResults(Bundle bundle) {
+
+            }
+
+            @Override
+            public void onEvent(int i, Bundle bundle) {
+
+            }
+        });
+
+        mSpeechOrbView.showListening();
+        mSpeechRecognizer.startListening(recognizerIntent);
+        mListening = true;
+    }
+
+    private void updateUi() {
+        if (DEBUG) Log.v(TAG, String.format("Update UI %s %s",
+                isVoiceMode() ? "Voice" : "Text",
+                hasFocus() ? "Focused" : "Unfocused"));
+        if (isVoiceMode()) {
+            mBarBackground.setAlpha(mBackgroundSpeechAlpha);
+            mSearchTextEditor.setTextColor(mTextSpeechColor);
+        } else {
+            mBarBackground.setAlpha(mBackgroundAlpha);
+            mSearchTextEditor.setTextColor(mTextColor);
+        }
+
+        updateHint();
+    }
+
+    private boolean isVoiceMode() {
+        return mSpeechOrbView.isFocused();
+    }
+
+    private void submitQuery() {
+        if (!TextUtils.isEmpty(mSearchQuery) && null != mSearchBarListener) {
+            mSearchBarListener.onSearchQuerySubmit(mSearchQuery);
+        }
+    }
+
+
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
index 41353c9..c421b6a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
@@ -50,7 +50,7 @@
         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
             if (DEBUG) Log.v(TAG, "Keyboard being dismissed");
             mKeyboardDismissListener.onKeyboardDismiss();
-            return true;
+            return false;
         }
         return super.onKeyPreIme(keyCode, event);
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java
index 1fb1c93..7b8c62d 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchOrbView.java
@@ -16,22 +16,38 @@
 
 package android.support.v17.leanback.widget;
 
-
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
 import android.support.v17.leanback.R;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
 
-public class SearchOrbView extends LinearLayout implements View.OnClickListener {
-    private final static String TAG = SearchOrbView.class.getSimpleName();
-    private final static boolean DEBUG = false;
-
+public class SearchOrbView extends FrameLayout implements View.OnClickListener {
     private OnClickListener mListener;
-    private LinearLayout mSearchOrbView;
+    private View mSearchOrbView;
+    private int mSearchOrbColor, mSearchOrbColorBright;
+    private final float mFocusedZoom;
+    private final float mBrightnessAlpha;
+    private final int mPulseDurationMs;
+    private final int mScaleDownDurationMs;
+    private ValueAnimator mColorAnimator;
+
+    private final ArgbEvaluator mColorEvaluator = new ArgbEvaluator();
+
+    private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+        @Override
+        public void onAnimationUpdate(ValueAnimator animator) {
+            Integer color = (Integer) animator.getAnimatedValue();
+            setOrbViewColor(color.intValue());
+        }
+    };
 
     public SearchOrbView(Context context) {
         this(context, null);
@@ -46,12 +62,22 @@
 
         LayoutInflater inflater = (LayoutInflater) context
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mSearchOrbView = (LinearLayout) inflater.inflate(R.layout.lb_search_orb, this, true);
+        View root = inflater.inflate(R.layout.lb_search_orb, this, true);
+        mSearchOrbView = root.findViewById(R.id.search_orb);
 
         // By default we are not visible
         setVisibility(INVISIBLE);
         setFocusable(true);
-        mSearchOrbView.setAlpha(0.5f);
+        setClipChildren(false);
+
+        mFocusedZoom = context.getResources().getFraction(
+                R.fraction.lb_search_orb_focused_zoom, 1, 1);
+        mBrightnessAlpha = context.getResources().getFraction(
+                R.fraction.lb_search_orb_brightness_alpha, 1, 1);
+        mPulseDurationMs = context.getResources().getInteger(
+                R.integer.lb_search_orb_pulse_duration_ms);
+        mScaleDownDurationMs = context.getResources().getInteger(
+                R.integer.lb_search_orb_scale_down_duration_ms);
 
         setOnClickListener(this);
     }
@@ -66,11 +92,31 @@
     @Override
     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        if (DEBUG) Log.v(TAG, "onFocusChanged " + gainFocus + " " + direction);
-        if (gainFocus) {
-            mSearchOrbView.setAlpha(1.0f);
-        } else {
-            mSearchOrbView.setAlpha(0.5f);
+        final float zoom = gainFocus ? mFocusedZoom : 1f;
+        final int duration = gainFocus ? mPulseDurationMs : mScaleDownDurationMs;
+        mSearchOrbView.animate().scaleX(zoom).scaleY(zoom).setDuration(duration).start();
+        enableOrbColorAnimation(gainFocus);
+    }
+
+    private void enableOrbColorAnimation(boolean enable) {
+        if (mColorAnimator != null) {
+            mColorAnimator.end();
+            mColorAnimator = null;
+        }
+        if (enable) {
+            // TODO: set interpolator (quantum if available)
+            mColorAnimator = ValueAnimator.ofObject(mColorEvaluator,
+                    mSearchOrbColor, mSearchOrbColorBright, mSearchOrbColor);
+            mColorAnimator.setRepeatCount(ValueAnimator.INFINITE);
+            mColorAnimator.setDuration(mPulseDurationMs * 2);
+            mColorAnimator.addUpdateListener(mUpdateListener);
+            mColorAnimator.start();
+        }
+    }
+
+    private void setOrbViewColor(int color) {
+        if (mSearchOrbView.getBackground() instanceof GradientDrawable) {
+            ((GradientDrawable) mSearchOrbView.getBackground()).setColor(color);
         }
     }
 
@@ -87,4 +133,23 @@
         }
     }
 
+    private int getBrightColor(int color) {
+        final float brightnessValue = 0xff * mBrightnessAlpha;
+        int red = (int)(Color.red(color) * (1 - mBrightnessAlpha) + brightnessValue);
+        int green = (int)(Color.green(color) * (1 - mBrightnessAlpha) + brightnessValue);
+        int blue = (int)(Color.blue(color) * (1 - mBrightnessAlpha) + brightnessValue);
+        int alpha = (int)(Color.alpha(color) * (1 - mBrightnessAlpha) + brightnessValue);
+        return Color.argb(alpha, red, green, blue);
+    }
+
+    public void setOrbColor(int color) {
+        mSearchOrbColor = color;
+        mSearchOrbColorBright = getBrightColor(color);
+
+        if (mColorAnimator == null) {
+            setOrbViewColor(color);
+        } else {
+            enableOrbColorAnimation(true);
+        }
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
index 00ed8d4..898758e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ShadowHelper.java
@@ -86,10 +86,36 @@
     }
 
     /**
+     * Implementation used on api 21 (and above).
+     */
+    private static final class ShadowHelperApi21Impl implements ShadowHelperVersionImpl {
+
+        @Override
+        public void prepareParent(ViewGroup parent) {
+            // do nothing
+        }
+
+        @Override
+        public Object addShadow(ViewGroup shadowContainer) {
+            return ShadowHelperApi21.addShadow(shadowContainer);
+        }
+
+        @Override
+        public void setShadowFocusLevel(Object impl, float level) {
+            ShadowHelperApi21.setShadowFocusLevel(impl, level);
+        }
+
+    }
+
+    /**
      * Returns the ShadowHelper.
      */
     private ShadowHelper() {
-        if (Build.VERSION.SDK_INT >= 18) {
+     // TODO: we should use version number once "L" is published
+        if ("L".equals(Build.VERSION.RELEASE)) {
+            mSupportsShadow = true;
+            mImpl = new ShadowHelperApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 18) {
             mSupportsShadow = true;
             mImpl = new ShadowHelperJbmr2Impl();
         } else {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java b/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java
new file mode 100644
index 0000000..46165d5
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SpeechOrbView.java
@@ -0,0 +1,121 @@
+package android.support.v17.leanback.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.support.v17.leanback.R;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+public class SpeechOrbView extends FrameLayout implements View.OnClickListener {
+    private OnClickListener mListener;
+    private View mSpeechOrbView;
+    private final float mFocusedZoom;
+    private final float mSoundLevelMaxZoom;
+    private final int mNotRecordingColor;
+    private final int mRecordingColor;
+    private ImageView mIcon;
+
+    private int mCurrentLevel = 0;
+    private boolean mListening = false;
+
+    public SpeechOrbView(Context context) {
+        this(context, null);
+    }
+
+    public SpeechOrbView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SpeechOrbView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View root = inflater.inflate(R.layout.lb_speech_orb, this, true);
+        mSpeechOrbView = root.findViewById(R.id.lb_speech_orb);
+        mIcon = (ImageView)root.findViewById(R.id.lb_speech_icon);
+
+        setFocusable(true);
+        setClipChildren(false);
+
+        Resources resources = context.getResources();
+        mFocusedZoom =
+                resources.getFraction(R.fraction.lb_search_bar_speech_orb_focused_zoom, 1, 1);
+        mSoundLevelMaxZoom =
+                resources.getFraction(R.fraction.lb_search_bar_speech_orb_max_level_zoom, 1, 1);
+        mNotRecordingColor = resources.getColor(R.color.lb_speech_orb_not_recording);
+        mRecordingColor = resources.getColor(R.color.lb_speech_orb_recording);
+
+        setOnClickListener(this);
+        showNotListening();
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (null != mListener) {
+            mListener.onClick(view);
+        }
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        final float zoom = gainFocus ? mFocusedZoom : 1f;
+        mSpeechOrbView.animate().scaleX(zoom).scaleY(zoom).setDuration(200).start();
+        if (gainFocus) {
+            mIcon.setImageResource(R.drawable.lb_ic_search_mic);
+        } else {
+            mIcon.setImageResource(R.drawable.lb_ic_search_mic_out);
+        }
+    }
+
+    /**
+     * Set the on click listener for the orb
+     * @param listener The listener.
+     */
+    public void setOnOrbClickedListener(OnClickListener listener) {
+        mListener = listener;
+    }
+
+    public void showListening() {
+        setOrbColor(mRecordingColor);
+        mSpeechOrbView.setScaleX(1f);
+        mSpeechOrbView.setScaleY(1f);
+        mListening = true;
+    }
+
+    public void showNotListening() {
+        setOrbColor(mNotRecordingColor);
+        mSpeechOrbView.setScaleX(1f);
+        mSpeechOrbView.setScaleY(1f);
+        mListening = false;
+    }
+
+    public void setSoundLevel(int level) {
+        if (!mListening) return;
+
+        // Either ease towards the target level, or decay away from it depending on whether
+        // its higher or lower than the current.
+        if (level > mCurrentLevel) {
+            mCurrentLevel = mCurrentLevel + ((level - mCurrentLevel) / 4);
+        } else {
+            mCurrentLevel = (int) (mCurrentLevel * 0.95f);
+        }
+
+        float zoom = mFocusedZoom + ((mSoundLevelMaxZoom - mFocusedZoom) * mCurrentLevel) / 100;
+        mSpeechOrbView.setScaleX(zoom);
+        mSpeechOrbView.setScaleY(zoom);
+    }
+
+    public void setOrbColor(int color) {
+        if (mSpeechOrbView.getBackground() instanceof GradientDrawable) {
+            ((GradientDrawable) mSpeechOrbView.getBackground()).setColor(color);
+        }
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java
index 0b3f453..5b10d41 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridView.java
@@ -18,6 +18,8 @@
 import android.support.v17.leanback.R;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ViewGroup;
 
 /**
  * A view that shows items in a vertically scrolling list. The items come from
@@ -42,13 +44,24 @@
     protected void initAttributes(Context context, AttributeSet attrs) {
         initBaseGridViewAttributes(context, attrs);
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbVerticalGridView);
-        setColumnWidth(a.getDimensionPixelSize(R.styleable.lbVerticalGridView_columnWidth, 0));
+        setColumnWidth(a);
         setNumColumns(a.getInt(R.styleable.lbVerticalGridView_numberOfColumns, 1));
         a.recycle();
     }
 
+    void setColumnWidth(TypedArray array) {
+        TypedValue typedValue = array.peekValue(R.styleable.lbVerticalGridView_columnWidth);
+        int size;
+        if (typedValue != null && typedValue.type == TypedValue.TYPE_DIMENSION) {
+            size = array.getDimensionPixelSize(R.styleable.lbVerticalGridView_columnWidth, 0);
+        } else {
+            size = array.getInt(R.styleable.lbVerticalGridView_columnWidth, 0);
+        }
+        setColumnWidth(size);
+    }
+
     /**
-     * Set the number of columns.
+     * Set the number of columns.  Defaults to one.
      */
     public void setNumColumns(int numColumns) {
         mLayoutManager.setNumRows(numColumns);
@@ -57,6 +70,9 @@
 
     /**
      * Set the column width.
+     *
+     * @param width May be WRAP_CONTENT, or a size in pixels. If zero,
+     * column width will be fixed based on number of columns and view width.
      */
     public void setColumnWidth(int width) {
         mLayoutManager.setRowHeight(width);
diff --git a/v4/Android.mk b/v4/Android.mk
index d1fad84..c1ae34e 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -14,7 +14,7 @@
 
 LOCAL_PATH := $(call my-dir)
 
-# A common helper sub-library that only uses base (API 4) APIs.
+# A helper sub-library that makes direct use of Donut APIs.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4-donut
 LOCAL_SDK_VERSION := 4
@@ -143,7 +143,7 @@
 
 # -----------------------------------------------------------------------
 
-# A helper sub-library that makes direct use of the upcoming API
+# A helper sub-library that makes direct use of the upcoming API.
 # TODO: Apply a real name and SDK version when available
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4-api20
@@ -154,6 +154,17 @@
 
 # -----------------------------------------------------------------------
 
+# A helper sub-library that makes direct use of the upcoming API.
+# TODO: Apply a real name and SDK version when available
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v4-api21
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api21)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4-api20
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# -----------------------------------------------------------------------
+
 # Here is the final static library that apps can link against.
 include $(CLEAR_VARS)
 LOCAL_MODULE := android-support-v4
@@ -162,6 +173,6 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, java) \
     $(call all-Iaidl-files-under, java)
 
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api20
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4-api21
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-annotations
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v4/api20/android/support/v4/app/NotificationCompatApi20.java b/v4/api20/android/support/v4/app/NotificationCompatApi20.java
index da35b28..41e1255 100644
--- a/v4/api20/android/support/v4/app/NotificationCompatApi20.java
+++ b/v4/api20/android/support/v4/app/NotificationCompatApi20.java
@@ -38,7 +38,9 @@
                 PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
                 int mProgressMax, int mProgress, boolean mProgressIndeterminate,
                 boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
-                Bundle extras, String groupKey, boolean groupSummary, String sortKey) {
+                String category, ArrayList<String> people, Bundle extras, int color,
+                int visibility, Notification publicVersion, String groupKey, boolean groupSummary,
+                String sortKey) {
             b = new Notification.Builder(context)
                 .setWhen(n.when)
                 .setSmallIcon(n.icon, n.iconLevel)
@@ -65,10 +67,17 @@
                 .setPriority(priority)
                 .setProgress(mProgressMax, mProgress, mProgressIndeterminate)
                 .setLocalOnly(localOnly)
+                .setCategory(category)
                 .setExtras(extras)
+                .setColor(color)
+                .setVisibility(visibility)
+                .setPublicVersion(publicVersion)
                 .setGroup(groupKey)
                 .setGroupSummary(groupSummary)
                 .setSortKey(sortKey);
+            for (String person: people) {
+                b.addPerson(person);
+            }
         }
 
         @Override
diff --git a/v4/api20/android/support/v4/speech/tts/TTSImplementationV2.java b/v4/api20/android/support/v4/speech/tts/TTSImplementationV2.java
new file mode 100644
index 0000000..e7f9b6e
--- /dev/null
+++ b/v4/api20/android/support/v4/speech/tts/TTSImplementationV2.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.v4.speech.tts.TextToSpeechClient.ConnectionCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.EngineStatus;
+import android.support.v4.speech.tts.TextToSpeechClient.RequestCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.UtteranceId;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/** Simple bridge to the actual TTS V2 implementation. */
+class TTSImplementationV2 implements ITextToSpeechClient {
+    private static final String TAG = "android.support.v4.speech.tts";
+
+    interface TextToSpeechClientConstructor {
+        public android.speech.tts.TextToSpeechClient newClient(
+                Context context, String engine, boolean fallbackToDefaultEngine,
+                android.speech.tts.TextToSpeechClient.RequestCallbacks defaultRequestCallbacks,
+                android.speech.tts.TextToSpeechClient.ConnectionCallbacks connectionCallbacks);
+    }
+
+    private TextToSpeechClientConstructor mClientConstructor;
+
+    private android.speech.tts.TextToSpeechClient mClient;
+
+    TTSImplementationV2() {
+    }
+
+    TTSImplementationV2(TextToSpeechClientConstructor constructor) {
+        mClientConstructor = constructor;
+    }
+
+    @Override
+    public void setup(Context context, String engine, boolean fallbackToDefaultEngine,
+            RequestCallbacks defaultRequestCallbacks, ConnectionCallbacks connectionCallbacks) {
+        if (mClient != null) {
+            Log.e(TAG, "Implementation already set up");
+            return;
+        }
+        if (mClientConstructor != null) {
+            mClient = mClientConstructor.newClient(
+                    context, engine, fallbackToDefaultEngine, convert(defaultRequestCallbacks),
+                    convert(connectionCallbacks));
+        } else {
+            mClient = new android.speech.tts.TextToSpeechClient(context, engine,
+                fallbackToDefaultEngine, convert(defaultRequestCallbacks),
+                convert(connectionCallbacks));
+        }
+    }
+
+    private static class InternalUtteranceId extends
+        android.speech.tts.TextToSpeechClient.UtteranceId {
+        private UtteranceId mExternalUtteranceId;
+
+        public InternalUtteranceId(UtteranceId externalUtteranceId) {
+            mExternalUtteranceId = externalUtteranceId;
+        }
+
+        public UtteranceId getExternalUtteranceId() {
+            return mExternalUtteranceId;
+        }
+    }
+
+    @Override
+    public void connect() {
+        if (mClient != null) {
+            mClient.connect();
+        } else {
+            Log.e(TAG, "Implementation is not set up");
+        }
+    }
+
+    @Override
+    public boolean isConnected() {
+        return (mClient != null) && mClient.isConnected();
+    }
+
+    @Override
+    public void disconnect() {
+        if (mClient != null) {
+            mClient.disconnect();
+        } else {
+            Log.e(TAG, "Implementation is not set up");
+        }
+    }
+
+    @Override
+    public EngineStatus getEngineStatus() {
+        return convert(mClient.getEngineStatus());
+    }
+
+    @Override
+    public void stop() {
+        mClient.stop();
+    }
+
+    @Override
+    public void queueSpeak(String utterance, UtteranceId utteranceId, RequestConfig config,
+            RequestCallbacks callbacks) {
+        mClient.queueSpeak(utterance, convert(utteranceId), convert(config), convert(callbacks));
+    }
+
+    @Override
+    public void queueSynthesizeToFile(String utterance, UtteranceId utteranceId, File outputFile,
+            RequestConfig config, RequestCallbacks callbacks) {
+        mClient.queueSynthesizeToFile(utterance, convert(utteranceId), outputFile, convert(config),
+                convert(callbacks));
+    }
+
+    @Override
+    public void queueSilence(long durationInMs, UtteranceId utteranceId,
+            RequestCallbacks callbacks) {
+        mClient.queueSilence(durationInMs, convert(utteranceId), convert(callbacks));
+    }
+
+    @Override
+    public void queueAudio(Uri audioUrl, UtteranceId utteranceId, RequestConfig config,
+            RequestCallbacks callbacks) {
+        mClient.queueAudio(audioUrl, convert(utteranceId), convert(config), convert(callbacks));
+    }
+
+    static android.speech.tts.RequestConfig convert(RequestConfig config) {
+        android.speech.tts.RequestConfig.Builder builder =
+                android.speech.tts.RequestConfig.Builder.newBuilder();
+        builder.setVoice(convert(config.getVoice()));
+        for (String key : config.getAudioParams().keySet()) {
+            builder.setAudioParam(key, config.getAudioParams().get(key));
+        }
+        for (String key : config.getVoiceParams().keySet()) {
+            builder.setVoiceParam(key, config.getVoiceParams().get(key));
+        }
+        return builder.build();
+    }
+
+    static android.speech.tts.VoiceInfo convert(VoiceInfo voice) {
+        return (android.speech.tts.VoiceInfo)voice.getPrivateData();
+    }
+
+
+    static UtteranceId convert(android.speech.tts.TextToSpeechClient.UtteranceId utteranceId) {
+        return ((InternalUtteranceId)utteranceId).getExternalUtteranceId();
+    }
+
+    static android.speech.tts.TextToSpeechClient.UtteranceId convert(UtteranceId utteranceId) {
+        return new InternalUtteranceId(utteranceId);
+    }
+
+    static TextToSpeechClient.EngineStatus convert(
+            android.speech.tts.TextToSpeechClient.EngineStatus engineStatus) {
+        ArrayList<VoiceInfo> voices = new ArrayList<VoiceInfo>();
+        for (android.speech.tts.VoiceInfo coreVoiceInfo : engineStatus.getVoices()) {
+            voices.add(convert(coreVoiceInfo));
+        }
+        return new TextToSpeechClient.EngineStatus(
+                    engineStatus.getEnginePackage(), voices);
+    }
+
+    static VoiceInfo convert(android.speech.tts.VoiceInfo voiceInfo) {
+        return new VoiceInfo(voiceInfo.getName(), voiceInfo.getLocale(),
+                voiceInfo.getQuality(), voiceInfo.getLatency(),
+                voiceInfo.getRequiresNetworkConnection(), voiceInfo.getParamsWithDefaults(),
+                voiceInfo.getAdditionalFeatures(), voiceInfo);
+    }
+
+    static android.speech.tts.TextToSpeechClient.RequestCallbacks convert(
+            final TextToSpeechClient.RequestCallbacks callbacks) {
+        return new android.speech.tts.TextToSpeechClient.RequestCallbacks() {
+
+            @Override
+            public void onSynthesisFailure(
+                    android.speech.tts.TextToSpeechClient.UtteranceId utteranceId, int errorCode) {
+                callbacks.onSynthesisFailure(convert(utteranceId), errorCode);
+            }
+            @Override
+            public void onSynthesisProgress(
+                    android.speech.tts.TextToSpeechClient.UtteranceId utteranceId, int charIndex,
+                    int msFromStart) {
+                callbacks.onSynthesisProgress(convert(utteranceId), charIndex, msFromStart);
+            }
+            @Override
+            public void onSynthesisFallback(
+                    android.speech.tts.TextToSpeechClient.UtteranceId utteranceId) {
+                callbacks.onSynthesisFallback(convert(utteranceId));
+            }
+            @Override
+            public void onSynthesisStart(
+                    android.speech.tts.TextToSpeechClient.UtteranceId utteranceId) {
+                callbacks.onSynthesisStart(convert(utteranceId));
+            }
+            @Override
+            public void onSynthesisStop(
+                    android.speech.tts.TextToSpeechClient.UtteranceId utteranceId) {
+                callbacks.onSynthesisStop(convert(utteranceId));
+            }
+            @Override
+            public void onSynthesisSuccess(
+                    android.speech.tts.TextToSpeechClient.UtteranceId utteranceId) {
+                callbacks.onSynthesisSuccess(convert(utteranceId));
+            }
+        };
+    }
+
+    static android.speech.tts.TextToSpeechClient.ConnectionCallbacks convert(
+            final TextToSpeechClient.ConnectionCallbacks connectionCallbacks) {
+        return new android.speech.tts.TextToSpeechClient.ConnectionCallbacks() {
+            @Override
+            public void onConnectionSuccess() {
+                connectionCallbacks.onConnectionSuccess();
+            }
+
+            @Override
+            public void onConnectionFailure() {
+                connectionCallbacks.onConnectionFailure();
+            }
+
+            @Override
+            public void onServiceDisconnected() {
+                connectionCallbacks.onServiceDisconnected();
+            }
+
+            @Override
+            public void onEngineStatusChange(
+                    android.speech.tts.TextToSpeechClient.EngineStatus newEngineStatus) {
+                connectionCallbacks.onEngineStatusChange(convert(newEngineStatus));
+            }
+        };
+    }
+}
diff --git a/v4/api21/android/support/v4/content/ContextCompatApi21.java b/v4/api21/android/support/v4/content/ContextCompatApi21.java
new file mode 100644
index 0000000..be2de1a
--- /dev/null
+++ b/v4/api21/android/support/v4/content/ContextCompatApi21.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.content;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+class ContextCompatApi21 {
+    public static Drawable getDrawable(Context context, int id) {
+        return context.getDrawable(id);
+    }
+}
diff --git a/v4/build.gradle b/v4/build.gradle
index 0cac202..59790bd 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -1,6 +1,11 @@
 apply plugin: 'android-library'
 archivesBaseName = 'support-v4'
 
+// create a jar task for the code internal implementation
+tasks.create(name: "internalJar", type: Jar) {
+    baseName "internal_impl"
+}
+
 // --------------------------
 // TO ADD NEW PLATFORM SPECIFIC CODE, UPDATE THIS:
 // create and configure the sourcesets/dependencies for platform-specific code.
@@ -22,6 +27,7 @@
 def jbMr2SS        = createApiSourceset('jellybeanmr2', 'jellybean-mr2', '18',      jbMr1SS)
 def kitkatSS       = createApiSourceset('kitkat',       'kitkat',        '19',      jbMr2SS)
 def api20SS        = createApiSourceset('api20',        'api20',         'current', kitkatSS)
+def api21SS        = createApiSourceset('api21',        'api21',         'current', api20SS)
 
 
 def createApiSourceset(String name, String folder, String apiLevel, SourceSet previousSource) {
@@ -35,6 +41,9 @@
         setupDependencies(configName, previousSource)
     }
     ext.allSS.add(sourceSet)
+
+    internalJar.from sourceSet.output
+
     return sourceSet
 }
 
@@ -43,15 +52,6 @@
     project.getDependencies().add(configName, previousSourceSet.compileClasspath)
 }
 
-// create a jar task for the code above
-tasks.create(name: "internalJar", type: Jar) {
-    baseName "internal_impl"
-}
-
-ext.allSS.each { ss ->
-    internalJar.from ss.output
-}
-
 dependencies {
     compile project(':support-annotations')
 
diff --git a/v4/donut/android/support/v4/speech/tts/ITextToSpeechClient.java b/v4/donut/android/support/v4/speech/tts/ITextToSpeechClient.java
new file mode 100644
index 0000000..051f101
--- /dev/null
+++ b/v4/donut/android/support/v4/speech/tts/ITextToSpeechClient.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.v4.speech.tts.TextToSpeechClient.ConnectionCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.EngineStatus;
+import android.support.v4.speech.tts.TextToSpeechClient.RequestCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.UtteranceId;
+
+import java.io.File;
+
+interface ITextToSpeechClient {
+  public void setup(Context context, String engine, boolean fallbackToDefaultEngine,
+      RequestCallbacks defaultRequestCallbacks, ConnectionCallbacks connectionCallbacks);
+  public void connect();
+  public boolean isConnected();
+  public void disconnect();
+  public EngineStatus getEngineStatus();
+  public void stop();
+  public void queueSpeak(final String utterance, final UtteranceId utteranceId,
+      final RequestConfig config, final RequestCallbacks callbacks);
+  public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId,
+      final File outputFile, final RequestConfig config,
+      final RequestCallbacks callbacks);
+  public void queueSilence(final long durationInMs, final UtteranceId utteranceId,
+      final RequestCallbacks callbacks);
+  public void queueAudio(final Uri audioUrl, final UtteranceId utteranceId,
+      final RequestConfig config, final RequestCallbacks callbacks);
+}
\ No newline at end of file
diff --git a/v4/donut/android/support/v4/speech/tts/RequestConfig.java b/v4/donut/android/support/v4/speech/tts/RequestConfig.java
new file mode 100644
index 0000000..9b3e78c
--- /dev/null
+++ b/v4/donut/android/support/v4/speech/tts/RequestConfig.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.media.AudioManager;
+import android.os.Bundle;
+
+/**
+ * Synthesis request configuration.
+ *
+ * This class is immutable, and can only be constructed using
+ * {@link RequestConfig.Builder}.
+ */
+public final class RequestConfig {
+
+    /** Builder for constructing RequestConfig objects. */
+    public static final class Builder {
+        private VoiceInfo mCurrentVoiceInfo;
+        private Bundle mVoiceParams;
+        private Bundle mAudioParams;
+
+        Builder(VoiceInfo currentVoiceInfo, Bundle voiceParams, Bundle audioParams) {
+            mCurrentVoiceInfo = currentVoiceInfo;
+            mVoiceParams = voiceParams;
+            mAudioParams = audioParams;
+        }
+
+        /**
+         * Create new RequestConfig builder.
+         */
+        public static Builder newBuilder() {
+            return new Builder(null, new Bundle(), new Bundle());
+        }
+
+        /**
+         * Create new RequestConfig builder.
+         * @param prototype
+         *            Prototype of new RequestConfig. Copies all fields of the
+         *            prototype to the constructed object.
+         */
+        public static Builder newBuilder(RequestConfig prototype) {
+            return new Builder(prototype.mCurrentVoiceInfo,
+                    (Bundle)prototype.mVoiceParams.clone(),
+                    (Bundle)prototype.mAudioParams.clone());
+        }
+
+        /** Set voice for request. Will reset voice parameters to the defaults. */
+        public Builder setVoice(VoiceInfo voice) {
+            mCurrentVoiceInfo = voice;
+            mVoiceParams = (Bundle)voice.getParamsWithDefaults().clone();
+            return this;
+        }
+
+        /**
+         * Set request voice parameter.
+         *
+         * @param paramName
+         *            The name of the parameter. It has to be one of the keys
+         *            from {@link VoiceInfo#getParamsWithDefaults()}
+         * @param value
+         *            Value of the parameter. Its type can be one of: Integer, Float,
+         *            Boolean, String, VoiceInfo (will be set as a String, result of a call to
+         *            the {@link VoiceInfo#getName()}) or byte[]. It has to be of the same type
+         *            as the default value from {@link VoiceInfo#getParamsWithDefaults()}
+         *            for that parameter.
+         * @throws IllegalArgumentException
+         *            If paramName is not a valid parameter name or its value is of a wrong
+         *            type.
+         * @throws IllegalStateException
+         *            If no voice is set.
+         */
+        public Builder setVoiceParam(String paramName, Object value){
+            if (mCurrentVoiceInfo == null) {
+                throw new IllegalStateException(
+                        "Couldn't set voice parameter, no voice is set");
+            }
+            Object defaultValue = mCurrentVoiceInfo.getParamsWithDefaults().get(paramName);
+            if (defaultValue == null) {
+                throw new IllegalArgumentException(
+                        "Parameter \"" + paramName + "\" is not available in set voice with " +
+                                "name: " + mCurrentVoiceInfo.getName());
+            }
+
+            // If it's VoiceInfo, get its name
+            if (value instanceof VoiceInfo) {
+                value = ((VoiceInfo)value).getName();
+            }
+
+            // Check type information
+            if (!defaultValue.getClass().equals(value.getClass())) {
+                throw new IllegalArgumentException(
+                        "Parameter \"" + paramName +"\" is of different type. Value passed has " +
+                                "type " + value.getClass().getSimpleName() + " but should have " +
+                                "type " + defaultValue.getClass().getSimpleName());
+            }
+
+            setParam(mVoiceParams, paramName, value);
+            return this;
+        }
+
+        /**
+         * Set request audio parameter.
+         *
+         * Doesn't requires a set voice.
+         *
+         * @param paramName
+         *            Name of parameter.
+         * @param value
+         *            Value of parameter. Its type can be one of: Integer, Float, Boolean, String
+         *            or byte[].
+         */
+        public Builder setAudioParam(String paramName, Object value) {
+            setParam(mAudioParams, paramName, value);
+            return this;
+        }
+
+        /**
+         * Set the {@link TextToSpeechClient.Params#AUDIO_PARAM_STREAM} audio parameter.
+         *
+         * @param streamId One of the STREAM_ constants defined in {@link AudioManager}.
+         */
+        public void setAudioParamStream(int streamId) {
+            setAudioParam(TextToSpeechClient.Params.AUDIO_PARAM_STREAM, streamId);
+        }
+
+        /**
+         * Set the {@link TextToSpeechClient.Params#AUDIO_PARAM_VOLUME} audio parameter.
+         *
+         * @param volume Float in range of 0.0 to 1.0.
+         */
+        public void setAudioParamVolume(float volume) {
+            setAudioParam(TextToSpeechClient.Params.AUDIO_PARAM_VOLUME, volume);
+        }
+
+        /**
+         * Set the {@link TextToSpeechClient.Params#AUDIO_PARAM_PAN} audio parameter.
+         *
+         * @param pan Float in range of -1.0 to +1.0.
+         */
+        public void setAudioParamPan(float pan) {
+            setAudioParam(TextToSpeechClient.Params.AUDIO_PARAM_PAN, pan);
+        }
+
+        private void setParam(Bundle bundle, String featureName, Object value) {
+            if (value instanceof String) {
+                bundle.putString(featureName, (String)value);
+            } else if(value instanceof byte[]) {
+                bundle.putByteArray(featureName, (byte[])value);
+            } else if(value instanceof Integer) {
+                bundle.putInt(featureName, (Integer)value);
+            } else if(value instanceof Float) {
+                bundle.putFloat(featureName, (Float)value);
+            } else if(value instanceof Double) {
+                bundle.putFloat(featureName, (Float)value);
+            } else if(value instanceof Boolean) {
+                bundle.putBoolean(featureName, (Boolean)value);
+            } else {
+                throw new IllegalArgumentException("Illegal type of object");
+            }
+            return;
+        }
+
+        /**
+         * Build new RequestConfig instance.
+         */
+        public RequestConfig build() {
+            RequestConfig config =
+                    new RequestConfig(mCurrentVoiceInfo, mVoiceParams, mAudioParams);
+            return config;
+        }
+    }
+
+    RequestConfig(VoiceInfo voiceInfo, Bundle voiceParams, Bundle audioParams) {
+        mCurrentVoiceInfo = voiceInfo;
+        mVoiceParams = voiceParams;
+        mAudioParams = audioParams;
+    }
+
+    /**
+     * Currently set voice.
+     */
+    private final VoiceInfo mCurrentVoiceInfo;
+
+    /**
+     * Voice parameters bundle.
+     */
+    private final Bundle mVoiceParams;
+
+    /**
+     * Audio parameters bundle.
+     */
+    private final Bundle mAudioParams;
+
+    /**
+     * @return Currently set request voice.
+     */
+    public VoiceInfo getVoice() {
+        return mCurrentVoiceInfo;
+    }
+
+    /**
+     * @return Request audio parameters.
+     */
+    public Bundle getAudioParams() {
+        return mAudioParams;
+    }
+
+    /**
+     * @return Request voice parameters.
+     */
+    public Bundle getVoiceParams() {
+        return mVoiceParams;
+    }
+
+}
diff --git a/v4/donut/android/support/v4/speech/tts/RequestConfigHelper.java b/v4/donut/android/support/v4/speech/tts/RequestConfigHelper.java
new file mode 100644
index 0000000..4385bda
--- /dev/null
+++ b/v4/donut/android/support/v4/speech/tts/RequestConfigHelper.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.support.v4.speech.tts.TextToSpeechClient.EngineStatus;
+
+import java.util.Locale;
+
+/**
+ * Set of common heuristics for selecting {@link VoiceInfo} from
+ * {@link TextToSpeechClient#getEngineStatus()} output.
+ */
+public final class RequestConfigHelper {
+    private RequestConfigHelper() {}
+
+    /**
+     * Interface for scoring VoiceInfo object.
+     */
+    public static interface VoiceScorer {
+        /**
+         * Score VoiceInfo. If the score is less than or equal to zero, that voice is discarded.
+         * If two voices have same desired primary characteristics (highest quality, lowest
+         * latency or others), the one with the higher score is selected.
+         */
+        public int scoreVoice(VoiceInfo voiceInfo);
+    }
+
+    /**
+     * Score positively voices that exactly match the locale supplied to the constructor.
+     */
+    public static final class ExactLocaleMatcher implements VoiceScorer {
+        private final Locale mLocale;
+
+        /**
+         * Score positively voices that exactly match the given locale
+         * @param locale Reference locale. If null, the default locale will be used.
+         */
+        public ExactLocaleMatcher(Locale locale) {
+            if (locale == null) {
+                mLocale = Locale.getDefault();
+            } else {
+                mLocale = locale;
+            }
+        }
+        @Override
+        public int scoreVoice(VoiceInfo voiceInfo) {
+            return mLocale.equals(voiceInfo.getLocale()) ? 1 : 0;
+        }
+    }
+
+    /**
+     * Score positively voices that match exactly the given locale (score 3)
+     * or that share same language and country (score 2), or that share just a language (score 1).
+     */
+    public static final class LanguageMatcher implements VoiceScorer {
+        private final Locale mLocale;
+
+        /**
+         * Score positively voices with similar locale.
+         * @param locale Reference locale. If null, default will be used.
+         */
+        public LanguageMatcher(Locale locale) {
+            if (locale == null) {
+                mLocale = Locale.getDefault();
+            } else {
+                mLocale = locale;
+            }
+        }
+
+        @Override
+        public int scoreVoice(VoiceInfo voiceInfo) {
+            final Locale voiceLocale = voiceInfo.getLocale();
+            if (mLocale.equals(voiceLocale)) {
+                return 3;
+            } else {
+                if (mLocale.getLanguage().equals(voiceLocale.getLanguage())) {
+                    if (mLocale.getCountry().equals(voiceLocale.getCountry())) {
+                        return 2;
+                    }
+                    return 1;
+                }
+                return 0;
+            }
+        }
+    }
+
+    /**
+     * Get the highest quality voice from voices that score more than zero from the passed scorer.
+     * If there is more than one voice with the same highest quality, then this method returns one
+     * with the highest score. If they share same score as well, one with the lower index in the
+     * voices list is returned.
+     *
+     * @param engineStatus
+     *            Voices status received from a {@link TextToSpeechClient#getEngineStatus()} call.
+     * @param voiceScorer
+     *            Used to discard unsuitable voices and help settle cases where more than
+     *            one voice has the desired characteristic.
+     * @param hasToBeEmbedded
+     *            If true, require the voice to be an embedded voice (no network
+     *            access will be required for synthesis).
+     */
+    private static VoiceInfo getHighestQualityVoice(EngineStatus engineStatus,
+            VoiceScorer voiceScorer, boolean hasToBeEmbedded) {
+        VoiceInfo bestVoice = null;
+        int bestScoreMatch = 1;
+        int bestVoiceQuality = 0;
+
+        for (VoiceInfo voice : engineStatus.getVoices()) {
+            int score = voiceScorer.scoreVoice(voice);
+            if (score <= 0 || hasToBeEmbedded && voice.getRequiresNetworkConnection()
+                    || voice.getQuality() < bestVoiceQuality) {
+                continue;
+            }
+
+            if (bestVoice == null ||
+                    voice.getQuality() > bestVoiceQuality ||
+                    score > bestScoreMatch) {
+                bestVoice = voice;
+                bestScoreMatch = score;
+                bestVoiceQuality = voice.getQuality();
+            }
+        }
+        return bestVoice;
+    }
+
+    /**
+     * Get highest quality voice.
+     *
+     * Highest quality voice is selected from voices that score more than zero from the passed
+     * scorer. If there is more than one voice with the same highest quality, then this method
+     * will return one with the highest score. If they share same score as well, one with the lower
+     * index in the voices list is returned.
+
+     * @param engineStatus
+     *            Voices status received from a {@link TextToSpeechClient#getEngineStatus()} call.
+     * @param hasToBeEmbedded
+     *            If true, require the voice to be an embedded voice (no network
+     *            access will be required for synthesis).
+     * @param voiceScorer
+     *            Scorer is used to discard unsuitable voices and help settle cases where more than
+     *            one voice has highest quality.
+     * @return RequestConfig with selected voice or null if suitable voice was not found.
+     */
+    public static RequestConfig highestQuality(EngineStatus engineStatus,
+            boolean hasToBeEmbedded, VoiceScorer voiceScorer) {
+        VoiceInfo voice = getHighestQualityVoice(engineStatus, voiceScorer, hasToBeEmbedded);
+        if (voice == null) {
+            return null;
+        }
+        return RequestConfig.Builder.newBuilder().setVoice(voice).build();
+    }
+
+    /**
+     * Get highest quality voice for the default locale.
+     *
+     * Call {@link #highestQuality(EngineStatus, boolean, VoiceScorer)} with
+     * {@link LanguageMatcher} set to device default locale.
+     *
+     * @param engineStatus
+     *            Voices status received from a {@link TextToSpeechClient#getEngineStatus()} call.
+     * @param hasToBeEmbedded
+     *            If true, require the voice to be an embedded voice (no network
+     *            access will be required for synthesis).
+     * @return RequestConfig with selected voice or null if suitable voice was not found.
+     */
+    public static RequestConfig highestQuality(EngineStatus engineStatus,
+            boolean hasToBeEmbedded) {
+        return highestQuality(engineStatus, hasToBeEmbedded,
+                new LanguageMatcher(Locale.getDefault()));
+    }
+
+}
diff --git a/v4/donut/android/support/v4/speech/tts/TextToSpeechClient.java b/v4/donut/android/support/v4/speech/tts/TextToSpeechClient.java
new file mode 100644
index 0000000..ac8351e
--- /dev/null
+++ b/v4/donut/android/support/v4/speech/tts/TextToSpeechClient.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
+import android.speech.tts.TextToSpeech;
+import android.util.Log;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Synthesizes speech from text for immediate playback or to create a sound
+ * file.
+ * <p>
+ * This is an updated version of the speech synthesis client that supersedes
+ * {@link android.speech.tts.TextToSpeech}.
+ * <p>
+ * A TextToSpeechClient instance can only be used to synthesize text once it has
+ * connected to the service. The TextToSpeechClient instance will start establishing
+ * the connection after a call to the {@link #connect()} method. This is usually done in
+ * {@link android.app.Application#onCreate()} or {@link android.app.Activity#onCreate}. When the
+ * connection is established, the instance will call back using the
+ * {@link TextToSpeechClient.ConnectionCallbacks} interface. Only after a
+ * successful callback is the client usable.
+ * <p>
+ * After successful connection, the list of all available voices can be obtained
+ * by calling the {@link TextToSpeechClient#getEngineStatus()} method. The client can
+ * choose a voice using some custom heuristic and build a {@link RequestConfig} object
+ * using {@link RequestConfig.Builder}, or can use one of the common heuristics found
+ * in ({@link RequestConfigHelper}.
+ * <p>
+ * When you are done using the TextToSpeechClient instance, call the
+ * {@link #disconnect()} method to release the connection.
+ * <p>
+ * In the rare case of a change to the set of available voices, the service will call to the
+ * {@link ConnectionCallbacks#onEngineStatusChange} with new set of available voices as argument.
+ * In response, the client HAVE to recreate all {@link RequestConfig} instances in use.
+ */
+public class TextToSpeechClient {
+    private static final String TAG = "TextToSpeechClient";
+
+    /** Common voices parameters */
+    public static final class Params {
+        private Params() {}
+
+        /**
+         * Maximum allowed time for a single request attempt, in milliseconds, before synthesis
+         * fails (or fallback request starts, if requested using
+         * {@link #FALLBACK_VOICE_NAME}).
+         */
+        public static final String NETWORK_TIMEOUT_MS = "networkTimeoutMs";
+
+        /**
+         * Number of network request retries that are attempted in case of failure
+         */
+        public static final String NETWORK_RETRIES_COUNT = "networkRetriesCount";
+
+        /**
+         * Should synthesizer report sub-utterance progress on synthesis. Only applicable
+         * for the {@link TextToSpeechClient#queueSpeak} method.
+         */
+        public static final String TRACK_SUBUTTERANCE_PROGRESS = "trackSubutteranceProgress";
+
+        /**
+         * If a voice exposes this parameter then it supports the fallback request feature.
+         *
+         * If it is set to a valid name of some other voice ({@link VoiceInfo#getName()}) then
+         * in case of request failure (due to network problems or missing data), fallback request
+         * will be attempted. Request will be done using the voice referenced by this parameter.
+         * If it is the case, the client will be informed by a callback to the {@link
+         * RequestCallbacks#onSynthesisFallback(UtteranceId)}.
+         */
+        public static final String FALLBACK_VOICE_NAME = "fallbackVoiceName";
+
+        /**
+         * Audio parameter for specifying a linear multiplier to the speaking speed of the voice.
+         * The value is a float. Values below zero decrease speed of the synthesized speech
+         * values above one increase it. If the value of this parameter is equal to zero,
+         * then it will be replaced by a settings-configurable default before it reaches
+         * TTS service.
+         */
+        public static final String SPEECH_SPEED = "speechSpeed";
+
+        /**
+         * Audio parameter for controlling the pitch of the output. The Value is a positive float,
+         * with default of {@code 1.0}. The value is used to scale the primary frequency linearly.
+         * Lower values lower the tone of the synthesized voice, greater values increase it.
+         */
+        public static final String SPEECH_PITCH = "speechPitch";
+
+        /**
+         * Audio parameter for controlling output volume. Value is a float with scale of 0 to 1
+         */
+        public static final String AUDIO_PARAM_VOLUME = "volume";
+
+        /**
+         * Audio parameter for controlling output pan.
+         * Value is a float ranging from -1 to +1 where -1 maps to a hard-left pan,
+         * 0 to center (the default behavior), and +1 to hard-right.
+         */
+        public static final String AUDIO_PARAM_PAN = "pan";
+
+        /**
+         * Audio parameter for specifying the audio stream type to be used when speaking text
+         * or playing back a file. The value should be one of the STREAM_ constants
+         * defined in {@link android.media.AudioManager}.
+         */
+        public static final String AUDIO_PARAM_STREAM = TextToSpeech.Engine.KEY_PARAM_STREAM;
+    }
+
+    /**
+     * Result codes for TTS operations.
+     */
+    public static final class Status {
+        private Status() {}
+
+        /**
+         * Denotes a successful operation.
+         */
+        public static final int SUCCESS = 0;
+
+        /**
+         * Denotes a stop requested by a client. It's used only on the service side of the API,
+         * client should never expect to see this result code.
+         */
+        public static final int STOPPED = 100;
+
+        /**
+         * Denotes a generic failure.
+         */
+        public static final int ERROR_UNKNOWN = -1;
+
+        /**
+         * Denotes a failure of a TTS engine to synthesize the given input.
+         */
+        public static final int ERROR_SYNTHESIS = 10;
+
+        /**
+         * Denotes a failure of a TTS service.
+         */
+        public static final int ERROR_SERVICE = 11;
+
+        /**
+         * Denotes a failure related to the output (audio device or a file).
+         */
+        public static final int ERROR_OUTPUT = 12;
+
+        /**
+         * Denotes a failure caused by a network connectivity problems.
+         */
+        public static final int ERROR_NETWORK = 13;
+
+        /**
+         * Denotes a failure caused by network timeout.
+         */
+        public static final int ERROR_NETWORK_TIMEOUT = 14;
+
+        /**
+         * Denotes a failure caused by an invalid request.
+         */
+        public static final int ERROR_INVALID_REQUEST = 15;
+
+        /**
+         * Denotes a failure related to passing a non-unique utterance id.
+         */
+        public static final int ERROR_NON_UNIQUE_UTTERANCE_ID = 16;
+
+        /**
+         * Denotes a failure related to missing data. The TTS implementation may download
+         * the missing data, and if so, request will succeed in future. This error can only happen
+         * for voices with {@link VoiceInfo#FEATURE_MAY_AUTOINSTALL} feature.
+         * Note: the recommended way to avoid this error is to create a request with the fallback
+         * voice.
+         */
+        public static final int ERROR_DOWNLOADING_ADDITIONAL_DATA = 17;
+    }
+
+    /**
+     * Set of callbacks for the events related to the progress of a synthesis request
+     * through the synthesis queue. Each synthesis request is associated with a call to
+     * {@link #queueSpeak} or {@link #queueSynthesizeToFile}.
+     *
+     * The callbacks specified in this method will NOT be called on UI thread.
+     */
+    public static abstract class RequestCallbacks  {
+        /**
+         * Called after synthesis of utterance successfully starts.
+         */
+        public void onSynthesisStart(UtteranceId utteranceId) {}
+
+        /**
+         * Called after synthesis successfully finishes.
+         * @param utteranceId
+         *            Unique identifier of synthesized utterance.
+         */
+        public void onSynthesisSuccess(UtteranceId utteranceId) {}
+
+        /**
+         * Called after synthesis was stopped in middle of synthesis process.
+         * @param utteranceId
+         *            Unique identifier of synthesized utterance.
+         */
+        public void onSynthesisStop(UtteranceId utteranceId) {}
+
+        /**
+         * Called when requested synthesis failed and fallback synthesis is about to be attempted.
+         *
+         * Requires voice with available {@link TextToSpeechClient.Params#FALLBACK_VOICE_NAME}
+         * parameter, and request with this parameter enabled.
+         *
+         * This callback will be followed by callback to the {@link #onSynthesisStart},
+         * {@link #onSynthesisFailure} or {@link #onSynthesisSuccess} that depends on the
+         * fallback outcome.
+         *
+         * For more fallback feature reference, look at the
+         * {@link TextToSpeechClient.Params#FALLBACK_VOICE_NAME}.
+         *
+         * @param utteranceId
+         *            Unique identifier of synthesized utterance.
+         */
+        public void onSynthesisFallback(UtteranceId utteranceId) {}
+
+        /**
+         * Called after synthesis of utterance fails.
+         *
+         * It may be called instead or after a {@link #onSynthesisStart} callback.
+         *
+         * @param utteranceId
+         *            Unique identifier of synthesized utterance.
+         * @param errorCode
+         *            One of the values from {@link Status}.
+         */
+        public void onSynthesisFailure(UtteranceId utteranceId, int errorCode) {}
+
+        /**
+         * Called during synthesis to mark synthesis progress.
+         *
+         * Requires voice with available
+         * {@link TextToSpeechClient.Params#TRACK_SUBUTTERANCE_PROGRESS} parameter, and
+         * request with this parameter enabled.
+         *
+         * @param utteranceId
+         *            Unique identifier of synthesized utterance.
+         * @param charIndex
+         *            String index (java char offset) of recently synthesized character.
+         * @param msFromStart
+         *            Miliseconds from the start of the synthesis.
+         */
+        public void onSynthesisProgress(UtteranceId utteranceId, int charIndex,
+                int msFromStart) {}
+    }
+
+    /**
+     * Interface definition of callbacks that are called when the client is
+     * connected or disconnected from the TTS service.
+     */
+    public static interface ConnectionCallbacks {
+        /**
+         * After calling {@link TextToSpeechClient#connect()}, this method will be invoked
+         * asynchronously when the connect request has successfully completed.
+         *
+         * Clients are strongly encouraged to call {@link TextToSpeechClient#getEngineStatus()}
+         * and create {@link RequestConfig} objects used in subsequent synthesis requests.
+         */
+        public void onConnectionSuccess();
+
+        /**
+         * After calling {@link TextToSpeechClient#connect()}, this method may be invoked
+         * asynchronously when the connect request has failed to complete.
+         *
+         * It may be also invoked synchronously, from the body of
+         * {@link TextToSpeechClient#connect()} method.
+         */
+        public void onConnectionFailure();
+
+        /**
+         * Called when the connection to the service is lost. This can happen if there is a problem
+         * with the speech service (e.g. a crash or resource problem causes it to be killed by the
+         * system). When called, all requests have been canceled and no outstanding listeners will
+         * be executed. Applications should disable UI components that require the service.
+         */
+        public void onServiceDisconnected();
+
+        /**
+         * After receiving {@link #onConnectionSuccess()} callback, this method may be invoked
+         * if engine status obtained from {@link TextToSpeechClient#getEngineStatus()}) changes.
+         * It usually means that some voices were removed, changed or added.
+         *
+         * Clients are required to recreate {@link RequestConfig} objects used in subsequent
+         * synthesis requests.
+         */
+        public void onEngineStatusChange(EngineStatus newEngineStatus);
+    }
+
+    /** State of voices as provided by engine and user. */
+    public static final class EngineStatus {
+        /** All available voices. */
+        private final List<VoiceInfo> mVoices;
+
+        /** Name of the TTS engine package */
+        private final String mPackageName;
+
+        EngineStatus(String packageName, List<VoiceInfo> voices) {
+            this.mVoices =  Collections.unmodifiableList(voices);
+            this.mPackageName = packageName;
+        }
+
+        /**
+         * Get an immutable list of all Voices exposed by the TTS engine.
+         */
+        public List<VoiceInfo> getVoices() {
+            return mVoices;
+        }
+
+        /**
+         * Get name of the TTS engine package currently in use.
+         */
+        public String getEnginePackage() {
+            return mPackageName;
+        }
+    }
+
+    /** Unique synthesis request identifier. */
+    public static class UtteranceId {
+        /** Unique identifier */
+        private final int id;
+
+        /** Unique identifier generator */
+        private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+        /**
+         * Create new, unique UtteranceId instance.
+         */
+        public UtteranceId() {
+            id = ID_GENERATOR.getAndIncrement();
+        }
+
+        /**
+         * Returns a unique string associated with an instance of this object.
+         *
+         * This string will be used to identify the synthesis request/utterance inside the
+         * TTS service.
+         */
+        public final String toUniqueString() {
+            return "UID" + id;
+        }
+    }
+
+    private final ITextToSpeechClient mImplementation;
+
+    TextToSpeechClient(ITextToSpeechClient implementation,
+            Context context, String engine, boolean fallbackToDefaultEngine,
+            RequestCallbacks defaultRequestCallbacks,
+            ConnectionCallbacks connectionCallbacks) {
+        mImplementation = implementation;
+        mImplementation.setup(context, engine, fallbackToDefaultEngine, defaultRequestCallbacks,
+                connectionCallbacks);
+    }
+
+    /**
+     * Connects the client to TTS service. This method returns immediately, and connects to the
+     * service in the background.
+     *
+     * After connection initializes successfully, {@link ConnectionCallbacks#onConnectionSuccess()}
+     * is called. On a failure {@link ConnectionCallbacks#onConnectionFailure} is called.
+     *
+     * Both of those callback may be called asynchronously on the main thread,
+     * {@link ConnectionCallbacks#onConnectionFailure} may be called synchronously, before
+     * this method returns.
+     */
+    public void connect() {
+        mImplementation.connect();
+    }
+
+    /**
+     * Checks if the client is currently connected to the service, so that
+     * requests to other methods will succeed.
+     */
+    public boolean isConnected() {
+        return mImplementation.isConnected();
+    }
+
+    /**
+     * Closes the connection to TextToSpeech service. No calls can be made on this object after
+     * calling this method.
+     * It is good practice to call this method in the onDestroy() method of an Activity
+     * so the TextToSpeech engine can be cleanly stopped.
+     */
+    public void disconnect() {
+        mImplementation.disconnect();
+    }
+
+    /**
+     * Retrieve TTS engine status {@link EngineStatus}. Requires connected client.
+     */
+    public EngineStatus getEngineStatus() {
+        return mImplementation.getEngineStatus();
+    }
+
+    /**
+     * Interrupts the current utterance spoken (whether played or rendered to file) and discards
+     * other utterances in the queue.
+     */
+    public void stop() {
+        if (mImplementation.isConnected()) {
+            mImplementation.stop();
+        } else {
+            Log.e(TAG, "TTS Client is not connected");
+        }
+    }
+
+    /**
+     * Speaks the string using the specified queuing strategy using current
+     * voice. This method is asynchronous, i.e. the method just adds the request
+     * to the queue of TTS requests and then returns. The synthesis might not
+     * have finished (or even started!) at the time when this method returns.
+     *
+     * @param utterance The string of text to be spoken. No longer than
+     *            1000 characters.
+     * @param utteranceId Unique identificator used to track the synthesis progress
+     *            in {@link RequestCallbacks}.
+     * @param config Synthesis request configuration. Can't be null. Has to contain a
+     *            voice.
+     * @param callbacks Synthesis request callbacks. If null, default request
+     *            callbacks object will be used.
+     */
+    public void queueSpeak(final String utterance, final UtteranceId utteranceId,
+            final RequestConfig config,
+            final RequestCallbacks callbacks) {
+        if (utterance == null) {
+            throw new IllegalArgumentException("utterance can't be null");
+        }
+        if (utteranceId == null) {
+            throw new IllegalArgumentException("utteranceId can't be null");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("config can't be null");
+        }
+
+        if (mImplementation.isConnected()) {
+            mImplementation.queueSpeak(utterance, utteranceId, config, callbacks);
+        } else {
+            Log.e(TAG, "TTS Client is not connected");
+        }
+    }
+
+    /**
+     * Synthesizes the given text to a file using the specified parameters. This
+     * method is asynchronous, i.e. the method just adds the request to the
+     * queue of TTS requests and then returns. The synthesis might not have
+     * finished (or even started!) at the time when this method returns.
+     *
+     * @param utterance The text that should be synthesized. No longer than
+     *            1000 characters.
+     * @param utteranceId Unique identificator used to track the synthesis progress
+     *            in {@link RequestCallbacks}.
+     * @param outputFile File to write the generated audio data to.
+     * @param config Synthesis request configuration. Can't be null. Have to contain a
+     *            voice.
+     * @param callbacks Synthesis request callbacks. If null, default request
+     *            callbacks object will be used.
+     */
+    public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId,
+            final File outputFile, final RequestConfig config,
+            final RequestCallbacks callbacks) {
+        if (utterance == null) {
+            throw new IllegalArgumentException("utterance can't be null");
+        }
+        if (utteranceId == null) {
+            throw new IllegalArgumentException("utteranceId can't be null");
+        }
+        if (outputFile == null) {
+            throw new IllegalArgumentException("outputFile can't be null");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("config can't be null");
+        }
+
+        if (mImplementation.isConnected()) {
+            mImplementation.queueSynthesizeToFile(utterance, utteranceId, outputFile, config,
+                    callbacks);
+        } else {
+            Log.e(TAG, "TTS Client is not connected");
+        }
+    }
+
+    /**
+     * Plays silence for the specified amount of time. This method is asynchronous,
+     * i.e. the method just adds the request to the queue of TTS requests and then
+     * returns. The synthesis might not have finished (or even started!) at the time
+     * when this method returns.
+     *
+     * @param durationInMs The duration of the silence in milliseconds.
+     * @param utteranceId Unique identificator used to track the synthesis progress
+     *            in {@link RequestCallbacks}.
+     * @param callbacks Synthesis request callbacks. If null, default request
+     *            callbacks object will be used.
+     */
+    public void queueSilence(final long durationInMs, final UtteranceId utteranceId,
+            final RequestCallbacks callbacks) {
+        if (utteranceId == null) {
+            throw new IllegalArgumentException("utteranceId can't be null");
+        }
+
+        if (mImplementation.isConnected()) {
+            mImplementation.queueSilence(durationInMs, utteranceId, callbacks);
+        } else {
+            Log.e(TAG, "TTS Client is not connected");
+        }
+    }
+
+    /**
+     * Plays the audio resource using the specified parameters.
+     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+     * requests and then returns. The synthesis might not have finished (or even started!) at the
+     * time when this method returns.
+     *
+     * @param audioUrl The audio resource that should be played
+     * @param utteranceId Unique identificator used to track synthesis progress
+     *            in {@link RequestCallbacks}.
+     * @param config Synthesis request configuration. Can't be null. Doesn't have to contain a
+     *            voice (only system parameters are used).
+     * @param callbacks Synthesis request callbacks. If null, default request
+     *            callbacks object will be used.
+     */
+    public void queueAudio(final Uri audioUrl, final UtteranceId utteranceId,
+            final RequestConfig config, final RequestCallbacks callbacks) {
+        if (audioUrl == null) {
+            throw new IllegalArgumentException("audioUrl can't be null");
+        }
+        if (utteranceId == null) {
+            throw new IllegalArgumentException("utteranceId can't be null");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("config can't be null");
+        }
+
+        if (mImplementation.isConnected()) {
+            mImplementation.queueAudio(audioUrl, utteranceId, config, callbacks);
+        } else {
+            Log.e(TAG, "TTS Client is not connected");
+        }
+    }
+}
diff --git a/v4/donut/android/support/v4/speech/tts/VoiceInfo.java b/v4/donut/android/support/v4/speech/tts/VoiceInfo.java
new file mode 100644
index 0000000..e86e1d1
--- /dev/null
+++ b/v4/donut/android/support/v4/speech/tts/VoiceInfo.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * Characteristics and features of a Text-To-Speech Voice. Each TTS Engine can expose
+ * multiple voices for multiple locales, with different set of features.
+ *
+ * Each VoiceInfo has an unique name. This name can be obtained using the {@link #getName()} method
+ * and will persist until the client is asked to re-evaluate the list of available voices in the
+ * {@link TextToSpeechClient.ConnectionCallbacks#onEngineStatusChange(TextToSpeechClient.EngineStatus)}
+ * callback. The name can be used to reference a VoiceInfo in an instance of {@link RequestConfig};
+ * the {@link TextToSpeechClient.Params#FALLBACK_VOICE_NAME} voice parameter is an example of this.
+ * It is recommended that the voice name never change during the TTS service lifetime.
+ */
+public class VoiceInfo {
+    /** Very low, but still intelligible quality of speech synthesis */
+    public static final int QUALITY_VERY_LOW = 100;
+
+    /** Low, not human-like quality of speech synthesis */
+    public static final int QUALITY_LOW = 200;
+
+    /** Normal quality of speech synthesis */
+    public static final int QUALITY_NORMAL = 300;
+
+    /** High, human-like quality of speech synthesis */
+    public static final int QUALITY_HIGH = 400;
+
+    /** Very high, almost human-indistinguishable quality of speech synthesis */
+    public static final int QUALITY_VERY_HIGH = 500;
+
+    /** Very low expected synthesizer latency (< 20ms) */
+    public static final int LATENCY_VERY_LOW = 100;
+
+    /** Low expected synthesizer latency (~20ms) */
+    public static final int LATENCY_LOW = 200;
+
+    /** Normal expected synthesizer latency (~50ms) */
+    public static final int LATENCY_NORMAL = 300;
+
+    /** Network based expected synthesizer latency (~200ms) */
+    public static final int LATENCY_HIGH = 400;
+
+    /** Very slow network based expected synthesizer latency (> 200ms) */
+    public static final int LATENCY_VERY_HIGH = 500;
+
+    /** Additional feature key, with string value, gender of the speaker */
+    public static final String FEATURE_SPEAKER_GENDER = "speakerGender";
+
+    /** Additional feature key, with integer value, speaking speed in words per minute
+     * when {@link TextToSpeechClient.Params#SPEECH_SPEED} parameter is set to {@code 1.0} */
+    public static final String FEATURE_WORDS_PER_MINUTE = "wordsPerMinute";
+
+    /**
+     * Additional feature key, with boolean value, that indicates that voice may need to
+     * download additional data if used for synthesis.
+     *
+     * Making a request with a voice that has this feature may result in a
+     * {@link TextToSpeechClient.Status#ERROR_DOWNLOADING_ADDITIONAL_DATA} error. It's recommended
+     * to set the {@link TextToSpeechClient.Params#FALLBACK_VOICE_NAME} voice parameter to reference
+     * a fully installed voice (or network voice) that can serve as replacement.
+     *
+     * Note: It's a good practice for a TTS engine to provide a sensible fallback voice as the
+     * default value for {@link TextToSpeechClient.Params#FALLBACK_VOICE_NAME} parameter if this
+     * feature is present.
+     */
+    public static final String FEATURE_MAY_AUTOINSTALL = "mayAutoInstall";
+
+    private final String mName;
+    private final Locale mLocale;
+    private final int mQuality;
+    private final int mLatency;
+    private final boolean mRequiresNetworkConnection;
+    private final Bundle mParams;
+    private final Bundle mAdditionalFeatures;
+
+    private final Object mPrivateData;
+
+    VoiceInfo(String name,
+            Locale locale,
+            int quality,
+            int latency,
+            boolean requiresNetworkConnection,
+            Bundle params,
+            Bundle additionalFeatures,
+            Object privateData) {
+        this.mName = name;
+        this.mLocale = locale;
+        this.mQuality = quality;
+        this.mLatency = latency;
+        this.mRequiresNetworkConnection = requiresNetworkConnection;
+        this.mParams = params;
+        this.mAdditionalFeatures = additionalFeatures;
+        this.mPrivateData = privateData;
+    }
+
+    /** Builder, allows TTS engines to create VoiceInfo instances. */
+    static final class Builder {
+        private String name;
+        private Locale locale;
+        private int quality = VoiceInfo.QUALITY_NORMAL;
+        private int latency = VoiceInfo.LATENCY_NORMAL;
+        private boolean requiresNetworkConnection;
+        private Bundle params;
+        private Bundle additionalFeatures;
+        private Object privateData;
+
+        public Builder() {
+
+        }
+
+        /**
+         * Copy fields from given VoiceInfo instance.
+         */
+        public Builder(VoiceInfo voiceInfo) {
+            this.name = voiceInfo.mName;
+            this.locale = voiceInfo.mLocale;
+            this.quality = voiceInfo.mQuality;
+            this.latency = voiceInfo.mLatency;
+            this.requiresNetworkConnection = voiceInfo.mRequiresNetworkConnection;
+            this.params = (Bundle)voiceInfo.mParams.clone();
+            this.additionalFeatures = (Bundle) voiceInfo.mAdditionalFeatures.clone();
+        }
+
+        /**
+         * Sets the voice's unique name. It will be used by clients to reference the voice used by a
+         * request.
+         *
+         * It's recommended that each voice use the same consistent name during the TTS service
+         * lifetime.
+         */
+        public Builder setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * Sets voice locale. This has to be a valid locale, built from ISO 639-1 and ISO 3166-1
+         * two letter codes.
+         */
+        public Builder setLocale(Locale locale) {
+            this.locale = locale;
+            return this;
+        }
+
+        /**
+         * Sets map of all available request parameters with their default values.
+         * Some common parameter names can be found in {@link TextToSpeechClient.Params} static
+         * members.
+         */
+        public Builder setParamsWithDefaults(Bundle params) {
+            this.params = params;
+            return this;
+        }
+
+        /**
+         * Sets map of additional voice features. Some common feature names can be found in
+         * {@link VoiceInfo} static members.
+         */
+        public Builder setAdditionalFeatures(Bundle additionalFeatures) {
+            this.additionalFeatures = additionalFeatures;
+            return this;
+        }
+
+        /**
+         * Sets the voice quality (higher is better).
+         */
+        public Builder setQuality(int quality) {
+            this.quality = quality;
+            return this;
+        }
+
+        /**
+         * Sets the voice latency (lower is better).
+         */
+        public Builder setLatency(int latency) {
+            this.latency = latency;
+            return this;
+        }
+
+        /**
+         * Sets whether the voice requires network connection to work properly.
+         */
+        public Builder setRequiresNetworkConnection(boolean requiresNetworkConnection) {
+            this.requiresNetworkConnection = requiresNetworkConnection;
+            return this;
+        }
+
+        /**
+         * Sets implementation specific private data
+         */
+        public void setPrivateData(Object privateData) {
+            this.privateData = privateData;
+        }
+
+        /**
+         * @return The built VoiceInfo instance.
+         */
+        public VoiceInfo build() {
+            if (name == null || name.length() == 0) {
+                throw new IllegalStateException("Name can't be null or empty");
+            }
+            if (locale == null) {
+                throw new IllegalStateException("Locale can't be null");
+            }
+
+            return new VoiceInfo(name, locale, quality, latency,
+                        requiresNetworkConnection,
+                        ((params == null) ? new Bundle() :
+                            (Bundle)params.clone()),
+                        ((additionalFeatures == null) ? new Bundle() :
+                            (Bundle)additionalFeatures.clone()), privateData);
+        }
+    }
+
+    /**
+     * @return The voice's locale
+     */
+    public Locale getLocale() {
+        return mLocale;
+    }
+
+    /**
+     * @return The voice's quality (higher is better)
+     */
+    public int getQuality() {
+        return mQuality;
+    }
+
+    /**
+     * @return The voice's latency (lower is better)
+     */
+    public int getLatency() {
+        return mLatency;
+    }
+
+    /**
+     * @return Does the Voice require a network connection to work.
+     */
+    public boolean getRequiresNetworkConnection() {
+        return mRequiresNetworkConnection;
+    }
+
+    /**
+     * @return Bundle of all available parameters with their default values.
+     */
+    public Bundle getParamsWithDefaults() {
+        return mParams;
+    }
+
+    /**
+     * @return Unique voice name.
+     *
+     * Each VoiceInfo has an unique name, that persists until client is asked to re-evaluate the
+     * set of the available languages in the {@link TextToSpeechClient.ConnectionCallbacks#onEngineStatusChange(TextToSpeechClient.EngineStatus)}
+     * callback (Voice may disappear from the set if voice was removed by the user).
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * @return Additional features of the voice.
+     */
+    public Bundle getAdditionalFeatures() {
+        return mAdditionalFeatures;
+    }
+
+    /**
+     * @return Implementation private data.
+     */
+    Object getPrivateData() {
+        return mPrivateData;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(64);
+        return builder.append("VoiceInfo[Name: ").append(mName)
+                .append(", locale: ").append(mLocale)
+                .append(", quality: ").append(mQuality)
+                .append(", latency: ").append(mLatency)
+                .append(", requiresNetwork: ").append(mRequiresNetworkConnection)
+                .append(", paramsWithDefaults: ").append(mParams.toString())
+                .append(", additionalFeatures: ").append(mAdditionalFeatures.toString())
+                .append("]").toString();
+    }
+}
diff --git a/v4/ics-mr1/android/support/v4/speech/tts/TextToSpeechICSMR1.java b/v4/ics-mr1/android/support/v4/speech/tts/TextToSpeechICSMR1.java
new file mode 100644
index 0000000..bbad4c1
--- /dev/null
+++ b/v4/ics-mr1/android/support/v4/speech/tts/TextToSpeechICSMR1.java
@@ -0,0 +1,74 @@
+package android.support.v4.speech.tts;
+
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.UtteranceProgressListener;
+import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+
+import java.util.Locale;
+import java.util.Set;
+
+/** Helper class for TTS functionality introduced in ICS MR1 */
+class TextToSpeechICSMR1 {
+    /**
+     * Call {@link TextToSpeech#getFeatures} if available.
+     *
+     * @return {@link TextToSpeech#getFeatures} or null on older devices.
+     */
+    static Set<String> getFeatures(TextToSpeech tts, Locale locale) {
+        if (android.os.Build.VERSION.SDK_INT >=
+                android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+            return tts.getFeatures(locale);
+        }
+        return null;
+    }
+
+    public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
+    public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
+
+    static interface UtteranceProgressListenerICSMR1 {
+        void onDone(String utteranceId);
+        void onError(String utteranceId);
+        void onStart(String utteranceId);
+    }
+
+    /**
+     * Call {@link TextToSpeech#setOnUtteranceProgressListener} if ICS-MR1 or newer.
+     *
+     * On pre ICS-MR1 devices,{@link TextToSpeech#setOnUtteranceCompletedListener} is
+     * used to emulate its behavior - at the end of synthesis we call
+     * {@link UtteranceProgressListenerICSMR1#onStart(String)} and
+     * {@link UtteranceProgressListenerICSMR1#onDone(String)} one after the other.
+     * Errors can't be detected.
+     */
+    static void setUtteranceProgressListener(TextToSpeech tts,
+            final UtteranceProgressListenerICSMR1 listener) {
+        if (android.os.Build.VERSION.SDK_INT >=
+                android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+            tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
+                @Override
+                public void onStart(String utteranceId) {
+                    listener.onStart(utteranceId);
+                }
+
+                @Override
+                public void onError(String utteranceId) {
+                    listener.onError(utteranceId);
+                }
+
+                @Override
+                public void onDone(String utteranceId) {
+                    listener.onDone(utteranceId);
+                }
+            });
+        } else {
+            tts.setOnUtteranceCompletedListener(new OnUtteranceCompletedListener() {
+                @Override
+                public void onUtteranceCompleted(String utteranceId) {
+                    // Emulate onStart. Clients are expecting it will happen.
+                    listener.onStart(utteranceId);
+                    listener.onDone(utteranceId);
+                }
+            });
+        }
+    }
+}
diff --git a/v4/ics/android/support/v4/speech/tts/TextToSpeechICS.java b/v4/ics/android/support/v4/speech/tts/TextToSpeechICS.java
new file mode 100644
index 0000000..4e0c041
--- /dev/null
+++ b/v4/ics/android/support/v4/speech/tts/TextToSpeechICS.java
@@ -0,0 +1,27 @@
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.os.Build;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.util.Log;
+
+/** Helper class for TTS functionality introduced in ICS */
+class TextToSpeechICS {
+    private static final String TAG = "android.support.v4.speech.tts";
+
+    static TextToSpeech construct(Context context, OnInitListener onInitListener,
+            String engineName) {
+        if (Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            if (engineName == null) {
+                return new TextToSpeech(context, onInitListener);
+            } else {
+                Log.w(TAG, "Can't specify tts engine on this device");
+                return new TextToSpeech(context, onInitListener);
+            }
+        } else {
+            return new TextToSpeech(context, onInitListener, engineName);
+        }
+    }
+
+}
diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java
index 9a4ef16..c4e00f8 100644
--- a/v4/java/android/support/v4/app/NotificationCompat.java
+++ b/v4/java/android/support/v4/app/NotificationCompat.java
@@ -20,6 +20,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Build;
@@ -281,6 +282,36 @@
      */
     public static final String EXTRA_PEOPLE = "android.people";
 
+    /**
+     * Value of {@link Notification#color} equal to 0 (also known as
+     * {@link android.graphics.Color#TRANSPARENT Color.TRANSPARENT}),
+     * telling the system not to decorate this notification with any special color but instead use
+     * default colors when presenting this notification.
+     */
+    public static final int COLOR_DEFAULT = Color.TRANSPARENT;
+
+    /**
+     * Notification visibility: Show this notification in its entirety on all lockscreens.
+     *
+     * {@see android.app.Notification#visibility}
+     */
+    public static final int VISIBILITY_PUBLIC = 1;
+
+    /**
+     * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or
+     * private information on secure lockscreens.
+     *
+     * {@see android.app.Notification#visibility}
+     */
+    public static final int VISIBILITY_PRIVATE = 0;
+
+    /**
+     * Notification visibility: Do not reveal any part of this notification on a secure lockscreen.
+     *
+     * {@see android.app.Notification#visibility}
+     */
+    public static final int VISIBILITY_SECRET = -1;
+
     private static final NotificationCompatImpl IMPL;
 
     interface NotificationCompatImpl {
@@ -462,7 +493,7 @@
                     b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
                     b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
                     b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
-                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mExtras,
+                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mPeople, b.mExtras,
                     b.mGroupKey, b.mGroupSummary, b.mSortKey);
             addActionsToBuilder(builder, b.mActions);
             addStyleToBuilderJellybean(builder, b.mStyle);
@@ -513,7 +544,8 @@
                     b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
                     b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
                     b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
-                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mExtras,
+                    b.mUseChronometer, b.mPriority, b.mSubText, b.mLocalOnly, b.mCategory,
+                    b.mPeople, b.mExtras, b.mColor, b.mVisibility, b.mPublicVersion,
                     b.mGroupKey, b.mGroupSummary, b.mSortKey);
             addActionsToBuilder(builder, b.mActions);
             addStyleToBuilderJellybean(builder, b.mStyle);
@@ -598,7 +630,8 @@
     }
 
     static {
-        if (Build.VERSION.SDK_INT >= 20) {
+        // TODO: Replace this if clause when SDK_INT is incremented to 20.
+        if (Build.VERSION.RELEASE.equals("L")) {
             IMPL = new NotificationCompatImplApi20();
         } else if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new NotificationCompatImplKitKat();
@@ -658,9 +691,14 @@
         String mSortKey;
         ArrayList<Action> mActions = new ArrayList<Action>();
         boolean mLocalOnly = false;
+        String mCategory;
         Bundle mExtras;
+        int mColor = COLOR_DEFAULT;
+        int mVisibility = VISIBILITY_PRIVATE;
+        Notification mPublicVersion;
 
         Notification mNotification = new Notification();
+        public ArrayList<String> mPeople;
 
         /**
          * Constructor.
@@ -680,6 +718,7 @@
             mNotification.when = System.currentTimeMillis();
             mNotification.audioStreamType = Notification.STREAM_DEFAULT;
             mPriority = PRIORITY_DEFAULT;
+            mPeople = new ArrayList<String>();
         }
 
         /**
@@ -969,6 +1008,18 @@
         }
 
         /**
+         * Set the notification category.
+         *
+         * <p>Must be one of the predefined notification categories (see the <code>CATEGORY_*</code>
+         * constants in {@link Notification}) that best describes this notification.
+         * May be used by the system for ranking and filtering.
+         */
+        public Builder setCategory(String category) {
+            mCategory = category;
+            return this;
+        }
+
+        /**
          * Set the default notification options that will be used.
          * <p>
          * The value should be one or more of the following fields combined with
@@ -1017,6 +1068,16 @@
         }
 
         /**
+         * Add a person that is relevant to this notification.
+         *
+         * @see Notification#EXTRA_PEOPLE
+         */
+        public Builder addPerson(String handle) {
+            mPeople.add(handle);
+            return this;
+        }
+
+        /**
          * Set this notification to be part of a group of notifications sharing the same key.
          * Grouped notifications may display in a cluster or stack on devices which
          * support such rendering.
@@ -1172,6 +1233,43 @@
         }
 
         /**
+         * Sets {@link Notification#color}.
+         *
+         * @param argb The accent color to use
+         *
+         * @return The same Builder.
+         */
+        public Builder setColor(int argb) {
+            mColor = argb;
+            return this;
+        }
+
+        /**
+         * Sets {@link Notification#visibility}.
+         *
+         * @param visibility One of {@link Notification#VISIBILITY_PRIVATE} (the default),
+         *                   {@link Notification#VISIBILITY_PUBLIC}, or
+         *                   {@link Notification#VISIBILITY_SECRET}.
+         */
+        public Builder setVisibility(int visibility) {
+            mVisibility = visibility;
+            return this;
+        }
+
+        /**
+         * Supply a replacement Notification whose contents should be shown in insecure contexts
+         * (i.e. atop the secure lockscreen). See {@link Notification#visibility} and
+         * {@link #VISIBILITY_PUBLIC}.
+         *
+         * @param n A replacement notification, presumably with some or all info redacted.
+         * @return The same Builder.
+         */
+        public Builder setPublicVersion(Notification n) {
+            mPublicVersion = n;
+            return this;
+        }
+
+        /**
          * Apply an extender to this notification builder. Extenders may be used to add
          * metadata or change options on this builder.
          */
diff --git a/v4/java/android/support/v4/content/ContextCompat.java b/v4/java/android/support/v4/content/ContextCompat.java
index 8fbf4bb..7119c3c 100644
--- a/v4/java/android/support/v4/content/ContextCompat.java
+++ b/v4/java/android/support/v4/content/ContextCompat.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources.Theme;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
@@ -297,4 +299,24 @@
         }
         return cur;
     }
+
+    /**
+     * Return a drawable object associated with a particular resource ID.
+     * <p>
+     * Starting in {@link android.os.Build.VERSION_CODES#L}, the returned
+     * drawable will be styled for the specified Context's theme.
+     *
+     * @param id The desired resource identifier, as generated by the aapt tool.
+     *            This integer encodes the package, type, and resource entry.
+     *            The value 0 is an invalid identifier.
+     * @return Drawable An object that can be used to draw this resource.
+     */
+    public final Drawable getDrawable(Context context, int id) {
+        final int version = Build.VERSION.SDK_INT;
+        if (version >= 21) {
+            return ContextCompatApi21.getDrawable(context, id);
+        } else {
+            return context.getResources().getDrawable(id);
+        }
+    }
 }
diff --git a/v4/java/android/support/v4/graphics/BitmapCompat.java b/v4/java/android/support/v4/graphics/BitmapCompat.java
new file mode 100644
index 0000000..6c97a0d
--- /dev/null
+++ b/v4/java/android/support/v4/graphics/BitmapCompat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.graphics;
+
+import android.graphics.Bitmap;
+
+/**
+ * Helper for accessing features in {@link android.graphics.Bitmap}
+ * introduced after API level 4 in a backwards compatible fashion.
+ */
+public class BitmapCompat {
+    /**
+     * Interface for the full API.
+     */
+    interface BitmapImpl {
+        public boolean hasMipMap(Bitmap bitmap);
+        public void setHasMipMap(Bitmap bitmap, boolean hasMipMap);
+    }
+
+    /**
+     * Interface implementation that doesn't use anything about v4 APIs.
+     */
+    static class BaseBitmapImpl implements BitmapImpl {
+        @Override
+        public boolean hasMipMap(Bitmap bitmap) {
+            return false;
+        }
+
+        @Override
+        public void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
+        }
+    }
+
+    static class JbMr2BitmapCompatImpl extends BaseBitmapImpl {
+        @Override
+        public boolean hasMipMap(Bitmap bitmap){
+            return BitmapCompatJellybeanMR2.hasMipMap(bitmap);
+        }
+
+        @Override
+        public void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
+            BitmapCompatJellybeanMR2.setHasMipMap(bitmap, hasMipMap);
+        }
+    }
+
+    /**
+     * Select the correct implementation to use for the current platform.
+     */
+    static final BitmapImpl IMPL;
+    static {
+        final int version = android.os.Build.VERSION.SDK_INT;
+        if (version >= 18) {
+            IMPL = new JbMr2BitmapCompatImpl();
+        } else {
+            IMPL = new BaseBitmapImpl();
+        }
+    }
+
+    public static boolean hasMipMap(Bitmap bitmap) {
+        return IMPL.hasMipMap(bitmap);
+    }
+
+    public static void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
+        IMPL.setHasMipMap(bitmap, hasMipMap);
+    }
+}
\ No newline at end of file
diff --git a/v4/java/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java b/v4/java/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
new file mode 100644
index 0000000..a8174a3
--- /dev/null
+++ b/v4/java/android/support/v4/graphics/drawable/RoundedBitmapDrawable.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.graphics.drawable;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.BitmapCompat;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * A Drawable that wraps a bitmap and can be drawn with rounded corners. You can create a
+ * RoundedBitmapDrawable from a file path, an input stream, or from a
+ * {@link android.graphics.Bitmap} object.
+ * <p>
+ * Also see the {@link android.graphics.Bitmap} class, which handles the management and
+ * transformation of raw bitmap graphics, and should be used when drawing to a
+ * {@link android.graphics.Canvas}.
+ * </p>
+ */
+public class RoundedBitmapDrawable extends Drawable {
+
+    private static final String TAG = "RoundedBitmapDrawable";
+    private static final int DEFAULT_PAINT_FLAGS =
+            Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;
+    private Bitmap mBitmap;
+    private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+    private int mGravity = Gravity.FILL;
+    private Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
+    private BitmapShader mBitmapShader;
+    private float mCornerRadius;
+
+    private final Rect mDstRect = new Rect();   // Gravity.apply() sets this
+    private final RectF mDstRectF = new RectF();
+
+    private boolean mApplyGravity = true;
+
+     // These are scaled to match the target density.
+    private int mBitmapWidth;
+    private int mBitmapHeight;
+
+
+    /**
+     * Returns a new drawable by creating it from a bitmap, setting initial target density based on
+     * the display metrics of the resources.
+     */
+    public static RoundedBitmapDrawable createRoundedBitmapDrawable(Resources res, Bitmap bitmap) {
+        return new RoundedBitmapDrawable(res, bitmap);
+    }
+
+    /**
+     * Returns a new drawable, creating it by opening a given file path and decoding the bitmap.
+     */
+    public static RoundedBitmapDrawable createRoundedBitmapDrawable(Resources res,
+            String filepath) {
+        final RoundedBitmapDrawable drawable =
+                createRoundedBitmapDrawable(res, BitmapFactory.decodeFile(filepath));
+        if (drawable.mBitmap == null) {
+            Log.w(TAG, "BitmapDrawable cannot decode " + filepath);
+        }
+        return drawable;
+    }
+
+
+    /**
+     * Returns a new drawable, creating it by decoding a bitmap from the given input stream.
+     */
+    public static RoundedBitmapDrawable createRoundedBitmapDrawable(Resources res,
+            java.io.InputStream is) {
+        final RoundedBitmapDrawable drawable =
+                createRoundedBitmapDrawable(res, BitmapFactory.decodeStream(is));
+        if (drawable.mBitmap == null) {
+            Log.w(TAG, "BitmapDrawable cannot decode " + is);
+        }
+        return drawable;
+    }
+
+    /**
+     * Returns the paint used to render this drawable.
+     */
+    public final Paint getPaint() {
+        return mPaint;
+    }
+
+    /**
+     * Returns the bitmap used by this drawable to render. May be null.
+     */
+    public final Bitmap getBitmap() {
+        return mBitmap;
+    }
+
+    private void computeBitmapSize() {
+        mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity);
+        mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity);
+    }
+
+    /**
+     * Set the density scale at which this drawable will be rendered. This
+     * method assumes the drawable will be rendered at the same density as the
+     * specified canvas.
+     *
+     * @param canvas The Canvas from which the density scale must be obtained.
+     *
+     * @see android.graphics.Bitmap#setDensity(int)
+     * @see android.graphics.Bitmap#getDensity()
+     */
+    public void setTargetDensity(Canvas canvas) {
+        setTargetDensity(canvas.getDensity());
+    }
+
+    /**
+     * Set the density scale at which this drawable will be rendered.
+     *
+     * @param metrics The DisplayMetrics indicating the density scale for this drawable.
+     *
+     * @see android.graphics.Bitmap#setDensity(int)
+     * @see android.graphics.Bitmap#getDensity()
+     */
+    public void setTargetDensity(DisplayMetrics metrics) {
+        setTargetDensity(metrics.densityDpi);
+    }
+
+    /**
+     * Set the density at which this drawable will be rendered.
+     *
+     * @param density The density scale for this drawable.
+     *
+     * @see android.graphics.Bitmap#setDensity(int)
+     * @see android.graphics.Bitmap#getDensity()
+     */
+    public void setTargetDensity(int density) {
+        if (mTargetDensity != density) {
+            mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
+            if (mBitmap != null) {
+                computeBitmapSize();
+            }
+            invalidateSelf();
+        }
+    }
+
+    /**
+     * Get the gravity used to position/stretch the bitmap within its bounds.
+     *
+     * @return the gravity applied to the bitmap
+     *
+     * @see android.view.Gravity
+     */
+    public int getGravity() {
+        return mGravity;
+    }
+
+    /**
+     * Set the gravity used to position/stretch the bitmap within its bounds.
+     *
+     * @param gravity the gravity
+     *
+     * @see android.view.Gravity
+     */
+    public void setGravity(int gravity) {
+        if (mGravity != gravity) {
+            mGravity = gravity;
+            mApplyGravity = true;
+            invalidateSelf();
+        }
+    }
+
+    /**
+     * Enables or disables the mipmap hint for this drawable's bitmap.
+     * See {@link Bitmap#setHasMipMap(boolean)} for more information.
+     *
+     * If the bitmap is null, or the current API version does not support setting a mipmap hint,
+     * calling this method has no effect.
+     *
+     * @param mipMap True if the bitmap should use mipmaps, false otherwise.
+     *
+     * @see #hasMipMap()
+     */
+    public void setMipMap(boolean mipMap) {
+        if (mBitmap != null) {
+            BitmapCompat.setHasMipMap(mBitmap, mipMap);
+            invalidateSelf();
+        }
+    }
+
+    /**
+     * Indicates whether the mipmap hint is enabled on this drawable's bitmap.
+     *
+     * @return True if the mipmap hint is set, false otherwise. If the bitmap
+     *         is null, this method always returns false.
+     *
+     * @see #setMipMap(boolean)
+     */
+    public boolean hasMipMap() {
+        return mBitmap != null && BitmapCompat.hasMipMap(mBitmap);
+    }
+
+    /**
+     * Enables or disables anti-aliasing for this drawable. Anti-aliasing affects
+     * the edges of the bitmap only so it applies only when the drawable is rotated.
+     *
+     * @param aa True if the bitmap should be anti-aliased, false otherwise.
+     *
+     * @see #hasAntiAlias()
+     */
+    public void setAntiAlias(boolean aa) {
+        mPaint.setAntiAlias(aa);
+        invalidateSelf();
+    }
+
+    /**
+     * Indicates whether anti-aliasing is enabled for this drawable.
+     *
+     * @return True if anti-aliasing is enabled, false otherwise.
+     *
+     * @see #setAntiAlias(boolean)
+     */
+    public boolean hasAntiAlias() {
+        return mPaint.isAntiAlias();
+    }
+
+    @Override
+    public void setFilterBitmap(boolean filter) {
+        mPaint.setFilterBitmap(filter);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setDither(boolean dither) {
+        mPaint.setDither(dither);
+        invalidateSelf();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Bitmap bitmap = mBitmap;
+        if (bitmap == null) {
+            return;
+        }
+
+        final Paint paint = mPaint;
+
+        final Shader shader = paint.getShader();
+        if (shader == null) {
+            if (mApplyGravity) {
+                GravityCompat.apply(mGravity, mBitmapWidth, mBitmapHeight,
+                        getBounds(), mDstRect, ViewCompat.LAYOUT_DIRECTION_LTR);
+                mDstRectF.set(mDstRect);
+                mApplyGravity = false;
+            }
+
+            canvas.drawBitmap(bitmap, null, mDstRect, paint);
+
+        } else {
+            if (mApplyGravity) {
+                copyBounds(mDstRect);
+                mDstRectF.set(mDstRect);
+                mApplyGravity = false;
+            }
+
+            canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, paint);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final int oldAlpha = mPaint.getAlpha();
+        if (alpha != oldAlpha) {
+            mPaint.setAlpha(alpha);
+            invalidateSelf();
+        }
+    }
+
+    public int getAlpha() {
+        return mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mPaint.setColorFilter(cf);
+        invalidateSelf();
+    }
+
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    /**
+     * Sets the corner radius to be applied when drawing the bitmap.
+     */
+    public void setCornerRadius(float cornerRadius) {
+        if (isGreaterThanZero(cornerRadius)) {
+            mPaint.setShader(mBitmapShader);
+        } else {
+            mPaint.setShader(null);
+        }
+        mCornerRadius = cornerRadius;
+    }
+
+    /**
+     * @return The corner radius applied when drawing the bitmap.
+     */
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mBitmapWidth;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mBitmapHeight;
+    }
+
+    @Override
+    public int getOpacity() {
+        if (mGravity != Gravity.FILL) {
+            return PixelFormat.TRANSLUCENT;
+        }
+        Bitmap bm = mBitmap;
+        return (bm == null
+                || bm.hasAlpha()
+                || mPaint.getAlpha() < 255
+                || isGreaterThanZero(mCornerRadius))
+                ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
+    }
+
+    private RoundedBitmapDrawable(Resources res, Bitmap bitmap) {
+        if (res != null) {
+            mTargetDensity = res.getDisplayMetrics().densityDpi;
+        }
+
+        mBitmap = bitmap;
+        if (mBitmap != null) {
+            computeBitmapSize();
+            mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+        } else {
+            mBitmapWidth = mBitmapHeight = -1;
+        }
+    }
+
+    private boolean isGreaterThanZero(float toCompare) {
+        return Float.compare(toCompare, +0.0f) > 0;
+    }
+}
diff --git a/v4/java/android/support/v4/media/TransportMediator.java b/v4/java/android/support/v4/media/TransportMediator.java
index 789ea7b..16bf94b 100644
--- a/v4/java/android/support/v4/media/TransportMediator.java
+++ b/v4/java/android/support/v4/media/TransportMediator.java
@@ -84,28 +84,28 @@
     public static final int KEYCODE_MEDIA_RECORD = 130;
 
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PREVIOUS
-     * RemoveControlClient.FLAG_KEY_MEDIA_PREVIOUS */
+     * RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS */
     public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_REWIND
-     * RemoveControlClient.FLAG_KEY_MEDIA_REWIND */
+     * RemoteControlClient.FLAG_KEY_MEDIA_REWIND */
     public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY
-     * RemoveControlClient.FLAG_KEY_MEDIA_PLAY */
+     * RemoteControlClient.FLAG_KEY_MEDIA_PLAY */
     public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY_PAUSE
-     * RemoveControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE */
+     * RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE */
     public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PAUSE
-     * RemoveControlClient.FLAG_KEY_MEDIA_PAUSE */
+     * RemoteControlClient.FLAG_KEY_MEDIA_PAUSE */
     public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_STOP
-     * RemoveControlClient.FLAG_KEY_MEDIA_STOP */
+     * RemoteControlClient.FLAG_KEY_MEDIA_STOP */
     public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_FAST_FORWARD
-     * RemoveControlClient.FLAG_KEY_MEDIA_FAST_FORWARD */
+     * RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD */
     public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
     /** Synonym for {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_NEXT
-     * RemoveControlClient.FLAG_KEY_MEDIA_NEXT */
+     * RemoteControlClient.FLAG_KEY_MEDIA_NEXT */
     public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
 
     static boolean isMediaKey(int keyCode) {
diff --git a/v4/java/android/support/v4/speech/tts/TTSImplementationV1.java b/v4/java/android/support/v4/speech/tts/TTSImplementationV1.java
new file mode 100644
index 0000000..434f7a3
--- /dev/null
+++ b/v4/java/android/support/v4/speech/tts/TTSImplementationV1.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.Engine;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+import android.support.v4.speech.tts.TextToSpeechClient.ConnectionCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.EngineStatus;
+import android.support.v4.speech.tts.TextToSpeechClient.RequestCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.Status;
+import android.support.v4.speech.tts.TextToSpeechClient.UtteranceId;
+import android.support.v4.speech.tts.TextToSpeechICSMR1.UtteranceProgressListenerICSMR1;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.Set;
+
+/**
+ * Implementation of the TTS V2 API using V1 API.
+ */
+class TTSImplementationV1 implements ITextToSpeechClient {
+    private static final String TAG = "android.support.v4.speech.tts";
+    private TextToSpeech mOldClient;
+    private Context mContext;
+    private String mEngine;
+    private RequestCallbacks mDefaultRequestCallbacks;
+    private ConnectionCallbacks mConnectionCallbacks;
+    private EngineStatus mEngineStatus;
+    private Object mLock = new Object();
+
+    private volatile boolean mConnected = false;
+
+    public TTSImplementationV1() {
+    }
+
+    TTSImplementationV1(TextToSpeech client) {
+        mOldClient = client;
+    }
+
+    @Override
+    public void setup(Context context, String engine, boolean fallbackToDefaultEngine,
+            RequestCallbacks defaultRequestCallbacks, ConnectionCallbacks connectionCallbacks) {
+        mContext = context;
+        mEngine = engine;
+        mDefaultRequestCallbacks = defaultRequestCallbacks;
+        mConnectionCallbacks = connectionCallbacks;
+    }
+
+    @Override
+    public EngineStatus getEngineStatus() {
+        return mEngineStatus;
+    }
+
+    OnInitListener mOnInitListener = new OnInitListener() {
+        public void onInit(int status) {
+            mConnected = true;
+            if (status == TextToSpeech.SUCCESS) {
+                TTSImplementationV1.this.onInit();
+                mConnectionCallbacks.onConnectionSuccess();
+            } else {
+                mConnectionCallbacks.onConnectionFailure();
+            }
+        }
+    };
+
+    private void onInit() {
+        mEngineStatus = generateEngineStatus();
+        TextToSpeechICSMR1.setUtteranceProgressListener(mOldClient,
+                new UtteranceProgressListenerICSMR1() {
+            @Override
+            public void onStart(String utteranceId) {
+                RequestInternal requestInternal = getCallback(utteranceId);
+                if (requestInternal == null) {
+                    return;
+                }
+                requestInternal.requestCallbacks.onSynthesisStart(
+                        requestInternal.utteranceId);
+            }
+
+            @Override
+            public void onError(String utteranceId) {
+                RequestInternal requestInternal =
+                        removeCallback(utteranceId);
+                if (requestInternal == null) {
+                    return;
+                }
+                requestInternal.requestCallbacks.onSynthesisFailure(
+                        requestInternal.utteranceId,
+                        TextToSpeechClient.Status.ERROR_UNKNOWN);
+            }
+
+            @Override
+            public void onDone(String utteranceId) {
+                RequestInternal requestInternal =
+                        removeCallback(utteranceId);
+                if (requestInternal == null) {
+                    return;
+                }
+                requestInternal.requestCallbacks.onSynthesisSuccess(
+                        requestInternal.utteranceId);
+            }
+        });
+    }
+
+    @Override
+    public void connect() {
+        if (mOldClient != null) {
+            Log.w(TAG, "Already connected");
+            return;
+        }
+        mOldClient = TextToSpeechICS.construct(mContext, mOnInitListener, mEngine);
+    }
+
+    /** Internal info about a request. */
+    private static class RequestInternal {
+        UtteranceId utteranceId;
+        RequestCallbacks requestCallbacks;
+
+        public RequestInternal(UtteranceId utteranceId, RequestCallbacks requestCallbacks) {
+            super();
+            this.utteranceId = utteranceId;
+            this.requestCallbacks = requestCallbacks;
+        }
+    }
+
+    /** Map of request, keyed by their utteranceId */
+    private HashMap<String, RequestInternal > mUtteranceIdToRequest =
+            new HashMap<String, RequestInternal>();
+
+    /**
+     * Register callback.
+     *
+     * @param utteranceId Non-null utteranceIf instance.
+     * @param callback Non-null callbacks for the request
+     * @return Status.SUCCESS or error code in case of invalid arguments.
+     */
+    private int addCallback(UtteranceId utteranceId, RequestCallbacks callback) {
+        synchronized (mLock) {
+            if (utteranceId == null || callback == null) {
+                return Status.ERROR_INVALID_REQUEST;
+            }
+            if (mUtteranceIdToRequest.put(utteranceId.toUniqueString(),
+                    new RequestInternal(utteranceId, callback)) != null) {
+                return Status.ERROR_NON_UNIQUE_UTTERANCE_ID;
+            }
+            return Status.SUCCESS;
+        }
+    }
+
+    /**
+     * Remove and return callback.
+     *
+     * @param utteranceIdStr Unique string obtained from {@link UtteranceId#toUniqueString}.
+     */
+    private RequestInternal removeCallback(String utteranceIdStr) {
+        synchronized (mLock) {
+            return mUtteranceIdToRequest.remove(utteranceIdStr);
+        }
+    }
+
+    /**
+     * Get callback and utterance id.
+     *
+     * @param utteranceIdStr Unique string obtained from {@link UtteranceId#toUniqueString}.
+     */
+    private RequestInternal getCallback(String utteranceIdStr) {
+        synchronized (mLock) {
+            return mUtteranceIdToRequest.get(utteranceIdStr);
+        }
+    }
+
+    /**
+     * Remove callback and call {@link RequestCallbacks#onSynthesisFailure} with passed
+     * error code.
+     *
+     * @param utteranceIdStr Unique string obtained from {@link UtteranceId#toUniqueString}.
+     * @param errorCode argument to {@link RequestCallbacks#onSynthesisFailure} call.
+     */
+    private void removeCallbackAndErr(String utteranceIdStr, int errorCode) {
+        synchronized (mLock) {
+            RequestInternal c = mUtteranceIdToRequest.remove(utteranceIdStr);
+            c.requestCallbacks.onSynthesisFailure(c.utteranceId, errorCode);
+        }
+    }
+
+    /** Internal private data attached to VoiceInfo */
+    private static class VoiceInfoPrivate {
+        static final int VOICE_TYPE_EMBEDDED = 1;
+        static final int VOICE_TYPE_NETWORK = 2;
+        static final int VOICE_TYPE_UNKNOWN = 3;
+
+        int mVoiceType;
+
+        public VoiceInfoPrivate(int voiceType) {
+            super();
+            this.mVoiceType = voiceType;
+        }
+
+        static int getVoiceType(VoiceInfo voiceInfo) {
+            return ((VoiceInfoPrivate)voiceInfo.getPrivateData()).mVoiceType;
+        }
+    }
+
+    private EngineStatus generateEngineStatus() {
+        Bundle defaultParams = new Bundle();
+        defaultParams.putFloat(TextToSpeechClient.Params.SPEECH_PITCH, 1.0f);
+        // Zero value will make it use system default
+        defaultParams.putFloat(TextToSpeechClient.Params.SPEECH_SPEED, 0.0f);
+
+        // Enumerate all locales and check if they are available
+        ArrayList<VoiceInfo> voicesInfo = new ArrayList<VoiceInfo>();
+        for (Locale locale : Locale.getAvailableLocales()) {
+            int expectedStatus = TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE;
+            if (locale.getVariant().length() == 0) {
+                if (locale.getCountry().length() == 0) {
+                    expectedStatus = TextToSpeech.LANG_AVAILABLE;
+                } else {
+                    expectedStatus = TextToSpeech.LANG_COUNTRY_AVAILABLE;
+                }
+            }
+            try {
+                // Call those to prevent log spam from isLanguageAvailable.
+                locale.getISO3Language();
+                if (locale.getCountry() != null && locale.getCountry().length() > 0) {
+                    locale.getISO3Country();
+                }
+                if (mOldClient.isLanguageAvailable(locale) != expectedStatus) {
+                    continue;
+                }
+            } catch (MissingResourceException e) {
+                // Ignore locale without iso 3 codes
+                continue;
+            }
+
+            Set<String> features = TextToSpeechICSMR1.getFeatures(mOldClient, locale);
+
+            VoiceInfo.Builder builder = new VoiceInfo.Builder();
+            builder.setLatency(VoiceInfo.LATENCY_NORMAL);
+            builder.setQuality(VoiceInfo.QUALITY_NORMAL);
+            builder.setLocale(locale);
+            builder.setParamsWithDefaults(defaultParams);
+
+            boolean isUnknown = true;
+            if (features != null && features.contains("embeddedTts")) {
+                isUnknown = false;
+                builder.setName(locale.toString() + "-embedded");
+                builder.setRequiresNetworkConnection(false);
+                builder.setPrivateData(
+                        new VoiceInfoPrivate(
+                                VoiceInfoPrivate.VOICE_TYPE_EMBEDDED));
+                voicesInfo.add(builder.build());
+            }
+
+            if (features != null && features.contains("networkTts")) {
+                isUnknown = false;
+                builder.setName(locale.toString() + "-network");
+                builder.setRequiresNetworkConnection(true);
+                builder.setPrivateData(
+                        new VoiceInfoPrivate(
+                                VoiceInfoPrivate.VOICE_TYPE_NETWORK));
+                voicesInfo.add(builder.build());
+            }
+
+            if (isUnknown) {
+                builder.setName(locale.toString());
+                builder.setRequiresNetworkConnection(false);
+                builder.setPrivateData(
+                        new VoiceInfoPrivate(
+                                VoiceInfoPrivate.VOICE_TYPE_UNKNOWN));
+                voicesInfo.add(builder.build());
+            }
+        }
+
+        return new EngineStatus(mEngine, voicesInfo);
+    }
+
+    @Override
+    public boolean isConnected() {
+        return (mOldClient != null) && mConnected;
+    }
+
+    @Override
+    public void disconnect() {
+        if (mOldClient == null) {
+            Log.w(TAG, "Already disconnected");
+            return;
+        }
+        synchronized (mOldClient) {
+            mOldClient.shutdown();
+            mOldClient = null;
+        }
+    }
+
+    @Override
+    public void stop() {
+        if (mOldClient == null) {
+            Log.e(TAG, "Client is not connected");
+            return;
+        }
+        synchronized (mOldClient) {
+            mOldClient.stop();
+        }
+    }
+
+    private HashMap<String, String> createParameters(final RequestConfig config,
+            final UtteranceId utteranceId) {
+        HashMap<String, String> parameters = new HashMap<String, String>();
+        parameters.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId.toUniqueString());
+
+        int voiceType = VoiceInfoPrivate.getVoiceType(config.getVoice());
+        if (voiceType == VoiceInfoPrivate.VOICE_TYPE_NETWORK) {
+            parameters.put(TextToSpeechICSMR1.KEY_FEATURE_NETWORK_SYNTHESIS,
+                    "true");
+        } else if (voiceType == VoiceInfoPrivate.VOICE_TYPE_EMBEDDED) {
+            parameters.put(TextToSpeechICSMR1.KEY_FEATURE_EMBEDDED_SYNTHESIS,
+                    "true");
+        }
+        return parameters;
+    }
+
+    @Override
+    public void queueSpeak(final String utterance, final UtteranceId utteranceId,
+            final RequestConfig config, final RequestCallbacks callbacks) {
+        if (mOldClient == null || !mConnected) {
+            Log.e(TAG, "Client is not connected");
+            return;
+        }
+
+        synchronized (mOldClient) {
+            if (callbacks != null) {
+                addCallback(utteranceId, callbacks);
+            } else {
+                addCallback(utteranceId, mDefaultRequestCallbacks);
+            }
+            mOldClient.setLanguage(config.getVoice().getLocale());
+            float speed = config.getVoiceParams().getFloat(
+                    TextToSpeechClient.Params.SPEECH_SPEED);
+            // We use getDefaultSpeechRate, because once we set a speed,
+            // there's no mechanism to revert it to the default
+            mOldClient.setSpeechRate((speed > 0) ? speed : getDefaultSpeechRate());
+            mOldClient.setPitch(config.getVoiceParams().getFloat(
+                    TextToSpeechClient.Params.SPEECH_PITCH));
+            if (mOldClient.speak(utterance, TextToSpeech.QUEUE_ADD,
+                    createParameters(config, utteranceId)) != TextToSpeech.SUCCESS) {
+                removeCallbackAndErr(utteranceId.toUniqueString(),
+                        TextToSpeechClient.Status.ERROR_UNKNOWN);
+            }
+        }
+    }
+
+    @Override
+    public void queueSynthesizeToFile(final String utterance, final UtteranceId utteranceId,
+            final File outputFile, final RequestConfig config,
+            final RequestCallbacks callbacks) {
+        if (mOldClient == null || !mConnected) {
+            Log.e(TAG, "Client is not connected");
+            return;
+        }
+
+        synchronized (mOldClient) {
+            if (callbacks != null) {
+                addCallback(utteranceId, callbacks);
+            } else {
+                addCallback(utteranceId, mDefaultRequestCallbacks);
+            }
+            mOldClient.setLanguage(config.getVoice().getLocale());
+            float speed = config.getVoiceParams().getFloat(
+                    TextToSpeechClient.Params.SPEECH_SPEED);
+            // We use getDefaultSpeechRate, because once we set a speed,
+            // there's no mechanism to revert it to the default
+            mOldClient.setSpeechRate((speed > 0) ? speed : getDefaultSpeechRate());
+            mOldClient.setPitch(config.getVoiceParams().getFloat(
+                    TextToSpeechClient.Params.SPEECH_PITCH));
+            if (mOldClient.synthesizeToFile(utterance, createParameters(config, utteranceId),
+                    outputFile.getAbsolutePath()) != TextToSpeech.SUCCESS) {
+                removeCallbackAndErr(utteranceId.toUniqueString(),
+                        TextToSpeechClient.Status.ERROR_UNKNOWN);
+            }
+        }
+    }
+
+    @Override
+    public void queueSilence(final long durationInMs, final UtteranceId utteranceId,
+            final RequestCallbacks callbacks) {
+        if (mOldClient == null || !mConnected) {
+            Log.e(TAG, "Client is not connected");
+            return;
+        }
+
+        synchronized (mOldClient) {
+            if (callbacks != null) {
+                addCallback(utteranceId, callbacks);
+            } else {
+                addCallback(utteranceId, mDefaultRequestCallbacks);
+            }
+            if (mOldClient.playSilence(durationInMs, TextToSpeech.QUEUE_ADD,
+                    createParameters(null, utteranceId)) != TextToSpeech.SUCCESS) {
+                removeCallbackAndErr(utteranceId.toUniqueString(),
+                        TextToSpeechClient.Status.ERROR_UNKNOWN);
+            }
+        }
+    }
+
+    @Override
+    public void queueAudio(final Uri audioUri, final UtteranceId utteranceId,
+            final RequestConfig config, final RequestCallbacks callbacks) {
+        if (mOldClient == null || !mConnected) {
+            Log.e(TAG, "Client is not connected");
+            return;
+        }
+
+        synchronized (mOldClient) {
+            if (callbacks != null) {
+                addCallback(utteranceId, callbacks);
+            } else {
+                addCallback(utteranceId, mDefaultRequestCallbacks);
+            }
+            final String earconName = audioUri.toString();
+            mOldClient.setLanguage(config.getVoice().getLocale());
+            mOldClient.addEarcon(earconName, earconName);
+            if (mOldClient.playEarcon(earconName, TextToSpeech.QUEUE_ADD,
+                    createParameters(config, utteranceId)) != TextToSpeech.SUCCESS) {
+                removeCallbackAndErr(utteranceId.toUniqueString(),
+                        TextToSpeechClient.Status.ERROR_UNKNOWN);
+            }
+        }
+    }
+
+    /** Read default speech speed rate from settings */
+    float getDefaultSpeechRate() {
+        return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, 100) / 100.0f;
+    }
+
+    int getSecureSettingInt(String name, int defaultValue) {
+        return Settings.Secure.getInt(mContext.getContentResolver(), name, defaultValue);
+    }
+}
\ No newline at end of file
diff --git a/v4/java/android/support/v4/speech/tts/TextToSpeechClientCompat.java b/v4/java/android/support/v4/speech/tts/TextToSpeechClientCompat.java
new file mode 100644
index 0000000..2f7489a
--- /dev/null
+++ b/v4/java/android/support/v4/speech/tts/TextToSpeechClientCompat.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.support.v4.speech.tts;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.v4.speech.tts.TextToSpeechClient.ConnectionCallbacks;
+import android.support.v4.speech.tts.TextToSpeechClient.RequestCallbacks;
+
+/**
+ * Helper for accessing features in {@link android.speech.tts.TextToSpeechClient}
+ * introduced in API level 20 in a backwards compatible fashion.
+ */
+public class TextToSpeechClientCompat {
+    /**
+     * Create TextToSpeech service client.
+     *
+     * Will connect to the default TTS service. In order to be usable,
+     * {@link TextToSpeechClient#connect()} need to be called first and successful
+     * connection callback need to be received.
+     *
+     * @param context
+     *            The context this instance is running in.
+     * @param engine
+     *            Package name of requested TTS engine. If it's null, then default engine will
+     *            be selected regardless of {@code fallbackToDefaultEngine} parameter value.
+     * @param fallbackToDefaultEngine
+     *            If requested engine is not available, should we fallback to the default engine?
+     * @param defaultRequestCallbacks
+     *            Default request callbacks, it will be used for all synthesis requests without
+     *            supplied RequestCallbacks instance. Can't be null.
+     * @param connectionCallbacks
+     *            Callbacks for connecting and disconnecting from the service. Can't be null.
+     */
+    public static TextToSpeechClient createTextToSpeechClient(Context context,
+            String engine, boolean fallbackToDefaultEngine,
+            RequestCallbacks defaultRequestCallbacks,
+            ConnectionCallbacks connectionCallbacks) {
+        ITextToSpeechClient implementation;
+        if (Build.VERSION.CODENAME.equals("L")) {
+            implementation = new TTSImplementationV2();
+        } else {
+            implementation = new TTSImplementationV1();
+        }
+        return new TextToSpeechClient(implementation, context, engine, fallbackToDefaultEngine,
+                defaultRequestCallbacks, connectionCallbacks);
+    }
+
+    /**
+     * Create TextToSpeech service client. Will connect to the default TTS
+     * service. In order to be usable, {@link TextToSpeechClient#connect()} need to be called
+     * first and successful connection callback need to be received.
+     *
+     * @param context Context this instance is running in.
+     * @param defaultRequestCallbacks Default request callbacks, it
+     *            will be used for all synthesis requests without supplied
+     *            RequestCallbacks instance. Can't be null.
+     * @param connectionCallbacks Callbacks for connecting and disconnecting
+     *            from the service. Can't be null.
+     */
+    public static TextToSpeechClient createTextToSpeechClient(Context context,
+            RequestCallbacks defaultRequestCallbacks, ConnectionCallbacks connectionCallbacks) {
+        return createTextToSpeechClient(context, null, true, defaultRequestCallbacks,
+                connectionCallbacks);
+    }
+
+}
diff --git a/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
index 43aab0a..d9fd9b6 100644
--- a/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/v4/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -22,8 +22,6 @@
 import android.support.v4.accessibilityservice.AccessibilityServiceInfoCompat;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -34,6 +32,85 @@
  */
 public class AccessibilityNodeInfoCompat {
 
+    public static class CollectionInfoCompat {
+        private final Object mInfo;
+
+        private CollectionInfoCompat(Object info) {
+            mInfo = info;
+        }
+
+        public int getColumnCount() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionInfo.getColumnCount(mInfo);
+        }
+
+        public int getRowCount() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionInfo.getRowCount(mInfo);
+        }
+
+        public boolean isHierarchical() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionInfo.isHierarchical(mInfo);
+        }
+    }
+
+    public static class CollectionItemInfoCompat {
+        private final Object mInfo;
+
+        private CollectionItemInfoCompat(Object info) {
+            mInfo = info;
+        }
+
+        public int getColumnIndex() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getColumnIndex(mInfo);
+        }
+
+        public int getColumnSpan() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getColumnSpan(mInfo);
+        }
+
+        public int getRowIndex() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getRowIndex(mInfo);
+        }
+
+        public int getRowSpan() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.getRowSpan(mInfo);
+        }
+
+        public boolean isHeading() {
+            return AccessibilityNodeInfoCompatKitKat.CollectionItemInfo.isHeading(mInfo);
+        }
+    }
+
+    public static class RangeInfoCompat {
+        /** Range type: integer. */
+        public static final int RANGE_TYPE_INT = 0;
+        /** Range type: float. */
+        public static final int RANGE_TYPE_FLOAT = 1;
+        /** Range type: percent with values from zero to one.*/
+        public static final int RANGE_TYPE_PERCENT = 2;
+
+        private final Object mInfo;
+
+        private RangeInfoCompat(Object info) {
+            mInfo = info;
+        }
+
+        public float getCurrent() {
+            return AccessibilityNodeInfoCompatKitKat.RangeInfo.getCurrent(mInfo);
+        }
+
+        public float getMax() {
+            return AccessibilityNodeInfoCompatKitKat.RangeInfo.getMax(mInfo);
+        }
+
+        public float getMin() {
+            return AccessibilityNodeInfoCompatKitKat.RangeInfo.getMin(mInfo);
+        }
+
+        public int getType() {
+            return AccessibilityNodeInfoCompatKitKat.RangeInfo.getType(mInfo);
+        }
+    }
+
     static interface AccessibilityNodeInfoImpl {
         public Object obtain();
         public Object obtain(View source);
@@ -99,6 +176,9 @@
         public void setViewIdResourceName(Object info, String viewId);
         public int getLiveRegion(Object info);
         public void setLiveRegion(Object info, int mode);
+        public Object getCollectionInfo(Object info);
+        public Object getCollectionItemInfo(Object info);
+        public Object getRangeInfo(Object info);
     }
 
     static class AccessibilityNodeInfoStubImpl implements AccessibilityNodeInfoImpl {
@@ -421,6 +501,21 @@
         public void setLiveRegion(Object info, int mode) {
             // No-op
         }
+
+        @Override
+        public Object getCollectionInfo(Object info) {
+            return null;
+        }
+
+        @Override
+        public Object getCollectionItemInfo(Object info) {
+            return null;
+        }
+
+        @Override
+        public Object getRangeInfo(Object info) {
+            return null;
+        }
     }
 
     static class AccessibilityNodeInfoIcsImpl extends AccessibilityNodeInfoStubImpl {
@@ -750,6 +845,21 @@
         public void setLiveRegion(Object info, int mode) {
             AccessibilityNodeInfoCompatKitKat.setLiveRegion(info, mode);
         }
+
+        @Override
+        public Object getCollectionInfo(Object info) {
+            return AccessibilityNodeInfoCompatKitKat.getCollectionInfo(info);
+        }
+
+        @Override
+        public Object getCollectionItemInfo(Object info) {
+            return AccessibilityNodeInfoCompatKitKat.getCollectionItemInfo(info);
+        }
+
+        @Override
+        public Object getRangeInfo(Object info) {
+            return AccessibilityNodeInfoCompatKitKat.getRangeInfo(info);
+        }
     }
 
     static {
@@ -956,6 +1066,22 @@
      */
     public static final int ACTION_SET_SELECTION = 0x00020000;
 
+    /**
+     * Action that sets the text of the node. Performing the action without argument, using <code>
+     * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
+     * cursor at the end of text.
+     * <p>
+     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
+     * <strong>Example:</strong>
+     * <code><pre><p>
+     *   Bundle arguments = new Bundle();
+     *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
+     *       "android");
+     *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
+     * </code></pre></p>
+     */
+    public static final int ACTION_SET_TEXT = 0x00200000;
+
     // Action arguments
 
     /**
@@ -1019,6 +1145,18 @@
     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
             "ACTION_ARGUMENT_SELECTION_END_INT";
 
+    /**
+     * Argument for specifying the text content to set
+     * <p>
+     * <strong>Type:</strong> CharSequence<br>
+     * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
+     * </p>
+     *
+     * @see #ACTION_SET_TEXT
+     */
+    public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
+            "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+
     // Focus types
 
     /**
@@ -1943,6 +2081,41 @@
         IMPL.setLiveRegion(mInfo, mode);
     }
 
+    /**
+     * Gets the collection info if the node is a collection. A collection
+     * child is always a collection item.
+     *
+     * @return The collection info.
+     */
+    public CollectionInfoCompat getCollectionInfo() {
+        Object info = IMPL.getCollectionInfo(mInfo);
+        if (info == null) return null;
+        return new CollectionInfoCompat(info);
+    }
+
+    /**
+     * Gets the collection item info if the node is a collection item. A collection
+     * item is always a child of a collection.
+     *
+     * @return The collection item info.
+     */
+    public CollectionItemInfoCompat getCollectionItemInfo() {
+        Object info = IMPL.getCollectionItemInfo(mInfo);
+        if (info == null) return null;
+        return new CollectionItemInfoCompat(info);
+    }
+
+    /**
+     * Gets the range info if this node is a range.
+     *
+     * @return The range.
+     */
+    public RangeInfoCompat getRangeInfo() {
+        Object info = IMPL.getRangeInfo(mInfo);
+        if (info == null) return null;
+        return new RangeInfoCompat(info);
+    }
+
     @Override
     public int hashCode() {
         return (mInfo == null) ? 0 : mInfo.hashCode();
diff --git a/v4/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java b/v4/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
new file mode 100644
index 0000000..21f0d49
--- /dev/null
+++ b/v4/jellybean-mr2/android/support/v4/graphics/BitmapCompatJellybeanMR2.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.graphics;
+
+import android.graphics.Bitmap;
+
+class BitmapCompatJellybeanMR2 {
+    public static boolean hasMipMap(Bitmap bitmap) {
+        return bitmap.hasMipMap();
+    }
+
+    public static void setHasMipMap(Bitmap bitmap, boolean hasMipMap) {
+        bitmap.setHasMipMap(hasMipMap);
+    }
+}
diff --git a/v4/kitkat/android/support/v4/app/NotificationCompatKitKat.java b/v4/kitkat/android/support/v4/app/NotificationCompatKitKat.java
index 350b275..9ad8992 100644
--- a/v4/kitkat/android/support/v4/app/NotificationCompatKitKat.java
+++ b/v4/kitkat/android/support/v4/app/NotificationCompatKitKat.java
@@ -40,7 +40,8 @@
                 PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
                 int mProgressMax, int mProgress, boolean mProgressIndeterminate,
                 boolean useChronometer, int priority, CharSequence subText, boolean localOnly,
-                Bundle extras, String groupKey, boolean groupSummary, String sortKey) {
+                ArrayList<String> people, Bundle extras, String groupKey, boolean groupSummary,
+                String sortKey) {
             b = new Notification.Builder(context)
                 .setWhen(n.when)
                 .setSmallIcon(n.icon, n.iconLevel)
@@ -70,6 +71,10 @@
             if (extras != null) {
                 mExtras.putAll(extras);
             }
+            if (people != null && !people.isEmpty()) {
+                mExtras.putStringArray(Notification.EXTRA_PEOPLE,
+                        people.toArray(new String[people.size()]));
+            }
             if (localOnly) {
                 mExtras.putBoolean(NotificationCompatJellybean.EXTRA_LOCAL_ONLY, true);
             }
diff --git a/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java b/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
index 618a02d..4f77b8b 100644
--- a/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
+++ b/v4/kitkat/android/support/v4/view/accessibility/AccessibilityNodeInfoCompatKitKat.java
@@ -22,11 +22,77 @@
  * KitKat-specific AccessibilityNodeInfo API implementation.
  */
 class AccessibilityNodeInfoCompatKitKat {
-    public static int getLiveRegion(Object info) {
+    static int getLiveRegion(Object info) {
         return ((AccessibilityNodeInfo) info).getLiveRegion();
     }
 
-    public static void setLiveRegion(Object info, int mode) {
+    static void setLiveRegion(Object info, int mode) {
         ((AccessibilityNodeInfo) info).setLiveRegion(mode);
     }
+
+    static Object getCollectionInfo(Object info) {
+        return ((AccessibilityNodeInfo) info).getCollectionInfo();
+    }
+
+    static Object getCollectionItemInfo(Object info) {
+        return ((AccessibilityNodeInfo) info).getCollectionItemInfo();
+    }
+
+    static Object getRangeInfo(Object info) {
+        return ((AccessibilityNodeInfo) info).getRangeInfo();
+    }
+
+    static class CollectionInfo {
+        static int getColumnCount(Object info) {
+            return ((AccessibilityNodeInfo.CollectionInfo) info).getColumnCount();
+        }
+
+        static int getRowCount(Object info) {
+            return ((AccessibilityNodeInfo.CollectionInfo) info).getRowCount();
+        }
+
+        static boolean isHierarchical(Object info) {
+            return ((AccessibilityNodeInfo.CollectionInfo) info).isHierarchical();
+        }
+    }
+
+    static class CollectionItemInfo {
+        static int getColumnIndex(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnIndex();
+        }
+
+        static int getColumnSpan(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnSpan();
+        }
+
+        static int getRowIndex(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowIndex();
+        }
+
+        static int getRowSpan(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowSpan();
+        }
+
+        static boolean isHeading(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).isHeading();
+        }
+    }
+
+    static class RangeInfo {
+        static float getCurrent(Object info) {
+            return ((AccessibilityNodeInfo.RangeInfo) info).getCurrent();
+        }
+
+        static float getMax(Object info) {
+            return ((AccessibilityNodeInfo.RangeInfo) info).getMax();
+        }
+
+        static float getMin(Object info) {
+            return ((AccessibilityNodeInfo.RangeInfo) info).getMin();
+        }
+
+        static int getType(Object info) {
+            return ((AccessibilityNodeInfo.RangeInfo) info).getType();
+        }
+    }
 }
diff --git a/v4/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java b/v4/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java
index a5c96b0..d4420fe 100644
--- a/v4/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java
+++ b/v4/tests/java/android/support/v4/widget/GingerbreadScrollerCompatTest.java
@@ -20,6 +20,6 @@
 public class GingerbreadScrollerCompatTest extends ScrollerCompatTestBase {
 
     public GingerbreadScrollerCompatTest() {
-        super(Build.VERSION_CODES.GINGERBREAD);
+        super(9);
     }
 }
diff --git a/v4/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java b/v4/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java
index ccdc68d..7d0e9a5 100644
--- a/v4/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java
+++ b/v4/tests/java/android/support/v4/widget/IcsScrollerCompatTest.java
@@ -20,6 +20,6 @@
 public class IcsScrollerCompatTest extends ScrollerCompatTestBase {
 
     public IcsScrollerCompatTest() {
-        super(Build.VERSION_CODES.ICE_CREAM_SANDWICH);
+        super(14);
     }
 }
diff --git a/v7/appcompat/res/values-af/strings.xml b/v7/appcompat/res/values-af/strings.xml
new file mode 100644
index 0000000..f7348c4
--- /dev/null
+++ b/v7/appcompat/res/values-af/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Klaar"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigeer tuis"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigeer op"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Nog opsies"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Soek"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Soeknavraag"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Vee navraag uit"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Dien navraag in"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Stemsoektog"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Kies \'n program"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Sien alles"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Deel met %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Deel met"</string>
+</resources>
diff --git a/v7/appcompat/res/values-am/strings.xml b/v7/appcompat/res/values-am/strings.xml
new file mode 100644
index 0000000..e849b31
--- /dev/null
+++ b/v7/appcompat/res/values-am/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"ተከናውኗል"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"ወደ መነሻ ይዳስሱ"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ወደ ላይ ይዳስሱ"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"ተጨማሪ አማራጮች"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ፍለጋ"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"የፍለጋ ጥያቄ"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"መጠይቅ አጽዳ"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"መጠይቅ ያስረክቡ"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"የድምፅ ፍለጋ"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"መተግበሪያ ይምረጡ"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ሁሉንም ይመልከቱ"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"ከ%s ጋር ያጋሩ"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ከሚከተለው ጋር ያጋሩ"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ar/strings.xml b/v7/appcompat/res/values-ar/strings.xml
new file mode 100644
index 0000000..93dde04
--- /dev/null
+++ b/v7/appcompat/res/values-ar/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"تم"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"التنقل إلى الشاشة الرئيسية"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"التنقل إلى أعلى"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"خيارات إضافية"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"بحث"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"طلب البحث"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"محو طلب البحث"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"إرسال طلب البحث"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"البحث الصوتي"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"اختيار تطبيق"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"عرض الكل"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"‏مشاركة مع %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"مشاركة مع"</string>
+</resources>
diff --git a/v7/appcompat/res/values-bg/strings.xml b/v7/appcompat/res/values-bg/strings.xml
new file mode 100644
index 0000000..de3bde8
--- /dev/null
+++ b/v7/appcompat/res/values-bg/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Готово"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Придвижване към „Начало“"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Придвижване нагоре"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Още опции"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Търсене"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Заявка за търсене"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Изчистване на заявката"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Изпращане на заявката"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Гласово търсене"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Изберете приложение"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Вижте всички"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Споделяне със: %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Споделяне със:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ca/strings.xml b/v7/appcompat/res/values-ca/strings.xml
new file mode 100644
index 0000000..bfd4cb0
--- /dev/null
+++ b/v7/appcompat/res/values-ca/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Fet"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navega a la pàgina d\'inici"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navega cap a dalt"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Més opcions"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Cerca"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de cerca"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Esborra la consulta"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Envia la consulta"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Cerca per veu"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Selecciona una aplicació"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Mostra\'ls tots"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Comparteix amb %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Comparteix amb"</string>
+</resources>
diff --git a/v7/appcompat/res/values-cs/strings.xml b/v7/appcompat/res/values-cs/strings.xml
new file mode 100644
index 0000000..1465fdc
--- /dev/null
+++ b/v7/appcompat/res/values-cs/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Hotovo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Přejít na plochu"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Přejít nahoru"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Více možností"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Hledat"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Vyhledávací dotaz"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Smazat dotaz"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Odeslat dotaz"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Hlasové vyhledávání"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Vybrat aplikaci"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Zobrazit vše"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Sdílet pomocí %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Sdílet pomocí"</string>
+</resources>
diff --git a/v7/appcompat/res/values-da/strings.xml b/v7/appcompat/res/values-da/strings.xml
new file mode 100644
index 0000000..b178513
--- /dev/null
+++ b/v7/appcompat/res/values-da/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Luk"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Naviger hjem"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Naviger op"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Flere muligheder"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Søg"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Søgeforespørgsel"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Ryd forespørgslen"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Indsend forespørgslen"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Stemmesøgning"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Vælg en app"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Se alle"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Del med %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Del med"</string>
+</resources>
diff --git a/v7/appcompat/res/values-de/strings.xml b/v7/appcompat/res/values-de/strings.xml
new file mode 100644
index 0000000..6da4b71
--- /dev/null
+++ b/v7/appcompat/res/values-de/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Fertig"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Zur Startseite"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Nach oben"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Weitere Optionen"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Suchen"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Suchanfrage"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Suchanfrage löschen"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Suchanfrage senden"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Sprachsuche"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"App auswählen"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Alle ansehen"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Freigeben für %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Freigeben für"</string>
+</resources>
diff --git a/v7/appcompat/res/values-el/strings.xml b/v7/appcompat/res/values-el/strings.xml
new file mode 100644
index 0000000..4c0e286
--- /dev/null
+++ b/v7/appcompat/res/values-el/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Τέλος"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Πλοήγηση στην αρχική σελίδα"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Πλοήγηση προς τα επάνω"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Περισσότερες επιλογές"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Αναζήτηση"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Ερώτημα αναζήτησης"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Διαγραφή ερωτήματος"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Υποβολή ερωτήματος"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Φωνητική αναζήτηση"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Επιλέξτε κάποια εφαρμογή"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Προβολή όλων"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Κοινή χρήση με %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Κοινή χρήση με"</string>
+</resources>
diff --git a/v7/appcompat/res/values-en-rGB/strings.xml b/v7/appcompat/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..3ec0b0e
--- /dev/null
+++ b/v7/appcompat/res/values-en-rGB/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Finished"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigate home"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigate up"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"More options"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Search"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Search query"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Clear query"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Submit query"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Voice search"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Choose an app"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"See all"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Share with %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Share with"</string>
+</resources>
diff --git a/v7/appcompat/res/values-en-rIN/strings.xml b/v7/appcompat/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..3ec0b0e
--- /dev/null
+++ b/v7/appcompat/res/values-en-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Finished"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigate home"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigate up"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"More options"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Search"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Search query"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Clear query"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Submit query"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Voice search"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Choose an app"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"See all"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Share with %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Share with"</string>
+</resources>
diff --git a/v7/appcompat/res/values-es-rUS/strings.xml b/v7/appcompat/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..6ab7942
--- /dev/null
+++ b/v7/appcompat/res/values-es-rUS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Listo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navegar a la página principal"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navegar hacia arriba"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Más opciones"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Búsqueda"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de búsqueda"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Eliminar la consulta"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Búsqueda por voz"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Elige una aplicación."</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Ver todo"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Compartir con %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Compartir con"</string>
+</resources>
diff --git a/v7/appcompat/res/values-es/strings.xml b/v7/appcompat/res/values-es/strings.xml
new file mode 100644
index 0000000..ed15b35
--- /dev/null
+++ b/v7/appcompat/res/values-es/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Listo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ir a la pantalla de inicio"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Desplazarse hacia arriba"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Más opciones"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Buscar"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Borrar consulta"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Búsqueda por voz"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Seleccionar una aplicación"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Ver todo"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Compartir con %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Compartir con"</string>
+</resources>
diff --git a/v7/appcompat/res/values-et-rEE/strings.xml b/v7/appcompat/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..2ae925d
--- /dev/null
+++ b/v7/appcompat/res/values-et-rEE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Valmis"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigeerimine avaekraanile"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigeerimine üles"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Rohkem valikuid"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Otsing"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Otsingupäring"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Päringu tühistamine"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Päringu esitamine"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Häälotsing"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Valige rakendus"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Kuva kõik"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Jagamine kasutajaga %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Jagamine:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-fa/strings.xml b/v7/appcompat/res/values-fa/strings.xml
new file mode 100644
index 0000000..8e10e92
--- /dev/null
+++ b/v7/appcompat/res/values-fa/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"انجام شد"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"پیمایش به صفحه اصلی"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"پیمایش به بالا"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"گزینه‌های بیشتر"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"جستجو"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"عبارت جستجو"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"پاک کردن عبارت جستجو"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ارسال عبارت جستجو"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"جستجوی شفاهی"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"انتخاب برنامه"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"مشاهده همه"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"‏اشتراک‌گذاری با %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"اشتراک‌گذاری با"</string>
+</resources>
diff --git a/v7/appcompat/res/values-fi/strings.xml b/v7/appcompat/res/values-fi/strings.xml
new file mode 100644
index 0000000..6755cea
--- /dev/null
+++ b/v7/appcompat/res/values-fi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Valmis"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Siirry etusivulle"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Siirry ylös"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Lisää"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Haku"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Hakulauseke"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Tyhjennä kysely"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Lähetä kysely"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Puhehaku"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Valitse sovellus"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Näytä kaikki"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Jakaminen: %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Jakaminen:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-fr-rCA/strings.xml b/v7/appcompat/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..417705a
--- /dev/null
+++ b/v7/appcompat/res/values-fr-rCA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Terminé"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Revenir à l\'accueil"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Revenir en haut de la page"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Plus d\'options"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Rechercher"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Requête de recherche"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Effacer la requête"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Envoyer la requête"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Recherche vocale"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Sélectionnez une application"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Voir toutes les chaînes"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Partager avec %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Partager avec"</string>
+</resources>
diff --git a/v7/appcompat/res/values-fr/strings.xml b/v7/appcompat/res/values-fr/strings.xml
new file mode 100644
index 0000000..27b8f38
--- /dev/null
+++ b/v7/appcompat/res/values-fr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"OK"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Revenir à l\'accueil"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Revenir en haut de la page"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Plus d\'options"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Rechercher"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Requête de recherche"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Effacer la requête"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Envoyer la requête"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Recherche vocale"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Sélectionner une application"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Tout afficher"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Partager avec %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Partager avec"</string>
+</resources>
diff --git a/v7/appcompat/res/values-hi/strings.xml b/v7/appcompat/res/values-hi/strings.xml
new file mode 100644
index 0000000..b236ebb
--- /dev/null
+++ b/v7/appcompat/res/values-hi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"पूर्ण"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"मुखपृष्ठ पर नेविगेट करें"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ऊपर नेविगेट करें"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"अधिक विकल्प"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"खोजें"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"खोज क्वेरी"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"क्‍वेरी साफ़ करें"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"क्वेरी सबमिट करें"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"ध्वनि खोज"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"कोई एप्‍लिकेशन चुनें"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"सभी देखें"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"%s के साथ साझा करें"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"इसके द्वारा साझा करें"</string>
+</resources>
diff --git a/v7/appcompat/res/values-hr/strings.xml b/v7/appcompat/res/values-hr/strings.xml
new file mode 100644
index 0000000..680e39f
--- /dev/null
+++ b/v7/appcompat/res/values-hr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Gotovo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Idi na početnu"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Idi gore"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Dodatne opcije"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pretraživanje"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Upit za pretraživanje"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Izbriši upit"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pošalji upit"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Glasovno pretraživanje"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Odabir aplikacije"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Prikaži sve"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Dijeljenje sa: %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Dijeljenje sa"</string>
+</resources>
diff --git a/v7/appcompat/res/values-hu/strings.xml b/v7/appcompat/res/values-hu/strings.xml
new file mode 100644
index 0000000..52dafb0
--- /dev/null
+++ b/v7/appcompat/res/values-hu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Kész"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ugrás a főoldalra"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Felfelé mozgatás"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"További lehetőségek"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Keresés"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Keresési lekérdezés"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Lekérdezés törlése"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Lekérdezés küldése"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Hangalapú keresés"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Válasszon ki egy alkalmazást"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Összes megtekintése"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Megosztás a következővel: %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Megosztás a következővel:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-hy-rAM/strings.xml b/v7/appcompat/res/values-hy-rAM/strings.xml
new file mode 100644
index 0000000..6c0ee27
--- /dev/null
+++ b/v7/appcompat/res/values-hy-rAM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Կատարված է"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ուղղվել տուն"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Ուղղվել վերև"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Այլ ընտրանքներ"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Որոնել"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Որոնման հարցում"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Մաքրել հարցումը"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Ուղարկել հարցումը"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Ձայնային որոնում"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Ընտրել ծրագիր"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Տեսնել բոլորը"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Տարածել ըստ %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Տարածել"</string>
+</resources>
diff --git a/v7/appcompat/res/values-in/strings.xml b/v7/appcompat/res/values-in/strings.xml
new file mode 100644
index 0000000..9481e83
--- /dev/null
+++ b/v7/appcompat/res/values-in/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Selesai"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigasi ke beranda"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigasi naik"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Opsi lain"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Telusuri"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Kueri penelusuran"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Hapus kueri"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Kirim kueri"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Penelusuran suara"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Pilih aplikasi"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Lihat semua"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Bagikan dengan %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Bagikan dengan"</string>
+</resources>
diff --git a/v7/appcompat/res/values-it/strings.xml b/v7/appcompat/res/values-it/strings.xml
new file mode 100644
index 0000000..a8b0f2c
--- /dev/null
+++ b/v7/appcompat/res/values-it/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Fine"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Vai alla home page"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Vai in alto"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Altre opzioni"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Cerca"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Query di ricerca"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Cancella query"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Invia query"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Ricerca vocale"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Scegli un\'applicazione"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Visualizza tutte"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Condividi con %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Condividi con"</string>
+</resources>
diff --git a/v7/appcompat/res/values-iw/strings.xml b/v7/appcompat/res/values-iw/strings.xml
new file mode 100644
index 0000000..1af07df
--- /dev/null
+++ b/v7/appcompat/res/values-iw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"בוצע"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"נווט לדף הבית"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"נווט למעלה"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"עוד אפשרויות"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"חפש"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"שאילתת חיפוש"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"מחק שאילתה"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"שלח שאילתה"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"חיפוש קולי"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"בחר אפליקציה"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ראה הכול"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"‏שתף עם %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"שתף עם"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ja/strings.xml b/v7/appcompat/res/values-ja/strings.xml
new file mode 100644
index 0000000..659358a
--- /dev/null
+++ b/v7/appcompat/res/values-ja/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"完了"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"ホームへ移動"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"上へ移動"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"その他のオプション"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"検索"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"検索キーワード"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"検索キーワードを削除"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"検索キーワードを送信"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"音声検索"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"アプリの選択"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"すべて表示"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"%sと共有"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"共有"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ka-rGE/strings.xml b/v7/appcompat/res/values-ka-rGE/strings.xml
new file mode 100644
index 0000000..0c430b1
--- /dev/null
+++ b/v7/appcompat/res/values-ka-rGE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"დასრულდა"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"მთავარზე ნავიგაცია"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ზემოთ ნავიგაცია"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"მეტი ვარიანტები"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ძიება"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ძიების მოთხოვნა"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"მოთხოვნის გასუფთავება"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"მოთხოვნის გადაგზავნა"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"ხმოვანი ძიება"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"აპის არჩევა"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ყველას ნახვა"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"%s-თან გაზიარება"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"გაზიარება:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-km-rKH/strings.xml b/v7/appcompat/res/values-km-rKH/strings.xml
new file mode 100644
index 0000000..597aec5
--- /dev/null
+++ b/v7/appcompat/res/values-km-rKH/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"រួចរាល់"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"រកមើល​ទៅ​ដើម"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"រកមើល​ឡើងលើ"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"ជម្រើស​ច្រើន​ទៀត"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ស្វែងរក"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ស្វែងរក​សំណួរ"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"សម្អាត​សំណួរ"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ដាក់​​​ស្នើ​សំណួរ"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"ការស្វែងរក​សំឡេង"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"ជ្រើស​កម្មវិធី"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"មើល​ទាំងអស់"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"ចែករំលែក​ជាមួយ %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ចែករំលែក​ជាមួយ"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ko/strings.xml b/v7/appcompat/res/values-ko/strings.xml
new file mode 100644
index 0000000..d331975
--- /dev/null
+++ b/v7/appcompat/res/values-ko/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"완료"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"홈 탐색"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"위로 탐색"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"옵션 더보기"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"검색"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"검색어"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"검색어 삭제"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"검색어 보내기"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"음성 검색"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"앱 선택"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"전체 보기"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"%s와(과) 공유"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"공유 대상"</string>
+</resources>
diff --git a/v7/appcompat/res/values-lo-rLA/strings.xml b/v7/appcompat/res/values-lo-rLA/strings.xml
new file mode 100644
index 0000000..7eb42ea
--- /dev/null
+++ b/v7/appcompat/res/values-lo-rLA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"ແລ້ວໆ"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"ກັບໄປໜ້າຫຼັກ"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ຂຶ້ນເທິງ"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"ໂຕເລືອກອື່ນ"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ຊອກຫາ"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ຊອກຫາ"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ລຶບຂໍ້ຄວາມຊອກຫາ"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ສົ່ງການຊອກຫາ"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"ຊອກຫາດ້ວຍສຽງ"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"ເລືອກແອັບຯ"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ເບິ່ງທັງຫມົດ"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"ແບ່ງ​ປັນ​ກັບ​ %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"ແບ່ງປັນກັບ"</string>
+</resources>
diff --git a/v7/appcompat/res/values-lt/strings.xml b/v7/appcompat/res/values-lt/strings.xml
new file mode 100644
index 0000000..c4738a7
--- /dev/null
+++ b/v7/appcompat/res/values-lt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Atlikta"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Eiti į pagrindinį puslapį"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Eiti į viršų"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Daugiau parinkčių"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Paieška"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Paieškos užklausa"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Išvalyti užklausą"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pateikti užklausą"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Paieška balsu"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Pasirinkti programą"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Peržiūrėti viską"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Bendrinti naudojant „%s“"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Bendrinti naudojant"</string>
+</resources>
diff --git a/v7/appcompat/res/values-lv/strings.xml b/v7/appcompat/res/values-lv/strings.xml
new file mode 100644
index 0000000..c33858a
--- /dev/null
+++ b/v7/appcompat/res/values-lv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Gatavs"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Pārvietoties uz sākuma ekrānu"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Pārvietoties augšup"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Vairāk opciju"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Meklēt"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Meklēšanas vaicājums"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Notīrīt vaicājumu"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Iesniegt vaicājumu"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Meklēšana ar balsi"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Izvēlieties lietotni"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Skatīt visu"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Kopīgot ar %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Kopīgot ar:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-mn-rMN/strings.xml b/v7/appcompat/res/values-mn-rMN/strings.xml
new file mode 100644
index 0000000..203e959
--- /dev/null
+++ b/v7/appcompat/res/values-mn-rMN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Дууссан"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Нүүр хуудас руу шилжих"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Дээш шилжих"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Нэмэлт сонголтууд"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Хайх"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Хайх асуулга"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Асуулгыг цэвэрлэх"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Асуулгыг илгээх"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Дуут хайлт"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Апп сонгох"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Бүгдийг харах"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"%s-тай хуваалцах"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Хуваалцах"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ms-rMY/strings.xml b/v7/appcompat/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..b174068
--- /dev/null
+++ b/v7/appcompat/res/values-ms-rMY/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Selesai"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigasi skrin utama"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigasi ke atas"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Lagi pilihan"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Cari"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Pertanyaan carian"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Kosongkan pertanyaan"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Serah pertanyaan"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Carian suara"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Pilih apl"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Lihat semua"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Kongsi dengan %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Kongsi dengan"</string>
+</resources>
diff --git a/v7/appcompat/res/values-nb/strings.xml b/v7/appcompat/res/values-nb/strings.xml
new file mode 100644
index 0000000..6630acf
--- /dev/null
+++ b/v7/appcompat/res/values-nb/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Ferdig"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Gå til startsiden"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Gå opp"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Flere alternativer"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Søk"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Søkeord"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Slett søket"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Utfør søket"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Talesøk"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Velg en app"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Se alle"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Del med %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Del med"</string>
+</resources>
diff --git a/v7/appcompat/res/values-nl/strings.xml b/v7/appcompat/res/values-nl/strings.xml
new file mode 100644
index 0000000..1375f9e
--- /dev/null
+++ b/v7/appcompat/res/values-nl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Gereed"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigeren naar startpositie"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Omhoog navigeren"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Meer opties"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Zoeken"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Zoekopdracht"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Zoekopdracht wissen"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Zoekopdracht verzenden"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Gesproken zoekopdracht"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Een app selecteren"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Alles weergeven"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Delen met %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Delen met"</string>
+</resources>
diff --git a/v7/appcompat/res/values-pl/strings.xml b/v7/appcompat/res/values-pl/strings.xml
new file mode 100644
index 0000000..aa1ba79
--- /dev/null
+++ b/v7/appcompat/res/values-pl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Gotowe"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Przejdź do strony głównej"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Przejdź wyżej"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Więcej opcji"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Szukaj"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Wyszukiwane hasło"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Wyczyść zapytanie"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Wyślij zapytanie"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Wyszukiwanie głosowe"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Wybierz aplikację"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Zobacz wszystkie"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Udostępnij dla %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Udostępnij dla"</string>
+</resources>
diff --git a/v7/appcompat/res/values-pt-rPT/strings.xml b/v7/appcompat/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..0d63f5f
--- /dev/null
+++ b/v7/appcompat/res/values-pt-rPT/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Concluído"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navegar para a página inicial"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navegar para cima"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Mais opções"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pesquisar"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de pesquisa"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Limpar consulta"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Pesquisa por voz"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Escolher uma aplicação"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Ver tudo"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Partilhar com %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Partilhar com"</string>
+</resources>
diff --git a/v7/appcompat/res/values-pt/strings.xml b/v7/appcompat/res/values-pt/strings.xml
new file mode 100644
index 0000000..88b09ea
--- /dev/null
+++ b/v7/appcompat/res/values-pt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Concluído"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navegar para a página inicial"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navegar para cima"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Mais opções"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Pesquisar"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Consulta de pesquisa"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Limpar consulta"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Enviar consulta"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Pesquisa por voz"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Selecione um aplicativo"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Ver tudo"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Compartilhar com %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Compartilhar com"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ro/strings.xml b/v7/appcompat/res/values-ro/strings.xml
new file mode 100644
index 0000000..36a7b31
--- /dev/null
+++ b/v7/appcompat/res/values-ro/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Terminat"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Navigați la ecranul de pornire"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigați în sus"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Mai multe opțiuni"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Căutați"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Interogare de căutare"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Ștergeți interogarea"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Trimiteți interogarea"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Căutare vocală"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Alegeți o aplicaţie"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Afișați-le pe toate"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Trimiteți la %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Trimiteți la"</string>
+</resources>
diff --git a/v7/appcompat/res/values-ru/strings.xml b/v7/appcompat/res/values-ru/strings.xml
new file mode 100644
index 0000000..5c22e5e
--- /dev/null
+++ b/v7/appcompat/res/values-ru/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Готово"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Перейти на главный экран"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Перейти вверх"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Другие параметры"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Поиск"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Поисковый запрос"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Удалить запрос"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Отправить запрос"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Голосовой поиск"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Выбрать приложение"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Показать все"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Открыть доступ пользователю %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Открыть доступ"</string>
+</resources>
diff --git a/v7/appcompat/res/values-sk/strings.xml b/v7/appcompat/res/values-sk/strings.xml
new file mode 100644
index 0000000..253f3e5
--- /dev/null
+++ b/v7/appcompat/res/values-sk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Hotovo"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Prejsť na plochu"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Prejsť hore"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Ďalšie možnosti"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Hľadať"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Vyhľadávací dopyt"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Vymazať dopyt"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Odoslať dopyt"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Hlasové vyhľadávanie"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Zvoľte aplikáciu"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Zobraziť všetko"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Zdieľať pomocou %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Zdieľať pomocou"</string>
+</resources>
diff --git a/v7/appcompat/res/values-sl/strings.xml b/v7/appcompat/res/values-sl/strings.xml
new file mode 100644
index 0000000..8e3e23e
--- /dev/null
+++ b/v7/appcompat/res/values-sl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Končano"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Krmarjenje domov"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Krmarjenje navzgor"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Več možnosti"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Iskanje"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Iskalna poizvedba"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Izbris poizvedbe"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Pošiljanje poizvedbe"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Glasovno iskanje"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Izbira aplikacije"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Pokaži vse"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Deljenje z:"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Deljenje z"</string>
+</resources>
diff --git a/v7/appcompat/res/values-sr/strings.xml b/v7/appcompat/res/values-sr/strings.xml
new file mode 100644
index 0000000..213c939
--- /dev/null
+++ b/v7/appcompat/res/values-sr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Готово"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Одлазак на Почетну"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Кретање нагоре"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Још опција"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Претрага"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Упит за претрагу"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Брисање упита"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Слање упита"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Гласовна претрага"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Избор апликације"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Прикажи све"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Дели са апликацијом %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Дели са"</string>
+</resources>
diff --git a/v7/appcompat/res/values-sv/strings.xml b/v7/appcompat/res/values-sv/strings.xml
new file mode 100644
index 0000000..49c7a5d
--- /dev/null
+++ b/v7/appcompat/res/values-sv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Klart"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Visa startsidan"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Navigera uppåt"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Fler alternativ"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Sök"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Sökfråga"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Ta bort frågan"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Skicka fråga"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Röstsökning"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Välj en app"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Visa alla"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Dela med %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Dela med"</string>
+</resources>
diff --git a/v7/appcompat/res/values-sw/strings.xml b/v7/appcompat/res/values-sw/strings.xml
new file mode 100644
index 0000000..6455ba5
--- /dev/null
+++ b/v7/appcompat/res/values-sw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Nimemaliza"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Nenda mwanzo"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Nenda juu"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Chaguo zaidi"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Tafuta"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Hoja ya utafutaji"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Futa hoja"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Wasilisha hoja"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Tafuta kwa kutamka"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Chagua programu"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Angalia zote"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Shiriki na %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Shiriki na:"</string>
+</resources>
diff --git a/v7/appcompat/res/values-th/strings.xml b/v7/appcompat/res/values-th/strings.xml
new file mode 100644
index 0000000..275dc57
--- /dev/null
+++ b/v7/appcompat/res/values-th/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"เสร็จสิ้น"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"นำทางไปหน้าแรก"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"นำทางขึ้น"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"ตัวเลือกอื่น"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"ค้นหา"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"ข้อความค้นหา"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"ล้างข้อความค้นหา"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"ส่งข้อความค้นหา"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"ค้นหาด้วยเสียง"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"เลือกแอป"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ดูทั้งหมด"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"แชร์กับ %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"แชร์กับ"</string>
+</resources>
diff --git a/v7/appcompat/res/values-tl/strings.xml b/v7/appcompat/res/values-tl/strings.xml
new file mode 100644
index 0000000..e0705d6
--- /dev/null
+++ b/v7/appcompat/res/values-tl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Tapos na"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Mag-navigate patungo sa home"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Mag-navigate pataas"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Higit pang mga opsyon"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Maghanap"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Query sa paghahanap"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"I-clear ang query"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Isumite ang query"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Paghahanap gamit ang boses"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Pumili ng isang app"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Tingnan lahat"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Ibahagi sa/kay %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Ibahagi sa/kay"</string>
+</resources>
diff --git a/v7/appcompat/res/values-tr/strings.xml b/v7/appcompat/res/values-tr/strings.xml
new file mode 100644
index 0000000..61cb966
--- /dev/null
+++ b/v7/appcompat/res/values-tr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Tamamlandı"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Ana ekrana git"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Yukarı git"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Diğer seçenekler"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Ara"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Arama sorgusu"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Sorguyu temizle"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Sorguyu gönder"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Sesli arama"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Bir uygulama seçin"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Tümünü göster"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"%s ile paylaş"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Şununla paylaş"</string>
+</resources>
diff --git a/v7/appcompat/res/values-uk/strings.xml b/v7/appcompat/res/values-uk/strings.xml
new file mode 100644
index 0000000..f670140
--- /dev/null
+++ b/v7/appcompat/res/values-uk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Готово"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Перейти на головний"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Перейти вгору"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Інші опції"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Пошук"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Пошуковий запит"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Очистити запит"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Надіслати запит"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Голосовий пошук"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Вибрати програму"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Переглянути всі"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Надіслати через %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Надіслати через"</string>
+</resources>
diff --git a/v7/appcompat/res/values-v11/themes_base.xml b/v7/appcompat/res/values-v11/themes_base.xml
index ddd9d59..0539a1a 100644
--- a/v7/appcompat/res/values-v11/themes_base.xml
+++ b/v7/appcompat/res/values-v11/themes_base.xml
@@ -16,6 +16,20 @@
 
 <resources>
 
+    <!--
+        Theme in the "Theme.Platform.AppCompat" family are designed to be aliases for the default
+        theme on a given platform version. They should not set any styleable attributes. Instead
+        you should create a "Theme.Base" theme which inherits from a "Theme.Platform" theme.
+    -->
+    <eat-comment/>
+    <style name="Theme.Platform.AppCompat" parent="android:Theme.Holo" />
+
+    <style name="Theme.Platform.AppCompat.Light" parent="android:Theme.Holo.Light" />
+
+    <style name="Theme.Platform.AppCompat.Dialog" parent="android:Theme.Holo.Dialog" />
+
+    <style name="Theme.Platform.AppCompat.Light.Dialog" parent="android:Theme.Holo.Light.Dialog" />
+
     <!-- Themes in the "Theme.Base" family vary based on the current platform
           version to provide the correct basis on each device. You probably don't
           want to use them directly in your apps.
@@ -28,7 +42,7 @@
     <eat-comment/>
 
     <!-- Base platform-dependent theme  -->
-    <style name="Theme.Base" parent="android:Theme.Holo">
+    <style name="Theme.Base" parent="Theme.Platform.AppCompat">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionBar">false</item>
 
@@ -46,7 +60,7 @@
     </style>
 
     <!-- Base platform-dependent theme providing a light-themed activity. -->
-    <style name="Theme.Base.Light" parent="android:Theme.Holo.Light">
+    <style name="Theme.Base.Light" parent="Theme.Platform.AppCompat.Light">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionBar">false</item>
 
@@ -63,7 +77,7 @@
         <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
     </style>
 
-    <style name="Theme.Base.AppCompat.Dialog.FixedSize" parent="android:Theme.Holo.Dialog">
+    <style name="Theme.Base.AppCompat.Dialog.FixedSize" parent="Theme.Platform.AppCompat.Dialog">
         <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
         <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
         <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
@@ -77,7 +91,7 @@
     </style>
 
     <style name="Theme.Base.AppCompat.Dialog.Light.FixedSize"
-           parent="android:Theme.Holo.Light.Dialog">
+           parent="Theme.Platform.AppCompat.Light.Dialog">
         <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
         <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
         <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
diff --git a/v7/appcompat/res/values-v14/themes_base.xml b/v7/appcompat/res/values-v14/themes_base.xml
index 3b30074..e182b2e 100644
--- a/v7/appcompat/res/values-v14/themes_base.xml
+++ b/v7/appcompat/res/values-v14/themes_base.xml
@@ -16,6 +16,23 @@
 
 <resources>
 
+    <!--
+        Theme in the "Theme.Platform.AppCompat" family are designed to be aliases for the default
+        theme on a given platform version. They should not set any styleable attributes. Instead
+        you should create a "Theme.Base" theme which inherits from a "Theme.Platform" theme.
+    -->
+    <eat-comment/>
+
+    <style name="Theme.Platform.AppCompat.Light.DarkActionBar"
+           parent="android:Theme.Holo.Light.DarkActionBar" />
+
+    <style name="Theme.Platform.AppCompat.DialogWhenLarge"
+           parent="android:Theme.Holo.DialogWhenLarge" />
+
+    <style name="Theme.Platform.AppCompat.Light.DialogWhenLarge"
+           parent="android:Theme.Holo.Light.DialogWhenLarge" />
+
+
     <!-- Themes in the "Theme.Base" family vary based on the current platform
           version to provide the correct basis on each device. You probably don't
           want to use them directly in your apps.
@@ -28,7 +45,7 @@
     <eat-comment/>
 
     <!-- Base platform-dependent theme providing an action bar in a dark-themed activity. -->
-    <style name="Theme.Base.AppCompat" parent="android:Theme.Holo">
+    <style name="Theme.Base.AppCompat" parent="Theme.Platform.AppCompat">
         <!-- Copy system flag values for our use -->
         <item name="windowActionBar">?android:attr/windowActionBar</item>
         <item name="actionBarSize">?android:attr/actionBarSize</item>
@@ -58,7 +75,7 @@
     </style>
 
     <!-- Base platform-dependent theme providing an action bar in a light-themed activity. -->
-    <style name="Theme.Base.AppCompat.Light" parent="android:Theme.Holo.Light">
+    <style name="Theme.Base.AppCompat.Light" parent="Theme.Platform.AppCompat.Light">
         <!-- Copy system flag values for our use -->
         <item name="windowActionBar">?android:attr/windowActionBar</item>
         <item name="actionBarSize">?android:attr/actionBarSize</item>
@@ -89,7 +106,7 @@
 
     <!-- Base platform-dependent theme providing a dark action bar in a light-themed activity. -->
     <style name="Theme.Base.AppCompat.Light.DarkActionBar"
-           parent="android:Theme.Holo.Light.DarkActionBar">
+           parent="Theme.Platform.AppCompat.Light.DarkActionBar">
         <!-- Copy system flag values for our use -->
         <item name="windowActionBar">?android:attr/windowActionBar</item>
         <item name="actionBarSize">?android:attr/actionBarSize</item>
@@ -132,7 +149,7 @@
     -->
 
     <style name="Theme.Base.AppCompat.DialogWhenLarge.Base"
-           parent="android:Theme.Holo.DialogWhenLarge">
+           parent="Theme.Platform.AppCompat.DialogWhenLarge">
         <!-- Copy system flag values for our use -->
         <item name="windowActionBar">?android:attr/windowActionBar</item>
         <item name="actionBarSize">?android:attr/actionBarSize</item>
@@ -158,7 +175,7 @@
     </style>
 
     <style name="Theme.Base.AppCompat.Light.DialogWhenLarge.Base"
-           parent="android:Theme.Holo.Light.DialogWhenLarge">
+           parent="Theme.Platform.AppCompat.Light.DialogWhenLarge">
         <!-- Copy system flag values for our use -->
         <item name="windowActionBar">?android:attr/windowActionBar</item>
         <item name="actionBarSize">?android:attr/actionBarSize</item>
diff --git a/v7/appcompat/res/values-v21/styles_base.xml b/v7/appcompat/res/values-v21/styles_base.xml
new file mode 100644
index 0000000..d3bef1b
--- /dev/null
+++ b/v7/appcompat/res/values-v21/styles_base.xml
@@ -0,0 +1,241 @@
+<?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>
+
+    <!-- Like in themes_base.xml, the namespace "*.AppCompat.Base" is used to
+     define base styles for the platform version. The "*.AppCompat"
+     variants are for direct use or use as parent styles by the app. -->
+    <eat-comment/>
+
+    <style name="Widget.AppCompat.Base.ActionBar"
+           parent="android:Widget.Quantum.ActionBar">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar"
+           parent="android:Widget.Quantum.Light.ActionBar">
+    </style>
+
+    <style name="Widget.AppCompat.Base.ActionBar.Solid"
+           parent="android:Widget.Quantum.ActionBar.Solid">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.Solid"
+           parent="android:Widget.Quantum.Light.ActionBar.Solid">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.Solid.Inverse"
+           parent="android:Widget.Quantum.Light.ActionBar.Solid">
+    </style>
+
+    <style name="Widget.AppCompat.Base.ActionBar.TabBar"
+           parent="android:Widget.Quantum.ActionBar.TabBar">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.TabBar"
+           parent="android:Widget.Quantum.Light.ActionBar.TabBar">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.TabBar.Inverse"
+           parent="android:Widget.Quantum.Light.ActionBar.TabBar">
+    </style>
+
+    <style name="Widget.AppCompat.Base.ActionBar.TabView"
+           parent="android:Widget.Quantum.ActionBar.TabView">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.TabView"
+           parent="android:Widget.Quantum.Light.ActionBar.TabView">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.TabView.Inverse"
+           parent="android:Widget.Quantum.Light.ActionBar.TabView">
+    </style>
+
+    <style name="Widget.AppCompat.Base.ActionBar.TabText"
+           parent="android:Widget.Quantum.ActionBar.TabText">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.TabText"
+           parent="android:Widget.Quantum.Light.ActionBar.TabText">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionBar.TabText.Inverse"
+           parent="android:Widget.Quantum.Light.ActionBar.TabText">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionMode.Inverse"
+           parent="android:Widget.Quantum.Light.ActionMode">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionBar.Menu"
+           parent="android:TextAppearance.Quantum.Widget.ActionBar.Menu">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionBar.Title"
+           parent="android:TextAppearance.Quantum.Widget.ActionBar.Title">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionBar.Subtitle"
+           parent="android:TextAppearance.Quantum.Widget.ActionBar.Subtitle">
+    </style>
+
+
+    <!--
+    TODO Hidden
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionBar.Title.Inverse"
+           parent="android:TextAppearance.Quantum.Widget.ActionBar.Title.Inverse">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionBar.Subtitle.Inverse"
+           parent="android:TextAppearance.Quantum.Widget.ActionBar.Subtitle.Inverse">
+    </style>
+    -->
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionMode.Title"
+           parent="android:TextAppearance.Quantum.Widget.ActionMode.Title">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionMode.Subtitle"
+           parent="android:TextAppearance.Quantum.Widget.ActionMode.Subtitle">
+    </style>
+
+    <!--
+    TODO Hidden
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionMode.Title.Inverse"
+           parent="android:TextAppearance.Quantum.Widget.ActionMode.Title.Inverse">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Widget.Base.ActionMode.Subtitle.Inverse"
+           parent="android:TextAppearance.Quantum.Widget.ActionMode.Subtitle.Inverse">
+    </style>
+    -->
+
+    <!-- Action Button Styles -->
+
+    <style name="Widget.AppCompat.Base.ActionButton"
+           parent="android:Widget.Quantum.ActionButton">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionButton"
+           parent="android:Widget.Quantum.Light.ActionButton">
+    </style>
+
+    <style name="Widget.AppCompat.Base.ActionButton.CloseMode"
+           parent="android:Widget.Quantum.ActionButton.CloseMode">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionButton.CloseMode"
+           parent="android:Widget.Quantum.Light.ActionButton.CloseMode">
+    </style>
+
+    <style name="Widget.AppCompat.Base.ActionButton.Overflow"
+           parent="android:Widget.Quantum.ActionButton.Overflow">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ActionButton.Overflow"
+           parent="android:Widget.Quantum.Light.ActionButton.Overflow">
+    </style>
+
+    <!-- Spinner Widgets -->
+
+    <style name="Widget.AppCompat.Base.ListView.DropDown"
+           parent="android:Widget.Quantum.ListView.DropDown"/>
+
+    <style name="Widget.AppCompat.Light.Base.ListView.DropDown"
+           parent="android:Widget.Quantum.ListView.DropDown"/>
+
+    <style name="Widget.AppCompat.Base.DropDownItem.Spinner"
+           parent="android:Widget.Quantum.DropDownItem.Spinner"/>
+
+    <style name="Widget.AppCompat.Light.Base.DropDownItem.Spinner"
+           parent="android:Widget.Quantum.Light.DropDownItem.Spinner"/>
+
+    <style name="Widget.AppCompat.Base.Spinner"
+           parent="android:Widget.Quantum.Spinner" />
+
+    <style name="Widget.AppCompat.Light.Base.Spinner"
+           parent="android:Widget.Quantum.Light.Spinner"/>
+
+    <style name="Widget.AppCompat.Base.ListView.Menu"
+           parent="android:Widget.ListView.Menu" />
+
+    <!-- Popup Menu -->
+
+    <style name="Widget.AppCompat.Base.ListPopupWindow"
+           parent="android:Widget.Quantum.ListPopupWindow">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.ListPopupWindow"
+           parent="android:Widget.Quantum.Light.ListPopupWindow">
+    </style>
+
+    <style name="Widget.AppCompat.Base.PopupMenu" parent="android:Widget.Quantum.PopupMenu">
+    </style>
+
+    <style name="Widget.AppCompat.Light.Base.PopupMenu"
+        parent="android:Widget.Quantum.Light.PopupMenu">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Base.Widget.PopupMenu.Large"
+        parent="android:TextAppearance.Quantum.Widget.PopupMenu.Large">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Base.Widget.PopupMenu.Small"
+        parent="android:TextAppearance.Quantum.Widget.PopupMenu.Small">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Light.Base.Widget.PopupMenu.Large"
+        parent="android:TextAppearance.Quantum.Widget.PopupMenu.Large">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Light.Base.Widget.PopupMenu.Small"
+        parent="android:TextAppearance.Quantum.Widget.PopupMenu.Small">
+    </style>
+
+    <!-- Search View result styles -->
+
+    <style name="TextAppearance.AppCompat.Base.SearchResult.Title"
+           parent="@android:TextAppearance.Quantum.SearchResult.Title">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Base.SearchResult.Subtitle"
+           parent="@android:TextAppearance.Quantum.SearchResult.Subtitle">
+    </style>
+
+    <!--
+        TextAppearance.Quantum.Light.SearchResult.* are private so we extend from the default
+        versions instead (which are exactly the same).
+    -->
+    <style name="TextAppearance.AppCompat.Light.Base.SearchResult.Title"
+           parent="TextAppearance.AppCompat.Base.SearchResult.Title">
+    </style>
+
+    <style name="TextAppearance.AppCompat.Light.Base.SearchResult.Subtitle"
+           parent="TextAppearance.AppCompat.Base.SearchResult.Subtitle">
+    </style>
+
+    <!-- TODO. Needs updating for QP -->
+    <style name="Widget.AppCompat.Base.ActivityChooserView" parent="">
+        <item name="android:gravity">center</item>
+        <item name="android:background">@drawable/abc_ab_share_pack_holo_dark</item>
+        <item name="android:divider">?attr/dividerVertical</item>
+        <item name="android:showDividers">middle</item>
+        <item name="android:dividerPadding">6dip</item>
+    </style>
+
+</resources>
diff --git a/v7/appcompat/res/values-v21/themes_base.xml b/v7/appcompat/res/values-v21/themes_base.xml
new file mode 100644
index 0000000..43a174b
--- /dev/null
+++ b/v7/appcompat/res/values-v21/themes_base.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <!--
+        Theme in the "Theme.Platform.AppCompat" family are designed to be aliases for the default
+        theme on a given platform version. They should not set any styleable attributes. Instead
+        you should create a "Theme.Base" theme which inherits from a "Theme.Platform" theme.
+    -->
+    <eat-comment/>
+    <style name="Theme.Platform.AppCompat" parent="android:Theme.Quantum" />
+
+    <style name="Theme.Platform.AppCompat.Light" parent="android:Theme.Quantum.Light" />
+
+    <style name="Theme.Platform.AppCompat.Light.DarkActionBar"
+           parent="android:Theme.Quantum.Light.DarkActionBar" />
+
+    <style name="Theme.Platform.AppCompat.DialogWhenLarge"
+           parent="android:Theme.Quantum.DialogWhenLarge" />
+
+    <style name="Theme.Platform.AppCompat.Light.DialogWhenLarge"
+           parent="android:Theme.Quantum.Light.DialogWhenLarge" />
+
+    <style name="Theme.Platform.AppCompat.Dialog"
+           parent="android:Theme.Quantum.Dialog" />
+
+    <style name="Theme.Platform.AppCompat.Light.Dialog"
+           parent="android:Theme.Quantum.Light.Dialog" />
+
+</resources>
diff --git a/v7/appcompat/res/values-vi/strings.xml b/v7/appcompat/res/values-vi/strings.xml
new file mode 100644
index 0000000..0840f73
--- /dev/null
+++ b/v7/appcompat/res/values-vi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Xong"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Điều hướng về trang chủ"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Điều hướng lên trên"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Thêm tùy chọn"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Tìm kiếm"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Tìm kiếm truy vấn"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Xóa truy vấn"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Gửi truy vấn"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Tìm kiếm bằng giọng nói"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Chọn một ứng dụng"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Xem tất cả"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Chia sẻ với %s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Chia sẻ với"</string>
+</resources>
diff --git a/v7/appcompat/res/values-zh-rCN/strings.xml b/v7/appcompat/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..87b36b0
--- /dev/null
+++ b/v7/appcompat/res/values-zh-rCN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"完成"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"转到主屏幕"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"转到上一层级"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"更多选项"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"搜索"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"搜索查询"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"清除查询"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"提交查询"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"语音搜索"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"选择应用"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"查看全部"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"通过%s分享"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"分享方式"</string>
+</resources>
diff --git a/v7/appcompat/res/values-zh-rHK/strings.xml b/v7/appcompat/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..f6a367d
--- /dev/null
+++ b/v7/appcompat/res/values-zh-rHK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"完成"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"瀏覽主頁"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"向上瀏覽"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"更多選項"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"搜尋"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"搜尋查詢"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"清除查詢"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"提交查詢"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"語音搜尋"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"選擇應用程式"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"顯示全部"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"與「%s」分享"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"分享對象"</string>
+</resources>
diff --git a/v7/appcompat/res/values-zh-rTW/strings.xml b/v7/appcompat/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..c804ccf
--- /dev/null
+++ b/v7/appcompat/res/values-zh-rTW/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"完成"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"瀏覽首頁"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"向上瀏覽"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"更多選項"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"搜尋"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"搜尋查詢"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"清除查詢"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"提交查詢"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"語音搜尋"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"選擇應用程式"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"查看全部"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"與「%s」分享"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"選擇分享對象"</string>
+</resources>
diff --git a/v7/appcompat/res/values-zu/strings.xml b/v7/appcompat/res/values-zu/strings.xml
new file mode 100644
index 0000000..92eac7e
--- /dev/null
+++ b/v7/appcompat/res/values-zu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="4076576682505996667">"Kwenziwe"</string>
+    <string name="abc_action_bar_home_description" msgid="4600421777120114993">"Zulazulela ekhaya"</string>
+    <string name="abc_action_bar_up_description" msgid="1594238315039666878">"Zulazulela phezulu"</string>
+    <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Izinketho eziningi"</string>
+    <string name="abc_searchview_description_search" msgid="8264924765203268293">"Sesha"</string>
+    <string name="abc_searchview_description_query" msgid="2550479030709304392">"Umbuzo wosesho"</string>
+    <string name="abc_searchview_description_clear" msgid="3691816814315814921">"Sula inkinga"</string>
+    <string name="abc_searchview_description_submit" msgid="8928215447528550784">"Hambisa umbuzo"</string>
+    <string name="abc_searchview_description_voice" msgid="893419373245838918">"Ukusesha ngezwi"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"Khetha uhlelo lokusebenza"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"Buka konke"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="7165123711973476752">"Yabelana no-%s"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"Yabelana no-"</string>
+</resources>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index c48b57c..f707c3a 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -16,6 +16,23 @@
 
 <resources>
 
+    <!--
+        Theme in the "Theme.Platform.AppCompat" family are designed to be aliases for the default
+        theme on a given platform version. They should not set any styleable attributes. Instead
+        you should create a "Theme.Base" theme which inherits from a "Theme.Platform" theme.
+    -->
+    <eat-comment/>
+    <style name="Theme.Platform.AppCompat" parent="android:Theme" />
+
+    <style name="Theme.Platform.AppCompat.Light" parent="android:Theme.Light" />
+
+    <style name="Theme.Platform.AppCompat.Dialog"
+           parent="android:Theme.Dialog" />
+
+    <style name="Theme.Platform.AppCompat.Light.Dialog"
+           parent="Theme.Platform.AppCompat.Dialog" />
+
+
     <!-- Themes in the "Theme.Base" family vary based on the current platform
          version to provide the correct basis on each device. You probably don't
          want to use them directly in your apps.
@@ -24,10 +41,10 @@
          directly by apps. -->
     <eat-comment/>
 
-    <style name="Theme.Base" parent="android:Theme">
+    <style name="Theme.Base" parent="Theme.Platform.AppCompat">
     </style>
 
-    <style name="Theme.Base.Light" parent="android:Theme.Light">
+    <style name="Theme.Base.Light" parent="Theme.Platform.AppCompat.Light">
     </style>
 
     <!-- Base platform-dependent theme providing an action bar in a dark-themed activity. -->
@@ -206,7 +223,7 @@
            parent="Theme.Base.AppCompat.Light">
     </style>
 
-    <style name="Theme.Base.AppCompat.Dialog.FixedSize" parent="android:Theme.Dialog">
+    <style name="Theme.Base.AppCompat.Dialog.FixedSize" parent="Theme.Platform.AppCompat.Dialog">
         <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
         <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
         <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
diff --git a/v7/cardview/Android.mk b/v7/cardview/Android.mk
new file mode 100644
index 0000000..5c8da8c
--- /dev/null
+++ b/v7/cardview/Android.mk
@@ -0,0 +1,75 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the resources using the current SDK version.
+# We do this here because the final static library must be compiled with an older
+# SDK version than the resources.  The resources library and the R class that it
+# contains will not be linked into the final static library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-cardview-res
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library to resolve cyclic dependencies between CardView and platform dependent
+# implementations
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-cardview-base
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under, base)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library that makes direct use of Eclair MR1 APIs
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-cardview-eclair-mr1
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under, eclair-mr1)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-base
+LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library that makes direct use of JB MR1 APIs
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-cardview-jellybean-mr1
+LOCAL_SDK_VERSION := 17
+LOCAL_SRC_FILES := $(call all-java-files-under, jellybean-mr1)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-eclair-mr1
+LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# A helper sub-library that makes direct use of L APIs
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-cardview-api21
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, api21)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-base \
+    android-support-v7-cardview-jellybean-mr1
+LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Here is the final static library that apps can link against.
+# The R class is automatically excluded from the generated library.
+# Applications that use this library must specify LOCAL_RESOURCE_DIR
+# in their makefiles to include the resources in their package.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-cardview
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-cardview-api21
+LOCAL_JAVA_LIBRARIES := android-support-v7-cardview-res
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v7/cardview/AndroidManifest.xml
similarity index 68%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v7/cardview/AndroidManifest.xml
index 36688bc..3ef02dc 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v7/cardview/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <?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.
@@ -14,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v7.cardview">
+    <uses-sdk android:minSdkVersion="7"/>
+    <application />
+</manifest>
diff --git a/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
new file mode 100644
index 0000000..4126fe3
--- /dev/null
+++ b/v7/cardview/api21/android/support/v7/widget/CardViewApi21.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.view.View;
+import android.support.v7.cardview.R;
+
+class CardViewApi21 implements CardViewImpl {
+
+    @Override
+    public void initialize(CardViewDelegate cardView, Context context, int backgroundColor,
+            float radius) {
+        cardView.setBackgroundDrawable(new RoundRectDrawable(backgroundColor, radius));
+        View view = (View) cardView;
+        view.setClipToOutline(true);
+        view.setElevation(context.getResources().getDimension(R.dimen.cardview_elevation));
+    }
+
+    @Override
+    public void setRadius(CardViewDelegate cardView, float radius) {
+        ((RoundRectDrawable) (cardView.getBackground())).setRadius(radius);
+    }
+
+    @Override
+    public void initStatic() {
+    }
+
+    @Override
+    public float getRadius(CardViewDelegate cardView) {
+        return ((RoundRectDrawable) (cardView.getBackground())).getRadius();
+    }
+
+}
\ No newline at end of file
diff --git a/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
new file mode 100644
index 0000000..83d73bd
--- /dev/null
+++ b/v7/cardview/api21/android/support/v7/widget/RoundRectDrawable.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Very simple drawable that draws a rounded rectangle background with arbitrary corners and also
+ * reports proper outline for L.
+ * <p>
+ * Simpler and uses less resources compared to GradientDrawable or ShapeDrawable.
+ */
+class RoundRectDrawable extends Drawable {
+    float mRadius;
+    final Paint mPaint;
+    final RectF mBounds;
+
+    public RoundRectDrawable(int backgroundColor, float radius) {
+        mRadius = radius;
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        mPaint.setColor(backgroundColor);
+        mBounds = new RectF();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.drawRoundRect(mBounds, mRadius, mRadius, mPaint);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+    @Override
+    public boolean getOutline(Outline outline) {
+        outline.setRoundRect(getBounds(), mRadius);
+        return true;
+    }
+
+    public void setRadius(float radius) {
+        if (radius == mRadius) {
+            return;
+        }
+        mRadius = radius;
+        invalidateSelf();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        // not supported because older versions do not support
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        // not supported because older versions do not support
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+
+    public float getRadius() {
+        return mRadius;
+    }
+}
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
new file mode 100644
index 0000000..beedbd1
--- /dev/null
+++ b/v7/cardview/base/android/support/v7/widget/CardViewDelegate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Interface provided by CardView to implementations.
+ * <p>
+ * Necessary to resolve circular dependency between base CardView and platform implementations.
+ */
+interface CardViewDelegate {
+    void setBackgroundDrawable(Drawable paramDrawable);
+    Drawable getBackground();
+}
\ No newline at end of file
diff --git a/v7/cardview/base/android/support/v7/widget/CardViewImpl.java b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
new file mode 100644
index 0000000..96b2eb5
--- /dev/null
+++ b/v7/cardview/base/android/support/v7/widget/CardViewImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+
+/**
+ * Interface for platform specific CardView implementations.
+ */
+interface CardViewImpl {
+    void initialize(CardViewDelegate cardView, Context context, int backgroundColor, float radius);
+
+    void setRadius(CardViewDelegate cardView, float radius);
+
+    float getRadius(CardViewDelegate cardView);
+
+    void initStatic();
+}
diff --git a/v7/cardview/dummy/Dummy.java b/v7/cardview/dummy/Dummy.java
new file mode 100644
index 0000000..be16dc7
--- /dev/null
+++ b/v7/cardview/dummy/Dummy.java
@@ -0,0 +1 @@
+// Dummy java file used to build the resource library.
diff --git a/v7/cardview/eclair-mr1/android/support/v7/widget/CardViewEclairMr1.java b/v7/cardview/eclair-mr1/android/support/v7/widget/CardViewEclairMr1.java
new file mode 100644
index 0000000..d5d98be
--- /dev/null
+++ b/v7/cardview/eclair-mr1/android/support/v7/widget/CardViewEclairMr1.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+class CardViewEclairMr1 implements CardViewImpl {
+
+    final RectF sCornerRect = new RectF();
+
+    @Override
+    public void initStatic() {
+        // Draws a round rect using 7 draw operations. This is faster than using
+        // canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw
+        // shapes.
+        RoundRectDrawableWithShadow.sRoundRectHelper
+                = new RoundRectDrawableWithShadow.RoundRectHelper() {
+            @Override
+            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
+                    Paint paint) {
+                final float twoRadius = cornerRadius * 2;
+                final float innerWidth = bounds.width() - twoRadius;
+                final float innerHeight = bounds.height() - twoRadius;
+                sCornerRect.set(bounds.left, bounds.top,
+                        bounds.left + cornerRadius * 2, bounds.top + cornerRadius * 2);
+
+                canvas.drawArc(sCornerRect, 180, 90, true, paint);
+                sCornerRect.offset(innerWidth, 0);
+                canvas.drawArc(sCornerRect, 270, 90, true, paint);
+                sCornerRect.offset(0, innerHeight);
+                canvas.drawArc(sCornerRect, 0, 90, true, paint);
+                sCornerRect.offset(-innerWidth, 0);
+                canvas.drawArc(sCornerRect, 90, 90, true, paint);
+
+                //draw top and bottom pieces
+                canvas.drawRect(bounds.left + cornerRadius, bounds.top,
+                        bounds.right - cornerRadius, bounds.top + cornerRadius,
+                        paint);
+                canvas.drawRect(bounds.left + cornerRadius,
+                        bounds.bottom - cornerRadius, bounds.right - cornerRadius,
+                        bounds.bottom, paint);
+
+                //center
+                canvas.drawRect(bounds.left, bounds.top + cornerRadius,
+                        bounds.right, bounds.bottom - cornerRadius, paint);
+            }
+        };
+    }
+
+    @Override
+    public void initialize(CardViewDelegate cardView, Context context, int backgroundColor,
+            float radius) {
+        RoundRectDrawableWithShadow background = new RoundRectDrawableWithShadow(
+                context.getResources(), backgroundColor, radius);
+        cardView.setBackgroundDrawable(background);
+    }
+
+    @Override
+    public void setRadius(CardViewDelegate cardView, float radius) {
+        ((RoundRectDrawableWithShadow) cardView.getBackground()).setCornerRadius(radius);
+    }
+
+    @Override
+    public float getRadius(CardViewDelegate cardView) {
+        return ((RoundRectDrawableWithShadow) cardView.getBackground()).getCornerRadius();
+    }
+}
\ No newline at end of file
diff --git a/v7/cardview/eclair-mr1/android/support/v7/widget/RoundRectDrawableWithShadow.java b/v7/cardview/eclair-mr1/android/support/v7/widget/RoundRectDrawableWithShadow.java
new file mode 100644
index 0000000..9f6014f
--- /dev/null
+++ b/v7/cardview/eclair-mr1/android/support/v7/widget/RoundRectDrawableWithShadow.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.support.v7.cardview.R;
+
+/**
+ * A rounded rectangle drawable which also includes a shadow around.
+ */
+class RoundRectDrawableWithShadow extends Drawable {
+
+    /*
+    * This helper is set by CardView implementations.
+    * <p>
+    * Prior to API 17, canvas.drawRoundRect is expensive; which is why we need this interface
+    * to draw efficient rounded rectangles before 17.
+    * */
+    static RoundRectHelper sRoundRectHelper;
+
+    Paint mPaint;
+
+    Paint mCornerShadowPaint;
+
+    Paint mEdgeShadowPaint;
+
+    final RectF mPreShadowBounds;
+
+    float mCornerRadius;
+
+    Path mCornerShadowPath;
+
+    float mShadowSize = 6;
+
+    private boolean mDirty = true;
+
+    private final int mShadowStartColor;
+
+    private final int mShadowEndColor;
+
+    RoundRectDrawableWithShadow(Resources resources, int backgroundColor, float radius) {
+        mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color);
+        mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color);
+
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        mPaint.setColor(backgroundColor);
+        mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+        mCornerShadowPaint.setStyle(Paint.Style.FILL);
+        mCornerShadowPaint.setDither(true);
+        mCornerRadius = radius;
+        mPreShadowBounds = new RectF();
+        mEdgeShadowPaint = new Paint(mCornerShadowPaint);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        // not supported
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        mDirty = true;
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        if (mCornerRadius == 0) {
+            return super.getPadding(padding);
+        }
+        final int intCornerRadius = (int) Math.ceil(mCornerRadius);
+        padding.set(intCornerRadius, intCornerRadius, intCornerRadius, intCornerRadius);
+        return true;
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        // not supported
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+
+    void setCornerRadius(float radius) {
+        if (mCornerRadius == radius) {
+            return;
+        }
+        mCornerRadius = radius;
+        mDirty = true;
+        invalidateSelf();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mDirty) {
+            buildComponents(getBounds());
+            mDirty = false;
+        }
+        drawShadow(canvas);
+        final float offset = mShadowSize / 2f;
+        canvas.translate(0, -offset);
+        mPreShadowBounds.bottom += offset;
+        sRoundRectHelper.drawRoundRect(canvas, mPreShadowBounds, mCornerRadius, mPaint);
+        mPreShadowBounds.bottom -= offset;
+        canvas.translate(0, offset);
+    }
+
+    private void drawShadow(Canvas canvas) {
+        int saved = canvas.save();
+
+        float cornerPathSize = 2 * (mCornerRadius + mShadowSize);
+        float edgeShadowTop = -mCornerRadius - mShadowSize;
+        final Rect bounds = getBounds();
+
+        // LT
+        canvas.translate(mPreShadowBounds.left + mCornerRadius,
+                mPreShadowBounds.top + mCornerRadius);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        canvas.drawRect(0, edgeShadowTop,
+                bounds.right - cornerPathSize, -mCornerRadius,
+                mEdgeShadowPaint);
+        // RB
+        canvas.rotate(180f);
+        canvas.translate(-bounds.width() + cornerPathSize, -bounds.height() + cornerPathSize);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        canvas.drawRect(0, edgeShadowTop,
+                bounds.right - cornerPathSize, -mCornerRadius + mShadowSize,
+                mEdgeShadowPaint);
+
+        // LB
+        canvas.rotate(90f);
+        canvas.translate(0, -bounds.width() + cornerPathSize);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        canvas.drawRect(0, edgeShadowTop,
+                bounds.bottom - cornerPathSize, -mCornerRadius,
+                mEdgeShadowPaint);
+
+        // RT
+        canvas.rotate(180f);
+        canvas.translate(-bounds.height() + cornerPathSize, -bounds.width() + cornerPathSize);
+        canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
+        canvas.drawRect(0, edgeShadowTop,
+                bounds.bottom - cornerPathSize, -mCornerRadius,
+                mEdgeShadowPaint);
+
+        canvas.restoreToCount(saved);
+    }
+
+    private void buildShadowCorners() {
+        RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
+        RectF outerBounds = new RectF(innerBounds);
+        outerBounds.inset(-mShadowSize, -mShadowSize);
+
+        if (mCornerShadowPath == null) {
+            mCornerShadowPath = new Path();
+        } else {
+            mCornerShadowPath.reset();
+        }
+        mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
+        mCornerShadowPath.moveTo(-mCornerRadius, 0);
+        mCornerShadowPath.rLineTo(-mShadowSize, 0);
+        // outer arc
+        mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
+        // inner arc
+        mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
+        mCornerShadowPath.close();
+
+        float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
+        mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
+                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
+                new float[]{0f, startRatio, 1f}
+                , Shader.TileMode.CLAMP));
+
+        // we offset the content shadowSize/2 pixels up to make it more realistic.
+        // this is why edge shadow shader has some extra space
+        // When drawing bottom edge shadow, we use that extra space.
+        mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
+                -mCornerRadius - mShadowSize,
+                new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
+                new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
+    }
+
+    private void buildComponents(Rect bounds) {
+        mPreShadowBounds.set(bounds.left + mShadowSize, bounds.top + mShadowSize,
+                bounds.right - mShadowSize, bounds.bottom - mShadowSize);
+        buildShadowCorners();
+    }
+
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
+    static interface RoundRectHelper {
+        void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint);
+    }
+}
diff --git a/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
new file mode 100644
index 0000000..8ea6e3e
--- /dev/null
+++ b/v7/cardview/jellybean-mr1/android/support/v7/widget/CardViewJellybeanMr1.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+class CardViewJellybeanMr1 extends CardViewEclairMr1 {
+
+    @Override
+    public void initStatic() {
+        RoundRectDrawableWithShadow.sRoundRectHelper
+                = new RoundRectDrawableWithShadow.RoundRectHelper() {
+            @Override
+            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
+                    Paint paint) {
+                canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v7/cardview/res/values/attrs.xml
similarity index 62%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v7/cardview/res/values/attrs.xml
index 36688bc..2a8373f 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v7/cardview/res/values/attrs.xml
@@ -1,6 +1,5 @@
 <?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.
@@ -15,7 +14,11 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<resources>
+    <declare-styleable name="CardView">
+        <!-- Background color for CardView. -->
+        <attr name="cardBackgroundColor" format="color" />
+        <!-- Corner radius for CardView. -->
+        <attr name="cardCornerRadius" format="dimension" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/v7/cardview/res/values/colors.xml b/v7/cardview/res/values/colors.xml
new file mode 100644
index 0000000..1b6f2d4
--- /dev/null
+++ b/v7/cardview/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Default background color for CardView. -->
+    <color name="cardview_light_background">#FFFAFAFA</color>
+    <!-- Shadow color for the first pixels around CardView. -->
+    <color name="cardview_shadow_start_color">#37000000</color>
+    <!-- Shadow color for the furthest pixels around CardView. -->
+    <color name="cardview_shadow_end_color">#03000000</color>
+</resources>
\ No newline at end of file
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v7/cardview/res/values/dimens.xml
similarity index 67%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v7/cardview/res/values/dimens.xml
index 36688bc..e4ca6f0 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v7/cardview/res/values/dimens.xml
@@ -1,6 +1,5 @@
 <?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.
@@ -15,7 +14,9 @@
      limitations under the License.
 -->
 
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<resources>
+    <!-- Default radius for CardView corners. -->
+    <dimen name="cardview_default_radius">2dp</dimen>
+    <!-- Elevation value to use for CardViews on L+. -->
+    <dimen name="cardview_elevation">2dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v7/cardview/res/values/styles.xml
similarity index 67%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v7/cardview/res/values/styles.xml
index 36688bc..12fa3a8 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v7/cardview/res/values/styles.xml
@@ -1,6 +1,5 @@
 <?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.
@@ -14,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<resources>
+    <style name="CardViewLight">
+        <item name="cardBackgroundColor">@color/cardview_light_background</item>
+        <item name="cardCornerRadius">@dimen/cardview_default_radius</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/v7/cardview/src/android/support/v7/widget/CardView.java b/v7/cardview/src/android/support/v7/widget/CardView.java
new file mode 100644
index 0000000..5c1af68
--- /dev/null
+++ b/v7/cardview/src/android/support/v7/widget/CardView.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.support.v7.cardview.R;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * A ViewGroup with a rounded corner background and shadow behind.
+ * <p>
+ * CardView uses <code>elevation</code> property on L for shadows and falls back to a custom shadow
+ * implementation on older platforms.
+ * <p>
+ * Due to expensive nature of rounded corner clipping, on platforms before L, CardView does not clip
+ * its children that intersect with rounded corners. Instead, it adds padding to avoid such
+ * intersection.
+ *
+ * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor
+ * @attr ref android.support.v7.cardview.R.styleable#CardView_cornerRadius
+ */
+public class CardView extends FrameLayout implements CardViewDelegate {
+
+    private final static CardViewImpl IMPL;
+    static {
+        if ("L".equals(Build.VERSION.CODENAME) || Build.VERSION.SDK_INT >= 21) {
+            IMPL = new CardViewApi21();
+        } else if (Build.VERSION.SDK_INT >= 17) {
+            IMPL = new CardViewJellybeanMr1();
+        } else {
+            IMPL = new CardViewEclairMr1();
+        }
+        IMPL.initStatic();
+    }
+
+    public CardView(Context context) {
+        super(context);
+        initialize(context, null, 0);
+    }
+
+    public CardView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initialize(context, attrs, 0);
+    }
+
+    public CardView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initialize(context, attrs, defStyleAttr);
+    }
+
+    private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr,
+                R.style.CardViewLight);
+        int backgroundColor = a.getColor(R.styleable.CardView_cardBackgroundColor, 0);
+        float radius = a.getDimension(R.styleable.CardView_cardCornerRadius, 0);
+
+        a.recycle();
+        IMPL.initialize(this, context, backgroundColor, radius);
+    }
+
+    /**
+     * Updates the corner radius of the CardView.
+     *
+     * @param radius The radius in pixels of the corners of the rectangle shape
+     *
+     * @attr ref android.support.v7.cardview.R.styleable#CardView_cornerRadius
+     *
+     * @see #setRadius(float)
+     *
+     */
+    public void setRadius(float radius) {
+        IMPL.setRadius(this, radius);
+    }
+
+    /**
+     * Returns the corner radius of the CardView.
+     *
+     * @return Corner radius of the CardView
+     *
+     * @see #getRadius()
+     */
+    public float getRadius() {
+        return IMPL.getRadius(this);
+    }
+}
\ No newline at end of file
diff --git a/v7/gridlayout/build.gradle b/v7/gridlayout/build.gradle
index da73b9e..0a44130 100644
--- a/v7/gridlayout/build.gradle
+++ b/v7/gridlayout/build.gradle
@@ -6,9 +6,8 @@
     compile project(':support-v4')
 }
 
-
 android {
-    compileSdkVersion 19
+    compileSdkVersion 'current'
     buildToolsVersion '19.0.3'
 
     sourceSets {
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index a755f5b..d4dae83 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -32,7 +32,7 @@
     jellybeanmr1Compile getAndroidPrebuilt('17')
     jellybeanmr1Compile sourceSets.jellybean.output
 
-    jellybeanmr2Compile getAndroidPrebuilt('18')
+    jellybeanmr2Compile getAndroidPrebuilt('current')
     jellybeanmr2Compile sourceSets.jellybean.output
     jellybeanmr2Compile sourceSets.jellybeanmr1.output
 
@@ -40,7 +40,7 @@
 }
 
 android {
-    compileSdkVersion 19
+    compileSdkVersion 'current'
     buildToolsVersion '19.0.3'
 
     sourceSets {
diff --git a/v7/mediarouter/res/values-af/strings.xml b/v7/mediarouter/res/values-af/strings.xml
new file mode 100644
index 0000000..885af10
--- /dev/null
+++ b/v7/mediarouter/res/values-af/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Stelsel"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Toestelle"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Media-uitvoer"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Koppel aan toestel"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Soek tans vir toestelle…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Ontkoppel"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-am/strings.xml b/v7/mediarouter/res/values-am/strings.xml
new file mode 100644
index 0000000..0027737
--- /dev/null
+++ b/v7/mediarouter/res/values-am/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ስርዓት"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"መሣሪያዎች"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"የሚዲያ ውፅዓት"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"ከመሳሪያ ጋር ያገናኙ"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"መሳሪያዎችን በመፈለግ ላይ…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"ግንኙነት አቋርጥ"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ar/strings.xml b/v7/mediarouter/res/values-ar/strings.xml
new file mode 100644
index 0000000..9289a35
--- /dev/null
+++ b/v7/mediarouter/res/values-ar/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"النظام"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"الأجهزة"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"المنفذ الإعلامي"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"الاتصال بجهاز"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"جارٍ البحث عن الأجهزة…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"قطع الاتصال"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-bg/strings.xml b/v7/mediarouter/res/values-bg/strings.xml
new file mode 100644
index 0000000..ff1401e
--- /dev/null
+++ b/v7/mediarouter/res/values-bg/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Устройства"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Изходяща мултимедия"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Свързване с устройство"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Търсят се устройства…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Прекратяване на връзката"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ca/strings.xml b/v7/mediarouter/res/values-ca/strings.xml
new file mode 100644
index 0000000..dd485de
--- /dev/null
+++ b/v7/mediarouter/res/values-ca/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositius"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Sortida de contingut multimèdia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Connecta al dispositiu"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"S\'estan cercant dispositius…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Desconnecta"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-cs/strings.xml b/v7/mediarouter/res/values-cs/strings.xml
new file mode 100644
index 0000000..4687100
--- /dev/null
+++ b/v7/mediarouter/res/values-cs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Systém"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Zařízení"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Výstup médií"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Připojení k zařízení"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Vyhledávání zařízení…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Odpojit"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-da/strings.xml b/v7/mediarouter/res/values-da/strings.xml
new file mode 100644
index 0000000..fd3b0fb
--- /dev/null
+++ b/v7/mediarouter/res/values-da/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheder"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medieudgang"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Opret forbindelse til enheden"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Søger efter enheder..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Afbryd forbindelsen"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-de/strings.xml b/v7/mediarouter/res/values-de/strings.xml
new file mode 100644
index 0000000..9df0ebf
--- /dev/null
+++ b/v7/mediarouter/res/values-de/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Geräte"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medienausgabe"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Mit Gerät verbinden"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Geräte werden gesucht…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Verbindung aufheben"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-el/strings.xml b/v7/mediarouter/res/values-el/strings.xml
new file mode 100644
index 0000000..5a61395
--- /dev/null
+++ b/v7/mediarouter/res/values-el/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Σύστημα"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Συσκευές"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Έξοδος μέσων"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Σύνδεση με τη συσκευή"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Αναζήτηση συσκευών…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Αποσύνδεση"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-en-rGB/strings.xml b/v7/mediarouter/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..b9af4bf
--- /dev/null
+++ b/v7/mediarouter/res/values-en-rGB/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Media output"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Connect to device"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Searching for devices…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Disconnect"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-en-rIN/strings.xml b/v7/mediarouter/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..b9af4bf
--- /dev/null
+++ b/v7/mediarouter/res/values-en-rIN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Devices"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Media output"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Connect to device"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Searching for devices…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Disconnect"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-es-rUS/strings.xml b/v7/mediarouter/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..211b400
--- /dev/null
+++ b/v7/mediarouter/res/values-es-rUS/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Salida multimedia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Conectar al dispositivo"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Buscando dispositivos…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Desconectar"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-es/strings.xml b/v7/mediarouter/res/values-es/strings.xml
new file mode 100644
index 0000000..d3a1639
--- /dev/null
+++ b/v7/mediarouter/res/values-es/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Salida multimedia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Conectar a dispositivo"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Buscando dispositivos…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Desconectar"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-et-rEE/strings.xml b/v7/mediarouter/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..7dbdf74
--- /dev/null
+++ b/v7/mediarouter/res/values-et-rEE/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Süsteem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Seadmed"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Meediaväljund"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Seadmega ühendamine"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Seadmete otsimine …"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Katkesta ühendus"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-fa/strings.xml b/v7/mediarouter/res/values-fa/strings.xml
new file mode 100644
index 0000000..2ffed50
--- /dev/null
+++ b/v7/mediarouter/res/values-fa/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"سیستم"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"دستگاه‌ها"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"خروجی رسانه"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"برقراری ارتباط با دستگاه"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"در حال جستجو برای دستگاه‌ها..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"قطع ارتباط"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-fi/strings.xml b/v7/mediarouter/res/values-fi/strings.xml
new file mode 100644
index 0000000..0692c2f
--- /dev/null
+++ b/v7/mediarouter/res/values-fi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Järjestelmä"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Laitteet"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Median äänentoisto"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Yhdistä laitteeseen"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Etsitään laitteita…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Katkaise yhteys"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-fr-rCA/strings.xml b/v7/mediarouter/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..9fa3c9c
--- /dev/null
+++ b/v7/mediarouter/res/values-fr-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Sortie multimédia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Connexion au périphérique"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Recherche d\'appareils en cours…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Déconnecter"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-fr/strings.xml b/v7/mediarouter/res/values-fr/strings.xml
new file mode 100644
index 0000000..5607a1c
--- /dev/null
+++ b/v7/mediarouter/res/values-fr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Système"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Appareils"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Sortie multimédia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Connecter à l\'appareil"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Recherche d\'appareils en cours…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Déconnecter"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-hi/strings.xml b/v7/mediarouter/res/values-hi/strings.xml
new file mode 100644
index 0000000..8acc2bb
--- /dev/null
+++ b/v7/mediarouter/res/values-hi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"सिस्टम"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"उपकरण"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"मीडिया आउटपुट"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"उपकरण से कनेक्ट करें"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"उपकरणों की खोज हो रही है…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"डिस्कनेक्ट करें"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-hr/strings.xml b/v7/mediarouter/res/values-hr/strings.xml
new file mode 100644
index 0000000..2946433
--- /dev/null
+++ b/v7/mediarouter/res/values-hr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sustav"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Uređaji"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medijski izlaz"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Povezivanje s uređajem"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Traženje uređaja…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Prekini vezu"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-hu/strings.xml b/v7/mediarouter/res/values-hu/strings.xml
new file mode 100644
index 0000000..b68fe16
--- /dev/null
+++ b/v7/mediarouter/res/values-hu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Rendszer"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Eszközök"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Médiakimenet"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Csatlakozás adott eszközhöz"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Eszközkeresés…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Leválasztás"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-hy-rAM/strings.xml b/v7/mediarouter/res/values-hy-rAM/strings.xml
new file mode 100644
index 0000000..77f1136
--- /dev/null
+++ b/v7/mediarouter/res/values-hy-rAM/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Համակարգ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Սարքեր"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Մեդիա արտածում"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Միանալ սարքին"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Որոնվում են սարքեր..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Անջատել"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-in/strings.xml b/v7/mediarouter/res/values-in/strings.xml
new file mode 100644
index 0000000..1d3b387
--- /dev/null
+++ b/v7/mediarouter/res/values-in/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Perangkat"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Keluaran media"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Sambungkan ke perangkat"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Menelusuri perangkat…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Putuskan sambungan"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-it/strings.xml b/v7/mediarouter/res/values-it/strings.xml
new file mode 100644
index 0000000..bd58755
--- /dev/null
+++ b/v7/mediarouter/res/values-it/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivi"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Uscita media"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Connetti al dispositivo"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Ricerca di dispositivi…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Disconnetti"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-iw/strings.xml b/v7/mediarouter/res/values-iw/strings.xml
new file mode 100644
index 0000000..59753b4
--- /dev/null
+++ b/v7/mediarouter/res/values-iw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"מערכת"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"מכשירים"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"פלט מדיה"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"התחבר למכשיר"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"מחפש מכשירים…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"התנתק"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ja/strings.xml b/v7/mediarouter/res/values-ja/strings.xml
new file mode 100644
index 0000000..1367489
--- /dev/null
+++ b/v7/mediarouter/res/values-ja/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"システム"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"端末"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"メディア出力"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"端末に接続"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"端末を検索しています…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"接続を解除"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ka-rGE/strings.xml b/v7/mediarouter/res/values-ka-rGE/strings.xml
new file mode 100644
index 0000000..413257e
--- /dev/null
+++ b/v7/mediarouter/res/values-ka-rGE/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"სისტემა"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"მოწყობილობები"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"მედია გამოსასვლელი"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"მოწყობილობასთან დაკავშირება"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"მოწყობილობების ძიება…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"კავშირის გაწყვეტა"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-km-rKH/strings.xml b/v7/mediarouter/res/values-km-rKH/strings.xml
new file mode 100644
index 0000000..e001dde
--- /dev/null
+++ b/v7/mediarouter/res/values-km-rKH/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ប្រព័ន្ធ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ឧបករណ៍"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"លទ្ធផល​មេឌៀ"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"ភ្ជាប់​ឧបករណ៍"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"កំពុង​ស្វែងរក​ឧបករណ៍..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"ផ្ដាច់"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ko/strings.xml b/v7/mediarouter/res/values-ko/strings.xml
new file mode 100644
index 0000000..21f82a0
--- /dev/null
+++ b/v7/mediarouter/res/values-ko/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"시스템"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"기기"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"미디어 출력"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"기기에 연결"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"기기 검색 중…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"연결 해제"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-lo-rLA/strings.xml b/v7/mediarouter/res/values-lo-rLA/strings.xml
new file mode 100644
index 0000000..31a03cd
--- /dev/null
+++ b/v7/mediarouter/res/values-lo-rLA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ລະບົບ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"ອຸປະກອນ"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"ມີເດຍເອົ້າພຸດ"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"ເຊື່ອມຕໍ່ຫາອຸປະກອນ"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"ກຳລັງຊອກຫາອຸປະກອນ..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"ຕັດການເຊື່ອມຕໍ່"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-lt/strings.xml b/v7/mediarouter/res/values-lt/strings.xml
new file mode 100644
index 0000000..ead3b73
--- /dev/null
+++ b/v7/mediarouter/res/values-lt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Įrenginiai"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medijos išvestis"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Prijungimas prie įrenginio"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Ieškoma įrenginių…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Atjungti"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-lv/strings.xml b/v7/mediarouter/res/values-lv/strings.xml
new file mode 100644
index 0000000..0914990
--- /dev/null
+++ b/v7/mediarouter/res/values-lv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistēma"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Ierīces"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Multivides izeja"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Savienojuma izveide ar ierīci"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Notiek ierīču meklēšana..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Atvienot"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-mn-rMN/strings.xml b/v7/mediarouter/res/values-mn-rMN/strings.xml
new file mode 100644
index 0000000..4eecdb4
--- /dev/null
+++ b/v7/mediarouter/res/values-mn-rMN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Төхөөрөмжүүд"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Медиа гаралт"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Төхөөрөмжтэй холбох"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Төхөөрөмжүүдийг хайж байна…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Салгах"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ms-rMY/strings.xml b/v7/mediarouter/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..dadaa30
--- /dev/null
+++ b/v7/mediarouter/res/values-ms-rMY/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Peranti"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Output media"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Sambung kepada peranti"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Mencari peranti..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Putuskan sambungan"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-nb/strings.xml b/v7/mediarouter/res/values-nb/strings.xml
new file mode 100644
index 0000000..fa4d9a4
--- /dev/null
+++ b/v7/mediarouter/res/values-nb/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheter"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medieutgang"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Koble til enheten"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Søker etter enheter …"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Koble fra"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-nl/strings.xml b/v7/mediarouter/res/values-nl/strings.xml
new file mode 100644
index 0000000..5572449
--- /dev/null
+++ b/v7/mediarouter/res/values-nl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Systeem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Apparaten"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Media-uitvoer"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Verbinding maken met apparaat"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Zoeken naar apparaten…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Verbinding verbreken"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-pl/strings.xml b/v7/mediarouter/res/values-pl/strings.xml
new file mode 100644
index 0000000..95a1d03
--- /dev/null
+++ b/v7/mediarouter/res/values-pl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Urządzenia"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Wyjście multimediów"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Połącz z urządzeniem"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Szukam urządzeń…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Rozłącz"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-pt-rPT/strings.xml b/v7/mediarouter/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..54b1dfc
--- /dev/null
+++ b/v7/mediarouter/res/values-pt-rPT/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Saída de som multimédia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Ligar ao dispositivo"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"A pesquisar dispositivos…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Desassociar"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-pt/strings.xml b/v7/mediarouter/res/values-pt/strings.xml
new file mode 100644
index 0000000..3ce1c38
--- /dev/null
+++ b/v7/mediarouter/res/values-pt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistema"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispositivos"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Saída de mídia"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Conectar ao dispositivo"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Procurando dispositivos…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Desconectar"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ro/strings.xml b/v7/mediarouter/res/values-ro/strings.xml
new file mode 100644
index 0000000..4c9e4b9
--- /dev/null
+++ b/v7/mediarouter/res/values-ro/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Dispozitive"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Rezultate media"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Conectați-vă la dispozitiv"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Se caută dispozitive..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Deconectați-vă"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-ru/strings.xml b/v7/mediarouter/res/values-ru/strings.xml
new file mode 100644
index 0000000..5cc2bba
--- /dev/null
+++ b/v7/mediarouter/res/values-ru/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Устройства"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Перенаправлять поток мультимедиа"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Подключение к устройству"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Поиск устройств…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Отключить"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-sk/strings.xml b/v7/mediarouter/res/values-sk/strings.xml
new file mode 100644
index 0000000..668800f
--- /dev/null
+++ b/v7/mediarouter/res/values-sk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Systém"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Zariadenia"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Výstup médií"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Pripojenie k zariadeniu"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Prebieha vyhľadávanie zariadení…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Odpojiť"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-sl/strings.xml b/v7/mediarouter/res/values-sl/strings.xml
new file mode 100644
index 0000000..3e3e8bb
--- /dev/null
+++ b/v7/mediarouter/res/values-sl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Naprave"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Izhod za predstavnost"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Povezovanje z napravo"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Iskanje naprav …"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Prekini povezavo"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-sr/strings.xml b/v7/mediarouter/res/values-sr/strings.xml
new file mode 100644
index 0000000..320f3e8
--- /dev/null
+++ b/v7/mediarouter/res/values-sr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Систем"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Уређаји"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Излаз медија"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Повежите са уређајем"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Претраживање уређаја…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Прекини везу"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-sv/strings.xml b/v7/mediarouter/res/values-sv/strings.xml
new file mode 100644
index 0000000..910c6f1
--- /dev/null
+++ b/v7/mediarouter/res/values-sv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Enheter"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medieuppspelning"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Anslut till enhet"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Söker efter enheter ..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Koppla från"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-sw/strings.xml b/v7/mediarouter/res/values-sw/strings.xml
new file mode 100644
index 0000000..fcbc590
--- /dev/null
+++ b/v7/mediarouter/res/values-sw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Mfumo"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Vifaa"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Towe la vyombo vya habari"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Unganisha kwenye kifaa"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Inatafuta vifaa..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Tenganisha"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-th/strings.xml b/v7/mediarouter/res/values-th/strings.xml
new file mode 100644
index 0000000..78e5a73
--- /dev/null
+++ b/v7/mediarouter/res/values-th/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"ระบบ"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"อุปกรณ์"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"เอาต์พุตสื่อ"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"เชื่อมต่อกับอุปกรณ์"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"กำลังค้นหาอุปกรณ์…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"ยกเลิกการเชื่อมต่อ"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-tl/strings.xml b/v7/mediarouter/res/values-tl/strings.xml
new file mode 100644
index 0000000..0953787
--- /dev/null
+++ b/v7/mediarouter/res/values-tl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"System"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Mga Device"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Output ng media"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Kumonekta sa device"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Naghahanap ng mga device…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Idiskonekta"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-tr/strings.xml b/v7/mediarouter/res/values-tr/strings.xml
new file mode 100644
index 0000000..12faaa6
--- /dev/null
+++ b/v7/mediarouter/res/values-tr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Medya çıkışı"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Cihaza bağlanın"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Cihaz arayın…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Bağlantıyı kes"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-uk/strings.xml b/v7/mediarouter/res/values-uk/strings.xml
new file mode 100644
index 0000000..b036dea
--- /dev/null
+++ b/v7/mediarouter/res/values-uk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Система"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Пристрої"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Вивід медіа-даних"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Під’єднатися до пристрою"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Пошук пристроїв…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Від’єднатися"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-vi/strings.xml b/v7/mediarouter/res/values-vi/strings.xml
new file mode 100644
index 0000000..a58d0e4
--- /dev/null
+++ b/v7/mediarouter/res/values-vi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Hệ thống"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Thiết bị"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Đầu ra phương tiện"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Kết nối với thiết bị"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Đang tìm kiếm thiết bị…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Ngắt kết nối"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-zh-rCN/strings.xml b/v7/mediarouter/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..71c4407
--- /dev/null
+++ b/v7/mediarouter/res/values-zh-rCN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"系统"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"设备"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"媒体输出线路"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"连接到设备"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"正在搜索设备…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"断开连接"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-zh-rHK/strings.xml b/v7/mediarouter/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..f499169
--- /dev/null
+++ b/v7/mediarouter/res/values-zh-rHK/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"系統"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"裝置"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"媒體輸出"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"連線至裝置"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"正在搜尋裝置…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"中斷連線"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-zh-rTW/strings.xml b/v7/mediarouter/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..a847615
--- /dev/null
+++ b/v7/mediarouter/res/values-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"系統"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"裝置"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"媒體輸出"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"連線至裝置"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"正在搜尋裝置..."</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"中斷連線"</string>
+</resources>
diff --git a/v7/mediarouter/res/values-zu/strings.xml b/v7/mediarouter/res/values-zu/strings.xml
new file mode 100644
index 0000000..be195be
--- /dev/null
+++ b/v7/mediarouter/res/values-zu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="mr_system_route_name" msgid="5441529851481176817">"Isistimu"</string>
+    <string name="mr_user_route_category_name" msgid="7498112907524977311">"Amadivayisi"</string>
+    <string name="mr_media_route_button_content_description" msgid="4271159405637008602">"Okukhiphayo kwabezindaba"</string>
+    <string name="mr_media_route_chooser_title" msgid="7106830097177242655">"Xhumeka kudivayisi"</string>
+    <string name="mr_media_route_chooser_searching" msgid="7553005460920830010">"Iseshela amadivayisi…"</string>
+    <string name="mr_media_route_controller_disconnect" msgid="109793632378378069">"Nqamula"</string>
+</resources>
diff --git a/v7/palette/.classpath b/v7/palette/.classpath
new file mode 100644
index 0000000..43cb38c
--- /dev/null
+++ b/v7/palette/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+    <classpathentry kind="src" path="src"/>
+    <classpathentry kind="src" path="gen"/>
+    <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+    <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+    <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/v7/palette/.project b/v7/palette/.project
new file mode 100644
index 0000000..76e11a6
--- /dev/null
+++ b/v7/palette/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+    <name>android-support-v7-palette</name>
+    <comment></comment>
+    <projects>
+    </projects>
+    <buildSpec>
+        <buildCommand>
+            <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+            <arguments>
+            </arguments>
+        </buildCommand>
+        <buildCommand>
+            <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+            <arguments>
+            </arguments>
+        </buildCommand>
+        <buildCommand>
+            <name>org.eclipse.jdt.core.javabuilder</name>
+            <arguments>
+            </arguments>
+        </buildCommand>
+        <buildCommand>
+            <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+            <arguments>
+            </arguments>
+        </buildCommand>
+    </buildSpec>
+    <natures>
+        <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+        <nature>org.eclipse.jdt.core.javanature</nature>
+    </natures>
+</projectDescription>
diff --git a/v7/palette/Android.mk b/v7/palette/Android.mk
new file mode 100644
index 0000000..8d75853
--- /dev/null
+++ b/v7/palette/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-v7-palette
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v17/leanback/res/drawable/lb_transition_action_bg.xml b/v7/palette/AndroidManifest.xml
similarity index 68%
copy from v17/leanback/res/drawable/lb_transition_action_bg.xml
copy to v7/palette/AndroidManifest.xml
index 36688bc..20e14c2 100644
--- a/v17/leanback/res/drawable/lb_transition_action_bg.xml
+++ b/v7/palette/AndroidManifest.xml
@@ -1,6 +1,5 @@
 <?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.
@@ -14,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@android:color/transparent" />
-    <item android:drawable="@drawable/lb_action_bg_focused" />
-</transition>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.v7.palette">
+    <uses-sdk android:minSdkVersion="7"/>
+    <application>
+    </application>
+</manifest>
diff --git a/v7/palette/README.txt b/v7/palette/README.txt
new file mode 100644
index 0000000..4a8b5e4
--- /dev/null
+++ b/v7/palette/README.txt
@@ -0,0 +1 @@
+Library Project including Palette for color extraction from images
diff --git a/v7/palette/build.gradle b/v7/palette/build.gradle
new file mode 100644
index 0000000..3d195ff
--- /dev/null
+++ b/v7/palette/build.gradle
@@ -0,0 +1,13 @@
+apply plugin: 'android-library'
+
+archivesBaseName = 'palette-v7'
+
+android {
+    compileSdkVersion 7
+    buildToolsVersion "19.0.1"
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDir 'src'
+    }
+}
diff --git a/v7/palette/project.properties b/v7/palette/project.properties
new file mode 100644
index 0000000..1e106c3
--- /dev/null
+++ b/v7/palette/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-7
+android.library=true
\ No newline at end of file
diff --git a/v7/palette/src/android/support/v7/graphics/ColorCutQuantizer.java b/v7/palette/src/android/support/v7/graphics/ColorCutQuantizer.java
new file mode 100644
index 0000000..2e1ce30
--- /dev/null
+++ b/v7/palette/src/android/support/v7/graphics/ColorCutQuantizer.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.PriorityQueue;
+
+/**
+ * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct
+ * colors rather than representation colors.
+ *
+ * The color space is represented as a 3-dimensional cube with each dimension being an RGB
+ * component. The cube is then repeatedly divided until we have reduced the color space to the
+ * requested number of colors. An average color is then generated from each cube.
+ *
+ * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes
+ * have roughly the same population, where this quantizer divides boxes based on their color volume.
+ * This means that the color space is divided into distinct colors, rather than representative
+ * colors.
+ */
+final class ColorCutQuantizer {
+
+    private static final String LOG_TAG = ColorCutQuantizer.class.getSimpleName();
+
+    private final float[] mTempHsl = new float[3];
+
+    private static final int BLACK_FILTER = 22;
+    private static final int WHITE_FILTER = 237;
+
+    private static final int COMPONENT_RED = -3;
+    private static final int COMPONENT_GREEN = -2;
+    private static final int COMPONENT_BLUE = -1;
+
+    private final int[] mColors;
+    private final SparseIntArray mColorPopulations;
+
+    private final List<PaletteItem> mQuantizedColors;
+
+    /**
+     * Factory-method to generate a {@link ColorCutQuantizer} from a {@link Bitmap} object.
+     *
+     * @param bitmap Bitmap to extract the pixel data from
+     * @param maxColors The maximum number of colors that should be in the result palette.
+     */
+    static ColorCutQuantizer fromBitmap(Bitmap bitmap, int maxColors) {
+        final int width = bitmap.getWidth();
+        final int height = bitmap.getHeight();
+
+        final int[] rgbPixels = new int[width * height];
+        bitmap.getPixels(rgbPixels, 0, width, 0, 0, width, height);
+
+        return new ColorCutQuantizer(rgbPixels, maxColors);
+    }
+
+    /**
+     * Private constructor.
+     *
+     * @param pixels array of rgb packed ints
+     * @param maxColors The maximum number of colors that should be in the result palette.
+     */
+    ColorCutQuantizer(int[] pixels, int maxColors) {
+        final ColorHistogram colorHist = new ColorHistogram(pixels);
+        final int rawColorCount = colorHist.getNumberOfColors();
+        final int[] rawColors = colorHist.getColors();
+        final int[] rawColorCounts = colorHist.getColorCounts();
+
+        // First, lets pack the populations into a SparseIntArray so that they can be easily
+        // retrieved without knowing a color's index
+        mColorPopulations = new SparseIntArray(rawColorCount);
+        for (int i = 0; i < rawColors.length; i++) {
+            mColorPopulations.append(rawColors[i], rawColorCounts[i]);
+        }
+
+        // Now go through all of the colors and keep those which we do not want to ignore
+        mColors = new int[rawColorCount];
+        int validColorCount = 0;
+        for (int color : rawColors) {
+            if (!shouldIgnoreColor(color)) {
+                mColors[validColorCount++] = color;
+            }
+        }
+
+        if (validColorCount <= maxColors) {
+            // The image has fewer colors than the maximum requested, so just return the colors
+            mQuantizedColors = new ArrayList<PaletteItem>();
+            for (final int color : mColors) {
+                mQuantizedColors.add(new PaletteItem(color, mColorPopulations.get(color)));
+            }
+        } else {
+            // We need use quantization to reduce the number of colors
+            mQuantizedColors = quantizePixels(validColorCount - 1, maxColors);
+        }
+    }
+
+    /**
+     * @return the list of quantized colors
+     */
+    List<PaletteItem> getQuantizedColors() {
+        return mQuantizedColors;
+    }
+
+    private List<PaletteItem> quantizePixels(int maxColorIndex, int maxColors) {
+        // Create the priority queue which is sorted by volume descending. This means we always
+        // split the largest box in the queue
+        final PriorityQueue<Vbox> pq = new PriorityQueue<Vbox>(maxColors, VBOX_COMPARATOR_VOLUME);
+
+        // To start, offer a box which contains all of the colors
+        pq.offer(new Vbox(0, maxColorIndex));
+
+        // Now go through the boxes, splitting them until we have reached maxColors or there are no
+        // more boxes to split
+        splitBoxes(pq, maxColors);
+
+        // Finally, return the average colors of the color boxes
+        return generateAverageColors(pq);
+    }
+
+    /**
+     * Iterate through the {@link java.util.Queue}, popping
+     * {@link ColorCutQuantizer.Vbox} objects from the queue
+     * and splitting them. Once split, the new box and the remaining box are offered back to the
+     * queue.
+     *
+     * @param queue {@link java.util.PriorityQueue} to poll for boxes
+     * @param maxSize Maximum amount of boxes to split
+     */
+    private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
+        while (queue.size() < maxSize) {
+            final Vbox vbox = queue.poll();
+
+            if (vbox != null && vbox.canSplit()) {
+                // First split the box, and offer the result
+                queue.offer(vbox.splitBox());
+                // Then offer the box back
+                queue.offer(vbox);
+            } else {
+                // If we get here then there are no more boxes to split, so return
+                return;
+            }
+        }
+    }
+
+    private List<PaletteItem> generateAverageColors(Collection<Vbox> vboxes) {
+        ArrayList<PaletteItem> colors = new ArrayList<PaletteItem>(vboxes.size());
+        for (Vbox vbox : vboxes) {
+            PaletteItem color = vbox.getAverageColor();
+            if (!shouldIgnoreColor(color)) {
+                // As we're averaging a color box, we can still get colors which we do not want, so
+                // we check again here
+                colors.add(color);
+            }
+        }
+        return colors;
+    }
+
+    /**
+     * Represents a tightly fitting box around a color space.
+     */
+    private class Vbox {
+        private int lowerIndex;
+        private int upperIndex;
+
+        private int minRed, maxRed;
+        private int minGreen, maxGreen;
+        private int minBlue, maxBlue;
+
+        Vbox(int lowerIndex, int upperIndex) {
+            this.lowerIndex = lowerIndex;
+            this.upperIndex = upperIndex;
+            fitBox();
+        }
+
+        int getVolume() {
+            return (maxRed - minRed + 1) * (maxGreen - minGreen + 1) * (maxBlue - minBlue + 1);
+        }
+
+        boolean canSplit() {
+            return getColorCount() > 1;
+        }
+
+        int getColorCount() {
+            return upperIndex - lowerIndex;
+        }
+
+        /**
+         * Recomputes the boundaries of this box to tightly fit the colors within the box.
+         */
+        void fitBox() {
+            // Reset the min and max to opposite values
+            minRed = minGreen = minBlue = 0xFF;
+            maxRed = maxGreen = maxBlue = 0x0;
+
+            for (int i = lowerIndex; i <= upperIndex; i++) {
+                final int color = mColors[i];
+                int r = Color.red(color);
+                int g = Color.green(color);
+                int b = Color.blue(color);
+                if (r > maxRed) {
+                    maxRed = r;
+                }
+                if (r < minRed) {
+                    minRed = r;
+                }
+                if (g > maxGreen) {
+                    maxGreen = g;
+                }
+                if (g < minGreen) {
+                    minGreen = g;
+                }
+                if (b > maxBlue) {
+                    maxBlue = b;
+                }
+                if (b < minBlue) {
+                    minBlue = b;
+                }
+            }
+        }
+
+        /**
+         * Split this color box at the mid-point along it's longest dimension
+         *
+         * @return the new ColorBox
+         */
+        Vbox splitBox() {
+            if (!canSplit()) {
+                throw new IllegalStateException("Can not split a box with only 1 color");
+            }
+
+            // find median along the longest dimension
+            final int splitPoint = findSplitPoint();
+
+            Vbox newBox = new Vbox(splitPoint + 1, upperIndex);
+
+            // Now change this box's upperIndex and recompute the color boundaries
+            upperIndex = splitPoint;
+            fitBox();
+
+            return newBox;
+        }
+
+        /**
+         * @return the dimension which this box is largest in
+         */
+        int getLongestColorDimension() {
+            final int redLength = maxRed - minRed;
+            final int greenLength = maxGreen - minGreen;
+            final int blueLength = maxBlue - minBlue;
+
+            if (redLength >= greenLength && redLength >= blueLength) {
+                return COMPONENT_RED;
+            } else if (greenLength >= redLength && greenLength >= blueLength) {
+                return COMPONENT_GREEN;
+            } else {
+                return COMPONENT_BLUE;
+            }
+        }
+
+        /**
+         * Finds the point within this box's lowerIndex and upperIndex index of where to split.
+         *
+         * This is calculated by finding the longest color dimension, and then sorting the
+         * sub-array based on that dimension value in each color. The colors are then iterated over
+         * until a color is found with at least the midpoint of the whole box's dimension midpoint.
+         *
+         * @return the index of the colors array to split from
+         */
+        int findSplitPoint() {
+            final int longestDimension = getLongestColorDimension();
+
+            // We need to sort the colors in this box based on the longest color dimension.
+            // As we can't use a Comparator to define the sort logic, we modify each color so that
+            // it's most significant is the desired dimension
+            modifySignificantOctet(longestDimension, lowerIndex, upperIndex);
+
+            // Now sort...
+            Arrays.sort(mColors, lowerIndex, upperIndex + 1);
+
+            // Now revert all of the colors so that they are packed as RGB again
+            modifySignificantOctet(longestDimension, lowerIndex, upperIndex);
+
+            final int dimensionMidPoint = midPoint(longestDimension);
+
+            for (int i = lowerIndex; i < upperIndex; i++)  {
+                final int color = mColors[i];
+
+                switch (longestDimension) {
+                    case COMPONENT_RED:
+                        if (Color.red(color) >= dimensionMidPoint) {
+                            return i;
+                        }
+                        break;
+                    case COMPONENT_GREEN:
+                        if (Color.green(color) >= dimensionMidPoint) {
+                            return i;
+                        }
+                        break;
+                    case COMPONENT_BLUE:
+                        if (Color.blue(color) > dimensionMidPoint) {
+                            return i;
+                        }
+                        break;
+                }
+            }
+
+            return lowerIndex;
+        }
+
+        /**
+         * @return the average color of this box.
+         */
+        PaletteItem getAverageColor() {
+            int redSum = 0;
+            int greenSum = 0;
+            int blueSum = 0;
+            int totalPopulation = 0;
+
+            for (int i = lowerIndex; i <= upperIndex; i++) {
+                final int color = mColors[i];
+                final int colorPopulation = mColorPopulations.get(color);
+
+                totalPopulation += colorPopulation;
+                redSum += colorPopulation * Color.red(color);
+                greenSum += colorPopulation * Color.green(color);
+                blueSum += colorPopulation * Color.blue(color);
+            }
+
+            final int redAverage = Math.round(redSum / (float) totalPopulation);
+            final int greenAverage = Math.round(greenSum / (float) totalPopulation);
+            final int blueAverage = Math.round(blueSum / (float) totalPopulation);
+
+            return new PaletteItem(redAverage, greenAverage, blueAverage, totalPopulation);
+        }
+
+        /**
+         * @return the midpoint of this box in the given {@code dimension}
+         */
+        int midPoint(int dimension) {
+            switch (dimension) {
+                case COMPONENT_RED:
+                default:
+                    return (minRed + maxRed) / 2;
+                case COMPONENT_GREEN:
+                    return (minGreen + maxGreen) / 2;
+                case COMPONENT_BLUE:
+                    return (minBlue + maxBlue) / 2;
+            }
+        }
+    }
+
+    /**
+     * Modify the significant octet in a packed color int. Allows sorting based on the value of a
+     * single color component.
+     *
+     * @see Vbox#findSplitPoint()
+     */
+    private void modifySignificantOctet(final int dimension, int lowIndex, int highIndex) {
+        switch (dimension) {
+            case COMPONENT_RED:
+                // Already in RGB, no need to do anything
+                break;
+            case COMPONENT_GREEN:
+                // We need to do a RGB to GRB swap, or vice-versa
+                for (int i = lowIndex; i <= highIndex; i++) {
+                    final int color = mColors[i];
+                    mColors[i] = Color.rgb((color >> 8) & 0xFF, (color >> 16) & 0xFF, color & 0xFF);
+                }
+                break;
+            case COMPONENT_BLUE:
+                // We need to do a RGB to BGR swap, or vice-versa
+                for (int i = lowIndex; i <= highIndex; i++) {
+                    final int color = mColors[i];
+                    mColors[i] = Color.rgb(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF);
+                }
+                break;
+        }
+    }
+
+    private boolean shouldIgnoreColor(PaletteItem color) {
+        return isWhite(color.getRgb()) || isBlack(color.getRgb()) || isSkinTone(color.getRgb());
+    }
+
+    private boolean shouldIgnoreColor(int color) {
+        return isWhite(color) || isBlack(color) || isSkinTone(color);
+    }
+
+    /**
+     * @return true if the color represents a color which is close to black.
+     */
+    private boolean isBlack(int color) {
+        return ((Color.red(color) + Color.green(color) + Color.blue(color)) / 3f) <= BLACK_FILTER;
+    }
+
+    /**
+     * @return true if the color represents a color which is close to white.
+     */
+    private boolean isWhite(int color) {
+        return ((Color.red(color) + Color.green(color) + Color.blue(color)) / 3f) >= WHITE_FILTER;
+    }
+
+    /**
+     * @return true if the color represents a skin tone.
+     */
+    private boolean isSkinTone(int color) {
+        ColorUtils.RGBtoHSL(Color.red(color), Color.green(color), Color.blue(color), mTempHsl);
+        return mTempHsl[0] >= 10f && mTempHsl[0] <= 37f && mTempHsl[1] <= 0.82f;
+    }
+
+    private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {
+        @Override
+        public int compare(Vbox lhs, Vbox rhs) {
+            return rhs.getVolume() - lhs.getVolume();
+        }
+    };
+
+}
diff --git a/v7/palette/src/android/support/v7/graphics/ColorHistogram.java b/v7/palette/src/android/support/v7/graphics/ColorHistogram.java
new file mode 100644
index 0000000..a196f0a
--- /dev/null
+++ b/v7/palette/src/android/support/v7/graphics/ColorHistogram.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.graphics;
+
+import java.util.Arrays;
+
+/**
+ * Class which provides a histogram for RGB values.
+ */
+final class ColorHistogram {
+
+    private final int[] mColors;
+    private final int[] mColorCounts;
+    private final int mNumberColors;
+
+    /**
+     * A new {@link ColorHistogram} instance.
+     *
+     * @param imagePixels array of image contents
+     */
+    ColorHistogram(final int[] imagePixels) {
+        // Lets take a copy of the original pixels
+        final int[] pixels = new int[imagePixels.length];
+        System.arraycopy(imagePixels, 0, pixels, 0, pixels.length);
+
+        // Sort the pixels to enable counting below
+        Arrays.sort(pixels);
+
+        mNumberColors = countDistinctColors(pixels);
+
+        // Create arrays
+        mColors = new int[mNumberColors];
+        mColorCounts = new int[mNumberColors];
+
+        countFrequencies(pixels);
+    }
+
+    /**
+     * @return number of distinct colors in the image.
+     */
+    int getNumberOfColors() {
+        return mNumberColors;
+    }
+
+    /**
+     * @return an array containing all of the distinct colors in the image.
+     */
+    int[] getColors() {
+        return mColors;
+    }
+
+    /**
+     * @return an array containing the frequency of a distinct colors within the image.
+     */
+    int[] getColorCounts() {
+        return mColorCounts;
+    }
+
+    static int countDistinctColors(final int[] pixels) {
+        if (pixels.length == 0) {
+            return 0;
+        }
+
+        // If we have at least 1 pixel, we have a minimum of 1 color
+        int colorCount = 1;
+        int currentColor = pixels[0];
+
+        // Now iterate from the second pixel to the end, population distinct colors
+        for (int i = 1; i < pixels.length; i++) {
+            // If we encounter a new color, increase the population
+            if (pixels[i] != currentColor) {
+                currentColor = pixels[i];
+                colorCount++;
+            }
+        }
+
+        return colorCount;
+    }
+
+    private void countFrequencies(final int[] pixels) {
+        if (pixels.length == 0) {
+            return;
+        }
+
+        int currentColorIndex = 0;
+        int currentColor = pixels[0];
+
+        mColors[currentColorIndex] = currentColor;
+        mColorCounts[currentColorIndex] = 1;
+
+        // Now iterate from the second pixel to the end, population distinct colors
+        for (int i = 1; i < pixels.length; i++) {
+            if (pixels[i] == currentColor) {
+                // We've hit the same color as before, increase population
+                mColorCounts[currentColorIndex]++;
+            } else {
+                // We've hit a new color, increase index
+                currentColor = pixels[i];
+
+                currentColorIndex++;
+                mColors[currentColorIndex] = currentColor;
+                mColorCounts[currentColorIndex] = 1;
+            }
+        }
+    }
+
+}
diff --git a/v7/palette/src/android/support/v7/graphics/ColorUtils.java b/v7/palette/src/android/support/v7/graphics/ColorUtils.java
new file mode 100644
index 0000000..34b075e
--- /dev/null
+++ b/v7/palette/src/android/support/v7/graphics/ColorUtils.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.graphics;
+
+import android.graphics.Color;
+
+final class ColorUtils {
+
+    private ColorUtils() {}
+
+    /**
+     * @return luma value according to to XYZ color space in the range 0.0 - 1.0
+     */
+    static float calculateXyzLuma(int color) {
+        return (0.2126f * Color.red(color) +
+                0.7152f * Color.green(color) +
+                0.0722f * Color.blue(color)) / 255f;
+    }
+
+    static float calculateContrast(int color1, int color2) {
+        return Math.abs(ColorUtils.calculateXyzLuma(color1) - ColorUtils.calculateXyzLuma(color2));
+    }
+
+    static void RGBtoHSL(int r, int g, int b, float[] hsl) {
+        final float rf = r / 255f;
+        final float gf = g / 255f;
+        final float bf = b / 255f;
+
+        final float max = Math.max(rf, Math.max(gf, bf));
+        final float min = Math.min(rf, Math.min(gf, bf));
+        final float deltaMaxMin = max - min;
+
+        float h, s;
+        float l = (max + min) / 2f;
+
+        if (max == min) {
+            // Monochromatic
+            h = s = 0f;
+        } else {
+            if (max == rf) {
+                h = ((gf - bf) / deltaMaxMin) % 6f;
+            } else if (max == gf) {
+                h = ((bf - rf) / deltaMaxMin) + 2f;
+            } else {
+                h = ((rf - gf) / deltaMaxMin) + 4f;
+            }
+
+            s =  deltaMaxMin / (1f - Math.abs(2f * l - 1f));
+        }
+
+        hsl[0] = (h * 60f) % 360f;
+        hsl[1] = s;
+        hsl[2] = l;
+    }
+
+    static int HSLtoRGB (float[] hsl) {
+        final float h = hsl[0];
+        final float s = hsl[1];
+        final float l = hsl[2];
+
+        final float c = (1f - Math.abs(2 * l - 1f)) * s;
+        final float m = l - 0.5f * c;
+        final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
+
+        final int hueSegment = (int) h / 60;
+
+        int r = 0, g = 0, b = 0;
+
+        switch (hueSegment) {
+            case 0:
+                r = Math.round(255 * (c + m));
+                g = Math.round(255 * (x + m));
+                b = Math.round(255 * m);
+                break;
+            case 1:
+                r = Math.round(255 * (x + m));
+                g = Math.round(255 * (c + m));
+                b = Math.round(255 * m);
+                break;
+            case 2:
+                r = Math.round(255 * m);
+                g = Math.round(255 * (c + m));
+                b = Math.round(255 * (x + m));
+                break;
+            case 3:
+                r = Math.round(255 * m);
+                g = Math.round(255 * (x + m));
+                b = Math.round(255 * (c + m));
+                break;
+            case 4:
+                r = Math.round(255 * (x + m));
+                g = Math.round(255 * m);
+                b = Math.round(255 * (c + m));
+                break;
+            case 5:
+            case 6:
+                r = Math.round(255 * (c + m));
+                g = Math.round(255 * m);
+                b = Math.round(255 * (x + m));
+                break;
+        }
+
+        r = Math.max(0, Math.min(255, r));
+        g = Math.max(0, Math.min(255, g));
+        b = Math.max(0, Math.min(255, b));
+
+        return Color.rgb(r, g, b);
+    }
+
+}
diff --git a/v7/palette/src/android/support/v7/graphics/Palette.java b/v7/palette/src/android/support/v7/graphics/Palette.java
new file mode 100644
index 0000000..9e57760
--- /dev/null
+++ b/v7/palette/src/android/support/v7/graphics/Palette.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.graphics;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper class to extract prominent colors from an image.
+ * <p>
+ * A number of colors with different profiles are extracted from the image:
+ * <ul>
+ *     <li>Vibrant</li>
+ *     <li>Vibrant Dark</li>
+ *     <li>Vibrant Light</li>
+ *     <li>Muted</li>
+ *     <li>Muted Dark</li>
+ *     <li>Muted Light</li>
+ * </ul>
+ * These can be retrieved from the appropriate getter method.
+ *
+ * <p>
+ * Instances can be created with the synchronous factory methods {@link #generate(Bitmap)} and
+ * {@link #generate(Bitmap, int)}.
+ * <p>
+ * These should be called on a background thread, ideally the one in
+ * which you load your images on. Sometimes that is not possible, so asynchronous factory methods
+ * have also been provided: {@link #generateAsync(Bitmap, PaletteAsyncListener)} and
+ * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}. These can be used as so:
+ *
+ * <pre>
+ * Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
+ *     @Override
+ *     public void onGenerated(Palette palette) {
+ *         // Do something with colors...
+ *     }
+ * });
+ * </pre>
+ */
+public final class Palette {
+
+    /**
+     * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or
+     * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)}
+     */
+    public interface PaletteAsyncListener {
+
+        /**
+         * Called when the {@link Palette} has been generated.
+         */
+        void onGenerated(Palette palette);
+    }
+
+    private static final int CALCULATE_BITMAP_MIN_DIMENSION = 100;
+    private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
+
+    private static final float TARGET_DARK_LUMA = 0.26f;
+    private static final float MAX_DARK_LUMA = 0.45f;
+
+    private static final float MIN_LIGHT_LUMA = 0.55f;
+    private static final float TARGET_LIGHT_LUMA = 0.74f;
+
+    private static final float MIN_NORMAL_LUMA = 0.3f;
+    private static final float TARGET_NORMAL_LUMA = 0.5f;
+    private static final float MAX_NORMAL_LUMA = 0.7f;
+
+    private static final float TARGET_MUTED_SATURATION = 0.3f;
+    private static final float MAX_MUTED_SATURATION = 0.4f;
+
+    private static final float TARGET_VIBRANT_SATURATION = 1f;
+    private static final float MIN_VIBRANT_SATURATION = 0.35f;
+
+    private final List<PaletteItem> mPallete;
+    private final int mHighestPopulation;
+
+    private PaletteItem mVibrantColor;
+    private PaletteItem mMutedColor;
+
+    private PaletteItem mDarkVibrantColor;
+    private PaletteItem mDarkMutedColor;
+
+    private PaletteItem mLightVibrantColor;
+    private PaletteItem mLightMutedColor;
+
+    /**
+     * Generate a {@link Palette} from a {@link Bitmap} using the default number of colors.
+     */
+    public static Palette generate(Bitmap bitmap) {
+        return generate(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS);
+    }
+
+    /**
+     * Generate a {@link Palette} from a {@link Bitmap} using the specified {@code numColors}.
+     * Good values for {@code numColors} depend on the source image type.
+     * For landscapes, a good values are in the range 12-16. For images which are largely made up
+     * of people's faces then this value should be increased to 24-32.
+     *
+     * @param numColors The maximum number of colors in the generated palette. Increasing this
+     *                  number will increase the time needed to compute the values.
+     */
+    public static Palette generate(Bitmap bitmap, int numColors) {
+        if (bitmap == null) {
+            throw new IllegalArgumentException("bitmap can not be null");
+        }
+
+        // First we'll scale down the bitmap so it's shortest dimension is 100px
+        final Bitmap scaledBitmap = scaleBitmapDown(bitmap);
+
+        // Now generate a quantizer from the Bitmap
+        ColorCutQuantizer quantizer = ColorCutQuantizer.fromBitmap(scaledBitmap, numColors);
+
+        // If created a new bitmap, recycle it
+        if (scaledBitmap != bitmap) {
+            scaledBitmap.recycle();
+        }
+
+        // Now return a ColorExtractor instance
+        return new Palette(quantizer.getQuantizedColors());
+    }
+
+    /**
+     * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)}
+     * will be called with the created instance. The resulting {@link Palette} is the same as
+     * what would be created by calling {@link #generate(Bitmap)}.
+     *
+     * @param listener Listener to be invoked when the {@link Palette} has been generated.
+     *
+     * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance.
+     */
+    public static AsyncTask<Void, Void, Palette> generateAsync(
+            Bitmap bitmap, PaletteAsyncListener listener) {
+        return generateAsync(bitmap, DEFAULT_CALCULATE_NUMBER_COLORS, listener);
+    }
+
+    /**
+     * Generate a {@link Palette} asynchronously. {@link PaletteAsyncListener#onGenerated(Palette)}
+     * will be called with the created instance. The resulting {@link Palette} is the same as what
+     * would be created by calling {@link #generate(Bitmap, int)}.
+     *
+     * @param listener Listener to be invoked when the {@link Palette} has been generated.
+     *
+     * @return the {@link android.os.AsyncTask} used to asynchronously generate the instance.
+     */
+    public static AsyncTask<Void, Void, Palette> generateAsync(
+            final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener can not be null");
+        }
+
+        AsyncTask<Void, Void, Palette> task = new AsyncTask<Void, Void, Palette>() {
+            @Override
+            protected Palette doInBackground(Void... voids) {
+                return generate(bitmap, numColors);
+            }
+
+            @Override
+            protected void onPostExecute(Palette colorExtractor) {
+                super.onPostExecute(colorExtractor);
+                listener.onGenerated(colorExtractor);
+            }
+        };
+        task.execute();
+        return task;
+    }
+
+    private Palette(List<PaletteItem> palette) {
+        mPallete = palette;
+        mHighestPopulation = findMaxPopulation();
+
+        mVibrantColor = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
+                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
+
+        mLightVibrantColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
+                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
+
+        mDarkVibrantColor = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
+                TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f);
+
+        mMutedColor = findColor(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA,
+                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
+
+        mLightMutedColor = findColor(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f,
+                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
+
+        mDarkMutedColor = findColor(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA,
+                TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION);
+
+        // Now try and generate any missing colors
+        generateEmptyColors();
+    }
+
+    /**
+     * The total palette of colors which make up the image.
+     */
+    public List<PaletteItem> getPallete() {
+        return Collections.unmodifiableList(mPallete);
+    }
+
+    /**
+     * Returns the most vibrant color in the image. Might be null.
+     */
+    public PaletteItem getVibrantColor() {
+        return mVibrantColor;
+    }
+
+    /**
+     * Returns a light and vibrant color from the image. Might be null.
+     */
+    public PaletteItem getLightVibrantColor() {
+        return mLightVibrantColor;
+    }
+
+    /**
+     * Returns a dark and vibrant color from the image. Might be null.
+     */
+    public PaletteItem getDarkVibrantColor() {
+        return mDarkVibrantColor;
+    }
+
+    /**
+     * Returns a muted color from the image. Might be null.
+     */
+    public PaletteItem getMutedColor() {
+        return mMutedColor;
+    }
+
+    /**
+     * Returns a muted and light color from the image. Might be null.
+     */
+    public PaletteItem getLightMutedColor() {
+        return mLightMutedColor;
+    }
+
+    /**
+     * Returns a muted and dark color from the image. Might be null.
+     */
+    public PaletteItem getDarkMutedColor() {
+        return mDarkMutedColor;
+    }
+
+    /**
+     * @return true if we have already selected {@code item}
+     */
+    private boolean isAlreadySelected(PaletteItem item) {
+        return mVibrantColor == item || mDarkVibrantColor == item || mLightVibrantColor == item ||
+                mMutedColor == item || mDarkMutedColor == item || mLightMutedColor == item;
+    }
+
+    private PaletteItem findColor(float targetLuma, float minLuma, float maxLuma,
+                                float targetSaturation, float minSaturation, float maxSaturation) {
+        PaletteItem max = null;
+        float maxValue = 0f;
+
+        for (PaletteItem paletteItem : mPallete) {
+            final float sat = paletteItem.getHsl()[1];
+            final float luma = paletteItem.getHsl()[2];
+
+            if (sat >= minSaturation && sat <= maxSaturation &&
+                    luma >= minLuma && luma <= maxLuma &&
+                    !isAlreadySelected(paletteItem)) {
+                float thisValue = createComparisonValue(sat, targetSaturation, luma, targetLuma,
+                        paletteItem.getPopulation(), mHighestPopulation);
+                if (max == null || thisValue > maxValue) {
+                    max = paletteItem;
+                    maxValue = thisValue;
+                }
+            }
+        }
+
+        return max;
+    }
+
+    /**
+     * Try and generate any missing colors from the colors we did find.
+     */
+    private void generateEmptyColors() {
+        if (mVibrantColor == null) {
+            // If we do not have a vibrant color...
+            if (mDarkVibrantColor != null) {
+                // ...but we do have a dark vibrant, generate the value by modifying the luma
+                final float[] newHsl = copyHslValues(mDarkVibrantColor);
+                newHsl[2] = TARGET_NORMAL_LUMA;
+                mVibrantColor = new PaletteItem(ColorUtils.HSLtoRGB(newHsl), 0);
+            }
+        }
+
+        if (mDarkVibrantColor == null) {
+            // If we do not have a dark vibrant color...
+            if (mVibrantColor != null) {
+                // ...but we do have a vibrant, generate the value by modifying the luma
+                final float[] newHsl = copyHslValues(mVibrantColor);
+                newHsl[2] = TARGET_DARK_LUMA;
+                mDarkVibrantColor = new PaletteItem(ColorUtils.HSLtoRGB(newHsl), 0);
+            }
+        }
+    }
+
+    /**
+     * Find the {@link PaletteItem} with the highest population value and return the population.
+     */
+    private int findMaxPopulation() {
+        int population = 0;
+        for (PaletteItem item : mPallete) {
+            population = Math.max(population, item.getPopulation());
+        }
+        return population;
+    }
+
+    /**
+     * Scale the bitmap down so that it's smallest dimension is
+     * {@value #CALCULATE_BITMAP_MIN_DIMENSION}px. If {@code bitmap} is smaller than this, than it
+     * is returned.
+     */
+    private static Bitmap scaleBitmapDown(Bitmap bitmap) {
+        final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
+
+        if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {
+            // If the bitmap is small enough already, just return it
+            return bitmap;
+        }
+
+        final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;
+        return Bitmap.createScaledBitmap(bitmap,
+                Math.round(bitmap.getWidth() * scaleRatio),
+                Math.round(bitmap.getHeight() * scaleRatio),
+                false);
+    }
+
+    private static float createComparisonValue(float saturation, float targetSaturation,
+            float luma, float targetLuma,
+            int population, int highestPopulation) {
+        return weightedMean(
+                invertDiff(saturation, targetSaturation), 3f,
+                invertDiff(luma, targetLuma), 6.5f,
+                population / (float) highestPopulation, 0.5f
+        );
+    }
+
+    /**
+     * Copy a {@link PaletteItem}'s HSL values into a new float[].
+     */
+    private static float[] copyHslValues(PaletteItem color) {
+        final float[] newHsl = new float[3];
+        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
+        return newHsl;
+    }
+
+    /**
+     * Returns a value in the range 0-1. 1 is returned when {@code value} equals the
+     * {@code targetValue} and then decreases as the absolute difference between {@code value} and
+     * {@code targetValue} increases.
+     *
+     * @param value the item's value
+     * @param targetValue the value which we desire
+     */
+    private static float invertDiff(float value, float targetValue) {
+        return 1f - Math.abs(value - targetValue);
+    }
+
+    private static float weightedMean(float... values) {
+        float sum = 0f;
+        float sumWeight = 0f;
+
+        for (int i = 0; i < values.length; i += 2) {
+            float value = values[i];
+            float weight = values[i + 1];
+
+            sum += (value * weight);
+            sumWeight += weight;
+        }
+
+        return sum / sumWeight;
+    }
+
+}
diff --git a/v7/palette/src/android/support/v7/graphics/PaletteItem.java b/v7/palette/src/android/support/v7/graphics/PaletteItem.java
new file mode 100644
index 0000000..ce73059
--- /dev/null
+++ b/v7/palette/src/android/support/v7/graphics/PaletteItem.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.graphics;
+
+import android.graphics.Color;
+
+import java.util.Arrays;
+
+/**
+ * Represents a color generated from an image's palette. The RGB color can be retrieved by
+ * calling {@link #getRgb()}.
+ */
+public final class PaletteItem {
+
+    final int red, green, blue;
+    final int rgb;
+    final int population;
+
+    private float[] hsl;
+
+    PaletteItem(int rgbColor, int population) {
+        this.red = Color.red(rgbColor);
+        this.green = Color.green(rgbColor);
+        this.blue = Color.blue(rgbColor);
+        this.rgb = rgbColor;
+        this.population = population;
+    }
+
+    PaletteItem(int red, int green, int blue, int population) {
+        this.red = red;
+        this.green = green;
+        this.blue = blue;
+        this.rgb = Color.rgb(red, green, blue);
+        this.population = population;
+    }
+
+    /**
+     * @return this item's RGB color value
+     */
+    public int getRgb() {
+        return rgb;
+    }
+
+    /**
+     * Return this item's HSL values.
+     *     hsv[0] is Hue [0 .. 360)
+     *     hsv[1] is Saturation [0...1]
+     *     hsv[2] is Lightness [0...1]
+     */
+    public float[] getHsl() {
+        if (hsl == null) {
+            // Lazily generate HSL values from RGB
+            hsl = new float[3];
+            ColorUtils.RGBtoHSL(red, green, blue, hsl);
+        }
+        return hsl;
+    }
+
+    /**
+     * @return the number of pixels represented by this color
+     */
+    int getPopulation() {
+        return population;
+    }
+
+    public String toString() {
+        return new StringBuilder(getClass().getSimpleName()).append(" ")
+                .append("[").append(Integer.toHexString(getRgb())).append(']')
+                .append("[HSL: ").append(Arrays.toString(getHsl())).append(']')
+                .append("[Population: ").append(population).append(']').toString();
+    }
+}
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index f7d0607..40a58ad 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -10,6 +10,10 @@
     compileSdkVersion 7
     buildToolsVersion "19.0.1"
 
+    defaultConfig {
+        minSdkVersion 7
+    }
+
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDir 'src'
@@ -21,4 +25,71 @@
         // TODO: fix errors and reenable.
         abortOnError false
     }
-}
\ No newline at end of file
+}
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+
+    if (name.equals(com.android.builder.BuilderConstants.DEBUG)) {
+        return; // Skip debug builds.
+    }
+    def suffix = name.capitalize()
+
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        from 'LICENSE.txt'
+    }
+    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
+        source android.sourceSets.main.allJava
+        classpath = files(variant.javaCompile.classpath.files) + files(
+                "${android.plugin.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
+    }
+
+    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
+        classifier = 'javadoc'
+        from 'build/docs/javadoc'
+    }
+
+    def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
+        classifier = 'sources'
+        from android.sourceSets.main.allSource
+    }
+
+    artifacts.add('archives', javadocJarTask);
+    artifacts.add('archives', sourcesJarTask);
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: uri(rootProject.ext.supportRepoOut)) {
+            }
+
+            pom.project {
+                name 'Android Support RecyclerView v7'
+                description "Android Support RecyclerView v7"
+                url 'http://developer.android.com/tools/extras/support-library.html'
+                inceptionYear '2011'
+
+                licenses {
+                    license {
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                        distribution 'repo'
+                    }
+                }
+
+                scm {
+                    url "http://source.android.com"
+                    connection "scm:git:https://android.googlesource.com/platform/frameworks/support"
+                }
+                developers {
+                    developer {
+                        name 'The Android Open Source Project'
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index 2e3b739..20ab40b 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -72,7 +72,7 @@
      * Based on {@link #mOrientation}, an implementation is lazily created in
      * {@link #ensureRenderState} method.
      */
-    private OrientationHelper mOrientationHelper;
+    OrientationHelper mOrientationHelper;
 
     /**
      * We need to track this so that we can ignore current position when it changes.
@@ -152,9 +152,7 @@
         SavedState state = new SavedState();
         if (getChildCount() > 0) {
             boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout;
-            state.mOrientation = mOrientation;
             state.mAnchorLayoutFromEnd = didLayoutFromEnd;
-
             if (didLayoutFromEnd) {
                 final View refChild = getChildClosestToEnd();
                 state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() -
@@ -172,6 +170,7 @@
         }
         state.mStackFromEnd = mStackFromEnd;
         state.mReverseLayout = mReverseLayout;
+        state.mOrientation = mOrientation;
         return state;
     }
 
@@ -208,6 +207,13 @@
      * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}
      */
     public void setStackFromEnd(boolean stackFromEnd) {
+        if (mPendingSavedState != null && mPendingSavedState.mStackFromEnd != stackFromEnd) {
+            // override pending state
+            mPendingSavedState.mStackFromEnd = stackFromEnd;
+        }
+        if (mStackFromEnd == stackFromEnd) {
+            return;
+        }
         mStackFromEnd = stackFromEnd;
         requestLayout();
     }
@@ -237,6 +243,10 @@
         if (orientation != HORIZONTAL && orientation != VERTICAL) {
             throw new IllegalArgumentException("invalid orientation.");
         }
+        if (mPendingSavedState != null && mPendingSavedState.mOrientation != orientation) {
+            // override pending state
+            mPendingSavedState.mOrientation = orientation;
+        }
         if (orientation == mOrientation) {
             return;
         }
@@ -284,6 +294,10 @@
      * {@link #setStackFromEnd(boolean)}
      */
     public void setReverseLayout(boolean reverseLayout) {
+        if (mPendingSavedState != null && mPendingSavedState.mReverseLayout != reverseLayout) {
+            // override pending state
+            mPendingSavedState.mReverseLayout = reverseLayout;
+        }
         if (reverseLayout == mReverseLayout) {
             return;
         }
@@ -665,7 +679,7 @@
         return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
     }
 
-    private void ensureRenderState() {
+    void ensureRenderState() {
         if (mRenderState == null) {
             mRenderState = new RenderState();
         }
@@ -1122,6 +1136,99 @@
         return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1);
     }
 
+    /**
+     * Returns the adapter position of the first visible view.
+     * <p>
+     * Note that, this value is not affected by layout orientation or item order traversal.
+     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
+     * not in the layout.
+     * <p>
+     * If RecyclerView has item decorators, they will be considered in calculations as well.
+     * <p>
+     * LinearLayoutManager may pre-cache some views that are not necessarily visible. Those views
+     * are ignored in this method.
+     *
+     * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if
+     * there aren't any visible items.
+     * @see #findFirstCompletelyVisibleItemPosition()
+     * @see #findLastVisibleItemPosition()
+     */
+    public int findFirstVisibleItemPosition() {
+        return findOneVisibleChild(0, getChildCount(), false);
+    }
+
+    /**
+     * Returns the adapter position of the first fully visible view.
+     * <p>
+     * Note that bounds check is only performed in the current orientation. That means, if
+     * LinearLayoutManager is horizontal, it will only check the view's left and right edges.
+     *
+     * @return The adapter position of the first fully visible item or
+     * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
+     * @see #findFirstVisibleItemPosition()
+     * @see #findLastCompletelyVisibleItemPosition()
+     */
+    public int findFirstCompletelyVisibleItemPosition() {
+        return findOneVisibleChild(0, getChildCount(), true);
+    }
+
+    /**
+     * Returns the adapter position of the last visible view.
+     * <p>
+     * Note that, this value is not affected by layout orientation or item order traversal.
+     * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
+     * not in the layout.
+     * <p>
+     * If RecyclerView has item decorators, they will be considered in calculations as well.
+     * <p>
+     * LinearLayoutManager may pre-cache some views that are not necessarily visible. Those views
+     * are ignored in this method.
+     *
+     * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if
+     * there aren't any visible items.
+     * @see #findLastCompletelyVisibleItemPosition()
+     * @see #findFirstVisibleItemPosition()
+     */
+    public int findLastVisibleItemPosition() {
+        return findOneVisibleChild(getChildCount() - 1, -1, false);
+    }
+
+    /**
+     * Returns the adapter position of the last fully visible view.
+     * <p>
+     * Note that bounds check is only performed in the current orientation. That means, if
+     * LinearLayoutManager is horizontal, it will only check the view's left and right edges.
+     *
+     * @return The adapter position of the last fully visible view or
+     * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
+     * @see #findLastVisibleItemPosition()
+     * @see #findFirstCompletelyVisibleItemPosition()
+     */
+    public int findLastCompletelyVisibleItemPosition() {
+        return findOneVisibleChild(getChildCount() - 1, -1, true);
+    }
+
+    int findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible) {
+        final int start = mOrientationHelper.getStartAfterPadding();
+        final int end = mOrientationHelper.getEndAfterPadding();
+        final int next = toIndex > fromIndex ? 1 : -1;
+        for (int i = fromIndex; i != toIndex; i+=next) {
+            final View child = getChildAt(i);
+            final int childStart = mOrientationHelper.getDecoratedStart(child);
+            final int childEnd = mOrientationHelper.getDecoratedEnd(child);
+            if (childStart < end && childEnd > start) {
+                if (completelyVisible) {
+                    if (childStart >= start && childEnd <= end) {
+                        return getPosition(child);
+                    }
+                } else {
+                    return getPosition(child);
+                }
+            }
+        }
+        return RecyclerView.NO_POSITION;
+    }
+
     @Override
     public View onFocusSearchFailed(View focused, int focusDirection,
             RecyclerView.Recycler recycler,
@@ -1358,7 +1465,7 @@
         }
     }
 
-    private OrientationHelper createVerticalOrientationHelper() {
+    OrientationHelper createVerticalOrientationHelper() {
         return new OrientationHelper() {
             @Override
             public int getEndAfterPadding() {
@@ -1410,7 +1517,7 @@
         };
     }
 
-    private OrientationHelper createHorizontalOrientationHelper() {
+    OrientationHelper createHorizontalOrientationHelper() {
         return new OrientationHelper() {
             @Override
             public int getEndAfterPadding() {
@@ -1466,7 +1573,7 @@
     /**
      * Helper interface to offload orientation based decisions
      */
-    private static interface OrientationHelper {
+    static interface OrientationHelper {
 
         /**
          * @param view The view element to check
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java b/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java
index 295aac7..8036a0c 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearSmoothScroller.java
@@ -168,7 +168,7 @@
         // area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x
         // which gives 0.100028 when x = .3356
         // this is why we divide linear scrolling time with .3356
-        return (int) (calculateTimeForScrolling(dx) / .3356);
+        return  (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
     }
 
     /**
@@ -179,7 +179,10 @@
      * @see #calculateSpeedPerPixel(android.util.DisplayMetrics)
      */
     protected int calculateTimeForScrolling(int dx) {
-        return Math.round(Math.abs(dx) * MILLISECONDS_PER_PX);
+        // In a case where dx is very small, rounding may return 0 although dx > 0.
+        // To avoid that issue, ceil the result so that if dx > 0, we'll always return positive
+        // time.
+        return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX);
     }
 
     /**
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index b96757e..bb9a613 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -1269,7 +1269,10 @@
             resumeRequestLayout(false);
         }
 
-        mLayout.onMeasure(widthSpec, heightSpec);
+        if (mAdapter != null) {
+            mState.mItemCount = mAdapter.getItemCount();
+        }
+        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
 
         final int widthSize = getMeasuredWidth();
         final int heightSize = getMeasuredHeight();
@@ -1386,7 +1389,7 @@
             mInPreLayout = true;
             final boolean didStructureChange = mState.mStructureChanged;
             mState.mStructureChanged = false;
-            // temporarly disable flag because we are asking for previous layout
+            // temporarily disable flag because we are asking for previous layout
             mLayout.onLayoutChildren(mRecycler, mState);
             mState.mStructureChanged = didStructureChange;
             mInPreLayout = false;
@@ -1483,7 +1486,7 @@
             }
         }
         resumeRequestLayout(false);
-        mLayout.removeAndRecycleScrapInt(mRecycler);
+        mLayout.removeAndRecycleScrapInt(mRecycler, !animateChanges);
         mState.mPreviousLayoutItemCount = mState.mItemCount;
         mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
     }
@@ -2359,7 +2362,7 @@
          */
         public void clear() {
             mAttachedScrap.clear();
-            mCachedViews.clear();
+            recycleCachedViews();
         }
 
         /**
@@ -2385,7 +2388,7 @@
 
         /**
          * Obtain a view initialized for the given position.
-         * 
+         *
          * <p>This method should be used by {@link LayoutManager} implementations to obtain
          * views to represent data from an {@link Adapter}.</p>
          *
@@ -2453,14 +2456,26 @@
             recycleViewHolder(getChildViewHolderInt(view));
         }
 
+        void recycleCachedViews() {
+            final int count = mCachedViews.size();
+            for (int i = count - 1; i >= 0; i--) {
+                final ViewHolder cachedView = mCachedViews.get(i);
+                if (cachedView.isRecyclable()) {
+                    getRecycledViewPool().putRecycledView(cachedView);
+                    dispatchViewRecycled(cachedView);
+                }
+                mCachedViews.remove(i);
+            }
+        }
+
         void recycleViewHolder(ViewHolder holder) {
             if (holder.isScrap() || holder.itemView.getParent() != null) {
                 throw new IllegalArgumentException(
                         "Scrapped or attached views may not be recycled.");
             }
 
-            if (mCachedViews.size() < mViewCacheMax && !holder.isInvalid() &&
-                    (mInPreLayout || !holder.isRemoved())) {
+            boolean cached = false;
+            if (!holder.isInvalid() && (mInPreLayout || !holder.isRemoved())) {
                 // Retire oldest cached views first
                 if (mCachedViews.size() == mViewCacheMax && !mCachedViews.isEmpty()) {
                     for (int i = 0; i < mCachedViews.size(); i++) {
@@ -2473,8 +2488,12 @@
                         }
                     }
                 }
-                mCachedViews.add(holder);
-            } else if (holder.isRecyclable()) {
+                if (mCachedViews.size() < mViewCacheMax) {
+                    mCachedViews.add(holder);
+                    cached = true;
+                }
+            }
+            if (!cached && holder.isRecyclable()) {
                 getRecycledViewPool().putRecycledView(holder);
                 dispatchViewRecycled(holder);
             }
@@ -3817,14 +3836,30 @@
             }
         }
 
-        void removeAndRecycleScrapInt(Recycler recycler) {
+        /**
+         * Recycles the scrapped views.
+         * <p>
+         * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
+         * the expected behavior if scrapped views are used for animations. Otherwise, we need to
+         * call remove and invalidate RecyclerView to ensure UI update.
+         *
+         * @param recycler Recycler
+         * @param remove   Whether scrapped views should be removed from ViewGroup or not. This
+         *                 method will invalidate RecyclerView if it removes any scrapped child.
+         */
+        void removeAndRecycleScrapInt(Recycler recycler, boolean remove) {
             final int scrapCount = recycler.getScrapCount();
             for (int i = 0; i < scrapCount; i++) {
                 final View scrap = recycler.getScrapViewAt(i);
-                ViewHolder holder = mRecyclerView.getChildViewHolder(scrap);
+                if (remove) {
+                    mRecyclerView.removeDetachedView(scrap, false);
+                }
                 recycler.quickRecycleScrapView(scrap);
             }
             recycler.clearScrap();
+            if (remove && scrapCount > 0) {
+                mRecyclerView.invalidate();
+            }
         }
 
 
@@ -4317,10 +4352,12 @@
          * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
          * will consume all available space.</p>
          *
+         * @param recycler Recycler
+         * @param state Transient state of RecyclerView
          * @param widthSpec Width {@link android.view.View.MeasureSpec}
          * @param heightSpec Height {@link android.view.View.MeasureSpec}
          */
-        public void onMeasure(int widthSpec, int heightSpec) {
+        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
             final int widthMode = MeasureSpec.getMode(widthSpec);
             final int heightMode = MeasureSpec.getMode(heightSpec);
             final int widthSize = MeasureSpec.getSize(widthSpec);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index 56b8b79..02be171 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -88,6 +88,34 @@
         });
     }
 
+    void scrollToPosition(final int position) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.getLayoutManager().scrollToPosition(position);
+            }
+        });
+    }
+
+    void smoothScrollToPosition(final int position)
+            throws Throwable {
+        Log.d(TAG, "SMOOTH scrolling to " + position);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.smoothScrollToPosition(position);
+            }
+        });
+        while (mRecyclerView.getLayoutManager().isSmoothScrolling() ||
+                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
+            if (mDebug) {
+                Log.d(TAG, "SMOOTH scrolling step");
+            }
+            Thread.sleep(200);
+        }
+        Log.d(TAG, "SMOOTH scrolling done");
+    }
+
     class TestViewHolder extends RecyclerView.ViewHolder {
 
         Item mBindedItem;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
new file mode 100644
index 0000000..e000bc9
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v7.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Includes tests for {@link LinearLayoutManager}.
+ * <p>
+ * Since most UI tests are not practical, these tests are focused on internal data representation
+ * and stability of LinearLayoutManager in response to different events (state change, scrolling
+ * etc) where it is very hard to do manual testing.
+ */
+public class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
+
+    private static final boolean DEBUG = false;
+
+    private static final String TAG = "LinearLayoutManagerTest";
+
+    WrappedLinearLayoutManager mLayoutManager;
+
+    TestAdapter mTestAdapter;
+
+    final List<Config> mBaseVariations = new ArrayList<Config>();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        for (int orientation : new int[]{LinearLayoutManager.VERTICAL,
+                LinearLayoutManager.HORIZONTAL}) {
+            for (boolean reverseLayout : new boolean[]{false, true}) {
+                for (boolean stackFromBottom : new boolean[]{false, true}) {
+                    mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom));
+                }
+            }
+        }
+    }
+
+    protected List<Config> addConfigVariation(List<Config> base, String fieldName,
+            Object... variations)
+            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
+        List<Config> newConfigs = new ArrayList<Config>();
+        Field field = Config.class.getDeclaredField(fieldName);
+        for (Config config : base) {
+            for (Object variation : variations) {
+                Config newConfig = (Config) config.clone();
+                field.set(newConfig, variation);
+                newConfigs.add(newConfig);
+            }
+        }
+        return newConfigs;
+    }
+
+    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
+        mRecyclerView = new RecyclerView(getActivity());
+        mRecyclerView.setHasFixedSize(true);
+        mTestAdapter = new TestAdapter(config.mItemCount);
+        mRecyclerView.setAdapter(mTestAdapter);
+        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
+                config.mReverseLayout);
+        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        if (waitForFirstLayout) {
+            waitForFirstLayout();
+        }
+    }
+
+    private void waitForFirstLayout() throws Throwable {
+        mLayoutManager.expectLayouts(1);
+        setRecyclerView(mRecyclerView);
+        mLayoutManager.waitForLayout(2);
+    }
+
+
+    public void testGetFirstLastChildrenTest() throws Throwable {
+        for (Config config : mBaseVariations) {
+            getFirstLastChildrenTest(config);
+        }
+    }
+
+    public void getFirstLastChildrenTest(final Config config) throws Throwable {
+        setupByConfig(config, true);
+        Runnable viewInBoundsTest = new Runnable() {
+            @Override
+            public void run() {
+                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
+                final String boundsLog = mLayoutManager.getBoundsLog();
+                assertEquals(config + ":\nfirst visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.firstVisiblePosition,
+                        mLayoutManager.findFirstVisibleItemPosition()
+                );
+                assertEquals(
+                        config + ":\nfirst fully visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
+                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
+                );
+
+                assertEquals(config + ":\nlast visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.lastVisiblePosition,
+                        mLayoutManager.findLastVisibleItemPosition()
+                );
+                assertEquals(
+                        config + ":\nlast fully visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
+                        mLayoutManager.findLastCompletelyVisibleItemPosition()
+                );
+            }
+        };
+        runTestOnUiThread(viewInBoundsTest);
+        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
+        // case
+        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.smoothScrollToPosition(scrollPosition);
+            }
+        });
+        while (mLayoutManager.isSmoothScrolling() ||
+                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
+            runTestOnUiThread(viewInBoundsTest);
+            Thread.sleep(200);
+        }
+        // delete all items
+        mLayoutManager.expectLayouts(2);
+        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
+        mLayoutManager.waitForLayout(2);
+        // test empty case
+        runTestOnUiThread(viewInBoundsTest);
+        // set a new adapter with huge items to test full bounds check
+        mLayoutManager.expectLayouts(1);
+        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
+        final TestAdapter newAdapter = new TestAdapter(100) {
+            @Override
+            public void onBindViewHolder(TestViewHolder holder,
+                    int position) {
+                super.onBindViewHolder(holder, position);
+                if (config.mOrientation == LinearLayoutManager.HORIZONTAL) {
+                    holder.itemView.setMinimumWidth(totalSpace + 5);
+                } else {
+                    holder.itemView.setMinimumHeight(totalSpace + 5);
+                }
+            }
+        };
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.setAdapter(newAdapter);
+            }
+        });
+        mLayoutManager.waitForLayout(2);
+        runTestOnUiThread(viewInBoundsTest);
+    }
+
+    public void testSavedState() throws Throwable {
+        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        // do nothing
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "doing nothing";
+                    }
+                },
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        mLayoutManager.expectLayouts(1);
+                        scrollToPosition(mTestAdapter.getItemCount() * 3 / 4);
+                        mLayoutManager.waitForLayout(2);
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "scroll to position";
+                    }
+                },
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        mLayoutManager.expectLayouts(1);
+                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3,
+                                50);
+                        mLayoutManager.waitForLayout(2);
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "scroll to position with positive offset";
+                    }
+                },
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        mLayoutManager.expectLayouts(1);
+                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3,
+                                -50);
+                        mLayoutManager.waitForLayout(2);
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "scroll to position with negative offset";
+                    }
+                }
+        };
+
+        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
+                new PostRestoreRunnable() {
+                    @Override
+                    public String describe() {
+                        return "Doing nothing";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        // update config as well so that restore assertions will work
+                        config.mOrientation = 1 - config.mOrientation;
+                        mLayoutManager.setOrientation(config.mOrientation);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return config.mItemCount == 0;
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "Changing orientation";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        config.mStackFromEnd = !config.mStackFromEnd;
+                        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return true; //stack from end should not move items on change
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "Changing stack from end";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        config.mReverseLayout = !config.mReverseLayout;
+                        mLayoutManager.setReverseLayout(config.mReverseLayout);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return config.mItemCount == 0;
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "Changing reverse layout";
+                    }
+                }
+        };
+        boolean[] waitForLayoutOptions = new boolean[]{false, true};
+        for (Config config : addConfigVariation(mBaseVariations, "mItemCount", 0, 300)) {
+            for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
+                for (boolean waitForLayout : waitForLayoutOptions) {
+                    for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) {
+                        savedStateTest((Config) config.clone(), waitForLayout, postLayoutRunnable,
+                                postRestoreRunnable);
+                        removeRecyclerView();
+                    }
+
+                }
+            }
+        }
+    }
+
+    public void savedStateTest(Config config, boolean waitForLayout,
+            PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation)
+            throws Throwable {
+        if (DEBUG) {
+            Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " +
+                    config + " post layout action " + postLayoutOperation.describe() +
+                    "post restore action " + postRestoreOperation.describe());
+        }
+        setupByConfig(config, false);
+        if (waitForLayout) {
+            waitForFirstLayout();
+            postLayoutOperation.run();
+        }
+        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
+        Parcelable savedState = mRecyclerView.onSaveInstanceState();
+        // we append a suffix to the parcelable to test out of bounds
+        String parcelSuffix = UUID.randomUUID().toString();
+        Parcel parcel = Parcel.obtain();
+        savedState.writeToParcel(parcel, 0);
+        parcel.writeString(parcelSuffix);
+        removeRecyclerView();
+        // reset for reading
+        parcel.setDataPosition(0);
+        // re-create
+        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
+        removeRecyclerView();
+
+        RecyclerView restored = new RecyclerView(getActivity());
+        // this config should be no op.
+        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
+                1 - config.mOrientation, !config.mReverseLayout);
+        mLayoutManager.setStackFromEnd(!config.mStackFromEnd);
+        restored.setLayoutManager(mLayoutManager);
+        // use the same adapter for Rect matching
+        restored.setAdapter(mTestAdapter);
+        restored.onRestoreInstanceState(savedState);
+        postRestoreOperation.onAfterRestore(config);
+        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
+                parcel.readString());
+        mLayoutManager.expectLayouts(1);
+        setRecyclerView(restored);
+        mLayoutManager.waitForLayout(2);
+        // calculate prefix here instead of above to include post restore changes
+        final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() +
+                "\npostRestore:" + postRestoreOperation.describe() + "\n";
+        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
+                config.mReverseLayout, mLayoutManager.getReverseLayout());
+        assertEquals(logPrefix + " on saved state, orientation should be preserved",
+                config.mOrientation, mLayoutManager.getOrientation());
+        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
+                config.mStackFromEnd, mLayoutManager.getStackFromEnd());
+        if (waitForLayout) {
+            if (postRestoreOperation.shouldLayoutMatch(config)) {
+                assertRectSetsEqual(
+                        logPrefix + ": on restore, previous view positions should be preserved",
+                        before, mLayoutManager.collectChildCoordinates());
+            } else {
+                assertRectSetsNotEqual(
+                        logPrefix
+                                + ": on restore with changes, previous view positions should NOT be preserved",
+                        before, mLayoutManager.collectChildCoordinates());
+            }
+        }
+    }
+
+    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mLayoutManager.scrollToPositionWithOffset(position, offset);
+            }
+        });
+    }
+
+    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
+            Map<Item, Rect> after) {
+        Throwable throwable = null;
+        try {
+            assertRectSetsEqual("NOT " + message, before, after);
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertNotNull(message + "\ntwo layout should be different", throwable);
+    }
+
+    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
+        if (DEBUG) {
+            Log.d(TAG, "checking rectangle equality.");
+            Log.d(TAG, "before:");
+            for (Map.Entry<Item, Rect> entry : before.entrySet()) {
+                Log.d(TAG, entry.getKey().originalIndex + ":" + entry.getValue());
+            }
+            Log.d(TAG, "after:");
+            for (Map.Entry<Item, Rect> entry : after.entrySet()) {
+                Log.d(TAG, entry.getKey().originalIndex + ":" + entry.getValue());
+            }
+        }
+        assertEquals(message + ":\nitem counts should be equal", before.size()
+                , after.size());
+        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
+            Rect afterRect = after.get(entry.getKey());
+            assertNotNull(message + ":\nSame item should be visible after simple re-layout",
+                    afterRect);
+            assertEquals(message + ":\nItem should be laid out at the same coordinates",
+                    entry.getValue(), afterRect);
+        }
+    }
+
+    static class VisibleChildren {
+
+        int firstVisiblePosition = RecyclerView.NO_POSITION;
+
+        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
+
+        int lastVisiblePosition = RecyclerView.NO_POSITION;
+
+        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
+
+        @Override
+        public String toString() {
+            return "VisibleChildren{" +
+                    "firstVisiblePosition=" + firstVisiblePosition +
+                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
+                    ", lastVisiblePosition=" + lastVisiblePosition +
+                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
+                    '}';
+        }
+    }
+
+    abstract private class PostLayoutRunnable {
+
+        abstract void run() throws Throwable;
+
+        abstract String describe();
+    }
+
+    abstract private class PostRestoreRunnable {
+
+        void onAfterRestore(Config config) throws Throwable {
+        }
+
+        abstract String describe();
+
+        boolean shouldLayoutMatch(Config config) {
+            return true;
+        }
+    }
+
+    class WrappedLinearLayoutManager extends LinearLayoutManager {
+
+        CountDownLatch layoutLatch;
+
+        OrientationHelper mSecondaryOrientation;
+
+        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
+            super(context, orientation, reverseLayout);
+        }
+
+        public void expectLayouts(int count) {
+            layoutLatch = new CountDownLatch(count);
+        }
+
+        public void waitForLayout(long timeout) throws InterruptedException {
+            waitForLayout(timeout, TimeUnit.SECONDS);
+        }
+
+        @Override
+        public void setOrientation(int orientation) {
+            super.setOrientation(orientation);
+            mSecondaryOrientation = null;
+        }
+
+        @Override
+        void ensureRenderState() {
+            super.ensureRenderState();
+            if (mSecondaryOrientation == null) {
+                mSecondaryOrientation = getOrientation() == HORIZONTAL
+                        ? createVerticalOrientationHelper()
+                        : createHorizontalOrientationHelper();
+            }
+        }
+
+        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
+            layoutLatch.await(timeout, timeUnit);
+            assertEquals("all expected layouts should be executed at the expected time",
+                    0, layoutLatch.getCount());
+        }
+
+        public String getBoundsLog() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
+                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
+            sb.append("\nchildren bounds\n");
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
+                        .append("[").append("start:").append(
+                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
+                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
+            }
+            return sb.toString();
+        }
+
+        public VisibleChildren traverseAndFindVisibleChildren() {
+            int childCount = getChildCount();
+            final VisibleChildren visibleChildren = new VisibleChildren();
+            final int start = mOrientationHelper.getStartAfterPadding();
+            final int end = mOrientationHelper.getEndAfterPadding();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                final int childStart = mOrientationHelper.getDecoratedStart(child);
+                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
+                final boolean fullyVisible = childStart >= start && childEnd <= end;
+                final boolean hidden = childEnd <= start || childStart >= end;
+                if (hidden) {
+                    continue;
+                }
+                final int position = getPosition(child);
+                if (fullyVisible) {
+                    if (position < visibleChildren.firstFullyVisiblePosition ||
+                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
+                        visibleChildren.firstFullyVisiblePosition = position;
+                    }
+
+                    if (position > visibleChildren.lastFullyVisiblePosition) {
+                        visibleChildren.lastFullyVisiblePosition = position;
+                    }
+                }
+
+                if (position < visibleChildren.firstVisiblePosition ||
+                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
+                    visibleChildren.firstVisiblePosition = position;
+                }
+
+                if (position > visibleChildren.lastVisiblePosition) {
+                    visibleChildren.lastVisiblePosition = position;
+                }
+
+            }
+            return visibleChildren;
+        }
+
+        Rect getViewBounds(View view) {
+            if (getOrientation() == HORIZONTAL) {
+                return new Rect(
+                        mOrientationHelper.getDecoratedStart(view),
+                        mSecondaryOrientation.getDecoratedStart(view),
+                        mOrientationHelper.getDecoratedEnd(view),
+                        mSecondaryOrientation.getDecoratedEnd(view));
+            } else {
+                return new Rect(
+                        mSecondaryOrientation.getDecoratedStart(view),
+                        mOrientationHelper.getDecoratedStart(view),
+                        mSecondaryOrientation.getDecoratedEnd(view),
+                        mOrientationHelper.getDecoratedEnd(view));
+            }
+
+        }
+
+        Map<Item, Rect> collectChildCoordinates() throws Throwable {
+            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    final int childCount = getChildCount();
+                    for (int i = 0; i < childCount; i++) {
+                        View child = getChildAt(i);
+                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
+                                .getLayoutParams();
+                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
+                        items.put(vh.mBindedItem, getViewBounds(child));
+                    }
+                }
+            });
+            return items;
+        }
+
+        @Override
+        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+            super.onLayoutChildren(recycler, state);
+            layoutLatch.countDown();
+        }
+    }
+
+    static class Config implements Cloneable {
+
+        private static final int DEFAULT_ITEM_COUNT = 300;
+
+        private boolean mStackFromEnd;
+
+        int mOrientation = LinearLayoutManager.VERTICAL;
+
+        boolean mReverseLayout = false;
+
+        int mItemCount = DEFAULT_ITEM_COUNT;
+
+        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
+            mOrientation = orientation;
+            mReverseLayout = reverseLayout;
+            mStackFromEnd = stackFromEnd;
+        }
+
+        public Config() {
+
+        }
+
+        Config orientation(int orientation) {
+            mOrientation = orientation;
+            return this;
+        }
+
+        Config stackFromBottom(boolean stackFromBottom) {
+            mStackFromEnd = stackFromBottom;
+            return this;
+        }
+
+        Config reverseLayout(boolean reverseLayout) {
+            mReverseLayout = reverseLayout;
+            return this;
+        }
+
+        public Config itemCount(int itemCount) {
+            mItemCount = itemCount;
+            return this;
+        }
+
+        // required by convention
+        @Override
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+
+        @Override
+        public String toString() {
+            return "Config{" +
+                    "mStackFromEnd=" + mStackFromEnd +
+                    ", mOrientation=" + mOrientation +
+                    ", mReverseLayout=" + mReverseLayout +
+                    ", mItemCount=" + mItemCount +
+                    '}';
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index 7d43610..215ab23 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -17,10 +17,12 @@
 package android.support.v7.widget;
 
 import android.content.Context;
+import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class RecyclerViewAnimationsTest extends BaseRecyclerViewInstrumentationTest {
@@ -56,7 +58,7 @@
 
     RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount)
             throws Throwable {
-        final RecyclerView recyclerView = new TestRecyclerView(getActivity());
+        final TestRecyclerView recyclerView = new TestRecyclerView(getActivity());
         recyclerView.setHasFixedSize(true);
         mTestAdapter = new TestAdapter(itemCount);
         recyclerView.setAdapter(mTestAdapter);
@@ -66,8 +68,11 @@
         mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = firstLayoutItemCount;
 
         mLayoutManager.expectLayouts(1);
+        recyclerView.expectDraw(1);
         setRecyclerView(recyclerView);
         mLayoutManager.waitForLayout(2);
+        recyclerView.waitForDraw(1);
+        mLayoutManager.mOnLayoutCallbacks.reset();
         return recyclerView;
     }
 
@@ -97,6 +102,26 @@
         mLayoutManager.waitForLayout(2);
     }
 
+    public TestRecyclerView getTestRecyclerView() {
+        return (TestRecyclerView) mRecyclerView;
+    }
+
+    public void testRemoveScrapInvalidate() throws Throwable {
+        setupBasic(10);
+        TestRecyclerView testRecyclerView = getTestRecyclerView();
+        mLayoutManager.expectLayouts(1);
+        testRecyclerView.expectDraw(1);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTestAdapter.mItems.clear();
+                mTestAdapter.notifyDataSetChanged();
+            }
+        });
+        mLayoutManager.waitForLayout(2);
+        testRecyclerView.waitForDraw(2);
+    }
+
     public void testDeleteVisibleAndInvisible() throws Throwable {
         setupBasic(11, 3, 5); //layout items  3 4 5 6 7
         mLayoutManager.expectLayouts(2);
@@ -298,6 +323,7 @@
     }
 
     class TestRecyclerView extends RecyclerView {
+        CountDownLatch drawLatch;
 
         public TestRecyclerView(Context context) {
             super(context);
@@ -311,6 +337,24 @@
             super(context, attrs, defStyle);
         }
 
+        public void expectDraw(int count) {
+            drawLatch = new CountDownLatch(count);
+        }
+
+        public void waitForDraw(long timeout) throws Throwable {
+            drawLatch.await(timeout * (DEBUG ? 100 : 1), TimeUnit.SECONDS);
+            assertEquals("all expected draws should happen at the expected time frame",
+                    0, drawLatch.getCount());
+        }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+            super.dispatchDraw(canvas);
+            if (drawLatch != null) {
+                drawLatch.countDown();
+            }
+        }
+
         @Override
         void dispatchLayout() {
             try {
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java b/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java
index fc986b3..9b820e2 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/ElementThunker.java
@@ -57,10 +57,6 @@
             return android.renderscript.Element.DataKind.PIXEL_RGB;
         case PIXEL_RGBA:
             return android.renderscript.Element.DataKind.PIXEL_RGBA;
-        case PIXEL_DEPTH:
-            return android.renderscript.Element.DataKind.PIXEL_DEPTH;
-        case PIXEL_YUV:
-            return android.renderscript.Element.DataKind.PIXEL_YUV;
         }
         return null;
     }