Merge  branch 'goog/pi-preview1-androidx-dev' into flatfoot-navigation

Change-Id: I1e4cf5b4050d64fe2e3a856f71f75a8610ee9ab8
diff --git a/.gitignore b/.gitignore
index ac3565c..6d32d4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@
 **/gen
 *.iml
 **/out
+**/build
 buildSrc/build
 lifecycle/common/build
 jacoco.exec
diff --git a/api/26.0.0-alpha1.txt b/api/26.0.0-alpha1.txt
index 7300fa5..d4699e7 100644
--- a/api/26.0.0-alpha1.txt
+++ b/api/26.0.0-alpha1.txt
@@ -5279,7 +5279,7 @@
     method public abstract android.support.v4.app.FragmentTransaction hide(android.support.v4.app.Fragment);
     method public abstract boolean isAddToBackStackAllowed();
     method public abstract boolean isEmpty();
-    method public abstract android.support.v4.app.FragmentTransaction postOnCommit(java.lang.Runnable);
+    method public abstract android.support.v4.app.FragmentTransaction runOnCommit(java.lang.Runnable);
     method public abstract android.support.v4.app.FragmentTransaction remove(android.support.v4.app.Fragment);
     method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment);
     method public abstract android.support.v4.app.FragmentTransaction replace(int, android.support.v4.app.Fragment, java.lang.String);
diff --git a/app-toolkit/init.gradle b/app-toolkit/init.gradle
index 6a91a4e..15c57c3 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -51,7 +51,7 @@
 }
 
 subprojects {
-    project.tasks.whenTaskAdded { task ->
+   project.tasks.whenTaskAdded { task ->
         if (task.name.startsWith("assembleAndroidTest")) {
             buildServerAnchorTask.dependsOn task
         }
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index d065af0..b674858 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -83,6 +83,20 @@
 includeProject(":jetifier-standalone", new File(supportRoot, "jetifier/jetifier/standalone"))
 includeProject(":jetifier-preprocessor", new File(supportRoot, "jetifier/jetifier/preprocessor"))
 
+includeProject(":navigation:navigation-common", new File(supportRoot, "navigation/common"))
+includeProject(":navigation:navigation-common-ktx", new File(supportRoot, "navigation/common/ktx"))
+includeProject(":navigation:navigation-runtime", new File(supportRoot, "navigation/runtime/"))
+includeProject(":navigation:navigation-runtime-ktx", new File(supportRoot, "navigation/runtime/ktx"))
+includeProject(":navigation:navigation-testing", new File(supportRoot, "navigation/testing"))
+includeProject(":navigation:navigation-testing-ktx", new File(supportRoot, "navigation/testing/ktx"))
+includeProject(":navigation:navigation-fragment", new File(supportRoot, "navigation/fragment"))
+includeProject(":navigation:navigation-fragment-ktx", new File(supportRoot, "navigation/fragment/ktx"))
+includeProject(":navigation:navigation-ui", new File(supportRoot, "navigation/ui"))
+includeProject(":navigation:navigation-ui-ktx", new File(supportRoot, "navigation/ui/ktx"))
+includeProject(":navigation:navigation-integration-tests:testapp", new File(supportRoot, "navigation/integration-tests/testapp"))
+includeProject(":navigation:navigation-safe-args-generator", new File(supportRoot, "navigation/safe-args-generator"))
+includeProject(":navigation:navigation-safe-args-gradle-plugin", new File(supportRoot, "navigation/safe-args-gradle-plugin"))
+
 /////////////////////////////
 //
 // External
diff --git a/browser/src/main/java/androidx/browser/browseractions/BrowserActionsFallbackMenuDialog.java b/browser/src/main/java/androidx/browser/browseractions/BrowserActionsFallbackMenuDialog.java
index 09efc9c..d2fa0f7 100644
--- a/browser/src/main/java/androidx/browser/browseractions/BrowserActionsFallbackMenuDialog.java
+++ b/browser/src/main/java/androidx/browser/browseractions/BrowserActionsFallbackMenuDialog.java
@@ -39,7 +39,7 @@
     private final View mContentView;
 
     BrowserActionsFallbackMenuDialog(Context context, View contentView) {
-        super(context);
+        super(context, android.support.customtabs.R.style.Theme_AppCompat_Light_Dialog);
         mContentView = contentView;
     }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index f879a9f..6e0e7e6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -80,6 +80,11 @@
     val ARCH_CORE_TESTING = ARCH_CORE
 
     /**
+     * Version code for Navigation
+     */
+    val NAVIGATION = Version("1.0.0-alpha01")
+
+    /**
      * Version code for Jetifier
      */
     val JETIFIER = Version("0.0.1")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 7faa7fc..1d078ea 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -51,13 +51,25 @@
  * https://github.com/xerial/sqlite-jdbc/issues/267
  */
 const val XERIAL = "org.xerial:sqlite-jdbc:3.20.1"
+const val XPP3 = "xpp3:xpp3:1.1.4c"
+const val XMLPULL = "xmlpull:xmlpull:1.1.3.1"
 
 const val ESPRESSO_CONTRIB_TMP = "com.android.temp.support.test.espresso:espresso-contrib:3.0.1"
 const val ESPRESSO_CORE_TMP = "com.android.temp.support.test.espresso:espresso-core:3.0.1"
 const val TEST_RUNNER_TMP = "com.android.temp.support.test:runner:1.0.1"
 const val TEST_RULES_TMP = "com.android.temp.support.test:rules:1.0.1"
 
-// AndroidX libraries
+private const val NAV_SUPPORT_VERSION = "27.1.0"
+const val NAV_SUPPORT_ANNOTATIONS = "com.android.support:support-annotations:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_APPCOMPAT = "com.android.support:appcompat-v7:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_CARDVIEW = "com.android.support:cardview-v7:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_COMPAT = "com.android.support:support-compat:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_CORE_UTILS = "com.android.support:support-core-utils:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_DESIGN = "com.android.support:design:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_FRAGMENTS = "com.android.support:support-fragment:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_RECYCLERVIEW = "com.android.support:recyclerview-v7:$NAV_SUPPORT_VERSION"
+const val NAV_SUPPORT_V4 = "com.android.support:support-v4:$NAV_SUPPORT_VERSION"
+
 private const val SUPPORT_VERSION = "1.0.0-alpha1"
 const val SUPPORT_ANNOTATIONS = "androidx.annotation:annotation:$SUPPORT_VERSION"
 const val SUPPORT_APPCOMPAT = "androidx.appcompat:appcompat:$SUPPORT_VERSION"
diff --git a/car/res/drawable/car_button_ripple_background_night.xml b/car/res/drawable/car_button_ripple_background_night.xml
deleted file mode 100644
index 6160768..0000000
--- a/car/res/drawable/car_button_ripple_background_night.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_light" />
diff --git a/car/res/layout/car_paged_scrollbar_buttons.xml b/car/res/layout/car_paged_scrollbar_buttons.xml
index cd62329..f455004 100644
--- a/car/res/layout/car_paged_scrollbar_buttons.xml
+++ b/car/res/layout/car_paged_scrollbar_buttons.xml
@@ -22,7 +22,7 @@
     android:gravity="center"
     android:orientation="vertical">
 
-    <ImageButton
+    <ImageView
         android:id="@+id/page_up"
         android:layout_width="@dimen/car_scroll_bar_button_size"
         android:layout_height="@dimen/car_scroll_bar_button_size"
@@ -63,7 +63,7 @@
             android:background="@drawable/car_scrollbar_thumb" />
     </FrameLayout>
 
-    <ImageButton
+    <ImageView
         android:id="@+id/page_down"
         android:layout_width="@dimen/car_scroll_bar_button_size"
         android:layout_height="@dimen/car_scroll_bar_button_size"
diff --git a/car/src/main/java/androidx/car/widget/PagedScrollBarView.java b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
index aa62813..ea681d3 100644
--- a/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
+++ b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
@@ -258,22 +258,22 @@
             case DayNightStyle.AUTO:
                 tintResId = R.color.car_tint;
                 thumbColorResId = R.color.car_scrollbar_thumb;
-                upDownBackgroundResId = R.drawable.car_button_ripple_background;
+                upDownBackgroundResId = R.drawable.car_card_ripple_background;
                 break;
             case DayNightStyle.AUTO_INVERSE:
                 tintResId = R.color.car_tint_inverse;
                 thumbColorResId = R.color.car_scrollbar_thumb_inverse;
-                upDownBackgroundResId = R.drawable.car_button_ripple_background_inverse;
+                upDownBackgroundResId = R.drawable.car_card_ripple_background_inverse;
                 break;
             case DayNightStyle.FORCE_NIGHT:
                 tintResId = R.color.car_tint_light;
                 thumbColorResId = R.color.car_scrollbar_thumb_light;
-                upDownBackgroundResId = R.drawable.car_button_ripple_background_night;
+                upDownBackgroundResId = R.drawable.car_card_ripple_background_night;
                 break;
             case DayNightStyle.FORCE_DAY:
                 tintResId = R.color.car_tint_dark;
                 thumbColorResId = R.color.car_scrollbar_thumb_dark;
-                upDownBackgroundResId = R.drawable.car_button_ripple_background_day;
+                upDownBackgroundResId = R.drawable.car_card_ripple_background_day;
                 break;
             default:
                 throw new IllegalArgumentException("Unknown DayNightStyle: " + mDayNightStyle);
diff --git a/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/src/main/java/androidx/fragment/app/Fragment.java
index ea99ddc..6c61009 100644
--- a/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -101,9 +101,6 @@
     // When instantiated from saved state, this is the saved state.
     Bundle mSavedFragmentState;
     SparseArray<Parcelable> mSavedViewState;
-    // If the userVisibleHint is changed before the state is set,
-    // it is stored here
-    @Nullable Boolean mSavedUserVisibleHint;
 
     // Index into active fragment array.
     int mIndex = -1;
diff --git a/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 9d637c4..bcfa0d9 100644
--- a/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -1366,13 +1366,8 @@
                                 f.mTargetRequestCode = f.mSavedFragmentState.getInt(
                                         FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
                             }
-                            if (f.mSavedUserVisibleHint != null) {
-                                f.mUserVisibleHint = f.mSavedUserVisibleHint;
-                                f.mSavedUserVisibleHint = null;
-                            } else {
-                                f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
-                                        FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
-                            }
+                            f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
+                                    FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
                             if (!f.mUserVisibleHint) {
                                 f.mDeferStart = true;
                                 if (newState > Fragment.STOPPED) {
diff --git a/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java b/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
index 99e6fc7..627d679 100644
--- a/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
+++ b/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
@@ -1391,150 +1391,6 @@
         assertEquals(leftEdge, mGridView.getLayoutManager().findViewByPosition(0).getLeft());
     }
 
-    void testSetSelectedPosition(final boolean inSmoothScroll, final boolean layoutRequested,
-            final boolean viewVisible, final boolean smooth,
-            final boolean resultLayoutRequested, final boolean resultSmoothScroller,
-            final int resultScrollState) throws Throwable {
-        Intent intent = new Intent();
-        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
-        intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1500);
-        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
-        mNumRows = 1;
-        initActivity(intent);
-        mOrientation = BaseGridView.VERTICAL;
-
-        if (inSmoothScroll) {
-            setSelectedPositionSmooth(500);
-        }
-        mActivityTestRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                if (layoutRequested) {
-                    mGridView.requestLayout();
-                }
-                final int position;
-                if (viewVisible) {
-                    position = mGridView.getChildAdapterPosition(mGridView.getChildAt(
-                            mGridView.getChildCount() - 1));
-                } else {
-                    position = 1000;
-                }
-                if (smooth) {
-                    mGridView.setSelectedPositionSmooth(position);
-                } else {
-                    mGridView.setSelectedPosition(position);
-                }
-                assertEquals("isLayoutRequested", resultLayoutRequested,
-                        mGridView.isLayoutRequested());
-                assertEquals("isSmoothScrolling", resultSmoothScroller,
-                        mGridView.getLayoutManager().isSmoothScrolling());
-                if (!resultSmoothScroller) {
-                    // getScrollState() only matters when is not running smoothScroller
-                    assertEquals("getScrollState", resultScrollState,
-                            mGridView.getScrollState());
-                }
-                assertEquals("isLayoutRequested", resultLayoutRequested,
-                        mGridView.isLayoutRequested());
-            }
-        });
-    }
-
-    @Test
-    public void testSelectedPosition01() throws Throwable {
-        testSetSelectedPosition(false, false, false, false,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition02() throws Throwable {
-        testSetSelectedPosition(false, false, false, true,
-                false, true, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition03() throws Throwable {
-        testSetSelectedPosition(false, false, true, false,
-                false, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition04() throws Throwable {
-        testSetSelectedPosition(false, false, true, true,
-                false, false, RecyclerView.SCROLL_STATE_SETTLING);
-    }
-
-    @Test
-    public void testSelectedPosition05() throws Throwable {
-        testSetSelectedPosition(false, true, false, false,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition06() throws Throwable {
-        testSetSelectedPosition(false, true, false, true,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition07() throws Throwable {
-        testSetSelectedPosition(false, true, true, false,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition08() throws Throwable {
-        testSetSelectedPosition(false, true, true, true,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition09() throws Throwable {
-        testSetSelectedPosition(true, false, false, false,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition10() throws Throwable {
-        testSetSelectedPosition(true, false, false, true,
-                false, true, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition11() throws Throwable {
-        testSetSelectedPosition(true, false, true, false,
-                false, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition12() throws Throwable {
-        testSetSelectedPosition(true, false, true, true,
-                false, true, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition13() throws Throwable {
-        testSetSelectedPosition(true, true, false, false,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition14() throws Throwable {
-        testSetSelectedPosition(true, true, false, true,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition15() throws Throwable {
-        testSetSelectedPosition(true, true, true, false,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
-    @Test
-    public void testSelectedPosition16() throws Throwable {
-        testSetSelectedPosition(true, true, true, true,
-                true, false, RecyclerView.SCROLL_STATE_IDLE);
-    }
-
     @Test
     public void testScrollAndStuck() throws Throwable {
         // see b/67370222 fastRelayout() may be stuck.
@@ -4734,6 +4590,7 @@
         });
         assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
     }
+
     private boolean hasAction(AccessibilityNodeInfoCompat info, Object action) {
         if (Build.VERSION.SDK_INT >= 21) {
             AccessibilityNodeInfoCompat.AccessibilityActionCompat convertedAction =
diff --git a/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java b/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
index 62b7bb2..0669917 100644
--- a/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
+++ b/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
@@ -197,23 +197,17 @@
      * Base class which scrolls to selected view in onStop().
      */
     abstract class GridLinearSmoothScroller extends LinearSmoothScroller {
-        boolean mSkipOnStopInternal;
-
         GridLinearSmoothScroller() {
             super(mBaseGridView.getContext());
         }
 
         @Override
         protected void onStop() {
-            super.onStop();
-            if (!mSkipOnStopInternal) {
+            mFlag |= PF_IN_ONSTOP_SMOOTHSCROLLER;
+            try {
                 onStopInternal();
-            }
-            if (mCurrentSmoothScroller == this) {
-                mCurrentSmoothScroller = null;
-            }
-            if (mPendingMoveSmoothScroller == this) {
-                mPendingMoveSmoothScroller = null;
+            } finally {
+                mFlag &= ~PF_IN_ONSTOP_SMOOTHSCROLLER;
             }
         }
 
@@ -226,6 +220,7 @@
                     // to the target position.
                     scrollToSelection(getTargetPosition(), 0, false, 0);
                 }
+                super.onStop();
                 return;
             }
             if (mFocusPosition != getTargetPosition()) {
@@ -239,6 +234,7 @@
             }
             dispatchChildSelected();
             dispatchChildSelectedAndPositioned();
+            super.onStop();
         }
 
         @Override
@@ -385,6 +381,7 @@
             super.onStopInternal();
             // if we hit wall,  need clear the remaining pending moves.
             mPendingMoves = 0;
+            mPendingMoveSmoothScroller = null;
             View v = findViewByPosition(getTargetPosition());
             if (v != null) scrollToView(v, true);
         }
@@ -547,6 +544,12 @@
 
     static final int PF_REVERSE_FLOW_MASK = PF_REVERSE_FLOW_PRIMARY | PF_REVERSE_FLOW_SECONDARY;
 
+    /**
+     * flag to prevent calling stop() in onStop() which will lead to stack overflow crash
+     * TODO: fix RecyclerView.SmoothScroller#stop() instead
+     */
+    static final int PF_IN_ONSTOP_SMOOTHSCROLLER = 1 << 20;
+
     int mFlag = PF_LAYOUT_ENABLED
             | PF_FOCUS_OUT_SIDE_START | PF_FOCUS_OUT_SIDE_END
             | PF_PRUNE_CHILD | PF_SCROLL_ENABLED;
@@ -571,13 +574,7 @@
     int mSubFocusPosition = 0;
 
     /**
-     * Current running SmoothScroller.
-     */
-    GridLinearSmoothScroller mCurrentSmoothScroller;
-
-    /**
-     * LinearSmoothScroller that consume pending DPAD movements. Can be same object as
-     * mCurrentSmoothScroller when mCurrentSmoothScroller is PendingMoveSmoothScroller.
+     * LinearSmoothScroller that consume pending DPAD movements.
      */
     PendingMoveSmoothScroller mPendingMoveSmoothScroller;
 
@@ -1255,7 +1252,7 @@
         int start = 0;
         // Iterate from left to right, which is a different index traversal
         // in RTL flow
-        if ((mFlag & PF_REVERSE_FLOW_SECONDARY) != 0) {
+        if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) {
             for (int i = mNumRows-1; i > rowIndex; i--) {
                 start += getRowSizeSecondary(i) + mSpacingSecondary;
             }
@@ -2668,7 +2665,8 @@
         // is still valid and no layout is requested, otherwise defer to next layout pass.
         // If it is still in smoothScrolling, we should either update smoothScroller or initiate
         // a layout.
-        final boolean notSmoothScrolling = !isSmoothScrolling();
+        final boolean notSmoothScrolling = !isSmoothScrolling()
+                || (mFlag & PF_IN_ONSTOP_SMOOTHSCROLLER) != 0;
         if (notSmoothScrolling && !mBaseGridView.isLayoutRequested()
                 && view != null && getAdapterPositionByView(view) == position) {
             mFlag |= PF_IN_SELECTION;
@@ -2681,7 +2679,7 @@
                 mFocusPositionOffset = Integer.MIN_VALUE;
                 return;
             }
-            if (smooth && !mBaseGridView.isLayoutRequested()) {
+            if (smooth) {
                 mFocusPosition = position;
                 mSubFocusPosition = subposition;
                 mFocusPositionOffset = Integer.MIN_VALUE;
@@ -2700,21 +2698,13 @@
                 // stopScroll might change mFocusPosition, so call it before assign value to
                 // mFocusPosition
                 if (!notSmoothScrolling) {
-                    skipSmoothScrollerOnStopInternal();
                     mBaseGridView.stopScroll();
                 }
-                if (!mBaseGridView.isLayoutRequested()
-                        && view != null && getAdapterPositionByView(view) == position) {
-                    mFlag |= PF_IN_SELECTION;
-                    scrollToView(view, smooth);
-                    mFlag &= ~PF_IN_SELECTION;
-                } else {
-                    mFocusPosition = position;
-                    mSubFocusPosition = subposition;
-                    mFocusPositionOffset = Integer.MIN_VALUE;
-                    mFlag |= PF_FORCE_FULL_LAYOUT;
-                    requestLayout();
-                }
+                mFocusPosition = position;
+                mSubFocusPosition = subposition;
+                mFocusPositionOffset = Integer.MIN_VALUE;
+                mFlag |= PF_FORCE_FULL_LAYOUT;
+                requestLayout();
             }
         }
         if (TRACE) TraceCompat.endSection();
@@ -2747,33 +2737,6 @@
         return linearSmoothScroller.getTargetPosition();
     }
 
-    /**
-     * when start a new SmoothScroller or scroll to a different location, dont need
-     * current SmoothScroller.onStopInternal() doing the scroll work.
-     */
-    void skipSmoothScrollerOnStopInternal() {
-        if (mCurrentSmoothScroller != null) {
-            mCurrentSmoothScroller.mSkipOnStopInternal = true;
-        }
-    }
-
-    @Override
-    public void startSmoothScroll(RecyclerView.SmoothScroller smoothScroller) {
-        skipSmoothScrollerOnStopInternal();
-        super.startSmoothScroll(smoothScroller);
-        if (smoothScroller.isRunning() && smoothScroller instanceof GridLinearSmoothScroller) {
-            mCurrentSmoothScroller = (GridLinearSmoothScroller) smoothScroller;
-            if (mCurrentSmoothScroller instanceof PendingMoveSmoothScroller) {
-                mPendingMoveSmoothScroller = (PendingMoveSmoothScroller) mCurrentSmoothScroller;
-            } else {
-                mPendingMoveSmoothScroller = null;
-            }
-        } else {
-            mCurrentSmoothScroller = null;
-            mPendingMoveSmoothScroller = null;
-        }
-    }
-
     private void processPendingMovement(boolean forward) {
         if (forward ? hasCreatedLastItem() : hasCreatedFirstItem()) {
             return;
@@ -2785,6 +2748,9 @@
                     forward ? 1 : -1, mNumRows > 1);
             mFocusPositionOffset = 0;
             startSmoothScroll(linearSmoothScroller);
+            if (linearSmoothScroller.isRunning()) {
+                mPendingMoveSmoothScroller = linearSmoothScroller;
+            }
         } else {
             if (forward) {
                 mPendingMoveSmoothScroller.increasePendingMoves();
@@ -3723,10 +3689,6 @@
     @Override
     public boolean performAccessibilityAction(Recycler recycler, State state, int action,
             Bundle args) {
-        if (!isScrollEnabled()) {
-            // eat action request so that talkback wont focus out of RV
-            return true;
-        }
         saveContext(recycler, state);
         int translatedAction = action;
         boolean reverseFlowPrimary = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0;
diff --git a/lifecycle/integration-tests/testapp/build.gradle b/lifecycle/integration-tests/testapp/build.gradle
index a842c45..6fd43c4 100644
--- a/lifecycle/integration-tests/testapp/build.gradle
+++ b/lifecycle/integration-tests/testapp/build.gradle
@@ -52,5 +52,3 @@
 }
 
 tasks['check'].dependsOn(tasks['connectedCheck'])
-
-uploadArchives.enabled = false
diff --git a/navigation/.gitignore b/navigation/.gitignore
new file mode 100644
index 0000000..be4e6f1
--- /dev/null
+++ b/navigation/.gitignore
@@ -0,0 +1,4 @@
+local.properties
+maven-repo/
+build/
+*.DS_Store
diff --git a/navigation/OWNERS b/navigation/OWNERS
new file mode 100644
index 0000000..c890670
--- /dev/null
+++ b/navigation/OWNERS
@@ -0,0 +1,2 @@
+ilake@google.com
+sergeyv@google.com
diff --git a/navigation/common/build.gradle b/navigation/common/build.gradle
new file mode 100644
index 0000000..eca1086
--- /dev/null
+++ b/navigation/common/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+    api(NAV_SUPPORT_COMPAT)
+
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(TEST_RUNNER)
+
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+//used by testCompile safe-args-generator
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
+
+supportLibrary {
+    name = "Android Navigation Common"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2017"
+    description = "Android Navigation-Common"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/common/ktx/build.gradle b/navigation/common/ktx/build.gradle
new file mode 100644
index 0000000..625c2dc
--- /dev/null
+++ b/navigation/common/ktx/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    buildTypes {
+        debug {
+            testCoverageEnabled = false // Breaks Kotlin compiler.
+        }
+    }
+}
+
+dependencies {
+    api(project(":navigation:navigation-common"))
+    api(KOTLIN_STDLIB)
+    testImplementation(project(":navigation:navigation-testing"))
+    testImplementation(JUNIT)
+    testImplementation(TEST_RUNNER)
+    androidTestImplementation(project(":navigation:navigation-testing"))
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Common Kotlin Extensions"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2018"
+    description = "Android Navigation-Common-Ktx"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/common/ktx/src/androidTest/AndroidManifest.xml
similarity index 79%
rename from car/res/drawable/car_button_ripple_background_inverse.xml
rename to navigation/common/ktx/src/androidTest/AndroidManifest.xml
index 660dbcd..5b5eccf 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/common/ktx/src/androidTest/AndroidManifest.xml
@@ -14,6 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.navigation.ktx.test">
+    <application>
+    </application>
+</manifest>
diff --git a/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt
new file mode 100644
index 0000000..dd52934
--- /dev/null
+++ b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavDestinationBuilderTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.os.Bundle
+import android.support.annotation.IdRes
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import androidx.navigation.testing.TestNavigator
+import androidx.navigation.testing.TestNavigatorProvider
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavDestinationTest {
+    private val provider = TestNavigatorProvider(InstrumentationRegistry.getTargetContext())
+
+    @Test
+    fun navDestination() {
+        val destination = provider.navDestination(DESTINATION_ID) { }
+        assertEquals("NavDestination should have id set",
+                DESTINATION_ID, destination.id)
+    }
+
+    @Test
+    fun navDestinationLabel() {
+        val destination = provider.navDestination(DESTINATION_ID) {
+            label = LABEL
+        }
+        assertEquals("NavDestination should have label set",
+                LABEL, destination.label)
+    }
+
+    @Test
+    fun navDestinationDefaultArguments() {
+        val arguments = Bundle()
+        val destination = provider.navDestination(DESTINATION_ID) {
+            defaultArguments = arguments
+        }
+        assertEquals("NavDestination should have default arguments set",
+                arguments, destination.defaultArguments)
+    }
+
+    @Test
+    fun navDestinationAction() {
+        val destination = provider.navDestination(DESTINATION_ID) {
+            action(ACTION_ID) {
+                destinationId = DESTINATION_ID
+                navOptions {
+                    popUpTo = DESTINATION_ID
+                }
+            }
+        }
+        val action = destination.getAction(ACTION_ID)
+        assertNotNull("NavDestination should have action that was added", action)
+        assertEquals("NavAction should have NavOptions set",
+                DESTINATION_ID, action?.navOptions?.popUpTo)
+    }
+}
+
+private const val DESTINATION_ID = 1
+private const val LABEL = "TEST"
+private const val ACTION_ID = 1
+
+/**
+ * Instead of constructing a NavGraph from the NavigatorProvider, construct
+ * a NavDestination directly to allow for testing NavDestinationBuilder in
+ * isolation.
+ */
+fun NavigatorProvider.navDestination(
+        @IdRes id: Int,
+        block: NavDestinationBuilder<NavDestination>.() -> Unit
+): NavDestination = NavDestinationBuilder(this[TestNavigator::class], id).apply(block).build()
diff --git a/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt
new file mode 100644
index 0000000..9ecffe9
--- /dev/null
+++ b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavGraphBuilderTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.annotation.IdRes
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import androidx.navigation.testing.TestNavigator
+import androidx.navigation.testing.TestNavigatorProvider
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavGraphBuilderTest {
+    private val provider = TestNavigatorProvider(InstrumentationRegistry.getTargetContext())
+
+    @Test
+    fun navigation() {
+        val graph = provider.navigation(startDestination = DESTINATION_ID) {
+            navDestination(DESTINATION_ID) {}
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+
+    @Test
+    fun navigationUnaryPlus() {
+        val graph = provider.navigation(startDestination = DESTINATION_ID) {
+            +NavDestination(provider[TestNavigator::class]).apply {
+                id = DESTINATION_ID
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+
+    @Test
+    fun navigationAddDestination() {
+        val graph = provider.navigation(startDestination = DESTINATION_ID) {
+            val destination = NavDestination(provider[TestNavigator::class]).apply {
+                id = DESTINATION_ID
+            }
+            addDestination(destination)
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun navigationMissingStartDestination() {
+        provider.navigation(startDestination = 0) {
+            navDestination(DESTINATION_ID) {}
+        }
+        fail("NavGraph should throw IllegalStateException if startDestination is zero")
+    }
+
+    @Test
+    fun navigationNested() {
+        val graph = provider.navigation(startDestination = DESTINATION_ID) {
+            navigation(DESTINATION_ID, startDestination = SECOND_DESTINATION_ID) {
+                navDestination(SECOND_DESTINATION_ID) {}
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+}
+
+private const val DESTINATION_ID = 1
+private const val SECOND_DESTINATION_ID = 2
+
+/**
+ * Create a base NavDestination. Generally, only subtypes of NavDestination should be
+ * added to a NavGraph (hence why this is not in the common-ktx library)
+ */
+fun NavGraphBuilder.navDestination(
+        @IdRes id: Int,
+        block: NavDestinationBuilder<NavDestination>.() -> Unit
+) = destination(NavDestinationBuilder(provider[TestNavigator::class], id).apply(block))
diff --git a/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavGraphTest.kt b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavGraphTest.kt
new file mode 100644
index 0000000..ebc11a7
--- /dev/null
+++ b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavGraphTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import androidx.navigation.testing.TestNavigator
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavGraphTest {
+    private val navGraphNavigator = NavGraphNavigator(InstrumentationRegistry.getTargetContext())
+    private val navigator = TestNavigator()
+
+    @Test
+    fun plusAssign() {
+        val graph = NavGraph(navGraphNavigator)
+        val destination = NavDestination(navigator).apply { id = DESTINATION_ID }
+        graph += destination
+        assertSame("plusAssign destination should be retrieved with get", destination,
+                graph[DESTINATION_ID])
+    }
+
+    @Test
+    fun minusAssign() {
+        val graph = NavGraph(navGraphNavigator)
+        val destination = NavDestination(navigator).apply { id = DESTINATION_ID }
+        graph += destination
+        assertSame("plusAssign destination should be retrieved with get", destination,
+                graph[DESTINATION_ID])
+        graph -= destination
+        assertFalse("Destination should be removed after minusAssign",
+                DESTINATION_ID in graph)
+    }
+
+    @Test
+    fun plusAssignGraph() {
+        val graph = NavGraph(navGraphNavigator)
+        val other = NavGraph(navGraphNavigator)
+        other += NavDestination(navigator).apply { id = DESTINATION_ID }
+        other += NavDestination(navigator).apply { id = SECOND_DESTINATION_ID }
+        graph += other
+        assertTrue("NavGraph should have destination1 from other",
+                DESTINATION_ID in graph)
+        assertFalse("other nav graph should not have destination1",
+                DESTINATION_ID in other)
+
+        assertTrue("NavGraph should have destination2 from other",
+                SECOND_DESTINATION_ID in graph)
+        assertFalse("other nav graph should not have destination2",
+                SECOND_DESTINATION_ID in other)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun getIllegalArgumentException() {
+        val graph = NavGraph(navGraphNavigator)
+        graph[DESTINATION_ID]
+    }
+}
+
+private const val DESTINATION_ID = 1
+private const val SECOND_DESTINATION_ID = 2
diff --git a/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavOptionsBuilderTest.kt b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavOptionsBuilderTest.kt
new file mode 100644
index 0000000..c33afe8
--- /dev/null
+++ b/navigation/common/ktx/src/androidTest/java/androidx/navigation/NavOptionsBuilderTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavOptionsTest {
+
+    @Test
+    fun launchSingleTop() {
+        val navOptions = navOptions {
+            launchSingleTop = true
+        }
+        assertTrue("NavOptions should have launchSingleTop set",
+                navOptions.shouldLaunchSingleTop())
+    }
+
+    @Test
+    fun launchDocument() {
+        val navOptions = navOptions {
+            launchDocument = true
+        }
+        assertTrue("NavOptions should have launchDocument set",
+                navOptions.shouldLaunchDocument())
+    }
+
+    @Test
+    fun clearTask() {
+        val navOptions = navOptions {
+            clearTask = true
+        }
+        assertTrue("NavOptions should have clearTask set",
+                navOptions.shouldClearTask())
+    }
+
+    @Test
+    fun popUpTo() {
+        val navOptions = navOptions {
+            popUpTo = DESTINATION_ID
+        }
+        assertEquals("NavOptions should have popUpTo destination id set",
+                DESTINATION_ID, navOptions.popUpTo)
+        assertFalse("NavOptions should have isPopUpToInclusive false by default",
+                navOptions.isPopUpToInclusive)
+    }
+
+    @Test
+    fun popUpToInclusive() {
+        val navOptions = navOptions {
+            popUpTo(DESTINATION_ID) {
+                inclusive = true
+            }
+        }
+        assertEquals("NavOptions should have popUpTo destination id set",
+                DESTINATION_ID, navOptions.popUpTo)
+        assertTrue("NavOptions should have isPopUpToInclusive set",
+                navOptions.isPopUpToInclusive)
+    }
+
+    @Test
+    fun anim() {
+        val navOptions = navOptions {
+            anim {
+                enter = ENTER_ANIM_ID
+                exit = EXIT_ANIM_ID
+                popEnter = POP_ENTER_ANIM_ID
+                popExit = POP_EXIT_ANIM_ID
+            }
+        }
+        assertEquals("NavOptions should have enter animation set",
+                ENTER_ANIM_ID, navOptions.enterAnim)
+        assertEquals("NavOptions should have exit animation set",
+                EXIT_ANIM_ID, navOptions.exitAnim)
+        assertEquals("NavOptions should have pop enter animation set",
+                POP_ENTER_ANIM_ID, navOptions.popEnterAnim)
+        assertEquals("NavOptions should have pop exit animation set",
+                POP_EXIT_ANIM_ID, navOptions.popExitAnim)
+    }
+}
+
+private const val DESTINATION_ID = 1
+
+private const val ENTER_ANIM_ID = 10
+private const val EXIT_ANIM_ID = 11
+private const val POP_ENTER_ANIM_ID = 12
+private const val POP_EXIT_ANIM_ID = 13
diff --git a/car/res/drawable/car_button_ripple_background.xml b/navigation/common/ktx/src/main/AndroidManifest.xml
similarity index 83%
copy from car/res/drawable/car_button_ripple_background.xml
copy to navigation/common/ktx/src/main/AndroidManifest.xml
index 13d0a49..799fc93 100644
--- a/car/res/drawable/car_button_ripple_background.xml
+++ b/navigation/common/ktx/src/main/AndroidManifest.xml
@@ -14,6 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background" />
+<manifest package="androidx.navigation.ktx"/>
diff --git a/navigation/common/ktx/src/main/java/androidx/navigation/NavDestinationBuilder.kt b/navigation/common/ktx/src/main/java/androidx/navigation/NavDestinationBuilder.kt
new file mode 100644
index 0000000..55cc97f
--- /dev/null
+++ b/navigation/common/ktx/src/main/java/androidx/navigation/NavDestinationBuilder.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.os.Bundle
+import android.support.annotation.IdRes
+
+@DslMarker
+annotation class NavDestinationDsl
+
+/**
+ * DSL for constructing a new [NavDestination]
+ */
+@NavDestinationDsl
+open class NavDestinationBuilder<out D : NavDestination>(
+        protected val navigator: Navigator<out D>,
+        @IdRes val id: Int
+) {
+    /**
+     * The descriptive label of the destination
+     */
+    var label: CharSequence? = null
+
+    /**
+     * The default arguments that should be passed to the destination
+     */
+    var defaultArguments: Bundle? = null
+
+    private var deepLinks = mutableListOf<String>()
+
+    /**
+     * Add a deep link to this destination.
+     *
+     * In addition to a direct Uri match, the following features are supported:
+     *
+     * *    Uris without a scheme are assumed as http and https. For example,
+     *      `www.example.com` will match `http://www.example.com` and
+     *      `https://www.example.com`.
+     * *    Placeholders in the form of `{placeholder_name}` matches 1 or more
+     *      characters. The String value of the placeholder will be available in the arguments
+     *      [Bundle] with a key of the same name. For example,
+     *      `http://www.example.com/users/{id}` will match
+     *      `http://www.example.com/users/4`.
+     * *    The `.*` wildcard can be used to match 0 or more characters.
+     *
+     * @param uriPattern The uri pattern to add as a deep link
+     */
+    fun deepLink(uriPattern: String) {
+        deepLinks.add(uriPattern)
+    }
+
+    private var actions = mutableMapOf<Int, NavAction>()
+
+    /**
+     * Adds a new [NavAction] to the destination
+     */
+    fun action(actionId: Int, block: NavActionBuilder.() -> Unit) {
+        actions[actionId] = NavActionBuilder().apply(block).build()
+    }
+
+    /**
+     * Build the NavDestination by calling [Navigator.createDestination].
+     */
+    open fun build(): D {
+        return navigator.createDestination().also { destination ->
+            destination.id = id
+            destination.label = label
+            destination.setDefaultArguments(defaultArguments)
+            deepLinks.forEach { deepLink ->
+                destination.addDeepLink(deepLink)
+            }
+            actions.forEach { actionId, action ->
+                destination.putAction(actionId, action)
+            }
+        }
+    }
+}
+
+/**
+ * DSL for building a [NavAction].
+ */
+@NavDestinationDsl
+class NavActionBuilder {
+    /**
+     * The ID of the destination that should be navigated to when this action is used
+     */
+    var destinationId: Int = 0
+
+    private var navOptions: NavOptions? = null
+
+    /**
+     * Sets the [NavOptions] for this action that should be used by default
+     */
+    fun navOptions(block: NavOptionsBuilder.() -> Unit) {
+        navOptions = NavOptionsBuilder().apply(block).build()
+    }
+
+    internal fun build() = NavAction(destinationId, navOptions)
+}
diff --git a/navigation/common/ktx/src/main/java/androidx/navigation/NavGraph.kt b/navigation/common/ktx/src/main/java/androidx/navigation/NavGraph.kt
new file mode 100644
index 0000000..eaf2775
--- /dev/null
+++ b/navigation/common/ktx/src/main/java/androidx/navigation/NavGraph.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.navigation
+
+import android.support.annotation.IdRes
+
+/**
+ * Returns the destination with `id`.
+ *
+ * @throws IllegalArgumentException if no destination is found with that id.
+ */
+inline operator fun NavGraph.get(@IdRes id: Int): NavDestination =
+        findNode(id) ?: throw IllegalArgumentException("No destination for $id was found in $this")
+
+/** Returns `true` if a destination with `id` is found in this navigation graph. */
+operator fun NavGraph.contains(@IdRes id: Int): Boolean = findNode(id) != null
+
+/**
+ * Adds a destination to this NavGraph. The destination must have an
+ * [id][NavDestination.getId] set.
+ *
+ * The destination must not have a [parent][NavDestination.getParent] set. If
+ * the destination is already part of a [NavGraph], call
+ * [NavGraph.remove] before calling this method.</p>
+ *
+ * @param node destination to add
+ */
+inline operator fun NavGraph.plusAssign(node: NavDestination) {
+    addDestination(node)
+}
+
+/**
+ * Add all destinations from another collection to this one. As each destination has at most
+ * one parent, the destinations will be removed from the given NavGraph.
+ *
+ * @param other collection of destinations to add. All destinations will be removed from the
+ * parameter graph after being added to this graph.
+ */
+inline operator fun NavGraph.plusAssign(other: NavGraph) {
+    addAll(other)
+}
+
+/** Removes `node` from this navigation graph. */
+inline operator fun NavGraph.minusAssign(node: NavDestination) {
+    remove(node)
+}
diff --git a/navigation/common/ktx/src/main/java/androidx/navigation/NavGraphBuilder.kt b/navigation/common/ktx/src/main/java/androidx/navigation/NavGraphBuilder.kt
new file mode 100644
index 0000000..97579ec
--- /dev/null
+++ b/navigation/common/ktx/src/main/java/androidx/navigation/NavGraphBuilder.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.annotation.IdRes
+
+/**
+ * Construct a new [NavGraph]
+ */
+inline fun NavigatorProvider.navigation(
+        @IdRes id: Int = 0,
+        @IdRes startDestination: Int,
+        block: NavGraphBuilder.() -> Unit
+) = NavGraphBuilder(this, id, startDestination).apply(block).build()
+
+/**
+ * Construct a nested [NavGraph]
+ */
+inline fun NavGraphBuilder.navigation(
+        @IdRes id: Int,
+        @IdRes startDestination: Int,
+        block: NavGraphBuilder.() -> Unit
+) = destination(NavGraphBuilder(provider, id, startDestination).apply(block))
+
+/**
+ * DSL for constructing a new [NavGraph]
+ */
+@NavDestinationDsl
+class NavGraphBuilder(
+        val provider: NavigatorProvider,
+        @IdRes id: Int,
+        @IdRes private var startDestination: Int
+) : NavDestinationBuilder<NavGraph>(provider[NavGraphNavigator::class], id) {
+    private val destinations = mutableListOf<NavDestination>()
+
+    /**
+     * Build and add a new destination to the [NavGraphBuilder]
+     */
+    fun <D : NavDestination> destination(navDestination: NavDestinationBuilder<D>) {
+        destinations += navDestination.build()
+    }
+
+    /**
+     * Adds this destination to the [NavGraphBuilder]
+     */
+    operator fun NavDestination.unaryPlus() {
+        addDestination(this)
+    }
+
+    /**
+     * Add the destination to the [NavGraphBuilder]
+     */
+    fun addDestination(destination: NavDestination) {
+        destinations += destination
+    }
+
+    override fun build(): NavGraph = super.build().also { navGraph ->
+        navGraph.addDestinations(destinations)
+        if (startDestination == 0) {
+            throw IllegalStateException("You must set a startDestination")
+        }
+        navGraph.startDestination = startDestination
+    }
+}
diff --git a/navigation/common/ktx/src/main/java/androidx/navigation/NavOptionsBuilder.kt b/navigation/common/ktx/src/main/java/androidx/navigation/NavOptionsBuilder.kt
new file mode 100644
index 0000000..5b4781d
--- /dev/null
+++ b/navigation/common/ktx/src/main/java/androidx/navigation/NavOptionsBuilder.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.annotation.AnimRes
+import android.support.annotation.AnimatorRes
+import android.support.annotation.IdRes
+
+@DslMarker
+annotation class NavOptionsDsl
+
+/**
+ * Construct a new [NavOptions]
+ */
+fun navOptions(block: NavOptionsBuilder.() -> Unit): NavOptions =
+        NavOptionsBuilder().apply(block).build()
+
+/**
+ * DSL for constructing a new [NavOptions]
+ */
+@NavOptionsDsl
+class NavOptionsBuilder {
+    private val builder = NavOptions.Builder()
+
+    /**
+     * Whether this navigation action should launch as single-top (i.e., there will be at most
+     * one copy of a given destination on the top of the back stack).
+     *
+     * This functions similarly to how [android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP]
+     * works with activites.
+     */
+    var launchSingleTop = false
+
+    /**
+     * Whether this navigation action should launch the destination in a new document.
+     *
+     * This functions similarly to how [android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT]
+     * works with activites.
+     */
+    var launchDocument = false
+
+    /**
+     * Whether this navigation action should clear the entire back stack
+     *
+     * This functions similarly to how [android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK]
+     * works with activites.
+     */
+    var clearTask = false
+
+    /**
+     * Pop up to a given destination before navigating. This pops all non-matching destinations
+     * from the back stack until this destination is found.
+     */
+    @IdRes
+    var popUpTo: Int = 0
+        set(value) {
+            field = value
+            inclusive = false
+        }
+    private var inclusive = false
+
+    /**
+     * Pop up to a given destination before navigating. This pops all non-matching destinations
+     * from the back stack until this destination is found.
+     */
+    fun popUpTo(@IdRes id: Int, block: PopUpToBuilder.() -> Unit) {
+        popUpTo = id
+        inclusive = PopUpToBuilder().apply(block).inclusive
+    }
+
+    /**
+     * Sets any custom Animation or Animator resources that should be used.
+     *
+     * Note: Animator resources are not supported for navigating to a new Activity
+     */
+    fun anim(block: AnimBuilder.() -> Unit) {
+        AnimBuilder().apply(block).run {
+            this@NavOptionsBuilder.builder.setEnterAnim(enter)
+                    .setExitAnim(exit)
+                    .setPopEnterAnim(popEnter)
+                    .setPopExitAnim(popExit)
+        }
+    }
+
+    internal fun build() = builder.apply {
+        setLaunchSingleTop(launchSingleTop)
+        setLaunchDocument(launchDocument)
+        setClearTask(clearTask)
+        setPopUpTo(popUpTo, inclusive)
+    }.build()
+}
+
+/**
+ * DSL for customizing [NavOptionsBuilder.popUpTo] operations.
+ */
+@NavOptionsDsl
+class PopUpToBuilder {
+    /**
+     * Whether the `popUpTo` destination should be popped from the back stack.
+     */
+    var inclusive: Boolean = false
+}
+
+/**
+ * DSL for setting custom Animation or Animator resources on a [NavOptionsBuilder]
+ */
+@NavOptionsDsl
+class AnimBuilder {
+    /**
+     * The custom Animation or Animator resource for the enter animation.
+     *
+     * Note: Animator resources are not supported for navigating to a new Activity
+     */
+    @AnimRes
+    @AnimatorRes
+    var enter = -1
+
+    /**
+     * The custom Animation or Animator resource for the exit animation.
+     *
+     * Note: Animator resources are not supported for navigating to a new Activity
+     */
+    @AnimRes
+    @AnimatorRes
+    var exit = -1
+
+    /**
+     * The custom Animation or Animator resource for the enter animation
+     * when popping off the back stack.
+     *
+     * Note: Animator resources are not supported for navigating to a new Activity
+     */
+    @AnimRes
+    @AnimatorRes
+    var popEnter = -1
+
+    /**
+     * The custom Animation or Animator resource for the exit animation
+     * when popping off the back stack.
+     *
+     * Note: Animator resources are not supported for navigating to a new Activity
+     */
+    @AnimRes
+    @AnimatorRes
+    var popExit = -1
+}
diff --git a/navigation/common/ktx/src/main/java/androidx/navigation/NavigatorProvider.kt b/navigation/common/ktx/src/main/java/androidx/navigation/NavigatorProvider.kt
new file mode 100644
index 0000000..ec43d45
--- /dev/null
+++ b/navigation/common/ktx/src/main/java/androidx/navigation/NavigatorProvider.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.navigation
+
+import kotlin.reflect.KClass
+
+/**
+ * Retrieves a registered [Navigator] by name.
+ *
+ * @throws IllegalStateException if the Navigator has not been added
+ */
+inline operator fun <D : NavDestination, T : Navigator<D>> NavigatorProvider.get(name: String): T =
+        getNavigator(name)
+
+/**
+ * Retrieves a registered [Navigator] using the name provided by the
+ * [Navigator.Name annotation][Navigator.Name].
+ *
+ * @throws IllegalStateException if the Navigator has not been added
+ */
+inline operator fun <D : NavDestination, T : Navigator<D>> NavigatorProvider.get(
+        clazz: KClass<T>
+): T = getNavigator(clazz.java)
+
+/**
+ * Register a [Navigator] by name. If a navigator by this name is already
+ * registered, this new navigator will replace it.
+ *
+ * @return the previously added [Navigator] for the given name, if any
+ */
+inline operator fun <D : NavDestination> NavigatorProvider.set(
+        name: String,
+        navigator: Navigator<D>
+) = addNavigator(name, navigator)
+
+/**
+ * Register a navigator using the name provided by the
+ * [Navigator.Name annotation][Navigator.Name].
+ */
+inline operator fun <D : NavDestination> NavigatorProvider.plusAssign(navigator: Navigator<D>) {
+    addNavigator(navigator)
+}
diff --git a/navigation/common/ktx/src/test/java/androidx/navigation/NavigatorProviderTest.kt b/navigation/common/ktx/src/test/java/androidx/navigation/NavigatorProviderTest.kt
new file mode 100644
index 0000000..95d7324
--- /dev/null
+++ b/navigation/common/ktx/src/test/java/androidx/navigation/NavigatorProviderTest.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.test.filters.SmallTest
+import androidx.navigation.testing.TestNavigator
+import org.junit.Assert.assertSame
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class NavigatorProviderTest {
+    private val provider = SimpleNavigatorProvider()
+
+    @Test
+    fun set() {
+        val navigator = TestNavigator()
+        provider[NAME] = navigator
+        val foundNavigator: Navigator<NavDestination> = provider[NAME]
+        assertSame("Set destination should be retrieved with get", navigator,
+                foundNavigator)
+    }
+
+    @Test
+    fun plusAssign() {
+        val navigator = TestNavigator()
+        provider += navigator
+        assertSame("Set destination should be retrieved with get", navigator,
+                provider[TestNavigator::class])
+    }
+}
+
+private const val NAME = "TEST"
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/common/src/androidTest/AndroidManifest.xml
similarity index 72%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/common/src/androidTest/AndroidManifest.xml
index 660dbcd..679327a 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/common/src/androidTest/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.navigation.common.test">
+    <application>
+    </application>
+</manifest>
diff --git a/navigation/common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.java b/navigation/common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.java
new file mode 100644
index 0000000..55c1b00
--- /dev/null
+++ b/navigation/common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class NavDeepLinkTest {
+    private static final String DEEP_LINK_EXACT_NO_SCHEME = "www.example.com";
+    private static final String DEEP_LINK_EXACT_HTTP = "http://" + DEEP_LINK_EXACT_NO_SCHEME;
+    private static final String DEEP_LINK_EXACT_HTTPS = "https://" + DEEP_LINK_EXACT_NO_SCHEME;
+
+    @Test
+    public void deepLinkExactMatch() {
+        NavDeepLink deepLink = new NavDeepLink(DEEP_LINK_EXACT_HTTP);
+
+        assertThat(deepLink.matches(Uri.parse(DEEP_LINK_EXACT_HTTP)), is(true));
+        assertThat(deepLink.matches(Uri.parse(DEEP_LINK_EXACT_HTTPS)), is(false));
+    }
+
+    @Test
+    public void deepLinkExactMatchWithHyphens() {
+        String deepLinkString = "android-app://com.example";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkString);
+
+        assertThat(deepLink.matches(Uri.parse(deepLinkString)), is(true));
+    }
+
+    @Test
+    public void deepLinkExactMatchNoScheme() {
+        NavDeepLink deepLink = new NavDeepLink(DEEP_LINK_EXACT_NO_SCHEME);
+
+        assertThat(deepLink.matches(Uri.parse(DEEP_LINK_EXACT_HTTP)), is(true));
+        assertThat(deepLink.matches(Uri.parse(DEEP_LINK_EXACT_HTTPS)), is(true));
+    }
+
+    @Test
+    public void deepLinkArgumentMatch() {
+        String deepLinkArgument = DEEP_LINK_EXACT_HTTPS + "/users/{id}/posts";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkArgument);
+
+        String id = "2";
+        Bundle matchArgs = deepLink.getMatchingArguments(
+                Uri.parse(deepLinkArgument.replace("{id}", id)));
+        assertThat(matchArgs, not(nullValue()));
+        assert matchArgs != null;
+        assertThat(matchArgs.getString("id"), is(id));
+    }
+
+    @Test
+    public void deepLinkMultipleArgumentMatch() {
+        String deepLinkArgument = DEEP_LINK_EXACT_HTTPS + "/users/{id}/posts/{postId}";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkArgument);
+
+        String id = "2";
+        String postId = "42";
+        Bundle matchArgs = deepLink.getMatchingArguments(
+                Uri.parse(deepLinkArgument.replace("{id}", id).replace("{postId}", postId)));
+        assertThat(matchArgs, not(nullValue()));
+        assert matchArgs != null;
+        assertThat(matchArgs.getString("id"), is(id));
+        assertThat(matchArgs.getString("postId"), is(postId));
+    }
+
+    @Test
+    public void deepLinkEmptyArgumentNoMatch() {
+        String deepLinkArgument = DEEP_LINK_EXACT_HTTPS + "/users/{id}/posts";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkArgument);
+
+        assertThat(deepLink.matches(
+                Uri.parse(deepLinkArgument.replace("{id}", ""))),
+                is(false));
+    }
+
+    @Test
+    public void deepLinkPrefixMatch() {
+        String deepLinkPrefix = DEEP_LINK_EXACT_HTTPS + "/posts/.*";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkPrefix);
+
+        assertThat(deepLink.matches(
+                Uri.parse(deepLinkPrefix.replace(".*", "test"))),
+                is(true));
+    }
+
+    @Test
+    public void deepLinkWildcardMatch() {
+        String deepLinkWildcard = DEEP_LINK_EXACT_HTTPS + "/posts/.*/new";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkWildcard);
+
+        assertThat(deepLink.matches(
+                Uri.parse(deepLinkWildcard.replace(".*", "test"))),
+                is(true));
+    }
+
+    @Test
+    public void deepLinkMultipleMatch() {
+        String deepLinkMultiple = DEEP_LINK_EXACT_HTTPS + "/users/{id}/posts/.*";
+        NavDeepLink deepLink = new NavDeepLink(deepLinkMultiple);
+
+        String id = "2";
+        Bundle matchArgs = deepLink.getMatchingArguments(
+                Uri.parse(deepLinkMultiple.replace("{id}", id)));
+        assertThat(matchArgs, not(nullValue()));
+        assert matchArgs != null;
+        assertThat(matchArgs.getString("id"), is(id));
+    }
+}
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/common/src/main/AndroidManifest.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/common/src/main/AndroidManifest.xml
index 16b1d0c..716a35e 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/common/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.navigation.common">
+
+</manifest>
diff --git a/navigation/common/src/main/java/androidx/navigation/NavAction.java b/navigation/common/src/main/java/androidx/navigation/NavAction.java
new file mode 100644
index 0000000..d233aa3
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavAction.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.Nullable;
+
+/**
+ * Navigation actions provide a level of indirection between your navigation code and the
+ * underlying destinations. This allows you to define common actions that change their destination
+ * or {@link NavOptions} based on the current {@link NavDestination}.
+ *
+ * <p>The {@link NavOptions} associated with a NavAction are used by default when navigating
+ * to this action via {@link NavController#navigate(int)} or
+ * {@link NavController#navigate(int, Bundle)}.</p>
+ *
+ * <p>Actions should be added via {@link NavDestination#putAction(int, int)} or
+ * {@link NavDestination#putAction(int, NavAction)}.</p>
+ */
+public class NavAction {
+    @IdRes
+    private final int mDestinationId;
+    private NavOptions mNavOptions;
+
+    /**
+     * Creates a new NavAction for the given destination.
+     *
+     * @param destinationId the ID of the destination that should be navigated to when this
+     *                      action is used.
+     */
+    public NavAction(@IdRes int destinationId) {
+        this(destinationId, null);
+    }
+
+    /**
+     * Creates a new NavAction for the given destination.
+     *
+     * @param destinationId the ID of the destination that should be navigated to when this
+     *                      action is used.
+     * @param navOptions special options for this action that should be used by default
+     */
+    public NavAction(@IdRes int destinationId, @Nullable NavOptions navOptions) {
+        mDestinationId = destinationId;
+        mNavOptions = navOptions;
+    }
+
+    /**
+     * Gets the ID of the destination that should be navigated to when this action is used
+     */
+    public int getDestinationId() {
+        return mDestinationId;
+    }
+
+    /**
+     * Sets the NavOptions to be used by default when navigating to this action.
+     *
+     * @param navOptions special options for this action that should be used by default
+     */
+    public void setNavOptions(@Nullable NavOptions navOptions) {
+        mNavOptions = navOptions;
+    }
+
+    /**
+     * Gets the NavOptions to be used by default when navigating to this action.
+     */
+    @Nullable
+    public NavOptions getNavOptions() {
+        return mNavOptions;
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavDeepLink.java b/navigation/common/src/main/java/androidx/navigation/NavDeepLink.java
new file mode 100644
index 0000000..be0c67a
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavDeepLink.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * NavDeepLink encapsulates the parsing and matching of a navigation deep link.
+ */
+class NavDeepLink {
+    private static final Pattern SCHEME_PATTERN = Pattern.compile("^(\\w+-)*\\w+:");
+
+    private final ArrayList<String> mArguments = new ArrayList<>();
+    private final Pattern mPattern;
+
+    /**
+     * NavDestinations should be created via {@link Navigator#createDestination}.
+     */
+    NavDeepLink(@NonNull String uri) {
+        StringBuffer uriRegex = new StringBuffer("^");
+
+        if (!SCHEME_PATTERN.matcher(uri).find()) {
+            uriRegex.append("http[s]?://");
+        }
+        Pattern fillInPattern = Pattern.compile("\\{(.+?)\\}");
+        Matcher matcher = fillInPattern.matcher(uri);
+        while (matcher.find()) {
+            String argName = matcher.group(1);
+            mArguments.add(argName);
+            matcher.appendReplacement(uriRegex, "");
+            uriRegex.append("(.+?)");
+        }
+        matcher.appendTail(uriRegex);
+        mPattern = Pattern.compile(uriRegex.toString());
+    }
+
+    boolean matches(@NonNull Uri deepLink) {
+        return mPattern.matcher(deepLink.toString()).matches();
+    }
+
+    @Nullable
+    Bundle getMatchingArguments(@NonNull Uri deepLink) {
+        Matcher matcher = mPattern.matcher(deepLink.toString());
+        if (!matcher.matches()) {
+            return null;
+        }
+        Bundle bundle = new Bundle();
+        int size = mArguments.size();
+        for (int index = 0; index < size; index++) {
+            String argument = mArguments.get(index);
+            bundle.putString(argument, matcher.group(index + 1));
+        }
+        return bundle;
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavDestination.java b/navigation/common/src/main/java/androidx/navigation/NavDestination.java
new file mode 100644
index 0000000..993675b
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavDestination.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.Pair;
+import android.support.v4.util.SparseArrayCompat;
+import android.util.AttributeSet;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+
+import androidx.navigation.common.R;
+
+/**
+ * NavDestination represents one node within an overall navigation graph.
+ *
+ * <p>Each destination is associated with a {@link Navigator} which knows how to navigate to this
+ * particular destination.</p>
+ *
+ * <p>Destinations declare a set of {@link #putAction(int, int) actions} that they
+ * support. These actions form a navigation API for the destination; the same actions declared
+ * on different destinations that fill similar roles allow application code to navigate based
+ * on semantic intent.</p>
+ *
+ * <p>Each destination has a set of {@link #getDefaultArguments() default arguments} that will
+ * be applied when {@link NavController#navigate(int, Bundle) navigating} to that destination.
+ * These arguments can be overridden at the time of navigation.</p>
+ */
+public class NavDestination {
+
+    /**
+     * Retrieve a suitable display name for a given id.
+     * @param context Context used to resolve a resource's name
+     * @param id The id to get a display name for
+     * @return The resource's name if it is a valid id or just the id itself if it is not
+     * a valid resource
+     */
+    @NonNull
+    static String getDisplayName(@NonNull Context context, int id) {
+        try {
+            return context.getResources().getResourceName(id);
+        } catch (Resources.NotFoundException e) {
+            return Integer.toString(id);
+        }
+    }
+
+    private final Navigator mNavigator;
+    private NavGraph mParent;
+    private int mId;
+    private CharSequence mLabel;
+    private Bundle mDefaultArgs;
+    private ArrayList<NavDeepLink> mDeepLinks;
+    private SparseArrayCompat<NavAction> mActions;
+
+    /**
+     * NavDestinations should be created via {@link Navigator#createDestination}.
+     */
+    public NavDestination(@NonNull Navigator<? extends NavDestination> navigator) {
+        mNavigator = navigator;
+    }
+
+    /**
+     * Called when inflating a destination from a resource.
+     *
+     * @param context local context performing inflation
+     * @param attrs attrs to parse during inflation
+     */
+    @CallSuper
+    public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
+        final TypedArray a = context.getResources().obtainAttributes(attrs,
+                R.styleable.Navigator);
+        setId(a.getResourceId(R.styleable.Navigator_android_id, 0));
+        setLabel(a.getText(R.styleable.Navigator_android_label));
+        a.recycle();
+    }
+
+    void setParent(NavGraph parent) {
+        mParent = parent;
+    }
+
+    /**
+     * Gets the {@link NavGraph} that contains this destination. This will be set when a
+     * destination is added to a NavGraph via {@link NavGraph#addDestination}.
+     * @return
+     */
+    @Nullable
+    public NavGraph getParent() {
+        return mParent;
+    }
+
+    /**
+     * Returns the destination's unique ID. This should be an ID resource generated by
+     * the Android resource system.
+     *
+     * @return this destination's ID
+     */
+    @IdRes
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Sets the destination's unique ID. This should be an ID resource generated by
+     * the Android resource system.
+     *
+     * @param id this destination's new ID
+     */
+    public void setId(@IdRes int id) {
+        mId = id;
+    }
+
+    /**
+     * Sets the descriptive label of this destination.
+     *
+     * @param label A descriptive label of this destination.
+     */
+    public void setLabel(@Nullable CharSequence label) {
+        mLabel = label;
+    }
+
+    /**
+     * Gets the descriptive label of this destination.
+     */
+    @Nullable
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Returns the destination's {@link Navigator}.
+     *
+     * @return this destination's navigator
+     */
+    @NonNull
+    public Navigator getNavigator() {
+        return mNavigator;
+    }
+
+    /**
+     * Returns the destination's default arguments bundle.
+     *
+     * @return the default arguments bundle
+     */
+    public @NonNull Bundle getDefaultArguments() {
+        if (mDefaultArgs == null) {
+            mDefaultArgs = new Bundle();
+        }
+        return mDefaultArgs;
+    }
+
+    /**
+     * Sets the destination's default arguments bundle.
+     *
+     * @param args the new bundle to set
+     */
+    public void setDefaultArguments(@Nullable Bundle args) {
+        mDefaultArgs = args;
+    }
+
+    /**
+     * Merges a bundle of arguments into the current default arguments for this destination.
+     * New values with the same keys will replace old values with those keys.
+     *
+     * @param args arguments to add
+     */
+    public void addDefaultArguments(@NonNull Bundle args) {
+        getDefaultArguments().putAll(args);
+    }
+
+    /**
+     * Add a deep link to this destination. Matching Uris sent to
+     * {@link NavController#onHandleDeepLink(Intent)} will trigger navigating to this destination.
+     * <p>
+     * In addition to a direct Uri match, the following features are supported:
+     * <ul>
+     *     <li>Uris without a scheme are assumed as http and https. For example,
+     *     <code>www.example.com</code> will match <code>http://www.example.com</code> and
+     *     <code>https://www.example.com</code>.</li>
+     *     <li>Placeholders in the form of <code>{placeholder_name}</code> matches 1 or more
+     *     characters. The String value of the placeholder will be available in the arguments
+     *     {@link Bundle} with a key of the same name. For example,
+     *     <code>http://www.example.com/users/{id}</code> will match
+     *     <code>http://www.example.com/users/4</code>.</li>
+     *     <li>The <code>.*</code> wildcard can be used to match 0 or more characters.</li>
+     * </ul>
+     * These Uris can be declared in your navigation XML files by adding one or more
+     * <code>&lt;deepLink app:uri="uriPattern" /&gt;</code> elements as
+     * a child to your destination.
+     * <p>
+     * Deep links added in navigation XML files will automatically replace instances of
+     * <code>${applicationId}</code> with the applicationId of your app.
+     * Programmatically added deep links should use {@link Context#getPackageName()} directly
+     * when constructing the uriPattern.
+     * @param uriPattern The uri pattern to add as a deep link
+     * @see NavController#onHandleDeepLink(Intent)
+     */
+    public void addDeepLink(@NonNull String uriPattern) {
+        if (mDeepLinks == null) {
+            mDeepLinks = new ArrayList<>();
+        }
+        mDeepLinks.add(new NavDeepLink(uriPattern));
+    }
+
+    /**
+     * Determines if this NavDestination has a deep link matching the given Uri.
+     * @param uri The Uri to match against all deep links added in {@link #addDeepLink(String)}
+     * @return The matching {@link NavDestination} and the appropriate {@link Bundle} of arguments
+     * extracted from the Uri, or null if no match was found.
+     */
+    @Nullable
+    Pair<NavDestination, Bundle> matchDeepLink(@NonNull Uri uri) {
+        if (mDeepLinks == null) {
+            return null;
+        }
+        for (NavDeepLink deepLink : mDeepLinks) {
+            Bundle matchingArguments = deepLink.getMatchingArguments(uri);
+            if (matchingArguments != null) {
+                return Pair.create(this, matchingArguments);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Build an array containing the hierarchy from the root down to this destination.
+     *
+     * @return An array containing all of the ids from the root to this destination
+     */
+    @NonNull
+    int[] buildDeepLinkIds() {
+        ArrayDeque<NavDestination> hierarchy = new ArrayDeque<>();
+        hierarchy.add(this);
+        while (hierarchy.peekFirst().getParent() != null) {
+            hierarchy.addFirst(hierarchy.peekFirst().getParent());
+        }
+        int[] deepLinkIds = new int[hierarchy.size()];
+        int index = 0;
+        for (NavDestination destination : hierarchy) {
+            deepLinkIds[index++] = destination.getId();
+        }
+        return deepLinkIds;
+    }
+
+    /**
+     * Returns the destination ID for a given action. This will recursively check the
+     * {@link #getParent() parent} of this destination if the action destination is not found in
+     * this destination.
+     *
+     * @param id action ID to fetch
+     * @return destination ID mapped to the given action id, or 0 if none
+     */
+    @Nullable
+    public NavAction getAction(@IdRes int id) {
+        NavAction destination = mActions == null ? null : mActions.get(id);
+        // Search the parent for the given action if it is not found in this destination
+        return destination != null
+                ? destination
+                : getParent() != null ? getParent().getAction(id) : null;
+    }
+
+    /**
+     * Sets a destination ID for an action ID.
+     *
+     * @param actionId action ID to bind
+     * @param destId destination ID for the given action
+     */
+    public void putAction(@IdRes int actionId, @IdRes int destId) {
+        putAction(actionId, new NavAction(destId));
+    }
+
+    /**
+     * Sets a destination ID for an action ID.
+     *
+     * @param actionId action ID to bind
+     * @param action action to associate with this action ID
+     */
+    public void putAction(@IdRes int actionId, @NonNull NavAction action) {
+        if (actionId == 0) {
+            throw new IllegalArgumentException("Cannot have an action with actionId 0");
+        }
+        if (mActions == null) {
+            mActions = new SparseArrayCompat<>();
+        }
+        mActions.put(actionId, action);
+    }
+
+    /**
+     * Unsets the destination ID for an action ID.
+     *
+     * @param actionId action ID to remove
+     */
+    public void removeAction(@IdRes int actionId) {
+        if (mActions == null) {
+            return;
+        }
+        mActions.delete(actionId);
+    }
+
+    /**
+     * Navigates to this destination.
+     *
+     * <p>Uses the {@link #getNavigator() configured navigator} to navigate to this destination.
+     * Apps should not call this directly, instead use {@link NavController}'s navigation methods
+     * to ensure consistent back stack tracking and behavior.</p>
+     *
+     * @param args arguments to the new destination
+     * @param navOptions options for navigation
+     */
+    public void navigate(@Nullable Bundle args, @Nullable NavOptions navOptions) {
+        Bundle defaultArgs = getDefaultArguments();
+        Bundle finalArgs = new Bundle();
+        finalArgs.putAll(defaultArgs);
+        if (args != null) {
+            finalArgs.putAll(args);
+        }
+        //noinspection unchecked
+        mNavigator.navigate(this, finalArgs, navOptions);
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavDirections.java b/navigation/common/src/main/java/androidx/navigation/NavDirections.java
new file mode 100644
index 0000000..30422ac
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavDirections.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.Nullable;
+
+/**
+ * An interface that describes a navigation operation: action's id and arguments
+ */
+public interface NavDirections {
+
+    /**
+     * Returns a action id to navigate with.
+     *
+     * @return id of an action
+     */
+    @IdRes
+    int getActionId();
+
+    /**
+     * Returns arguments to pass to the destination
+     */
+    @Nullable
+    Bundle getArguments();
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavGraph.java b/navigation/common/src/main/java/androidx/navigation/NavGraph.java
new file mode 100644
index 0000000..4fc681d
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavGraph.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.Pair;
+import android.support.v4.util.SparseArrayCompat;
+import android.util.AttributeSet;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import androidx.navigation.common.R;
+
+/**
+ * NavGraph is a collection of {@link NavDestination} nodes fetchable by ID.
+ *
+ * <p>A NavGraph serves as a 'virtual' destination: while the NavGraph itself will not appear
+ * on the back stack, navigating to the NavGraph will cause the
+ * {@link #getStartDestination starting destination} to be added to the back stack.</p>
+ */
+public class NavGraph extends NavDestination implements Iterable<NavDestination> {
+    private final SparseArrayCompat<NavDestination> mNodes = new SparseArrayCompat<>();
+    private int mStartDestId;
+
+    /**
+     * Construct a new NavGraph. This NavGraph is not valid until you
+     * {@link #addDestination(NavDestination) add a destination} and
+     * {@link #setStartDestination(int) set the starting destination}.
+     *
+     * @param navigatorProvider The {@link NavController} which this NavGraph
+     *                          will be associated with.
+     */
+    public NavGraph(@NonNull NavigatorProvider navigatorProvider) {
+        this(navigatorProvider.getNavigator(NavGraphNavigator.class));
+    }
+
+    /**
+     * Construct a new NavGraph. This NavGraph is not valid until you
+     * {@link #addDestination(NavDestination) add a destination} and
+     * {@link #setStartDestination(int) set the starting destination}.
+     *
+     * @param navGraphNavigator The {@link NavGraphNavigator} which this destination
+     *                          will be associated with. Generally retrieved via a
+     *                          {@link NavController}'s
+     *                          {@link NavigatorProvider#getNavigator(Class)} method.
+     */
+    public NavGraph(@NonNull Navigator<? extends NavGraph> navGraphNavigator) {
+        super(navGraphNavigator);
+    }
+
+    @Override
+    public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
+        super.onInflate(context, attrs);
+        TypedArray a = context.getResources().obtainAttributes(attrs,
+                R.styleable.NavGraphNavigator);
+        setStartDestination(
+                a.getResourceId(R.styleable.NavGraphNavigator_startDestination, 0));
+        a.recycle();
+    }
+
+    @Override
+    @Nullable
+    Pair<NavDestination, Bundle> matchDeepLink(@NonNull Uri uri) {
+        // First search through any deep links directly added to this NavGraph
+        Pair<NavDestination, Bundle> result = super.matchDeepLink(uri);
+        if (result != null) {
+            return result;
+        }
+        // Then search through all child destinations for a matching deep link
+        for (NavDestination child : this) {
+            Pair<NavDestination, Bundle> childResult = child.matchDeepLink(uri);
+            if (childResult != null) {
+                return childResult;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds a destination to this NavGraph. The destination must have an
+     * {@link NavDestination#getId()} id} set.
+     *
+     * <p>The destination must not have a {@link NavDestination#getParent() parent} set. If
+     * the destination is already part of a {@link NavGraph navigation graph}, call
+     * {@link #remove(NavDestination)} before calling this method.</p>
+     *
+     * @param node destination to add
+     */
+    public void addDestination(@NonNull NavDestination node) {
+        if (node.getId() == 0) {
+            throw new IllegalArgumentException("Destinations must have an id."
+                    + " Call setId() or include an android:id in your navigation XML.");
+        }
+        NavDestination existingDestination = mNodes.get(node.getId());
+        if (existingDestination == node) {
+            return;
+        }
+        if (node.getParent() != null) {
+            throw new IllegalStateException("Destination already has a parent set."
+                    + " Call NavGraph.remove() to remove the previous parent.");
+        }
+        if (existingDestination != null) {
+            existingDestination.setParent(null);
+        }
+        node.setParent(this);
+        mNodes.put(node.getId(), node);
+    }
+
+    /**
+     * Adds multiple destinations to this NavGraph. Each destination must have an
+     * {@link NavDestination#getId()} id} set.
+     *
+     * <p> Each destination must not have a {@link NavDestination#getParent() parent} set. If
+     * any destination is already part of a {@link NavGraph navigation graph}, call
+     * {@link #remove(NavDestination)} before calling this method.</p>
+     *
+     * @param nodes destinations to add
+     */
+    public void addDestinations(@NonNull Collection<NavDestination> nodes) {
+        for (NavDestination node : nodes) {
+            if (node == null) {
+                continue;
+            }
+            addDestination(node);
+        }
+    }
+
+    /**
+     * Adds multiple destinations to this NavGraph. Each destination must have an
+     * {@link NavDestination#getId()} id} set.
+     *
+     * <p> Each destination must not have a {@link NavDestination#getParent() parent} set. If
+     * any destination is already part of a {@link NavGraph navigation graph}, call
+     * {@link #remove(NavDestination)} before calling this method.</p>
+     *
+     * @param nodes destinations to add
+     */
+    public void addDestinations(@NonNull NavDestination... nodes) {
+        for (NavDestination node : nodes) {
+            if (node == null) {
+                continue;
+            }
+            addDestination(node);
+        }
+    }
+
+    /**
+     * Finds a destination in the collection by ID. This will recursively check the
+     * {@link #getParent() parent} of this navigation graph if node is not found in
+     * this navigation graph.
+     *
+     * @param resid ID to locate
+     * @return the node with ID resid
+     */
+    public NavDestination findNode(@IdRes int resid) {
+        return findNode(resid, true);
+    }
+
+    NavDestination findNode(@IdRes int resid, boolean searchParents) {
+        NavDestination destination = mNodes.get(resid);
+        // Search the parent for the NavDestination if it is not a child of this navigation graph
+        // and searchParents is true
+        return destination != null
+                ? destination
+                : searchParents && getParent() != null ? getParent().findNode(resid) : null;
+    }
+
+    @Override
+    public Iterator<NavDestination> iterator() {
+        return new Iterator<NavDestination>() {
+            private int mIndex = -1;
+            private boolean mWentToNext = false;
+
+            @Override
+            public boolean hasNext() {
+                return mIndex + 1 < mNodes.size();
+            }
+
+            @Override
+            public NavDestination next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                mWentToNext = true;
+                return mNodes.valueAt(++mIndex);
+            }
+
+            @Override
+            public void remove() {
+                if (!mWentToNext) {
+                    throw new IllegalStateException(
+                            "You must call next() before you can remove an element");
+                }
+                mNodes.valueAt(mIndex).setParent(null);
+                mNodes.removeAt(mIndex);
+                mIndex--;
+                mWentToNext = false;
+            }
+        };
+    }
+
+    /**
+     * Add all destinations from another collection to this one. As each destination has at most
+     * one parent, the destinations will be removed from the given NavGraph.
+     *
+     * @param other collection of destinations to add. All destinations will be removed from this
+     * graph after being added to this graph.
+     */
+    public void addAll(@NonNull NavGraph other) {
+        Iterator<NavDestination> iterator = other.iterator();
+        while (iterator.hasNext()) {
+            NavDestination destination = iterator.next();
+            iterator.remove();
+            addDestination(destination);
+        }
+    }
+
+    /**
+     * Remove a given destination from this NavGraph
+     *
+     * @param node the destination to remove.
+     */
+    public void remove(@NonNull NavDestination node) {
+        int index = mNodes.indexOfKey(node.getId());
+        if (index >= 0) {
+            mNodes.valueAt(index).setParent(null);
+            mNodes.removeAt(index);
+        }
+    }
+
+    /**
+     * Clear all destinations from this navigation graph.
+     */
+    public void clear() {
+        Iterator<NavDestination> iterator = iterator();
+        while (iterator.hasNext()) {
+            iterator.next();
+            iterator.remove();
+        }
+    }
+
+    /**
+     * Returns the starting destination for this NavGraph. When navigating to the NavGraph, this
+     * destination is the one the user will initially see.
+     * @return
+     */
+    @IdRes
+    public int getStartDestination() {
+        return mStartDestId;
+    }
+
+    /**
+     * Sets the starting destination for this NavGraph.
+     *
+     * @param startDestId The id of the destination to be shown when navigating to this NavGraph.
+     */
+    public void setStartDestination(@IdRes int startDestId) {
+        mStartDestId = startDestId;
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavGraphNavigator.java b/navigation/common/src/main/java/androidx/navigation/NavGraphNavigator.java
new file mode 100644
index 0000000..99cf2f2
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavGraphNavigator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * A Navigator built specifically for {@link NavGraph} elements. Handles navigating to the
+ * correct destination when the NavGraph is the target of navigation actions.
+ */
+@Navigator.Name("navigation")
+public class NavGraphNavigator extends Navigator<NavGraph> {
+    private Context mContext;
+
+    /**
+     * Construct a Navigator capable of routing incoming navigation requests to the proper
+     * destination within a {@link NavGraph}.
+     * @param context
+     */
+    public NavGraphNavigator(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Creates a new {@link NavGraph} associated with this navigator.
+     * @return
+     */
+    @NonNull
+    @Override
+    public NavGraph createDestination() {
+        return new NavGraph(this);
+    }
+
+    @Override
+    public void navigate(@NonNull NavGraph destination, @Nullable Bundle args,
+            @Nullable NavOptions navOptions) {
+        int startId = destination.getStartDestination();
+        if (startId == 0) {
+            throw new IllegalStateException("no start destination defined via"
+                    + " app:startDestination for "
+                    + (destination.getId() != 0
+                            ? NavDestination.getDisplayName(mContext, destination.getId())
+                            : "the root navigation"));
+        }
+        NavDestination startDestination = destination.findNode(startId, false);
+        if (startDestination == null) {
+            final String dest = NavDestination.getDisplayName(mContext, startId);
+            throw new IllegalArgumentException("navigation destination " + dest
+                    + " is not a direct child of this NavGraph");
+        }
+        dispatchOnNavigatorNavigated(destination.getId(), BACK_STACK_DESTINATION_ADDED);
+        startDestination.navigate(args, navOptions);
+    }
+
+    @Override
+    public boolean popBackStack() {
+        return false;
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavOptions.java b/navigation/common/src/main/java/androidx/navigation/NavOptions.java
new file mode 100644
index 0000000..9439128
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavOptions.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.AnimRes;
+import android.support.annotation.AnimatorRes;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * NavOptions stores special options for navigate actions
+ */
+public class NavOptions {
+    static final int LAUNCH_SINGLE_TOP = 0x1;
+    static final int LAUNCH_DOCUMENT = 0x2;
+    static final int LAUNCH_CLEAR_TASK = 0x4;
+
+    private static final String KEY_NAV_OPTIONS = "android-support-nav:navOptions";
+    private static final String KEY_LAUNCH_MODE = "launchMode";
+    private static final String KEY_POP_UP_TO = "popUpTo";
+    private static final String KEY_POP_UP_TO_INCLUSIVE = "popUpToInclusive";
+    private static final String KEY_ENTER_ANIM = "enterAnim";
+    private static final String KEY_EXIT_ANIM = "exitAnim";
+    private static final String KEY_POP_ENTER_ANIM = "popEnterAnim";
+    private static final String KEY_POP_EXIT_ANIM = "popExitAnim";
+
+    /**
+     * Add the {@link #getPopEnterAnim() pop enter} and {@link #getPopExitAnim() pop exit}
+     * animation to an Intent for later usage with
+     * {@link #applyPopAnimationsToPendingTransition(Activity)}.
+     * <p>
+     * This is automatically called for you by {@link ActivityNavigator}.
+     * </p>
+     *
+     * @param intent Intent being started with the given NavOptions
+     * @param navOptions NavOptions containing the pop animations.
+     * @see #applyPopAnimationsToPendingTransition(Activity)
+     * @see #getPopEnterAnim()
+     * @see #getPopExitAnim()
+     */
+    public static void addPopAnimationsToIntent(@NonNull Intent intent,
+            @Nullable NavOptions navOptions) {
+        if (navOptions != null) {
+            intent.putExtra(KEY_NAV_OPTIONS, navOptions.toBundle());
+        }
+    }
+
+    /**
+     * Apply any pop animations in the Intent of the given Activity to a pending transition.
+     * This should be used in place of  {@link Activity#overridePendingTransition(int, int)}
+     * to get the appropriate pop animations.
+     * @param activity An activity started from the {@link ActivityNavigator}.
+     * @see #addPopAnimationsToIntent(Intent, NavOptions)
+     * @see #getPopEnterAnim()
+     * @see #getPopExitAnim()
+     */
+    public static void applyPopAnimationsToPendingTransition(@NonNull Activity activity) {
+        Intent intent = activity.getIntent();
+        if (intent == null) {
+            return;
+        }
+        Bundle bundle = intent.getBundleExtra(KEY_NAV_OPTIONS);
+        if (bundle != null) {
+            NavOptions navOptions = NavOptions.fromBundle(bundle);
+            int popEnterAnim = navOptions.getPopEnterAnim();
+            int popExitAnim = navOptions.getPopExitAnim();
+            if (popEnterAnim != -1 || popExitAnim != -1) {
+                popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
+                popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
+                activity.overridePendingTransition(popEnterAnim, popExitAnim);
+            }
+        }
+    }
+
+    private int mLaunchMode;
+    @IdRes
+    private int mPopUpTo;
+    private boolean mPopUpToInclusive;
+    @AnimRes @AnimatorRes
+    private int mEnterAnim;
+    @AnimRes @AnimatorRes
+    private int mExitAnim;
+    @AnimRes @AnimatorRes
+    private int mPopEnterAnim;
+    @AnimRes @AnimatorRes
+    private int mPopExitAnim;
+
+    NavOptions(int launchMode, @IdRes int popUpTo, boolean popUpToInclusive,
+            @AnimRes @AnimatorRes int enterAnim, @AnimRes @AnimatorRes int exitAnim,
+            @AnimRes @AnimatorRes int popEnterAnim, @AnimRes @AnimatorRes int popExitAnim) {
+        mLaunchMode = launchMode;
+        mPopUpTo = popUpTo;
+        mPopUpToInclusive = popUpToInclusive;
+        mEnterAnim = enterAnim;
+        mExitAnim = exitAnim;
+        mPopEnterAnim = popEnterAnim;
+        mPopExitAnim = popExitAnim;
+    }
+
+    /**
+     * Whether this navigation action should launch as single-top (i.e., there will be at most
+     * one copy of a given destination on the top of the back stack).
+     * <p>
+     * This functions similarly to how {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
+     * works with activites.
+     */
+    public boolean shouldLaunchSingleTop() {
+        return (mLaunchMode & LAUNCH_SINGLE_TOP) != 0;
+    }
+
+    /**
+     * Whether this navigation action should launch the destination in a new document.
+     * <p>
+     * This functions similarly to how {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}
+     * works with activites.
+     */
+    public boolean shouldLaunchDocument() {
+        return (mLaunchMode & LAUNCH_DOCUMENT) != 0;
+    }
+
+    /**
+     * Whether this navigation action should clear the entire back stack
+     * <p>
+     * This functions similarly to how {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK}
+     * works with activites.
+     */
+    public boolean shouldClearTask() {
+        return (mLaunchMode & LAUNCH_CLEAR_TASK) != 0;
+    }
+
+    /**
+     * The destination to pop up to before navigating. When set, all non-matching destinations
+     * should be popped from the back stack.
+     * @return the destinationId to pop up to, clearing all intervening destinations
+     * @see Builder#setPopUpTo
+     * @see #isPopUpToInclusive
+     */
+    @IdRes
+    public int getPopUpTo() {
+        return mPopUpTo;
+    }
+
+    /**
+     * Whether the destination set in {@link #getPopUpTo} should be popped from the back stack.
+     * @see Builder#setPopUpTo
+     * @see #getPopUpTo
+     */
+    public boolean isPopUpToInclusive() {
+        return mPopUpToInclusive;
+    }
+
+    /**
+     * The custom enter Animation/Animator that should be run.
+     * @return the resource id of a Animation or Animator or -1 if none.
+     */
+    @AnimRes @AnimatorRes
+    public int getEnterAnim() {
+        return mEnterAnim;
+    }
+
+    /**
+     * The custom exit Animation/Animator that should be run.
+     * @return the resource id of a Animation or Animator or -1 if none.
+     */
+    @AnimRes @AnimatorRes
+    public int getExitAnim() {
+        return mExitAnim;
+    }
+
+    /**
+     * The custom enter Animation/Animator that should be run when this destination is
+     * popped from the back stack.
+     * @return the resource id of a Animation or Animator or -1 if none.
+     * @see #applyPopAnimationsToPendingTransition(Activity)
+     */
+    @AnimRes @AnimatorRes
+    public int getPopEnterAnim() {
+        return mPopEnterAnim;
+    }
+
+    /**
+     * The custom exit Animation/Animator that should be run when this destination is
+     * popped from the back stack.
+     * @return the resource id of a Animation or Animator or -1 if none.
+     * @see #applyPopAnimationsToPendingTransition(Activity)
+     */
+    @AnimRes @AnimatorRes
+    public int getPopExitAnim() {
+        return mPopExitAnim;
+    }
+
+    @NonNull
+    private Bundle toBundle() {
+        Bundle b = new Bundle();
+        b.putInt(KEY_LAUNCH_MODE, mLaunchMode);
+        b.putInt(KEY_POP_UP_TO, mPopUpTo);
+        b.putBoolean(KEY_POP_UP_TO_INCLUSIVE, mPopUpToInclusive);
+        b.putInt(KEY_ENTER_ANIM, mEnterAnim);
+        b.putInt(KEY_EXIT_ANIM, mExitAnim);
+        b.putInt(KEY_POP_ENTER_ANIM, mPopEnterAnim);
+        b.putInt(KEY_POP_EXIT_ANIM, mPopExitAnim);
+        return b;
+    }
+
+    @NonNull
+    private static NavOptions fromBundle(@NonNull Bundle b) {
+        return new NavOptions(b.getInt(KEY_LAUNCH_MODE, 0),
+                b.getInt(KEY_POP_UP_TO, 0), b.getBoolean(KEY_POP_UP_TO_INCLUSIVE, false),
+                b.getInt(KEY_ENTER_ANIM, -1), b.getInt(KEY_EXIT_ANIM, -1),
+                b.getInt(KEY_POP_ENTER_ANIM, -1), b.getInt(KEY_POP_EXIT_ANIM, -1));
+    }
+
+    /**
+     * Builder for constructing new instances of NavOptions.
+     */
+    public static class Builder {
+        int mLaunchMode;
+        @IdRes
+        int mPopUpTo;
+        boolean mPopUpToInclusive;
+        @AnimRes @AnimatorRes
+        int mEnterAnim = -1;
+        @AnimRes @AnimatorRes
+        int mExitAnim = -1;
+        @AnimRes @AnimatorRes
+        int mPopEnterAnim = -1;
+        @AnimRes @AnimatorRes
+        int mPopExitAnim = -1;
+
+        public Builder() {
+        }
+
+        /**
+         * Launch a navigation target as single-top if you are making a lateral navigation
+         * between instances of the same target (e.g. detail pages about similar data items)
+         * that should not preserve history.
+         *
+         * @param singleTop true to launch as single-top
+         */
+        @NonNull
+        public Builder setLaunchSingleTop(boolean singleTop) {
+            if (singleTop) {
+                mLaunchMode |= LAUNCH_SINGLE_TOP;
+            } else {
+                mLaunchMode &= ~LAUNCH_SINGLE_TOP;
+            }
+            return this;
+        }
+
+        /**
+         * Launch a navigation target as a document if you want it to appear as its own
+         * entry in the system Overview screen. If the same document is launched multiple times
+         * it will not create a new task, it will bring the existing document task to the front.
+         *
+         * <p>If the user presses the system Back key from a new document task they will land
+         * on their previous task. If the user reached the document task from the system Overview
+         * screen they will be taken to their home screen.</p>
+         *
+         * @param launchDocument true to launch a new document task
+         */
+        @NonNull
+        public Builder setLaunchDocument(boolean launchDocument) {
+            if (launchDocument) {
+                mLaunchMode |= LAUNCH_DOCUMENT;
+            } else {
+                mLaunchMode &= ~LAUNCH_DOCUMENT;
+            }
+            return this;
+        }
+
+        /**
+         * Clear the entire task before launching this target. If you are launching as a
+         * {@link #setLaunchDocument(boolean) document}, this will clear the document task.
+         * Otherwise it will clear the current task.
+         *
+         * @param clearTask
+         * @return
+         */
+        @NonNull
+        public Builder setClearTask(boolean clearTask) {
+            if (clearTask) {
+                mLaunchMode |= LAUNCH_CLEAR_TASK;
+            } else {
+                mLaunchMode &= ~LAUNCH_CLEAR_TASK;
+            }
+            return this;
+        }
+
+        /**
+         * Pop up to a given destination before navigating. This pops all non-matching destinations
+         * from the back stack until this destination is found.
+         *
+         * @param destinationId The destination to pop up to, clearing all intervening destinations.
+         * @param inclusive true to also pop the given destination from the back stack.
+         * @return this Builder
+         * @see NavOptions#getPopUpTo
+         * @see NavOptions#isPopUpToInclusive
+         */
+        @NonNull
+        public Builder setPopUpTo(@IdRes int destinationId, boolean inclusive) {
+            mPopUpTo = destinationId;
+            mPopUpToInclusive = inclusive;
+            return this;
+        }
+
+        /**
+         * Sets a custom Animation or Animator resource for the enter animation.
+         *
+         * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
+         * @param enterAnim Custom animation to run
+         * @return this Builder
+         * @see NavOptions#getEnterAnim()
+         */
+        public Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim) {
+            mEnterAnim = enterAnim;
+            return this;
+        }
+
+        /**
+         * Sets a custom Animation or Animator resource for the exit animation.
+         *
+         * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
+         * @param exitAnim Custom animation to run
+         * @return this Builder
+         * @see NavOptions#getExitAnim()
+         */
+        @NonNull
+        public Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim) {
+            mExitAnim = exitAnim;
+            return this;
+        }
+
+        /**
+         * Sets a custom Animation or Animator resource for the enter animation
+         * when popping off the back stack.
+         *
+         * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
+         * @param popEnterAnim Custom animation to run
+         * @return this Builder
+         * @see NavOptions#getPopEnterAnim()
+         */
+        @NonNull
+        public Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim) {
+            mPopEnterAnim = popEnterAnim;
+            return this;
+        }
+
+        /**
+         * Sets a custom Animation or Animator resource for the exit animation
+         * when popping off the back stack.
+         *
+         * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
+         * @param popExitAnim Custom animation to run
+         * @return this Builder
+         * @see NavOptions#getPopExitAnim()
+         */
+        @NonNull
+        public Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim) {
+            mPopExitAnim = popExitAnim;
+            return this;
+        }
+
+        /**
+         * @return a constructed NavOptions
+         */
+        @NonNull
+        public NavOptions build() {
+            return new NavOptions(mLaunchMode, mPopUpTo, mPopUpToInclusive,
+                    mEnterAnim, mExitAnim, mPopEnterAnim, mPopExitAnim);
+        }
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/Navigator.java b/navigation/common/src/main/java/androidx/navigation/Navigator.java
new file mode 100644
index 0000000..f66acf5
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/Navigator.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Navigator defines a mechanism for navigating within an app.
+ *
+ * <p>Each Navigator sets the policy for a specific type of navigation, e.g.
+ * {@link ActivityNavigator} knows how to launch into {@link NavDestination destinations}
+ * backed by activities using {@link Context#startActivity(Intent) startActivity}.</p>
+ *
+ * <p>Navigators should be able to manage their own back stack when navigating between two
+ * destinations that belong to that navigator. The {@link NavController} manages a back stack of
+ * navigators representing the current navigation stack across all navigators.</p>
+ *
+ * <p>Each Navigator should add the {@link Name Navigator.Name annotation} to their class. Any
+ * custom attributes used by the associated {@link NavDestination destination} subclass should
+ * have a name corresponding with the name of the Navigator, e.g., {@link ActivityNavigator} uses
+ * <code>&lt;declare-styleable name="ActivityNavigator"&gt;</code></p>
+ *
+ * @param <D> the subclass of {@link NavDestination} used with this Navigator which can be used
+ *           to hold any special data that will be needed to navigate to that destination.
+ *           Examples include information about an intent to navigate to other activities,
+ *           or a fragment class name to instantiate and swap to a new fragment.
+ */
+public abstract class Navigator<D extends NavDestination> {
+    /**
+     * This annotation should be added to each Navigator subclass to denote the default name used
+     * to register the Navigator with a {@link NavigatorProvider}.
+     *
+     * @see NavigatorProvider#addNavigator(Navigator)
+     * @see NavigatorProvider#getNavigator(Class)
+     */
+    @Retention(RUNTIME)
+    @Target({TYPE})
+    public @interface Name {
+        String value();
+    }
+
+    @Retention(SOURCE)
+    @IntDef({BACK_STACK_UNCHANGED, BACK_STACK_DESTINATION_ADDED, BACK_STACK_DESTINATION_POPPED})
+    @interface BackStackEffect {}
+
+    /**
+     * Indicator that the navigation event should not change the {@link NavController}'s back stack.
+     *
+     * <p>For example, a {@link NavOptions#shouldLaunchSingleTop() single top} navigation event may
+     * not result in a back stack change if the existing destination is on the top of the stack.</p>
+     *
+     * @see #dispatchOnNavigatorNavigated
+     */
+    public static final int BACK_STACK_UNCHANGED = 0;
+
+    /**
+     * Indicator that the navigation event has added a new entry to the back stack. Only
+     * destinations added with this flag will be handled by {@link NavController#navigateUp()}.
+     *
+     * @see #dispatchOnNavigatorNavigated
+     */
+    public static final int BACK_STACK_DESTINATION_ADDED = 1;
+
+    /**
+     * Indicator that the navigation event has popped an entry off the back stack.
+     *
+     * @see #dispatchOnNavigatorNavigated
+     */
+    public static final int BACK_STACK_DESTINATION_POPPED = 2;
+
+    private final CopyOnWriteArrayList<OnNavigatorNavigatedListener> mOnNavigatedListeners =
+            new CopyOnWriteArrayList<>();
+
+    /**
+     * Construct a new NavDestination associated with this Navigator.
+     *
+     * <p>Any initialization of the destination should be done in the destination's constructor as
+     * it is not guaranteed that every destination will be created through this method.</p>
+     * @return a new NavDestination
+     */
+    @NonNull
+    public abstract D createDestination();
+
+    /**
+     * Navigate to a destination.
+     *
+     * <p>Requests navigation to a given destination associated with this navigator in
+     * the navigation graph. This method generally should not be called directly;
+     * {@link NavController} will delegate to it when appropriate.</p>
+     *
+     * <p>Implementations should {@link #dispatchOnNavigatorNavigated} to notify
+     * listeners of the resulting navigation destination.</p>
+     *
+     * @param destination destination node to navigate to
+     * @param args arguments to use for navigation
+     * @param navOptions additional options for navigation
+     */
+    public abstract void navigate(@NonNull D destination, @Nullable Bundle args,
+                                     @Nullable NavOptions navOptions);
+
+    /**
+     * Attempt to pop this navigator's back stack, performing the appropriate navigation.
+     *
+     * <p>Implementations should {@link #dispatchOnNavigatorNavigated} to notify
+     * listeners of the resulting navigation destination and return {@code true} if navigation
+     * was successful. Implementations should return {@code false} if navigation could not
+     * be performed, for example if the navigator's back stack was empty.</p>
+     *
+     * @return {@code true} if pop was successful
+     */
+    public abstract boolean popBackStack();
+
+    /**
+     * Add a listener to be notified when this navigator changes navigation destinations.
+     *
+     * <p>Most application code should use
+     * {@link NavController#addOnNavigatedListener(NavController.OnNavigatedListener)} instead.
+     * </p>
+     *
+     * @param listener listener to add
+     */
+    public final void addOnNavigatorNavigatedListener(
+            @NonNull OnNavigatorNavigatedListener listener) {
+        mOnNavigatedListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener so that it will no longer be notified when this navigator changes
+     * navigation destinations.
+     *
+     * @param listener listener to remove
+     */
+    public final void removeOnNavigatorNavigatedListener(
+            @NonNull OnNavigatorNavigatedListener listener) {
+        mOnNavigatedListeners.remove(listener);
+    }
+
+    /**
+     * Dispatch a navigated event to all registered {@link OnNavigatorNavigatedListener listeners}.
+     * Utility for navigator implementations.
+     *
+     * @param destId id of the new destination
+     * @param backStackEffect how the navigation event affects the back stack
+     */
+    public final void dispatchOnNavigatorNavigated(@IdRes int destId,
+            @BackStackEffect int backStackEffect) {
+        for (OnNavigatorNavigatedListener listener : mOnNavigatedListeners) {
+            listener.onNavigatorNavigated(this, destId, backStackEffect);
+        }
+    }
+
+    /**
+     * Listener for observing navigation events for this specific navigator. Most app code
+     * should use {@link NavController.OnNavigatedListener} instead.
+     */
+    public interface OnNavigatorNavigatedListener {
+        /**
+         * This method is called after the Navigator navigates to a new destination.
+         *
+         * @param navigator
+         * @param destId
+         * @param backStackEffect
+         */
+        void onNavigatorNavigated(@NonNull Navigator navigator, @IdRes int destId,
+                @BackStackEffect int backStackEffect);
+    }
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/NavigatorProvider.java b/navigation/common/src/main/java/androidx/navigation/NavigatorProvider.java
new file mode 100644
index 0000000..792c894
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/NavigatorProvider.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+/**
+ * A NavigationProvider stores a set of {@link Navigator}s that are valid ways to navigate
+ * to a destination.
+ */
+@SuppressLint("TypeParameterUnusedInFormals")
+public interface NavigatorProvider {
+    /**
+     * Retrieves a registered {@link Navigator} using the name provided by the
+     * {@link Navigator.Name Navigator.Name annotation}.
+     *
+     * @param navigatorClass class of the navigator to return
+     * @return the registered navigator with the given {@link Navigator.Name}
+     *
+     * @throws IllegalArgumentException if the Navigator does not have a
+     * {@link Navigator.Name Navigator.Name annotation}
+     * @throws IllegalStateException if the Navigator has not been added
+     *
+     * @see #addNavigator(Navigator)
+     */
+    @NonNull
+    <D extends NavDestination, T extends Navigator<? extends D>> T getNavigator(
+            @NonNull Class<T> navigatorClass);
+
+    /**
+     * Retrieves a registered {@link Navigator} by name.
+     *
+     * @param name name of the navigator to return
+     * @return the registered navigator with the given name
+     *
+     * @throws IllegalStateException if the Navigator has not been added
+     *
+     * @see #addNavigator(String, Navigator)
+     */
+    @NonNull
+    <D extends NavDestination, T extends Navigator<? extends D>> T getNavigator(
+            @NonNull String name);
+
+    /**
+     * Register a navigator using the name provided by the
+     * {@link Navigator.Name Navigator.Name annotation}. {@link NavDestination destinations} may
+     * refer to any registered navigator by name for inflation. If a navigator by this name is
+     * already registered, this new navigator will replace it.
+     *
+     * @param navigator navigator to add
+     * @return the previously added Navigator for the name provided by the
+     * {@link Navigator.Name Navigator.Name annotation}, if any
+     */
+    @Nullable
+    Navigator<? extends NavDestination> addNavigator(
+            @NonNull Navigator<? extends NavDestination> navigator);
+
+    /**
+     * Register a navigator by name. {@link NavDestination destinations} may refer to any
+     * registered navigator by name for inflation. If a navigator by this name is already
+     * registered, this new navigator will replace it.
+     *
+     * @param name name for this navigator
+     * @param navigator navigator to add
+     * @return the previously added Navigator for the given name, if any
+     */
+    @Nullable
+    Navigator<? extends NavDestination> addNavigator(@NonNull String name,
+            @NonNull Navigator<? extends NavDestination> navigator);
+}
diff --git a/navigation/common/src/main/java/androidx/navigation/SimpleNavigatorProvider.java b/navigation/common/src/main/java/androidx/navigation/SimpleNavigatorProvider.java
new file mode 100644
index 0000000..ef1f222
--- /dev/null
+++ b/navigation/common/src/main/java/androidx/navigation/SimpleNavigatorProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+
+import java.util.HashMap;
+
+/**
+ * Simple implementation of a {@link NavigatorProvider} that stores instances of
+ * {@link Navigator navigators} by name, using the {@link Navigator.Name} when given a class name.
+ *
+ * @hide
+ */
+@SuppressLint("TypeParameterUnusedInFormals")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SimpleNavigatorProvider implements NavigatorProvider {
+    private static final HashMap<Class, String> sAnnotationNames = new HashMap<>();
+
+    private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
+            new HashMap<>();
+
+    @NonNull
+    private String getNameForNavigator(@NonNull Class<? extends Navigator> navigatorClass) {
+        String name = sAnnotationNames.get(navigatorClass);
+        if (name == null) {
+            Navigator.Name annotation = navigatorClass.getAnnotation(Navigator.Name.class);
+            name = annotation != null ? annotation.value() : null;
+            if (!validateName(name)) {
+                throw new IllegalArgumentException("No @Navigator.Name annotation found for "
+                        + navigatorClass.getSimpleName());
+            }
+            sAnnotationNames.put(navigatorClass, name);
+        }
+        return name;
+    }
+
+    @NonNull
+    @Override
+    public <D extends NavDestination, T extends Navigator<? extends D>> T getNavigator(
+            @NonNull Class<T> navigatorClass) {
+        String name = getNameForNavigator(navigatorClass);
+        return getNavigator(name);
+    }
+
+    @NonNull
+    @Override
+    public <D extends NavDestination, T extends Navigator<? extends D>> T getNavigator(
+            @NonNull String name) {
+        if (!validateName(name)) {
+            throw new IllegalArgumentException("navigator name cannot be an empty string");
+        }
+
+        Navigator<? extends NavDestination> navigator = mNavigators.get(name);
+        if (navigator == null) {
+            throw new IllegalStateException("Could not find Navigator with name \"" + name
+                    + "\". You must call NavController.addNavigator() for each navigation type.");
+        }
+        //noinspection unchecked
+        return (T) navigator;
+    }
+
+    @Nullable
+    @Override
+    public Navigator<? extends NavDestination> addNavigator(
+            @NonNull Navigator<? extends NavDestination> navigator) {
+        String name = getNameForNavigator(navigator.getClass());
+
+        return addNavigator(name, navigator);
+    }
+
+    @Nullable
+    @Override
+    public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
+            @NonNull Navigator<? extends NavDestination> navigator) {
+        if (!validateName(name)) {
+            throw new IllegalArgumentException("navigator name cannot be an empty string");
+        }
+        return mNavigators.put(name, navigator);
+    }
+
+    private boolean validateName(String name) {
+        return name != null && !name.isEmpty();
+    }
+}
diff --git a/navigation/common/src/main/res/values/attrs.xml b/navigation/common/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..2701cd6
--- /dev/null
+++ b/navigation/common/src/main/res/values/attrs.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <declare-styleable name="Navigator">
+        <attr name="android:id"/>
+        <attr name="android:label" />
+    </declare-styleable>
+
+    <declare-styleable name="NavGraphNavigator">
+        <attr name="startDestination" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="NavArgument">
+        <attr name="android:name" />
+        <attr name="android:defaultValue" />
+        <!--free format since in future it could be Parcelable-->
+        <attr name="type"/>
+    </declare-styleable>
+
+    <declare-styleable name="NavDeepLink">
+        <attr name="uri" format="string" />
+        <attr name="android:autoVerify" />
+    </declare-styleable>
+
+    <declare-styleable name="NavAction">
+        <attr name="android:id" />
+        <attr name="destination" format="reference" />
+        <attr name="launchSingleTop" format="boolean" />
+        <attr name="launchDocument" format="boolean" />
+        <attr name="clearTask" format="boolean" />
+        <attr name="popUpTo" format="reference" />
+        <attr name="popUpToInclusive" format="boolean" />
+        <attr name="enterAnim" format="reference" />
+        <attr name="exitAnim" format="reference" />
+        <attr name="popEnterAnim" format="reference" />
+        <attr name="popExitAnim" format="reference" />
+    </declare-styleable>
+</resources>
diff --git a/navigation/common/src/test/java/androidx/navigation/EmptyNavigator.java b/navigation/common/src/test/java/androidx/navigation/EmptyNavigator.java
new file mode 100644
index 0000000..2b2174b
--- /dev/null
+++ b/navigation/common/src/test/java/androidx/navigation/EmptyNavigator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.os.Bundle;
+
+/**
+ * An empty {@link Navigator} used to test {@link SimpleNavigatorProvider}.
+ */
+@Navigator.Name(EmptyNavigator.NAME)
+class EmptyNavigator extends Navigator<NavDestination> {
+    static final String NAME = "empty";
+
+    @Override
+    public NavDestination createDestination() {
+        return new NavDestination(this);
+    }
+
+    @Override
+    public void navigate(NavDestination destination, Bundle args, NavOptions navOptions) {
+        throw new IllegalStateException("navigate is not supported");
+    }
+
+    @Override
+    public boolean popBackStack() {
+        throw new IllegalStateException("popBackStack is not supported");
+    }
+}
diff --git a/navigation/common/src/test/java/androidx/navigation/NavActionTest.java b/navigation/common/src/test/java/androidx/navigation/NavActionTest.java
new file mode 100644
index 0000000..589a420
--- /dev/null
+++ b/navigation/common/src/test/java/androidx/navigation/NavActionTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.support.annotation.IdRes;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class NavActionTest {
+    @IdRes
+    private static final int DESTINATION_ID = 1;
+
+    @Test
+    public void createAction() {
+        NavAction action = new NavAction(DESTINATION_ID);
+
+        assertThat(action.getDestinationId(), is(DESTINATION_ID));
+    }
+
+    @Test
+    public void createActionWithNullNavOptions() {
+        NavAction action = new NavAction(DESTINATION_ID, null);
+
+        assertThat(action.getDestinationId(), is(DESTINATION_ID));
+        assertThat(action.getNavOptions(), nullValue());
+    }
+
+    @Test
+    public void setNavOptions() {
+        NavAction action = new NavAction(DESTINATION_ID);
+        NavOptions navOptions = new NavOptions.Builder().build();
+        action.setNavOptions(navOptions);
+
+        assertThat(action.getNavOptions(), is(navOptions));
+    }
+}
diff --git a/navigation/common/src/test/java/androidx/navigation/NavDestinationTest.java b/navigation/common/src/test/java/androidx/navigation/NavDestinationTest.java
new file mode 100644
index 0000000..ce5ad32
--- /dev/null
+++ b/navigation/common/src/test/java/androidx/navigation/NavDestinationTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.support.annotation.IdRes;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SuppressWarnings("unchecked")
+@RunWith(JUnit4.class)
+@SmallTest
+public class NavDestinationTest {
+    @IdRes
+    private static final int INVALID_ACTION_ID = 0;
+    @IdRes
+    private static final int ACTION_ID = 1;
+    @IdRes
+    private static final int DESTINATION_ID = 1;
+
+    @Test
+    public void buildDeepLinkIds() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        destination.setId(DESTINATION_ID);
+        int parentId = 2;
+        NavGraph parent = new NavGraph(mock(Navigator.class));
+        parent.setId(parentId);
+        destination.setParent(parent);
+        int[] deepLinkIds = destination.buildDeepLinkIds();
+        assertThat(deepLinkIds.length, is(2));
+        assertThat(deepLinkIds[0], is(parentId));
+        assertThat(deepLinkIds[1], is(DESTINATION_ID));
+    }
+
+    @Test
+    public void putActionByDestinationId() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        destination.putAction(ACTION_ID, DESTINATION_ID);
+
+        assertThat(destination.getAction(ACTION_ID), not(nullValue()));
+        assertThat(destination.getAction(ACTION_ID).getDestinationId(), is(DESTINATION_ID));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void putActionWithInvalidDestinationId() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        destination.putAction(INVALID_ACTION_ID, DESTINATION_ID);
+    }
+
+    @Test
+    public void putAction() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        NavAction action = new NavAction(DESTINATION_ID);
+        destination.putAction(ACTION_ID, action);
+
+        assertThat(destination.getAction(ACTION_ID), is(action));
+    }
+
+    @Test
+    public void removeAction() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        NavAction action = new NavAction(DESTINATION_ID);
+        destination.putAction(ACTION_ID, action);
+
+        assertThat(destination.getAction(ACTION_ID), is(action));
+
+        destination.removeAction(ACTION_ID);
+
+        assertThat(destination.getAction(ACTION_ID), nullValue());
+    }
+}
diff --git a/navigation/common/src/test/java/androidx/navigation/NavGraphTest.java b/navigation/common/src/test/java/androidx/navigation/NavGraphTest.java
new file mode 100644
index 0000000..beedfd9
--- /dev/null
+++ b/navigation/common/src/test/java/androidx/navigation/NavGraphTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.support.annotation.IdRes;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+@SuppressWarnings("unchecked")
+@RunWith(JUnit4.class)
+@SmallTest
+public class NavGraphTest {
+    @IdRes
+    private static final int FIRST_DESTINATION_ID = 1;
+    @IdRes
+    private static final int SECOND_DESTINATION_ID = 2;
+
+    private NavGraphNavigator mNavGraphNavigator;
+
+    @Before
+    public void setup() {
+        mNavGraphNavigator = new NavGraphNavigator(mock(Context.class));
+    }
+
+    private NavDestination createFirstDestination() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        destination.setId(FIRST_DESTINATION_ID);
+        return destination;
+    }
+
+    private NavDestination createSecondDestination() {
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        destination.setId(SECOND_DESTINATION_ID);
+        return destination;
+    }
+
+    private NavGraph createGraphWithDestination(NavDestination destination) {
+        NavGraph graph = mNavGraphNavigator.createDestination();
+        graph.addDestination(destination);
+        return graph;
+    }
+
+    private NavGraph createGraphWithDestinations(NavDestination... destinations) {
+        NavGraph graph = mNavGraphNavigator.createDestination();
+        graph.addDestinations(destinations);
+        return graph;
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void addDestinationWithoutId() {
+        NavGraph graph = mNavGraphNavigator.createDestination();
+        NavDestination destination = new NavDestination(mock(Navigator.class));
+        graph.addDestination(destination);
+    }
+
+    @Test
+    public void addDestination() {
+        NavDestination destination = createFirstDestination();
+        NavGraph graph = createGraphWithDestination(destination);
+
+        assertThat(destination.getParent(), is(graph));
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), is(destination));
+    }
+
+    @Test
+    public void addDestinationsAsCollection() {
+        NavGraph graph = mNavGraphNavigator.createDestination();
+        NavDestination destination = createFirstDestination();
+        NavDestination secondDestination = createSecondDestination();
+        graph.addDestinations(Arrays.asList(destination, secondDestination));
+
+        assertThat(destination.getParent(), is(graph));
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), is(destination));
+        assertThat(secondDestination.getParent(), is(graph));
+        assertThat(graph.findNode(SECOND_DESTINATION_ID), is(secondDestination));
+    }
+
+    @Test
+    public void addDestinationsAsVarArgs() {
+        NavDestination destination = createFirstDestination();
+        NavDestination secondDestination = createSecondDestination();
+        NavGraph graph = createGraphWithDestinations(destination, secondDestination);
+
+        assertThat(destination.getParent(), is(graph));
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), is(destination));
+        assertThat(secondDestination.getParent(), is(graph));
+        assertThat(graph.findNode(SECOND_DESTINATION_ID), is(secondDestination));
+    }
+
+    @Test
+    public void addReplacementDestination() {
+        NavDestination destination = createFirstDestination();
+        NavGraph graph = createGraphWithDestination(destination);
+
+        NavDestination replacementDestination = new NavDestination(mock(Navigator.class));
+        replacementDestination.setId(FIRST_DESTINATION_ID);
+        graph.addDestination(replacementDestination);
+
+        assertThat(destination.getParent(), nullValue());
+        assertThat(replacementDestination.getParent(), is(graph));
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), is(replacementDestination));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void addDestinationWithExistingParent() {
+        NavDestination destination = createFirstDestination();
+        createGraphWithDestination(destination);
+
+        NavGraph other = mNavGraphNavigator.createDestination();
+        other.addDestination(destination);
+    }
+
+    @Test
+    public void addAll() {
+        NavDestination destination = createFirstDestination();
+        NavGraph other = createGraphWithDestination(destination);
+
+        NavGraph graph = mNavGraphNavigator.createDestination();
+        graph.addAll(other);
+
+        assertThat(destination.getParent(), is(graph));
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), is(destination));
+        assertThat(other.findNode(FIRST_DESTINATION_ID), nullValue());
+    }
+
+    @Test
+    public void removeDestination() {
+        NavDestination destination = createFirstDestination();
+        NavGraph graph = createGraphWithDestination(destination);
+
+        graph.remove(destination);
+
+        assertThat(destination.getParent(), nullValue());
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), nullValue());
+    }
+
+    @Test
+    public void iterator() {
+        NavDestination destination = createFirstDestination();
+        NavDestination secondDestination = createSecondDestination();
+        NavGraph graph = createGraphWithDestinations(destination, secondDestination);
+
+        Iterator<NavDestination> iterator = graph.iterator();
+        assertTrue(iterator.hasNext());
+        assertThat(iterator.next(), anyOf(is(destination), is(secondDestination)));
+        assertTrue(iterator.hasNext());
+        assertThat(iterator.next(), anyOf(is(destination), is(secondDestination)));
+        assertFalse(iterator.hasNext());
+    }
+
+    @Test(expected = NoSuchElementException.class)
+    public void iteratorNoSuchElement() {
+        NavDestination destination = createFirstDestination();
+        NavGraph graph = createGraphWithDestination(destination);
+
+        Iterator<NavDestination> iterator = graph.iterator();
+        iterator.next();
+        iterator.next();
+    }
+
+    @Test
+    public void iteratorRemove() {
+        NavDestination destination = createFirstDestination();
+        NavGraph graph = createGraphWithDestination(destination);
+
+        Iterator<NavDestination> iterator = graph.iterator();
+        NavDestination value = iterator.next();
+        iterator.remove();
+        assertThat(value.getParent(), nullValue());
+        assertThat(graph.findNode(value.getId()), nullValue());
+    }
+
+    @Test
+    public void iteratorDoubleRemove() {
+        NavDestination destination = createFirstDestination();
+        NavDestination secondDestination = createSecondDestination();
+        NavGraph graph = createGraphWithDestinations(destination, secondDestination);
+
+        Iterator<NavDestination> iterator = graph.iterator();
+        iterator.next();
+        iterator.remove();
+        NavDestination value = iterator.next();
+        iterator.remove();
+        assertThat(value.getParent(), nullValue());
+        assertThat(graph.findNode(value.getId()), nullValue());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void iteratorDoubleRemoveWithoutNext() {
+        NavDestination destination = createFirstDestination();
+        NavDestination secondDestination = createSecondDestination();
+        NavGraph graph = createGraphWithDestinations(destination, secondDestination);
+
+        Iterator<NavDestination> iterator = graph.iterator();
+        iterator.next();
+        iterator.remove();
+        iterator.remove();
+    }
+
+    @Test
+    public void clear() {
+        NavDestination destination = createFirstDestination();
+        NavDestination secondDestination = createSecondDestination();
+        NavGraph graph = createGraphWithDestinations(destination, secondDestination);
+
+        graph.clear();
+        assertThat(destination.getParent(), nullValue());
+        assertThat(graph.findNode(FIRST_DESTINATION_ID), nullValue());
+        assertThat(secondDestination.getParent(), nullValue());
+        assertThat(graph.findNode(SECOND_DESTINATION_ID), nullValue());
+    }
+}
diff --git a/navigation/common/src/test/java/androidx/navigation/NoNameNavigator.java b/navigation/common/src/test/java/androidx/navigation/NoNameNavigator.java
new file mode 100644
index 0000000..8b22459
--- /dev/null
+++ b/navigation/common/src/test/java/androidx/navigation/NoNameNavigator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.os.Bundle;
+
+/**
+ * A {@link Navigator} that does not have a {@link Navigator.Name} used to test
+ * {@link SimpleNavigatorProvider}.
+ */
+class NoNameNavigator extends Navigator<NavDestination> {
+    @Override
+    public NavDestination createDestination() {
+        return new NavDestination(this);
+    }
+
+    @Override
+    public void navigate(NavDestination destination, Bundle args, NavOptions navOptions) {
+        throw new IllegalStateException("navigate is not supported");
+    }
+
+    @Override
+    public boolean popBackStack() {
+        throw new IllegalStateException("popBackStack is not supported");
+    }
+}
diff --git a/navigation/common/src/test/java/androidx/navigation/SimpleNavigatorProviderTest.java b/navigation/common/src/test/java/androidx/navigation/SimpleNavigatorProviderTest.java
new file mode 100644
index 0000000..ba7c648
--- /dev/null
+++ b/navigation/common/src/test/java/androidx/navigation/SimpleNavigatorProviderTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SuppressWarnings("unchecked")
+@RunWith(JUnit4.class)
+@SmallTest
+public class SimpleNavigatorProviderTest {
+    @Test
+    public void addWithMissingAnnotationName() {
+        SimpleNavigatorProvider provider = new SimpleNavigatorProvider();
+        Navigator navigator = new NoNameNavigator();
+        try {
+            provider.addNavigator(navigator);
+            fail("Adding a provider with no @Navigator.Name should cause an "
+                    + "IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void addWithMissingAnnotationNameGetWithExplicitName() {
+        SimpleNavigatorProvider provider = new SimpleNavigatorProvider();
+        Navigator navigator = new NoNameNavigator();
+        provider.addNavigator("name", navigator);
+        assertThat(provider.getNavigator("name"), is(navigator));
+    }
+
+    @Test
+    public void addWithExplicitNameGetWithExplicitName() {
+        SimpleNavigatorProvider provider = new SimpleNavigatorProvider();
+        Navigator navigator = new EmptyNavigator();
+        provider.addNavigator("name", navigator);
+        assertThat(provider.getNavigator("name"), is(navigator));
+        try {
+            provider.getNavigator(EmptyNavigator.class);
+            fail("getNavigator(Class) with an invalid name should cause an IllegalStateException");
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void addWithExplicitNameGetWithMissingAnnotationName() {
+        SimpleNavigatorProvider provider = new SimpleNavigatorProvider();
+        Navigator navigator = new NoNameNavigator();
+        provider.addNavigator("name", navigator);
+        try {
+            provider.getNavigator(NoNameNavigator.class);
+            fail("getNavigator(Class) with no @Navigator.Name should cause "
+                    + "an IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void addWithAnnotationNameGetWithAnnotationName() {
+        SimpleNavigatorProvider provider = new SimpleNavigatorProvider();
+        Navigator navigator = new EmptyNavigator();
+        provider.addNavigator(navigator);
+        assertThat(provider.getNavigator(EmptyNavigator.class), is(navigator));
+    }
+
+    @Test
+    public void addWithAnnotationNameGetWithExplicitName() {
+        SimpleNavigatorProvider provider = new SimpleNavigatorProvider();
+        Navigator navigator = new EmptyNavigator();
+        provider.addNavigator(navigator);
+        assertThat(provider.getNavigator(EmptyNavigator.NAME), is(navigator));
+    }
+}
diff --git a/navigation/fragment/build.gradle b/navigation/fragment/build.gradle
new file mode 100644
index 0000000..8014bf9
--- /dev/null
+++ b/navigation/fragment/build.gradle
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+    api(NAV_SUPPORT_FRAGMENTS)
+    api(project(":navigation:navigation-runtime"))
+
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(TEST_RUNNER)
+
+    androidTestImplementation(project(":navigation:navigation-testing"))
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Fragment"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2017"
+    description = "Android Navigation-Fragment"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/fragment/ktx/build.gradle b/navigation/fragment/ktx/build.gradle
new file mode 100644
index 0000000..90d7e3b
--- /dev/null
+++ b/navigation/fragment/ktx/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    buildTypes {
+        debug {
+            testCoverageEnabled = false // Breaks Kotlin compiler.
+        }
+    }
+}
+
+dependencies {
+    api(project(":navigation:navigation-fragment"))
+    // Ensure that the -ktx dependency graph mirrors the Java dependency graph
+    api(project(":navigation:navigation-runtime-ktx"))
+    api(KOTLIN_STDLIB)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Fragment Kotlin Extensions"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2018"
+    description = "Android Navigation-Fragment-Ktx"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
+
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/fragment/ktx/src/androidTest/AndroidManifest.xml
similarity index 66%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/fragment/ktx/src/androidTest/AndroidManifest.xml
index 660dbcd..54df6d4 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/fragment/ktx/src/androidTest/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.navigation.fragment.ktx.test">
+    <application>
+        <activity android:name="androidx.navigation.fragment.TestActivity" />
+    </application>
+</manifest>
diff --git a/navigation/fragment/ktx/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt b/navigation/fragment/ktx/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt
new file mode 100644
index 0000000..5c2a9c1
--- /dev/null
+++ b/navigation/fragment/ktx/src/androidTest/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilderTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment
+
+import android.support.test.annotation.UiThreadTest
+import android.support.test.filters.SmallTest
+import android.support.test.rule.ActivityTestRule
+import android.support.test.runner.AndroidJUnit4
+import android.support.v4.app.Fragment
+import androidx.navigation.contains
+import androidx.navigation.createGraph
+import androidx.navigation.get
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TestNavigatorDestinationBuilderTest {
+    @get:Rule
+    val activityRule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+    private val fragmentManager get() = activityRule.activity.supportFragmentManager
+
+    @UiThreadTest
+    @Test fun fragment() {
+        val navHostFragment = NavHostFragment()
+        fragmentManager.beginTransaction()
+                .add(android.R.id.content, navHostFragment)
+                .commitNow()
+        val graph = navHostFragment.createGraph(startDestination = DESTINATION_ID) {
+            fragment<BuilderTestFragment>(DESTINATION_ID)
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Fragment class should be set to BuilderTestFragment",
+                BuilderTestFragment::class.java,
+                (graph[DESTINATION_ID] as FragmentNavigator.Destination).fragmentClass)
+    }
+
+    @UiThreadTest
+    @Test fun fragmentWithBody() {
+        val navHostFragment = NavHostFragment()
+        fragmentManager.beginTransaction()
+                .add(android.R.id.content, navHostFragment)
+                .commitNow()
+        val graph = navHostFragment.createGraph(startDestination = DESTINATION_ID) {
+            fragment<BuilderTestFragment>(DESTINATION_ID) {
+                label = LABEL
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Fragment class should be set to BuilderTestFragment",
+                BuilderTestFragment::class.java,
+                (graph[DESTINATION_ID] as FragmentNavigator.Destination).fragmentClass)
+        assertEquals("Fragment should have label set",
+                LABEL, graph[DESTINATION_ID].label)
+    }
+}
+
+private const val DESTINATION_ID = 1
+private const val LABEL = "Test"
+class BuilderTestFragment : Fragment()
diff --git a/navigation/fragment/ktx/src/androidTest/java/androidx/navigation/fragment/FragmentTest.kt b/navigation/fragment/ktx/src/androidTest/java/androidx/navigation/fragment/FragmentTest.kt
new file mode 100644
index 0000000..2540852
--- /dev/null
+++ b/navigation/fragment/ktx/src/androidTest/java/androidx/navigation/fragment/FragmentTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment
+
+import android.support.test.annotation.UiThreadTest
+import android.support.test.filters.SmallTest
+import android.support.test.rule.ActivityTestRule
+import android.support.v4.app.Fragment
+import android.support.v4.app.FragmentActivity
+import androidx.navigation.fragment.ktx.test.R
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+
+@SmallTest
+class ActivityTest {
+    @get:Rule val activityRule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+    private val fragmentManager get() = activityRule.activity.supportFragmentManager
+    private val contentFragment get() = fragmentManager.findFragmentById(android.R.id.content)
+
+    @UiThreadTest
+    @Test fun navController() {
+        val navHostFragment = NavHostFragment.create(R.navigation.test_graph)
+        fragmentManager.beginTransaction()
+                .add(android.R.id.content, navHostFragment)
+                .commitNow()
+        assertTrue("Fragment should have NavController set",
+                contentFragment.navController == navHostFragment.navController)
+    }
+
+    @UiThreadTest
+    @Test fun navControllerNull() {
+        fragmentManager.beginTransaction()
+                .add(android.R.id.content, TestFragment())
+                .commitNow()
+        try {
+            contentFragment.navController
+            fail("navController should throw IllegalStateException if a NavController was not set")
+        } catch (e: IllegalStateException) {
+            // Expected
+        }
+    }
+
+    @UiThreadTest
+    @Test fun findNavController() {
+        val navHostFragment = NavHostFragment.create(R.navigation.test_graph)
+        fragmentManager.beginTransaction()
+                .add(android.R.id.content, navHostFragment)
+                .commitNow()
+
+        val foundNavController = contentFragment.findNavController()
+        assertNotNull("findNavController should return non-null if a NavController was set",
+                foundNavController)
+        assertTrue("Fragment should have NavController set",
+                foundNavController == navHostFragment.navController)
+    }
+
+    @UiThreadTest
+    @Test fun findNavControllerNull() {
+        fragmentManager.beginTransaction()
+                .add(android.R.id.content, TestFragment())
+                .commitNow()
+        assertNull("findNavController should return null if a NavController was never set",
+                contentFragment.findNavController())
+    }
+}
+
+class TestActivity : FragmentActivity()
+class TestFragment : Fragment()
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/fragment/ktx/src/androidTest/res/navigation/test_graph.xml
similarity index 69%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/fragment/ktx/src/androidTest/res/navigation/test_graph.xml
index 660dbcd..5f1268a 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/fragment/ktx/src/androidTest/res/navigation/test_graph.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
+<navigation
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:startDestination="@+id/start">
+    <fragment
+        android:id="@+id/start"
+        android:name="androidx.navigation.fragment.TestFragment"/>
+</navigation>
diff --git a/car/res/drawable/car_button_ripple_background.xml b/navigation/fragment/ktx/src/main/AndroidManifest.xml
similarity index 83%
rename from car/res/drawable/car_button_ripple_background.xml
rename to navigation/fragment/ktx/src/main/AndroidManifest.xml
index 13d0a49..bd32886 100644
--- a/car/res/drawable/car_button_ripple_background.xml
+++ b/navigation/fragment/ktx/src/main/AndroidManifest.xml
@@ -14,6 +14,5 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background" />
+
+<manifest package="androidx.navigation.fragment.ktx"/>
diff --git a/navigation/fragment/ktx/src/main/java/androidx/navigation/fragment/Fragment.kt b/navigation/fragment/ktx/src/main/java/androidx/navigation/fragment/Fragment.kt
new file mode 100644
index 0000000..41e6e43
--- /dev/null
+++ b/navigation/fragment/ktx/src/main/java/androidx/navigation/fragment/Fragment.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment
+
+import android.support.v4.app.Fragment
+import androidx.navigation.NavController
+
+/**
+ * Find a [NavController] given a [Fragment]
+ */
+fun Fragment.findNavController(): NavController? =
+        NavHostFragment.findNavController(this)
+
+/**
+ * Gets the [NavController] given a [Fragment].
+ *
+ * Calling this on a Fragment that is not a [NavHostFragment] or within a [NavHostFragment]
+ * will result in an [IllegalStateException]
+ */
+val Fragment.navController: NavController
+        get() = NavHostFragment.getNavController(this)
diff --git a/navigation/fragment/ktx/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt b/navigation/fragment/ktx/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt
new file mode 100644
index 0000000..a188bf0
--- /dev/null
+++ b/navigation/fragment/ktx/src/main/java/androidx/navigation/fragment/FragmentNavigatorDestinationBuilder.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment
+
+import android.support.annotation.IdRes
+import android.support.v4.app.Fragment
+import androidx.navigation.NavDestinationBuilder
+import androidx.navigation.NavDestinationDsl
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.get
+import kotlin.reflect.KClass
+
+/**
+ * Construct a new [FragmentNavigator.Destination]
+ */
+inline fun <reified F : Fragment> NavGraphBuilder.fragment(@IdRes id: Int) = fragment<F>(id) {}
+
+/**
+ * Construct a new [FragmentNavigator.Destination]
+ */
+inline fun <reified F : Fragment> NavGraphBuilder.fragment(
+        @IdRes id: Int,
+        block: FragmentNavigatorDestinationBuilder.() -> Unit
+) = destination(FragmentNavigatorDestinationBuilder(
+        provider[FragmentNavigator::class],
+        id,
+        F::class
+).apply(block))
+
+/**
+ * DSL for constructing a new [FragmentNavigator.Destination]
+ */
+@NavDestinationDsl
+class FragmentNavigatorDestinationBuilder(
+        navigator: FragmentNavigator,
+        @IdRes id: Int,
+        private val fragmentClass: KClass<out Fragment>
+) : NavDestinationBuilder<FragmentNavigator.Destination>(navigator, id) {
+
+    override fun build(): FragmentNavigator.Destination =
+            super.build().also { destination ->
+                destination.fragmentClass = fragmentClass.java
+            }
+}
diff --git a/navigation/fragment/src/androidTest/AndroidManifest.xml b/navigation/fragment/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..c9be5d4
--- /dev/null
+++ b/navigation/fragment/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.navigation.fragment.test">
+    <application>
+        <activity android:name="androidx.navigation.fragment.test.XmlNavigationActivity" />
+        <activity android:name="androidx.navigation.fragment.test.DynamicNavigationActivity" />
+    </application>
+</manifest>
diff --git a/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/BaseNavControllerTest.java b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/BaseNavControllerTest.java
new file mode 100644
index 0000000..704731e
--- /dev/null
+++ b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/BaseNavControllerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NavigationRes;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.v4.app.TaskStackBuilder;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import androidx.navigation.NavController;
+import androidx.navigation.NavDeepLinkBuilder;
+import androidx.navigation.fragment.test.BaseNavigationActivity;
+import androidx.navigation.fragment.test.R;
+import androidx.navigation.testing.TestNavigator;
+
+@SmallTest
+public abstract class BaseNavControllerTest<A extends BaseNavigationActivity> {
+    private static final String TEST_ARG = "test";
+    private static final String TEST_ARG_VALUE = "value";
+    private static final String TEST_DEEP_LINK_ACTION = "deep_link";
+
+    /**
+     * @return The concrete Activity class under test
+     */
+    protected abstract Class<A> getActivityClass();
+
+    @Rule
+    public ActivityTestRule<A> mActivityRule =
+            new ActivityTestRule<>(getActivityClass(), false, false);
+
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void getInstrumentation() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testDeeplink() throws Throwable {
+        BaseNavigationActivity activity = launchDeepLink(R.navigation.nav_deep_link,
+                R.id.deep_link_test, null);
+        NavController navController = activity.getNavController();
+
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.deep_link_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        // Test that the deep link Intent was passed through even though we don't pass in any args
+        Intent deepLinkIntent = navigator.mBackStack.peekLast().second
+                .getParcelable(NavController.KEY_DEEP_LINK_INTENT);
+        assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
+        //noinspection ConstantConditions
+        assertThat(deepLinkIntent.getAction(), is(TEST_DEEP_LINK_ACTION));
+    }
+
+    @Test
+    public void testDeeplinkWithArgs() throws Throwable {
+        Bundle args = new Bundle();
+        args.putString(TEST_ARG, TEST_ARG_VALUE);
+        BaseNavigationActivity activity = launchDeepLink(R.navigation.nav_deep_link,
+                R.id.deep_link_test, args);
+        NavController navController = activity.getNavController();
+
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.deep_link_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+        assertThat(navigator.mBackStack.peekLast().second.getString(TEST_ARG), is(TEST_ARG_VALUE));
+
+        // Test that the deep link Intent was passed in alongside our args
+        Intent deepLinkIntent = navigator.mBackStack.peekLast().second
+                .getParcelable(NavController.KEY_DEEP_LINK_INTENT);
+        assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
+        //noinspection ConstantConditions
+        assertThat(deepLinkIntent.getAction(), is(TEST_DEEP_LINK_ACTION));
+    }
+
+    @Test
+    public void testUriDeepLink() throws Throwable {
+        Uri deepLinkUri = Uri.parse("http://www.example.com/" + TEST_ARG_VALUE);
+        Intent intent = new Intent(Intent.ACTION_VIEW, deepLinkUri)
+                .setComponent(new ComponentName(mInstrumentation.getContext(),
+                        getActivityClass()))
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        BaseNavigationActivity activity = launchActivity(intent);
+        NavController navController = activity.getNavController();
+        navController.setGraph(R.navigation.nav_deep_link);
+
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.deep_link_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+        assertThat(navigator.mBackStack.peekLast().second.getString(TEST_ARG), is(TEST_ARG_VALUE));
+
+        // Test that the deep link Intent was passed in alongside our args
+        Intent deepLinkIntent = navigator.mBackStack.peekLast().second
+                .getParcelable(NavController.KEY_DEEP_LINK_INTENT);
+        assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
+        //noinspection ConstantConditions
+        assertThat(deepLinkIntent.getData(), is(deepLinkUri));
+    }
+
+    private BaseNavigationActivity launchActivity(Intent intent) throws Throwable {
+        BaseNavigationActivity activity = mActivityRule.launchActivity(intent);
+        mInstrumentation.waitForIdleSync();
+        NavController navController = activity.getNavController();
+        assertThat(navController, is(notNullValue(NavController.class)));
+        TestNavigator navigator = new TestNavigator();
+        navController.getNavigatorProvider().addNavigator(navigator);
+        return activity;
+    }
+
+    private BaseNavigationActivity launchDeepLink(@NavigationRes int graphId, @IdRes int destId,
+            Bundle args) throws Throwable {
+        TaskStackBuilder intents = new NavDeepLinkBuilder(mInstrumentation.getTargetContext())
+                .setGraph(graphId)
+                .setDestination(destId)
+                .setArguments(args)
+                .createTaskStackBuilder();
+        Intent intent = intents.editIntentAt(0);
+        intent.setAction(TEST_DEEP_LINK_ACTION);
+
+        // Now launch the deeplink Intent
+        BaseNavigationActivity deeplinkActivity = launchActivity(intent);
+        NavController navController = deeplinkActivity.getNavController();
+        navController.setGraph(graphId);
+
+        return deeplinkActivity;
+    }
+}
diff --git a/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/DynamicNavControllerTest.java b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/DynamicNavControllerTest.java
new file mode 100644
index 0000000..7996cd3
--- /dev/null
+++ b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/DynamicNavControllerTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment;
+
+import android.support.test.filters.SmallTest;
+
+import androidx.navigation.fragment.test.DynamicNavigationActivity;
+
+@SmallTest
+public class DynamicNavControllerTest extends BaseNavControllerTest<DynamicNavigationActivity> {
+    @Override
+    protected Class<DynamicNavigationActivity> getActivityClass() {
+        return DynamicNavigationActivity.class;
+    }
+}
diff --git a/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/XmlNavControllerTest.java b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/XmlNavControllerTest.java
new file mode 100644
index 0000000..d5e5a6e
--- /dev/null
+++ b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/XmlNavControllerTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment;
+
+import android.support.test.filters.SmallTest;
+
+import androidx.navigation.fragment.test.XmlNavigationActivity;
+
+@SmallTest
+public class XmlNavControllerTest extends BaseNavControllerTest<XmlNavigationActivity> {
+    @Override
+    protected Class<XmlNavigationActivity> getActivityClass() {
+        return XmlNavigationActivity.class;
+    }
+}
diff --git a/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/BaseNavigationActivity.java b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/BaseNavigationActivity.java
new file mode 100644
index 0000000..456b47a
--- /dev/null
+++ b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/BaseNavigationActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment.test;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+
+import androidx.navigation.NavController;
+import androidx.navigation.Navigation;
+
+/**
+ * Base Navigation Activity.
+ *
+ * <p>You must call {@link NavController#setGraph(int)}
+ * to set the appropriate graph for your test.</p>
+ */
+public abstract class BaseNavigationActivity extends FragmentActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @NonNull
+    public NavController getNavController() {
+        return Navigation.getNavController(this, R.id.nav_host);
+    }
+}
diff --git a/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/DynamicNavigationActivity.java b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/DynamicNavigationActivity.java
new file mode 100644
index 0000000..b1be2c3
--- /dev/null
+++ b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/DynamicNavigationActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment.test;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import androidx.navigation.NavController;
+import androidx.navigation.fragment.NavHostFragment;
+
+/**
+ * Test Navigation Activity that dynamically adds the {@link NavHostFragment}.
+ *
+ * <p>You must call {@link NavController#setGraph(int)}
+ * to set the appropriate graph for your test.</p>
+ */
+public class DynamicNavigationActivity extends BaseNavigationActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.dynamic_navigation_activity);
+
+        if (savedInstanceState == null) {
+            final NavHostFragment finalHost = new NavHostFragment();
+            getSupportFragmentManager().beginTransaction()
+                    .replace(R.id.nav_host, finalHost)
+                    .setPrimaryNavigationFragment(finalHost)
+                    .commit();
+        }
+    }
+}
diff --git a/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/XmlNavigationActivity.java b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/XmlNavigationActivity.java
new file mode 100644
index 0000000..c6a2575
--- /dev/null
+++ b/navigation/fragment/src/androidTest/java/androidx/navigation/fragment/test/XmlNavigationActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment.test;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import androidx.navigation.NavController;
+import androidx.navigation.fragment.NavHostFragment;
+
+/**
+ * Test Navigation Activity that adds the {@link NavHostFragment} in XML.
+ *
+ * <p>You must call {@link NavController#setGraph(int)}
+ * to set the appropriate graph for your test.</p>
+ */
+public class XmlNavigationActivity extends BaseNavigationActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.navigation_activity);
+    }
+}
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/fragment/src/androidTest/res/layout/dynamic_navigation_activity.xml
similarity index 78%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/fragment/src/androidTest/res/layout/dynamic_navigation_activity.xml
index 660dbcd..df82bf4 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/fragment/src/androidTest/res/layout/dynamic_navigation_activity.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
+
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/nav_host"
+/>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/fragment/src/androidTest/res/layout/navigation_activity.xml
similarity index 66%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/fragment/src/androidTest/res/layout/navigation_activity.xml
index 660dbcd..e6a924d 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/fragment/src/androidTest/res/layout/navigation_activity.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
+
+<fragment
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/nav_host"
+    android:name="androidx.navigation.fragment.NavHostFragment"
+    app:defaultNavHost="true"
+/>
\ No newline at end of file
diff --git a/navigation/fragment/src/androidTest/res/navigation/nav_deep_link.xml b/navigation/fragment/src/androidTest/res/navigation/nav_deep_link.xml
new file mode 100644
index 0000000..22c2ec3
--- /dev/null
+++ b/navigation/fragment/src/androidTest/res/navigation/nav_deep_link.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/start_test">
+
+    <test android:id="@+id/start_test" />
+
+    <test android:id="@+id/deep_link_test">
+        <deepLink app:uri="www.example.com/{test}" />
+    </test>
+</navigation>
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/fragment/src/main/AndroidManifest.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/fragment/src/main/AndroidManifest.xml
index 16b1d0c..7b28b7d 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/fragment/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.navigation.fragment">
+
+</manifest>
diff --git a/navigation/fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.java b/navigation/fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.java
new file mode 100644
index 0000000..971a589
--- /dev/null
+++ b/navigation/fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.util.AttributeSet;
+
+import java.util.HashMap;
+
+import androidx.navigation.NavController;
+import androidx.navigation.NavDestination;
+import androidx.navigation.NavOptions;
+import androidx.navigation.Navigator;
+import androidx.navigation.NavigatorProvider;
+
+/**
+ * Navigator that navigates through {@link FragmentTransaction fragment transactions}. Every
+ * destination using this Navigator must set a valid Fragment class name with
+ * <code>android:name</code> or {@link Destination#setFragmentClass}.
+ */
+@Navigator.Name("fragment")
+public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
+    private Context mContext;
+    private FragmentManager mFragmentManager;
+    private int mContainerId;
+    private int mBackStackCount;
+
+    private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
+            new FragmentManager.OnBackStackChangedListener() {
+                @Override
+                public void onBackStackChanged() {
+                    int newCount = mFragmentManager.getBackStackEntryCount();
+                    int backStackEffect;
+                    if (newCount < mBackStackCount) {
+                        backStackEffect = BACK_STACK_DESTINATION_POPPED;
+                    } else if (newCount > mBackStackCount) {
+                        backStackEffect = BACK_STACK_DESTINATION_ADDED;
+                    } else {
+                        backStackEffect = BACK_STACK_UNCHANGED;
+                    }
+                    mBackStackCount = newCount;
+
+                    int destId = 0;
+                    StateFragment state = getState();
+                    if (state != null) {
+                        destId = state.mCurrentDestId;
+                    }
+                    dispatchOnNavigatorNavigated(destId, backStackEffect);
+                }
+            };
+
+    public FragmentNavigator(@NonNull Context context, @NonNull FragmentManager manager,
+            int containerId) {
+        mContext = context;
+        mFragmentManager = manager;
+        mContainerId = containerId;
+
+        mBackStackCount = mFragmentManager.getBackStackEntryCount();
+        mFragmentManager.addOnBackStackChangedListener(mOnBackStackChangedListener);
+    }
+
+    @Override
+    public boolean popBackStack() {
+        return mFragmentManager.popBackStackImmediate();
+    }
+
+    @NonNull
+    @Override
+    public Destination createDestination() {
+        return new Destination(this);
+    }
+
+    @NonNull
+    private String getBackStackName(@IdRes int destinationId) {
+        // This gives us the resource name if it exists,
+        // or just the destinationId if it doesn't exist
+        try {
+            return mContext.getResources().getResourceName(destinationId);
+        } catch (Resources.NotFoundException e) {
+            return Integer.toString(destinationId);
+        }
+    }
+
+    @Override
+    public void navigate(@NonNull Destination destination, @Nullable Bundle args,
+                            @Nullable NavOptions navOptions) {
+        final Fragment frag = destination.createFragment(args);
+        final FragmentTransaction ft = mFragmentManager.beginTransaction();
+
+        int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
+        int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
+        int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
+        int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
+        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
+            enterAnim = enterAnim != -1 ? enterAnim : 0;
+            exitAnim = exitAnim != -1 ? exitAnim : 0;
+            popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
+            popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
+            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
+        }
+
+        ft.replace(mContainerId, frag);
+
+        final StateFragment oldState = getState();
+        if (oldState != null) {
+            ft.remove(oldState);
+        }
+
+        final @IdRes int destId = destination.getId();
+        final StateFragment newState = new StateFragment();
+        newState.mCurrentDestId = destId;
+        ft.add(newState, StateFragment.FRAGMENT_TAG);
+
+        final boolean initialNavigation = mFragmentManager.getFragments().isEmpty();
+        final boolean isClearTask = navOptions != null && navOptions.shouldClearTask();
+        // TODO Build first class singleTop behavior for fragments
+        final boolean isSingleTopReplacement = navOptions != null && oldState != null
+                && navOptions.shouldLaunchSingleTop()
+                && oldState.mCurrentDestId == destId;
+        if (!initialNavigation && !isClearTask && !isSingleTopReplacement) {
+            ft.addToBackStack(getBackStackName(destId));
+        } else {
+            ft.runOnCommit(new Runnable() {
+                @Override
+                public void run() {
+                    dispatchOnNavigatorNavigated(destId, isSingleTopReplacement
+                            ? BACK_STACK_UNCHANGED
+                            : BACK_STACK_DESTINATION_ADDED);
+                }
+            });
+        }
+        ft.commit();
+        mFragmentManager.executePendingTransactions();
+    }
+
+    private StateFragment getState() {
+        return (StateFragment) mFragmentManager.findFragmentByTag(StateFragment.FRAGMENT_TAG);
+    }
+
+    /**
+     * NavDestination specific to {@link FragmentNavigator}
+     */
+    public static class Destination extends NavDestination {
+        private static final HashMap<String, Class<? extends Fragment>> sFragmentClasses =
+                new HashMap<>();
+
+        private Class<? extends Fragment> mFragmentClass;
+
+        /**
+         * Construct a new fragment destination. This destination is not valid until you set the
+         * Fragment via {@link #setFragmentClass(Class)}.
+         *
+         * @param navigatorProvider The {@link NavController} which this destination
+         *                          will be associated with.
+         */
+        public Destination(@NonNull NavigatorProvider navigatorProvider) {
+            this(navigatorProvider.getNavigator(FragmentNavigator.class));
+        }
+
+        /**
+         * Construct a new fragment destination. This destination is not valid until you set the
+         * Fragment via {@link #setFragmentClass(Class)}.
+         *
+         * @param fragmentNavigator The {@link FragmentNavigator} which this destination
+         *                          will be associated with. Generally retrieved via a
+         *                          {@link NavController}'s
+         *                          {@link NavigatorProvider#getNavigator(Class)} method.
+         */
+        public Destination(@NonNull Navigator<? extends Destination> fragmentNavigator) {
+            super(fragmentNavigator);
+        }
+
+        @Override
+        public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
+            super.onInflate(context, attrs);
+            TypedArray a = context.getResources().obtainAttributes(attrs,
+                    R.styleable.FragmentNavigator);
+            setFragmentClass(getFragmentClassByName(context, a.getString(
+                            R.styleable.FragmentNavigator_android_name)));
+            a.recycle();
+        }
+
+        private Class<? extends Fragment> getFragmentClassByName(Context context, String name) {
+            if (name != null && name.charAt(0) == '.') {
+                name = context.getPackageName() + name;
+            }
+            Class<? extends Fragment> clazz = sFragmentClasses.get(name);
+            if (clazz == null) {
+                try {
+                    clazz = (Class<? extends Fragment>) Class.forName(name, true,
+                            context.getClassLoader());
+                    sFragmentClasses.put(name, clazz);
+                } catch (ClassNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return clazz;
+        }
+
+        /**
+         * Set the Fragment associated with this destination
+         * @param clazz The class name of the Fragment to show when you navigate to this
+         *              destination
+         * @return this {@link Destination}
+         */
+        public Destination setFragmentClass(Class<? extends Fragment> clazz) {
+            mFragmentClass = clazz;
+            return this;
+        }
+
+        /**
+         * Gets the Fragment associated with this destination
+         * @return
+         */
+        public Class<? extends Fragment> getFragmentClass() {
+            return mFragmentClass;
+        }
+
+        /**
+         * Create a new instance of the {@link Fragment} associated with this destination.
+         * @param args optional args to set on the new Fragment
+         * @return an instance of the {@link #getFragmentClass() Fragment class} associated
+         * with this destination
+         */
+        @SuppressWarnings("ClassNewInstance")
+        public Fragment createFragment(@Nullable Bundle args) {
+            Class<? extends Fragment> clazz = getFragmentClass();
+            if (clazz == null) {
+                throw new IllegalStateException("fragment class not set");
+            }
+
+            Fragment f;
+            try {
+                f = clazz.newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+            if (args != null) {
+                f.setArguments(args);
+            }
+            return f;
+        }
+    }
+
+    /**
+     * An internal fragment used by FragmentNavigator to track additional navigation state.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    public static class StateFragment extends Fragment {
+        static final String FRAGMENT_TAG = "android-support-nav:FragmentNavigator.StateFragment";
+
+        private static final String KEY_CURRENT_DEST_ID = "currentDestId";
+
+        int mCurrentDestId;
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            if (savedInstanceState != null) {
+                mCurrentDestId = savedInstanceState.getInt(KEY_CURRENT_DEST_ID);
+            }
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle outState) {
+            super.onSaveInstanceState(outState);
+            outState.putInt(KEY_CURRENT_DEST_ID, mCurrentDestId);
+        }
+    }
+}
diff --git a/navigation/fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java b/navigation/fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
new file mode 100644
index 0000000..8f6dfe5
--- /dev/null
+++ b/navigation/fragment/src/main/java/androidx/navigation/fragment/NavHostFragment.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.fragment;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.NavigationRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.navigation.NavController;
+import androidx.navigation.NavGraph;
+import androidx.navigation.NavHost;
+import androidx.navigation.Navigation;
+import androidx.navigation.Navigator;
+
+/**
+ * NavHostFragment provides an area within your layout for self-contained navigation to occur.
+ *
+ * <p>NavHostFragment is intended to be used as the content area within a layout resource
+ * defining your app's chrome around it, e.g.:</p>
+ *
+ * <pre class="prettyprint">
+ *     <android.support.v4.widget.DrawerLayout
+ *             xmlns:android="http://schemas.android.com/apk/res/android"
+ *             xmlns:app="http://schemas.android.com/apk/res-auto"
+ *             android:layout_width="match_parent"
+ *             android:layout_height="match_parent">
+ *         <fragment
+ *                 android:layout_width="match_parent"
+ *                 android:layout_height="match_parent"
+ *                 android:id="@+id/my_nav_host_fragment"
+ *                 android:name="androidx.navigation.fragment.NavHostFragment"
+ *                 app:navGraph="@xml/nav_sample"
+ *                 app:defaultNavHost="true" />
+ *         <android.support.design.widget.NavigationView
+ *                 android:layout_width="wrap_content"
+ *                 android:layout_height="match_parent"
+ *                 android:layout_gravity="start"/>
+ *     </android.support.v4.widget.DrawerLayout>
+ * </pre>
+ *
+ * <p>Each NavHostFragment has a {@link NavController} that defines valid navigation within
+ * the navigation host. This includes the {@link NavGraph navigation graph} as well as navigation
+ * state such as current location and back stack that will be saved and restored along with the
+ * NavHostFragment itself.</p>
+ *
+ * <p>NavHostFragments register their navigation controller at the root of their view subtree
+ * such that any descendant can obtain the controller instance through the {@link Navigation}
+ * helper class's methods such as {@link Navigation#findNavController(View)}. View event listener
+ * implementations such as {@link android.view.View.OnClickListener} within navigation destination
+ * fragments can use these helpers to navigate based on user interaction without creating a tight
+ * coupling to the navigation host.</p>
+ */
+public class NavHostFragment extends Fragment implements NavHost {
+    private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId";
+    private static final String KEY_NAV_CONTROLLER_STATE =
+            "android-support-nav:fragment:navControllerState";
+    private static final String KEY_DEFAULT_NAV_HOST = "android-support-nav:fragment:defaultHost";
+
+    /**
+     * Find a {@link NavController} given a local {@link Fragment}.
+     *
+     * <p>This method will locate the {@link NavController} associated with this Fragment,
+     * looking first for a {@link NavHostFragment} along the given Fragment's parent chain.
+     * If a {@link NavController} is not found, this method will look for one along this
+     * Fragment's {@link Fragment#getView() view hierarchy} as specified by
+     * {@link Navigation#findNavController(View)}.</p>
+     *
+     * @param fragment the locally scoped Fragment for navigation
+     * @return the locally scoped {@link NavController} for navigating from this {@link Fragment}
+     */
+    @Nullable
+    public static NavController findNavController(@Nullable Fragment fragment) {
+        if (fragment == null) {
+            return null;
+        }
+
+        Fragment findFragment = fragment;
+        while (findFragment != null) {
+            if (findFragment instanceof NavHostFragment) {
+                return ((NavHostFragment) findFragment).getNavController();
+            }
+            Fragment primaryNavFragment = findFragment.getFragmentManager()
+                    .getPrimaryNavigationFragment();
+            if (primaryNavFragment instanceof NavHostFragment) {
+                return ((NavHostFragment) primaryNavFragment).getNavController();
+            }
+            findFragment = findFragment.getParentFragment();
+        }
+
+        // Try looking for one associated with the view instead, if applicable
+        View view = fragment.getView();
+        return view != null ? Navigation.findNavController(view) : null;
+    }
+
+    /**
+     * Gets the {@link NavController} given a local {@link Fragment}.
+     *
+     * <p>This method will locate the {@link NavController} associated with this Fragment,
+     * looking first for a {@link NavHostFragment} along the given Fragment's parent chain.
+     * If a {@link NavController} is not found, this method will look for one along this
+     * Fragment's {@link Fragment#getView() view hierarchy} as specified by
+     * {@link Navigation#findNavController(View)}.</p>
+     *
+     * @param fragment the locally scoped Fragment for navigation
+     * @return the locally scoped {@link NavController} for navigating from this {@link Fragment}
+     * @throws IllegalStateException if the given Fragment does not correspond with a
+     * {@link NavHost} or is not within a NavHost.
+     */
+    public static NavController getNavController(@Nullable Fragment fragment) {
+        NavController navController = findNavController(fragment);
+        if (navController == null) {
+            throw new IllegalStateException("Fragment " + fragment
+                    + " does not have a NavController set");
+        }
+        return navController;
+    }
+
+    private NavController mNavController;
+
+    // State that will be saved and restored
+    private boolean mDefaultNavHost;
+
+    /**
+     * Create a new NavHostFragment instance with an inflated {@link NavGraph} resource.
+     *
+     * @param graphResId resource id of the navigation graph to inflate
+     * @return a new NavHostFragment instance
+     */
+    public static NavHostFragment create(@NavigationRes int graphResId) {
+        Bundle b = null;
+        if (graphResId != 0) {
+            b = new Bundle();
+            b.putInt(KEY_GRAPH_ID, graphResId);
+        }
+
+        final NavHostFragment result = new NavHostFragment();
+        if (b != null) {
+            result.setArguments(b);
+        }
+        return result;
+    }
+
+    /**
+     * Returns the {@link NavController navigation controller} for this navigation host.
+     * This method will return null until this host fragment's {@link #onCreate(Bundle)}
+     * has been called and it has had an opportunity to restore from a previous instance state.
+     *
+     * @return this host's navigation controller
+     * @throws IllegalStateException if called before {@link #onCreate(Bundle)}
+     */
+    @NonNull
+    @Override
+    public NavController getNavController() {
+        if (mNavController == null) {
+            throw new IllegalStateException("NavController is not available before onCreate()");
+        }
+        return mNavController;
+    }
+
+    /**
+     * Set a {@link NavGraph} for this navigation host's {@link NavController} by resource id.
+     * The existing graph will be replaced.
+     *
+     * @param graphResId resource id of the navigation graph to inflate
+     */
+    public void setGraph(@NavigationRes int graphResId) {
+        if (mNavController == null) {
+            Bundle args = getArguments();
+            if (args == null) {
+                args = new Bundle();
+            }
+            args.putInt(KEY_GRAPH_ID, graphResId);
+            setArguments(args);
+        } else {
+            mNavController.setGraph(graphResId);
+        }
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        // TODO This feature should probably be a first-class feature of the Fragment system,
+        // but it can stay here until we can add the necessary attr resources to
+        // the fragment lib.
+        if (mDefaultNavHost) {
+            getFragmentManager().beginTransaction().setPrimaryNavigationFragment(this).commit();
+        }
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Context context = getContext();
+
+        mNavController = new NavController(context);
+        mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator());
+
+        Bundle navState = null;
+        if (savedInstanceState != null) {
+            navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE);
+            if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
+                mDefaultNavHost = true;
+                getFragmentManager().beginTransaction().setPrimaryNavigationFragment(this).commit();
+            }
+        }
+
+        if (navState != null) {
+            // Navigation controller state overrides arguments
+            mNavController.restoreState(navState);
+        } else {
+            final Bundle args = getArguments();
+            final int graphId = args != null ? args.getInt(KEY_GRAPH_ID) : 0;
+            if (graphId != 0) {
+                mNavController.setGraph(graphId);
+            } else {
+                mNavController.setMetadataGraph();
+            }
+        }
+    }
+
+    /**
+     * Create the FragmentNavigator that this NavHostFragment will use. By default, this uses
+     * {@link FragmentNavigator}, which replaces the entire contents of the NavHostFragment.
+     * <p>
+     * This is only called once in {@link #onCreate(Bundle)} and should not be called directly by
+     * subclasses.
+     * @return a new instance of a FragmentNavigator
+     */
+    @NonNull
+    protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
+        return new FragmentNavigator(getContext(), getChildFragmentManager(), getId());
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        FrameLayout frameLayout = new FrameLayout(inflater.getContext());
+        // When added via XML, this has no effect (since this FrameLayout is given the ID
+        // automatically), but this ensures that the View exists as part of this Fragment's View
+        // hierarchy in cases where the NavHostFragment is added programmatically as is required
+        // for child fragment transactions
+        frameLayout.setId(getId());
+        return frameLayout;
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        if (!(view instanceof ViewGroup)) {
+            throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
+        }
+        // When added via XML, the parent is null and our view is the root of the NavHostFragment
+        // but when added programmatically, we need to set the NavController on the parent - i.e.,
+        // the View that has the ID matching this NavHostFragment.
+        View rootView = view.getParent() != null ? (View) view.getParent() : view;
+        Navigation.setViewNavController(rootView, mNavController);
+    }
+
+    @Override
+    public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
+        super.onInflate(context, attrs, savedInstanceState);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment);
+        final int graphId = a.getResourceId(R.styleable.NavHostFragment_navGraph, 0);
+        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
+
+        if (graphId != 0) {
+            setGraph(graphId);
+        }
+        if (defaultHost) {
+            mDefaultNavHost = true;
+            if (isAdded()) {
+                getFragmentManager().beginTransaction().setPrimaryNavigationFragment(this).commit();
+            }
+        }
+        a.recycle();
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+        Bundle navState = mNavController.saveState();
+        if (navState != null) {
+            outState.putBundle(KEY_NAV_CONTROLLER_STATE, navState);
+        }
+        if (mDefaultNavHost) {
+            outState.putBoolean(KEY_DEFAULT_NAV_HOST, true);
+        }
+    }
+}
diff --git a/navigation/fragment/src/main/java/androidx/navigation/fragment/package-info.java b/navigation/fragment/src/main/java/androidx/navigation/fragment/package-info.java
new file mode 100644
index 0000000..0ff94ab
--- /dev/null
+++ b/navigation/fragment/src/main/java/androidx/navigation/fragment/package-info.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The {@link androidx.navigation.fragment.NavHostFragment} provides a
+ * {@link androidx.navigation.NavHost} suitable for using
+ * {@link android.support.v4.app.Fragment Fragments} as destinations in your navigation graphs via
+ * &lt;fragment%gt; elements. Navigating to a Fragment will replace the contents of the
+ * NavHostFragment.
+ * <p>
+ * Below is a minimal implementation.
+ * <pre class="prettyprint">
+ * // File: res/xml/main_navigation.xml
+ * &lt;navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ *     xmlns:app="http://schemas.android.com/apk/res-auto"
+ *     app:startDestination="{@literal @}+id/home_fragment"&gt;
+ *   &lt;fragment android:id="{@literal @}+id/home_fragment"
+ *       android:name="com.example.HomeFragment"&gt;
+ *     &lt;action android:id="{@literal @}+id/details"
+ *       app:destination="{@literal @}+id/details_fragment" /&gt;
+ *   &lt;fragment /&gt;
+ *   &lt;fragment android:id="{@literal @}+id/details_fragment"
+ *       android:name="com.example.DetailsFragment"/&gt;
+ * &lt;navigation /&gt;
+ *
+ * // File: activity_main.xml
+ * &lt;fragment
+ *   android:id="{@literal @}+id/my_nav_host_fragment"
+ *   android:layout_width="match_parent"
+ *   android:layout_height="match_parent"
+ *   android:name="androidx.navigation.fragment.NavHostFragment"
+ *   app:navGraph="{@literal @}xml/main_navigation"
+ *   app:defaultNavHost="true"
+ * /&gt;
+ *
+ * // File: HomeFragment.java
+ * public void onViewCreated(View view, {@literal @}Nullable Bundle savedInstanceState) {
+ *   // For example purposes, assume our layout created in onCreateView has a Button
+ *   // that should navigate the user to a destination
+ *   Button button = view.findViewById(R.id.view_details);
+ *
+ *   // Retrieve the NavController from any Fragment created by a NavHostFragment by passing in
+ *   // this
+ *   final NavController navController = NavHostFragment.findNavController(this);
+ *   // Alternatively, retrieve the NavController from any View within the NavHostFragment
+ *   final NavController viewNavController = Navigation.findNavController(button);
+ *
+ *   // And set the listener
+ *   button.setOnClickListener(() -%gt; navController.navigate(R.id.details));
+ *
+ *   // Or use the convenience method in Navigation to combine all of the previous steps
+ *   button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.details));
+ * }
+ * </pre>
+ */
+package androidx.navigation.fragment;
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/fragment/src/main/res/values/attrs.xml
similarity index 62%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/fragment/src/main/res/values/attrs.xml
index 660dbcd..a5e9f55 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/fragment/src/main/res/values/attrs.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright 2018 The Android Open 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,6 +14,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<resources>
+    <declare-styleable name="FragmentNavigator">
+        <attr name="android:name" />
+    </declare-styleable>
+
+    <declare-styleable name="NavHostFragment">
+        <attr name="navGraph" format="reference" />
+        <attr name="defaultNavHost" format="boolean" />
+    </declare-styleable>
+</resources>
diff --git a/navigation/integration-tests/safeargs-testapp/build.gradle b/navigation/integration-tests/safeargs-testapp/build.gradle
new file mode 100644
index 0000000..adc5d0c
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/build.gradle
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static android.support.dependencies.DependenciesKt.*
+import android.support.LibraryVersions
+import android.support.LibraryGroups
+
+buildscript {
+    ext.supportRootFolder = new File("../../../")
+    apply from: "${supportRootFolder}/buildSrc/repos.gradle"
+    apply from: "${supportRootFolder}/buildSrc/init.gradle"
+    apply from: "${supportRootFolder}/buildSrc/build_dependencies.gradle"
+    init.setSdkInLocalPropertiesFile()
+    repos.addMavenRepositories(repositories)
+
+    repositories {
+        maven {
+            url "$buildDir/localMaven/"
+        }
+    }
+    dependencies {
+        classpath build_libs.gradle
+        classpath "${LibraryGroups.NAVIGATION}:safe-args-gradle-plugin:${LibraryVersions.NAVIGATION}"
+    }
+}
+
+plugins {
+    id("SupportAndroidTestAppPlugin")
+}
+
+apply plugin: 'androidx.navigation.safeargs'
+apply from: "${supportRootFolder}/buildSrc/repos.gradle"
+repos.addMavenRepositories(repositories)
+repositories {
+    maven {
+        url "$buildDir/localMaven/"
+    }
+}
+
+android {
+    flavorDimensions "mode"
+    productFlavors {
+        foo {
+            dimension "mode"
+            applicationIdSuffix ".foo"
+        }
+        notfoo {
+            dimension "mode"
+        }
+    }
+}
+
+dependencies {
+    implementation "${LibraryGroups.NAVIGATION}:runtime:${LibraryVersions.NAVIGATION}"
+    testCompile(JUNIT)
+    testCompile(MOCKITO_CORE)
+}
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/buildSrc/build.gradle b/navigation/integration-tests/safeargs-testapp/buildSrc/build.gradle
new file mode 100644
index 0000000..0941f4d
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/buildSrc/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ext.supportRootFolder = new File("../../../..")
+apply from: "$supportRootFolder/buildSrc/repos.gradle"
+repos.addMavenRepositories(repositories)
+
+ext.runningInBuildServer = System.env.DIST_DIR != null && System.env.OUT_DIR != null
+ext.repoDir = runningInBuildServer ? new File(System.env.OUT_DIR + '/gradle/frameworks/support/build') :
+        new File("$supportRootFolder/../../out/host/gradle/frameworks/support/build")
+
+task createArchive(type: Exec) {
+    inputs.files(fileTree(dir: "$supportRootFolder/navigation",
+            includes: ['**/*.java', '**/*.kt'], exclude: 'integration-tests/*'))
+    outputs.dir(repoDir)
+    workingDir "$supportRootFolder/app-toolkit/"
+    commandLine "./gradlew", "createArchive"
+}
+
+task unzip(type: Copy, dependsOn: createArchive) {
+    from "$repoDir/support_repo"
+    into "../build/localMaven/"
+}
+
+apply plugin: 'java'
+dependencies {
+    compile project(":moar-buildSrc")
+}
+
+tasks["build"].dependsOn unzip
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/buildSrc/settings.gradle b/navigation/integration-tests/safeargs-testapp/buildSrc/settings.gradle
new file mode 100644
index 0000000..ae61ad6
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/buildSrc/settings.gradle
@@ -0,0 +1,2 @@
+include ':moar-buildSrc'
+project(':moar-buildSrc').projectDir = new File(rootDir, "../../../../buildSrc/")
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/gradle b/navigation/integration-tests/safeargs-testapp/gradle
new file mode 120000
index 0000000..84b694e
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/gradle
@@ -0,0 +1 @@
+../../../gradle
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/gradle.properties b/navigation/integration-tests/safeargs-testapp/gradle.properties
new file mode 120000
index 0000000..591a8b3
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/gradle.properties
@@ -0,0 +1 @@
+../../../gradle.properties
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/gradlew b/navigation/integration-tests/safeargs-testapp/gradlew
new file mode 120000
index 0000000..ab9334b
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/gradlew
@@ -0,0 +1 @@
+../../../gradlew
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/src/foo/res/navigation/nav_test.xml b/navigation/integration-tests/safeargs-testapp/src/foo/res/navigation/nav_test.xml
new file mode 100644
index 0000000..70debb7
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/src/foo/res/navigation/nav_test.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/first_screen">
+    <fragment android:id="@+id/first_screen"
+              android:name=".MainFragment">
+        <action android:id="@+id/foo" app:destination="@+id/foo_fragment">
+            <argument android:name="arg"/>
+        </action>
+    </fragment>
+    <fragment android:id="@+id/foo_fragment"
+              android:name="android.arch.navigation.FooFragment">
+        <argument android:name="myarg2" />
+    </fragment>
+    <fragment android:id="@+id/next_fragment"/>
+</navigation>
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/src/main/AndroidManifest.xml b/navigation/integration-tests/safeargs-testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c97e190
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="android.arch.navigation.safeargs.testapp">
+
+    <application
+        android:allowBackup="true"
+        android:supportsRtl="true"
+        tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
+    </application>
+
+</manifest>
diff --git a/navigation/integration-tests/safeargs-testapp/src/main/java/android/arch/navigation/Foo.java b/navigation/integration-tests/safeargs-testapp/src/main/java/android/arch/navigation/Foo.java
new file mode 100644
index 0000000..a794771
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/src/main/java/android/arch/navigation/Foo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.arch.navigation;
+
+/**
+ * Dummy file, because android gradle plugin requires not empty sources
+ */
+public class Foo {
+}
diff --git a/navigation/integration-tests/safeargs-testapp/src/main/res/navigation/nav_test.xml b/navigation/integration-tests/safeargs-testapp/src/main/res/navigation/nav_test.xml
new file mode 100644
index 0000000..4d4be46
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/src/main/res/navigation/nav_test.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/first_screen">
+    <fragment android:id="@+id/first_screen"
+              android:name="android.arch.navigation.testapp.MainFragment">
+        <action android:id="@+id/next" app:destination="@+id/next_fragment">
+            <argument android:name="myarg2"/>
+            <argument android:name="randomArgument"/>
+        </action>
+    </fragment>
+    <fragment android:id="@+id/next_fragment"
+              android:name="android.arch.navigation.testapp.NextFragment">
+        <argument android:name="myarg2" />
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/navigation/integration-tests/safeargs-testapp/src/test/java/android/arch/navigation/integration/MainDestinationTest.java b/navigation/integration-tests/safeargs-testapp/src/test/java/android/arch/navigation/integration/MainDestinationTest.java
new file mode 100644
index 0000000..0d3bf09
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/src/test/java/android/arch/navigation/integration/MainDestinationTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.arch.navigation.integration;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.navigation.safeargs.testapp.R;
+import android.arch.navigation.testapp.MainFragmentDirections;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MainDestinationTest {
+
+    @Test
+    public void destinationsTest() {
+        // test that we correctly generated R class references, so it compiles
+        MainFragmentDirections.Next directions = MainFragmentDirections.next("foo", "some");
+        assertThat(directions.getDestinationId(), is(R.id.next_fragment));
+    }
+}
diff --git a/navigation/integration-tests/safeargs-testapp/src/testFoo/java/android/arch/navigation/integration/flavor/foo/FlavorDestinationTest.java b/navigation/integration-tests/safeargs-testapp/src/testFoo/java/android/arch/navigation/integration/flavor/foo/FlavorDestinationTest.java
new file mode 100644
index 0000000..3229c58
--- /dev/null
+++ b/navigation/integration-tests/safeargs-testapp/src/testFoo/java/android/arch/navigation/integration/flavor/foo/FlavorDestinationTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.arch.navigation.integration.flavor.foo;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.arch.navigation.safeargs.testapp.R;
+import android.arch.navigation.safeargs.testapp.foo.MainFragmentDirections;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * this is teeeeeeeeeest
+ */
+@RunWith(JUnit4.class)
+public class FlavorDestinationTest {
+
+    /**
+     * teeeest
+     */
+    @Test
+    public void destinationsTest() {
+        MainFragmentDirections.Foo directions = MainFragmentDirections.foo("foo", "some");
+        assertThat(directions.getDestinationId(), is(R.id.foo_fragment));
+    }
+}
diff --git a/navigation/integration-tests/testapp/.gitignore b/navigation/integration-tests/testapp/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/navigation/integration-tests/testapp/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/navigation/integration-tests/testapp/build.gradle b/navigation/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..46bec7c
--- /dev/null
+++ b/navigation/integration-tests/testapp/build.gradle
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("SupportAndroidTestAppPlugin")
+}
+
+dependencies {
+    implementation(project(":navigation:navigation-fragment"))
+    implementation(project(":navigation:navigation-ui"))
+}
+
+tasks['check'].dependsOn(tasks['connectedCheck'])
diff --git a/navigation/integration-tests/testapp/src/main/AndroidManifest.xml b/navigation/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cb53008
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="androidx.navigation.testapp">
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon">
+        <activity android:name=".NavigationActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <!-- Handle the 'More information' link on www.example.com -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.BROWSABLE"/>
+                <data android:scheme="https"/>
+                <data android:scheme="http"/>
+                <data android:host="www.iana.org"/>
+                <data android:pathPrefix="/domains/"/>
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".HelpActivity"
+            android:label="@string/help"/>
+
+        <receiver android:name=".DeepLinkAppWidgetProvider" >
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                android:resource="@xml/deep_link_appwidget_info" />
+        </receiver>
+    </application>
+
+</manifest>
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/AndroidFragment.java b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/AndroidFragment.java
new file mode 100644
index 0000000..1c8e768
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/AndroidFragment.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testapp;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.NotificationCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.navigation.Navigation;
+
+/**
+ * Fragment used to show how to deep link to a destination
+ */
+public class AndroidFragment extends Fragment {
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.android_fragment, container, false);
+    }
+
+    @Override
+    public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        TextView tv = view.findViewById(R.id.text);
+        tv.setText(getArguments().getString("myarg"));
+
+        Button b = view.findViewById(R.id.send_notification);
+        b.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                EditText editArgs = view.findViewById(R.id.edit_args);
+                Bundle args = new Bundle();
+                args.putString("myarg", editArgs.getText().toString());
+                PendingIntent deeplink = Navigation.getNavController(v).createDeepLink()
+                        .setDestination(R.id.android)
+                        .setArguments(args)
+                        .createPendingIntent();
+                NotificationManager notificationManager = (NotificationManager)
+                        getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                    notificationManager.createNotificationChannel(new NotificationChannel(
+                            "deeplink", "Deep Links", NotificationManager.IMPORTANCE_HIGH));
+                }
+                NotificationCompat.Builder builder = new NotificationCompat.Builder(
+                        getContext(), "deeplink")
+                        .setContentTitle("Navigation")
+                        .setContentText("Deep link to Android")
+                        .setSmallIcon(R.drawable.ic_android)
+                        .setContentIntent(deeplink)
+                        .setAutoCancel(true);
+                notificationManager.notify(0, builder.build());
+            }
+        });
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/DeepLinkAppWidgetProvider.java b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/DeepLinkAppWidgetProvider.java
new file mode 100644
index 0000000..7db6516
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/DeepLinkAppWidgetProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testapp;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.os.Bundle;
+import android.widget.RemoteViews;
+
+import androidx.navigation.NavDeepLinkBuilder;
+
+/**
+ * App Widget that deep links you to the {@link AndroidFragment}.
+ */
+public class DeepLinkAppWidgetProvider extends AppWidgetProvider {
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
+                R.layout.deep_link_appwidget);
+
+        Bundle args = new Bundle();
+        args.putString("myarg", "From Widget");
+        PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
+                .setGraph(R.navigation.nav_main)
+                .setDestination(R.id.android)
+                .setArguments(args)
+                .createPendingIntent();
+
+        remoteViews.setOnClickPendingIntent(R.id.deep_link, pendingIntent);
+        appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.java b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.java
new file mode 100644
index 0000000..fc0e1ef
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/HelpActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testapp;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.widget.TextView;
+
+import androidx.navigation.NavOptions;
+
+/**
+ * Simple 'Help' activity that shows the data URI passed to it. In a real world app, it would
+ * load the chosen help article, etc.
+ */
+public class HelpActivity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_help);
+
+        Toolbar toolbar = findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+        ((TextView) findViewById(R.id.data)).setText(getIntent().getData().toString());
+    }
+
+    @Override
+    public boolean onSupportNavigateUp() {
+        finish();
+        NavOptions.applyPopAnimationsToPendingTransition(this);
+        return true;
+    }
+
+    @Override
+    public void onBackPressed() {
+        super.onBackPressed();
+        NavOptions.applyPopAnimationsToPendingTransition(this);
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.java b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.java
new file mode 100644
index 0000000..2a4950e
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/MainFragment.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.navigation.Navigation;
+
+/**
+ * Fragment used to show how to navigate to another destination
+ */
+public class MainFragment extends Fragment {
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.main_fragment, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        TextView tv = (TextView) view.findViewById(R.id.text);
+        tv.setText(getArguments().getString("myarg"));
+
+        Button b = (Button) view.findViewById(R.id.next_button);
+        b.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next));
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/NavigationActivity.java b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/NavigationActivity.java
new file mode 100644
index 0000000..039af32
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/androidx/navigation/testapp/NavigationActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testapp;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomNavigationView;
+import android.support.design.widget.NavigationView;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.navigation.NavController;
+import androidx.navigation.NavDestination;
+import androidx.navigation.Navigation;
+import androidx.navigation.fragment.NavHostFragment;
+import androidx.navigation.ui.NavigationUI;
+
+/**
+ * A simple activity demonstrating use of a NavHostFragment with a navigation drawer.
+ */
+public class NavigationActivity extends AppCompatActivity {
+    private DrawerLayout mDrawerLayout;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.navigation_activity);
+
+        Toolbar toolbar = findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+
+        NavHostFragment host = (NavHostFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.my_nav_host_fragment);
+
+        if (host != null) {
+            NavController navController = host.getNavController();
+            mDrawerLayout = findViewById(R.id.drawer_layout);
+            NavigationUI.setupActionBarWithNavController(this, navController, mDrawerLayout);
+            NavigationView navigationView = findViewById(R.id.nav_view);
+            if (navigationView != null) {
+                NavigationUI.setupWithNavController(navigationView, navController);
+            }
+            BottomNavigationView bottomNavView = findViewById(R.id.bottom_nav_view);
+            if (bottomNavView != null) {
+                NavigationUI.setupWithNavController(bottomNavView, navController);
+            }
+            navController.addOnNavigatedListener(new NavController.OnNavigatedListener() {
+                @Override
+                public void onNavigated(NavController controller, NavDestination destination) {
+                    String dest;
+                    try {
+                        dest = getResources().getResourceName(destination.getId());
+                    } catch (Resources.NotFoundException e) {
+                        dest = Integer.toString(destination.getId());
+                    }
+                    Toast.makeText(NavigationActivity.this, "Navigated to "
+                            + dest,
+                            Toast.LENGTH_SHORT).show();
+                    Log.d("NavigationActivity", "Navigated to " + dest);
+                }
+            });
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        boolean retValue = super.onCreateOptionsMenu(menu);
+        NavigationView navigationView = findViewById(R.id.nav_view);
+        // The NavigationView already has these same navigation items, so we only add
+        // navigation items to the menu here if there isn't a NavigationView
+        if (navigationView == null) {
+            getMenuInflater().inflate(R.menu.menu_overflow, menu);
+            return true;
+        }
+        return retValue;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        return NavigationUI.onNavDestinationSelected(
+                item, Navigation.getNavController(this, R.id.my_nav_host_fragment))
+                || super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onSupportNavigateUp() {
+        return NavigationUI.navigateUp(
+                mDrawerLayout, Navigation.getNavController(this, R.id.my_nav_host_fragment)
+        );
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/res/drawable/ic_android.xml b/navigation/integration-tests/testapp/src/main/res/drawable/ic_android.xml
new file mode 100644
index 0000000..fccaeba
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/drawable/ic_android.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
+</vector>
diff --git a/navigation/integration-tests/testapp/src/main/res/drawable/ic_help.xml b/navigation/integration-tests/testapp/src/main/res/drawable/ic_help.xml
new file mode 100644
index 0000000..ec47054
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/drawable/ic_help.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/integration-tests/testapp/src/main/res/drawable/ic_home.xml
similarity index 60%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/integration-tests/testapp/src/main/res/drawable/ic_home.xml
index 660dbcd..fe1c541 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/integration-tests/testapp/src/main/res/drawable/ic_home.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +13,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
+</vector>
diff --git a/navigation/integration-tests/testapp/src/main/res/drawable/ic_settings.xml b/navigation/integration-tests/testapp/src/main/res/drawable/ic_settings.xml
new file mode 100644
index 0000000..aa7973b
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/drawable/ic_settings.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/navigation/integration-tests/testapp/src/main/res/layout-h470dp/navigation_activity.xml b/navigation/integration-tests/testapp/src/main/res/layout-h470dp/navigation_activity.xml
new file mode 100644
index 0000000..186cc05
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout-h470dp/navigation_activity.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/colorPrimary"
+        android:theme="@style/ThemeOverlay.AppCompat.Dark"/>
+    <fragment
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:id="@+id/my_nav_host_fragment"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        app:navGraph="@navigation/nav_main"
+        app:defaultNavHost="true"
+        />
+    <android.support.design.widget.BottomNavigationView
+        android:id="@+id/bottom_nav_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:menu="@menu/menu_bottom_nav"/>
+</LinearLayout>
diff --git a/navigation/integration-tests/testapp/src/main/res/layout-w960dp/navigation_activity.xml b/navigation/integration-tests/testapp/src/main/res/layout-w960dp/navigation_activity.xml
new file mode 100644
index 0000000..0db9801
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout-w960dp/navigation_activity.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <android.support.design.widget.NavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentStart="true"
+        app:elevation="0dp"
+        app:headerLayout="@layout/nav_view_header"
+        app:menu="@menu/menu_nav_drawer"/>
+    <View
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        android:layout_toRightOf="@id/nav_view"
+        android:layout_toEndOf="@id/nav_view"
+        android:background="?android:attr/listDivider"/>
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/colorPrimary"
+        android:theme="@style/ThemeOverlay.AppCompat.Dark"
+        android:layout_toRightOf="@id/nav_view"
+        android:layout_toEndOf="@id/nav_view"
+        android:layout_alignParentTop="true"/>
+    <fragment
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/my_nav_host_fragment"
+        android:name="androidx.navigation.fragment.NavHostFragment"
+        app:navGraph="@navigation/nav_main"
+        app:defaultNavHost="true"
+        android:layout_toRightOf="@id/nav_view"
+        android:layout_toEndOf="@id/nav_view"
+        android:layout_below="@id/toolbar"/>
+</RelativeLayout>
diff --git a/navigation/integration-tests/testapp/src/main/res/layout/activity_help.xml b/navigation/integration-tests/testapp/src/main/res/layout/activity_help.xml
new file mode 100644
index 0000000..793e273
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/activity_help.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/colorPrimary"
+        android:theme="@style/ThemeOverlay.AppCompat.Dark"/>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <TextView
+            android:id="@+id/data"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+    </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/navigation/integration-tests/testapp/src/main/res/layout/android_fragment.xml b/navigation/integration-tests/testapp/src/main/res/layout/android_fragment.xml
new file mode 100644
index 0000000..3201c8b
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/android_fragment.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="8dp">
+    <TextView android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        />
+    <EditText
+        android:id="@+id/edit_args"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="Type your deep link args"/>
+    <Button
+        android:id="@+id/send_notification"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="Send deep link notification"/>
+</LinearLayout>
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/integration-tests/testapp/src/main/res/layout/deep_link_appwidget.xml
similarity index 68%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/integration-tests/testapp/src/main/res/layout/deep_link_appwidget.xml
index 660dbcd..e5661e2 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/integration-tests/testapp/src/main/res/layout/deep_link_appwidget.xml
@@ -1,19 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
+  ~ Copyright (C) 2017 The Android Open Source Project
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
   ~ You may obtain a copy of the License at
-  ~
   ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
   ~ Unless required by applicable law or agreed to in writing, software
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/deep_link"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:text="@string/android" />
diff --git a/navigation/integration-tests/testapp/src/main/res/layout/main_fragment.xml b/navigation/integration-tests/testapp/src/main/res/layout/main_fragment.xml
new file mode 100644
index 0000000..9f797c9
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/main_fragment.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="8dp">
+    <TextView android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        />
+    <Button
+        android:id="@+id/next_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="Navigate next"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/navigation/integration-tests/testapp/src/main/res/layout/nav_view_header.xml b/navigation/integration-tests/testapp/src/main/res/layout/nav_view_header.xml
new file mode 100644
index 0000000..d7be09f
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/nav_view_header.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="?attr/actionBarSize">
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_gravity="bottom"
+        android:background="?android:attr/listDivider"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/navigation/integration-tests/testapp/src/main/res/layout/navigation_activity.xml b/navigation/integration-tests/testapp/src/main/res/layout/navigation_activity.xml
new file mode 100644
index 0000000..7384802
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/navigation_activity.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<android.support.v4.widget.DrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@color/colorPrimary"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark"/>
+        <fragment
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/my_nav_host_fragment"
+            android:name="androidx.navigation.fragment.NavHostFragment"
+            app:navGraph="@navigation/nav_main"
+            app:defaultNavHost="true"
+        />
+    </LinearLayout>
+    <android.support.design.widget.NavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        app:menu="@menu/menu_nav_drawer"/>
+</android.support.v4.widget.DrawerLayout>
diff --git a/navigation/integration-tests/testapp/src/main/res/menu/menu_bottom_nav.xml b/navigation/integration-tests/testapp/src/main/res/menu/menu_bottom_nav.xml
new file mode 100644
index 0000000..2da6461
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/menu/menu_bottom_nav.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@id/launcher_home"
+        android:icon="@drawable/ic_home"
+        android:title="@string/home" />
+    <item
+        android:id="@id/android"
+        android:icon="@drawable/ic_android"
+        android:title="@string/android" />
+</menu>
\ No newline at end of file
diff --git a/navigation/integration-tests/testapp/src/main/res/menu/menu_nav_drawer.xml b/navigation/integration-tests/testapp/src/main/res/menu/menu_nav_drawer.xml
new file mode 100644
index 0000000..4ce2ffb
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/menu/menu_nav_drawer.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <group android:id="@+id/primary">
+        <item
+            android:id="@id/launcher_home"
+            android:icon="@drawable/ic_home"
+            android:title="@string/home" />
+        <item
+            android:id="@id/android"
+            android:icon="@drawable/ic_android"
+            android:title="@string/android" />
+    </group>
+    <item
+        android:id="@id/settings_activity"
+        android:icon="@drawable/ic_settings"
+        android:title="@string/settings" />
+    <item
+        android:id="@id/help_activity"
+        android:icon="@drawable/ic_help"
+        android:title="@string/help" />
+</menu>
\ No newline at end of file
diff --git a/navigation/integration-tests/testapp/src/main/res/menu/menu_overflow.xml b/navigation/integration-tests/testapp/src/main/res/menu/menu_overflow.xml
new file mode 100644
index 0000000..e07453f
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/menu/menu_overflow.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@id/settings_activity"
+        android:icon="@drawable/ic_settings"
+        android:title="@string/settings"
+        android:menuCategory="secondary" />
+    <item
+        android:id="@id/help_activity"
+        android:icon="@drawable/ic_help"
+        android:title="@string/help"
+        android:menuCategory="secondary" />
+</menu>
\ No newline at end of file
diff --git a/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml b/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml
new file mode 100644
index 0000000..24fcf4f
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/navigation/nav_main.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    app:startDestination="@+id/launcher_home"
+    tools:ignore="DuplicateIds">
+    <fragment android:id="@+id/launcher_home"
+              android:label="@string/home"
+              android:name=".MainFragment">
+        <argument android:name="myarg" android:defaultValue="Home" />
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+    </fragment>
+    <fragment android:id="@+id/android"
+              android:label="@string/android"
+              android:name="androidx.navigation.testapp.AndroidFragment">
+        <argument android:name="myarg" android:defaultValue="Android!" />
+        <deepLink app:uri="www.iana.org/domains/{myarg}"/>
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+    </fragment>
+    <fragment android:id="@+id/first_screen"
+        android:name="androidx.navigation.testapp.MainFragment">
+        <argument android:name="myarg" android:defaultValue="one" />
+        <action android:id="@+id/next" app:destination="@+id/next_fragment"/>
+    </fragment>
+    <fragment android:id="@+id/next_fragment"
+        android:name="androidx.navigation.testapp.MainFragment">
+        <argument android:name="myarg" android:defaultValue="two" />
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+    </fragment>
+    <activity android:id="@+id/settings_activity"
+              app:action="android.settings.APPLICATION_DETAILS_SETTINGS"
+              app:data="package:androidx.navigation.testapp"/>
+    <activity android:id="@+id/help_activity"
+              android:name=".HelpActivity"
+              app:dataPattern="http://www.example.com/{topic}">
+        <argument android:name="topic" android:defaultValue="help_topic"/>
+    </activity>
+</navigation>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/integration-tests/testapp/src/main/res/values/colors.xml
similarity index 72%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/integration-tests/testapp/src/main/res/values/colors.xml
index 660dbcd..d0709b5 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/integration-tests/testapp/src/main/res/values/colors.xml
@@ -1,19 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
+  ~ Copyright (C) 2017 The Android Open Source Project
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
   ~ You may obtain a copy of the License at
-  ~
   ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
   ~ Unless required by applicable law or agreed to in writing, software
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/navigation/integration-tests/testapp/src/main/res/values/strings.xml b/navigation/integration-tests/testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0fe66e3
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <string name="app_name">Navigation</string>
+    <string name="drawer_navigation">Drawer Nav</string>
+    <string name="drawer_open">Open Navigation Drawer</string>
+    <string name="drawer_close">Close Navigation Drawer</string>
+    <string name="bottom_navigation">Bottom Nav</string>
+    <string name="home">Home</string>
+    <string name="android">Android</string>
+    <string name="settings">Settings</string>
+    <string name="help">Help</string>
+</resources>
diff --git a/navigation/integration-tests/testapp/src/main/res/values/styles.xml b/navigation/integration-tests/testapp/src/main/res/values/styles.xml
new file mode 100644
index 0000000..41777e2
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/integration-tests/testapp/src/main/res/xml/deep_link_appwidget_info.xml
similarity index 67%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/integration-tests/testapp/src/main/res/xml/deep_link_appwidget_info.xml
index 660dbcd..78cb1fb 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/integration-tests/testapp/src/main/res/xml/deep_link_appwidget_info.xml
@@ -1,19 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
+  ~ Copyright (C) 2017 The Android Open Source Project
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
   ~ You may obtain a copy of the License at
-  ~
   ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
   ~ Unless required by applicable law or agreed to in writing, software
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="110dp"
+    android:minHeight="40dp"
+    android:updatePeriodMillis="0"
+    android:initialLayout="@layout/deep_link_appwidget"/>
diff --git a/navigation/runtime/build.gradle b/navigation/runtime/build.gradle
new file mode 100644
index 0000000..9b8cd0e
--- /dev/null
+++ b/navigation/runtime/build.gradle
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+    api(NAV_SUPPORT_CORE_UTILS)
+    api(project(":navigation:navigation-common"))
+
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(TEST_RUNNER)
+
+    androidTestImplementation(project(":navigation:navigation-testing"))
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Runtime"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2017"
+    description = "Android Navigation-Runtime"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/runtime/ktx/build.gradle b/navigation/runtime/ktx/build.gradle
new file mode 100644
index 0000000..e4de0c6
--- /dev/null
+++ b/navigation/runtime/ktx/build.gradle
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    buildTypes {
+        debug {
+            testCoverageEnabled = false // Breaks Kotlin compiler.
+        }
+    }
+}
+
+dependencies {
+    api(project(":navigation:navigation-runtime"))
+    // Ensure that the -ktx dependency graph mirrors the Java dependency graph
+    api(project(":navigation:navigation-common-ktx"))
+    api(KOTLIN_STDLIB)
+    androidTestImplementation(project(":navigation:navigation-testing-ktx"))
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Runtime Kotlin Extensions"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2018"
+    description = "Android Navigation-Runtime-Ktx"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/runtime/ktx/src/androidTest/AndroidManifest.xml
similarity index 67%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/runtime/ktx/src/androidTest/AndroidManifest.xml
index 660dbcd..a1a7c59 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/runtime/ktx/src/androidTest/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.navigation.ktx.test">
+    <application>
+        <activity android:name="androidx.navigation.TestActivity"/>
+    </application>
+</manifest>
diff --git a/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
new file mode 100644
index 0000000..53704eb
--- /dev/null
+++ b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.net.Uri
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import junit.framework.Assert.assertTrue
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ActivityNavigatorDestinationBuilderTest {
+    private val navController = NavController(InstrumentationRegistry.getTargetContext())
+
+    @Test
+    fun activity() {
+        val graph = navController.createGraph(startDestination = DESTINATION_ID) {
+            activity(DESTINATION_ID) {
+                label = LABEL
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Destination should have label set",
+                LABEL,
+                graph[DESTINATION_ID].label)
+    }
+
+    @Test
+    fun activityClass() {
+        val graph = navController.createGraph(startDestination = DESTINATION_ID) {
+            activity(DESTINATION_ID) {
+                activityClass = TestActivity::class
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Destination should have ComponentName set",
+                TestActivity::class.java.name,
+                (graph[DESTINATION_ID] as ActivityNavigator.Destination).component.className)
+    }
+
+    @Test
+    fun action() {
+        val graph = navController.createGraph(startDestination = DESTINATION_ID) {
+            activity(DESTINATION_ID) {
+                action = ACTION
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Destination should have action set",
+                ACTION,
+                (graph[DESTINATION_ID] as ActivityNavigator.Destination).action)
+    }
+
+    @Test
+    fun data() {
+        val graph = navController.createGraph(startDestination = DESTINATION_ID) {
+            activity(DESTINATION_ID) {
+                data = DATA
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Destination should have data set",
+                DATA,
+                (graph[DESTINATION_ID] as ActivityNavigator.Destination).data)
+    }
+
+    @Test
+    fun dataPattern() {
+        val graph = navController.createGraph(startDestination = DESTINATION_ID) {
+            activity(DESTINATION_ID) {
+                dataPattern = DATA_PATTERN
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Destination should have data pattern set",
+                DATA_PATTERN,
+                (graph[DESTINATION_ID] as ActivityNavigator.Destination).dataPattern)
+    }
+}
+
+private const val DESTINATION_ID = 1
+private const val LABEL = "Test"
+private const val ACTION = "ACTION_TEST"
+private val DATA = Uri.parse("http://www.example.com")
+private const val DATA_PATTERN = "http://www.example.com/{id}"
diff --git a/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ActivityTest.kt b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ActivityTest.kt
new file mode 100644
index 0000000..6468d9a
--- /dev/null
+++ b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ActivityTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.app.Activity
+import android.os.Bundle
+import android.support.test.filters.SmallTest
+import android.support.test.rule.ActivityTestRule
+import android.view.View
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Rule
+import org.junit.Test
+
+@SmallTest
+class ActivityTest {
+    @get:Rule val activityRule = ActivityTestRule<TestActivity>(TestActivity::class.java)
+    private val view get() = activityRule.activity.findViewById<View>(VIEW_ID)
+
+    @Test fun navController() {
+        val navController = NavController(activityRule.activity)
+        view.navController = navController
+        assertTrue("View should have NavController set",
+                activityRule.activity.navController(VIEW_ID) == navController)
+    }
+
+    @Test fun navControllerNull() {
+        try {
+            activityRule.activity.navController(VIEW_ID)
+            fail("navController should throw IllegalStateException if a NavController was not set")
+        } catch (e: IllegalStateException) {
+            // Expected
+        }
+    }
+
+    @Test fun findNavController() {
+        val navController = NavController(activityRule.activity)
+        view.navController = navController
+
+        val foundNavController = activityRule.activity.findNavController(VIEW_ID)
+        assertNotNull("findNavController should return non-null if a NavController was set",
+                foundNavController)
+        assertTrue("View should have NavController set",
+                foundNavController == navController)
+    }
+
+    @Test fun findNavControllerNull() {
+        assertNull("findNavController should return null if a NavController was never set",
+                activityRule.activity.findNavController(VIEW_ID))
+    }
+}
+
+private const val VIEW_ID = 1
+
+class TestActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(View(this).apply {
+            id = VIEW_ID
+        })
+    }
+}
diff --git a/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/NavControllerTest.kt
new file mode 100644
index 0000000..bf6f332
--- /dev/null
+++ b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import androidx.navigation.testing.TestNavigator
+import androidx.navigation.testing.test
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class NavControllerTest {
+    private val navController = NavController(InstrumentationRegistry.getTargetContext()).apply {
+        navigatorProvider += TestNavigator()
+    }
+
+    @Test
+    fun createGraph() {
+        val graph = navController.createGraph(startDestination = DESTINATION_ID) {
+            test(DESTINATION_ID)
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+}
+
+private const val DESTINATION_ID = 1
diff --git a/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/NavHostTest.kt b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/NavHostTest.kt
new file mode 100644
index 0000000..7a74ab2
--- /dev/null
+++ b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/NavHostTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import androidx.navigation.testing.TestNavigator
+import androidx.navigation.testing.test
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class NavHostTest {
+    private val navController = NavController(InstrumentationRegistry.getTargetContext()).apply {
+        navigatorProvider += TestNavigator()
+    }
+    private val navHost = NavHost { this@NavHostTest.navController }
+
+    @Test
+    fun createGraph() {
+        val graph = navHost.createGraph(startDestination = DESTINATION_ID) {
+            test(DESTINATION_ID)
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+}
+
+private const val DESTINATION_ID = 1
diff --git a/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ViewTest.kt b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ViewTest.kt
new file mode 100644
index 0000000..f6d1978
--- /dev/null
+++ b/navigation/runtime/ktx/src/androidTest/java/androidx/navigation/ViewTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import android.view.View
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ViewTest {
+
+    @Test fun navController() {
+        val view = View(InstrumentationRegistry.getTargetContext())
+        val navController = NavController(InstrumentationRegistry.getTargetContext())
+        view.navController = navController
+        assertTrue("View should have NavController set",
+                view.navController == navController)
+    }
+
+    @Test fun navControllerNull() {
+        val view = View(InstrumentationRegistry.getTargetContext())
+        try {
+            view.navController
+            fail("navController should throw IllegalStateException if a NavController was not set")
+        } catch (e: IllegalStateException) {
+            // Expected
+        }
+    }
+
+    @Test fun findNavController() {
+        val view = View(InstrumentationRegistry.getTargetContext())
+        val navController = NavController(InstrumentationRegistry.getTargetContext())
+        view.navController = navController
+
+        val foundNavController = view.findNavController()
+        assertNotNull("findNavController should return non-null if a NavController was set",
+                foundNavController)
+        assertTrue("View should have NavController set",
+                foundNavController == navController)
+    }
+
+    @Test fun findNavControllerNull() {
+        val view = View(InstrumentationRegistry.getTargetContext())
+        assertNull("findNavController should return null if a NavController was never set",
+                view.findNavController())
+    }
+}
diff --git a/car/res/drawable/car_button_ripple_background.xml b/navigation/runtime/ktx/src/main/AndroidManifest.xml
similarity index 83%
copy from car/res/drawable/car_button_ripple_background.xml
copy to navigation/runtime/ktx/src/main/AndroidManifest.xml
index 13d0a49..799fc93 100644
--- a/car/res/drawable/car_button_ripple_background.xml
+++ b/navigation/runtime/ktx/src/main/AndroidManifest.xml
@@ -14,6 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background" />
+<manifest package="androidx.navigation.ktx"/>
diff --git a/navigation/runtime/ktx/src/main/java/androidx/navigation/Activity.kt b/navigation/runtime/ktx/src/main/java/androidx/navigation/Activity.kt
new file mode 100644
index 0000000..dff618a
--- /dev/null
+++ b/navigation/runtime/ktx/src/main/java/androidx/navigation/Activity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.app.Activity
+import android.support.annotation.IdRes
+
+/**
+ * Find a [NavController] given the id of a View and its containing
+ * [Activity].
+ */
+fun Activity.findNavController(@IdRes viewId: Int): NavController? =
+        Navigation.findNavController(this, viewId)
+
+/**
+ * Gets the [NavController] given the id of a View and its containing
+ * [Activity].
+ *
+ * Calling this on a View that is not a [NavHost] or within a [NavHost]
+ * will result in an [IllegalStateException]
+ */
+fun Activity.navController(@IdRes viewId: Int): NavController =
+        Navigation.getNavController(this, viewId)
diff --git a/navigation/runtime/ktx/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt b/navigation/runtime/ktx/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
new file mode 100644
index 0000000..6de930d
--- /dev/null
+++ b/navigation/runtime/ktx/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.navigation
+
+import android.app.Activity
+import android.content.ComponentName
+import android.net.Uri
+import android.support.annotation.IdRes
+import kotlin.reflect.KClass
+
+/**
+ * Construct a new [ActivityNavigator.Destination]
+ */
+inline fun NavGraphBuilder.activity(
+        @IdRes id: Int,
+        block: ActivityNavigatorDestinationBuilder.() -> Unit
+) = destination(ActivityNavigatorDestinationBuilder(
+        provider[ActivityNavigator::class],
+        id
+).apply(block))
+
+/**
+ * DSL for constructing a new [ActivityNavigator.Destination]
+ */
+@NavDestinationDsl
+class ActivityNavigatorDestinationBuilder(
+        navigator: ActivityNavigator,
+        @IdRes id: Int
+) : NavDestinationBuilder<ActivityNavigator.Destination>(navigator, id) {
+    private val context = navigator.context
+
+    var activityClass: KClass<out Activity>? = null
+
+    var action: String? = null
+
+    var data: Uri? = null
+
+    var dataPattern: String? = null
+
+    override fun build(): ActivityNavigator.Destination =
+            super.build().also { destination ->
+                activityClass?.let { clazz ->
+                    destination.setComponentName(ComponentName(context, clazz.java))
+                }
+                destination.action = action
+                destination.data = data
+                destination.dataPattern = dataPattern
+            }
+}
diff --git a/navigation/runtime/ktx/src/main/java/androidx/navigation/NavController.kt b/navigation/runtime/ktx/src/main/java/androidx/navigation/NavController.kt
new file mode 100644
index 0000000..76fb4dd
--- /dev/null
+++ b/navigation/runtime/ktx/src/main/java/androidx/navigation/NavController.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.annotation.IdRes
+
+/**
+ * Construct a new [NavGraph]
+ */
+inline fun NavController.createGraph(
+        @IdRes id: Int = 0,
+        @IdRes startDestination: Int,
+        block: NavGraphBuilder.() -> Unit
+): NavGraph = navigatorProvider.navigation(id, startDestination, block)
diff --git a/navigation/runtime/ktx/src/main/java/androidx/navigation/NavHost.kt b/navigation/runtime/ktx/src/main/java/androidx/navigation/NavHost.kt
new file mode 100644
index 0000000..1a3bc05
--- /dev/null
+++ b/navigation/runtime/ktx/src/main/java/androidx/navigation/NavHost.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.support.annotation.IdRes
+
+/**
+ * Construct a new [NavGraph]
+ */
+inline fun NavHost.createGraph(
+        @IdRes id: Int = 0,
+        @IdRes startDestination: Int,
+        block: NavGraphBuilder.() -> Unit
+): NavGraph = navController.createGraph(id, startDestination, block)
diff --git a/navigation/runtime/ktx/src/main/java/androidx/navigation/View.kt b/navigation/runtime/ktx/src/main/java/androidx/navigation/View.kt
new file mode 100644
index 0000000..2f5cd6c
--- /dev/null
+++ b/navigation/runtime/ktx/src/main/java/androidx/navigation/View.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation
+
+import android.view.View
+
+/**
+ * Find a [NavController] associated with a [View].
+ */
+fun View.findNavController(): NavController? =
+        Navigation.findNavController(this)
+
+/**
+ * Property for the [NavController] associated with a [View].
+ *
+ * Calling view.navController on a View not within a [NavHost] will result in an
+ * [IllegalStateException]
+ */
+var View.navController: NavController
+    get() = Navigation.getNavController(this)
+    set(value) {
+        Navigation.setViewNavController(this, value)
+    }
diff --git a/navigation/runtime/lint-baseline.xml b/navigation/runtime/lint-baseline.xml
new file mode 100644
index 0000000..10e3040
--- /dev/null
+++ b/navigation/runtime/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0">
+
+</issues>
\ No newline at end of file
diff --git a/navigation/runtime/proguard-rules.pro b/navigation/runtime/proguard-rules.pro
new file mode 100644
index 0000000..bb2cc55
--- /dev/null
+++ b/navigation/runtime/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /mnt/android-ssd/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/runtime/src/androidTest/AndroidManifest.xml
similarity index 73%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/runtime/src/androidTest/AndroidManifest.xml
index 660dbcd..394b021 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/runtime/src/androidTest/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.navigation.test">
+    <application>
+    </application>
+</manifest>
diff --git a/navigation/runtime/src/androidTest/java/androidx/navigation/NavControllerTest.java b/navigation/runtime/src/androidTest/java/androidx/navigation/NavControllerTest.java
new file mode 100644
index 0000000..46c9705
--- /dev/null
+++ b/navigation/runtime/src/androidTest/java/androidx/navigation/NavControllerTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v4.app.TaskStackBuilder;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import androidx.navigation.test.R;
+import androidx.navigation.testing.TestNavigator;
+
+@SmallTest
+public class NavControllerTest {
+    private static final String TEST_ARG = "test";
+    private static final String TEST_ARG_VALUE = "value";
+    private static final String TEST_OVERRIDDEN_VALUE_ARG = "test_overriden_value";
+    private static final String TEST_OVERRIDDEN_VALUE_ARG_VALUE = "override";
+
+    @Test
+    public void testStartDestination() {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_start_destination);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testMissingStartDestination() {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_missing_start_destination);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidStartDestination() {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_invalid_start_destination);
+    }
+
+    @Test
+    public void testNestedStartDestination() {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_nested_start_destination);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.nested_test));
+    }
+
+    @Test
+    public void testSetGraph() throws Throwable {
+        NavController navController = createNavController();
+        assertThat(navController.getGraph(), is(nullValue(NavGraph.class)));
+
+        navController.setGraph(R.navigation.nav_start_destination);
+        assertThat(navController.getGraph(), is(notNullValue(NavGraph.class)));
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+    }
+
+    @Test
+    public void testNavigate() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+    }
+
+    @Test
+    public void testSaveRestoreStateXml() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        NavController navController = new NavController(context);
+        TestNavigator navigator = new TestNavigator();
+        navController.getNavigatorProvider().addNavigator(navigator);
+        navController.setGraph(R.navigation.nav_simple);
+        navController.navigate(R.id.second_test);
+
+        Bundle savedState = navController.saveState();
+        navController = new NavController(context);
+        navController.getNavigatorProvider().addNavigator(navigator);
+
+        // Restore state should automatically re-inflate the graph
+        // Since the graph has a set id
+        navController.restoreState(savedState);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+    }
+
+    @Test
+    public void testSaveRestoreStateProgrammatic() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        NavController navController = new NavController(context);
+        TestNavigator navigator = new TestNavigator();
+        navController.getNavigatorProvider().addNavigator(navigator);
+        NavGraph graph = new NavInflater(context, navController.getNavigatorProvider())
+                .inflate(R.navigation.nav_simple);
+        navController.setGraph(graph);
+        navController.navigate(R.id.second_test);
+
+        Bundle savedState = navController.saveState();
+        navController = new NavController(context);
+        navController.getNavigatorProvider().addNavigator(navigator);
+
+        // Restore state doesn't recreate any graph
+        navController.restoreState(savedState);
+        assertThat(navController.getGraph(), is(nullValue(NavGraph.class)));
+
+        // Explicitly setting a graph then restores the state
+        navController.setGraph(graph);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+    }
+
+    @Test
+    public void testNavigateWithNoDefaultValue() throws Throwable {
+        Bundle returnedArgs = navigateWithArgs(null);
+
+        // Test that arguments without a default value aren't passed through at all
+        assertThat(returnedArgs.containsKey("test_no_default_value"), is(false));
+    }
+
+    @Test
+    public void testNavigateWithDefaultArgs() throws Throwable {
+        Bundle returnedArgs = navigateWithArgs(null);
+
+        // Test that default values are passed through
+        assertThat(returnedArgs.getString("test_default_value"), is("default"));
+    }
+
+    @Test
+    public void testNavigateWithArgs() throws Throwable {
+        Bundle args = new Bundle();
+        args.putString(TEST_ARG, TEST_ARG_VALUE);
+        Bundle returnedArgs = navigateWithArgs(args);
+
+        // Test that programmatically constructed arguments are passed through
+        assertThat(returnedArgs.getString(TEST_ARG), is(TEST_ARG_VALUE));
+    }
+
+    @Test
+    public void testNavigateWithOverriddenDefaultArgs() throws Throwable {
+        Bundle args = new Bundle();
+        args.putString(TEST_OVERRIDDEN_VALUE_ARG, TEST_OVERRIDDEN_VALUE_ARG_VALUE);
+        Bundle returnedArgs = navigateWithArgs(args);
+
+        // Test that default values can be overridden by programmatic values
+        assertThat(returnedArgs.getString(TEST_OVERRIDDEN_VALUE_ARG),
+                is(TEST_OVERRIDDEN_VALUE_ARG_VALUE));
+    }
+
+    private Bundle navigateWithArgs(Bundle args) throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_arguments);
+
+        navController.navigate(R.id.second_test, args);
+
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        args = navigator.mBackStack.peekLast().second;
+        assertThat(args, is(notNullValue(Bundle.class)));
+
+        return args;
+    }
+
+    @Test
+    public void testNavigateThenPop() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        navController.popBackStack();
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+    }
+
+    @Test
+    public void testNavigateThenNavigateUp() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        // This should function identically to popBackStack()
+        navController.navigateUp();
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+    }
+
+    @Test
+    public void testNavigateViaAction() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(1));
+
+        navController.navigate(R.id.second);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+    }
+
+    @Test
+    public void testNavigateOptionSingleTop() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        navController.navigate(R.id.self);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        assertThat(navigator.mBackStack.size(), is(2));
+    }
+
+    @Test
+    public void testNavigateOptionPopUpToInAction() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        navController.navigate(R.id.finish);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+    }
+
+    @Test
+    public void testNavigateWithPopUpOptionsOnly() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        NavOptions navOptions = new NavOptions.Builder().setPopUpTo(R.id.start_test, false).build();
+        // the same as to call .navigate(R.id.finish)
+        navController.navigate(0, null, navOptions);
+
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+    }
+
+    @Test
+    public void testNoDestinationNoPopUpTo() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        NavOptions options = new NavOptions.Builder().build();
+        try {
+            navController.navigate(0, null, options);
+            Assert.fail("navController.navigate must throw");
+        } catch (IllegalArgumentException e) {
+            // expected exception
+        }
+    }
+
+    @Test
+    public void testNavigateOptionPopSelf() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+        navController.navigate(R.id.second_test);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test));
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        assertThat(navigator.mBackStack.size(), is(2));
+
+        navController.navigate(R.id.finish_self);
+        assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test));
+        assertThat(navigator.mBackStack.size(), is(1));
+    }
+
+    @Test
+    public void testNavigateViaActionWithArgs() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_arguments);
+
+        Bundle args = new Bundle();
+        args.putString(TEST_ARG, TEST_ARG_VALUE);
+        args.putString(TEST_OVERRIDDEN_VALUE_ARG, TEST_OVERRIDDEN_VALUE_ARG_VALUE);
+        navController.navigate(R.id.second, args);
+
+        TestNavigator navigator = navController.getNavigatorProvider()
+                .getNavigator(TestNavigator.class);
+        Bundle returnedArgs = navigator.mBackStack.peekLast().second;
+        assertThat(returnedArgs, is(notNullValue(Bundle.class)));
+
+        // Test that arguments without a default value aren't passed through at all
+        assertThat(returnedArgs.containsKey("test_no_default_value"), is(false));
+        // Test that default values are passed through
+        assertThat(returnedArgs.getString("test_default_value"), is("default"));
+        // Test that programmatically constructed arguments are passed through
+        assertThat(returnedArgs.getString(TEST_ARG), is(TEST_ARG_VALUE));
+        // Test that default values can be overridden by programmatic values
+        assertThat(returnedArgs.getString(TEST_OVERRIDDEN_VALUE_ARG),
+                is(TEST_OVERRIDDEN_VALUE_ARG_VALUE));
+    }
+
+    @Test
+    public void testDeepLinkFromNavGraph() throws Throwable {
+        NavController navController = createNavController();
+        navController.setGraph(R.navigation.nav_simple);
+
+        TaskStackBuilder taskStackBuilder = navController.createDeepLink()
+                .setDestination(R.id.second_test)
+                .createTaskStackBuilder();
+        assertThat(taskStackBuilder, is(notNullValue(TaskStackBuilder.class)));
+        assertThat(taskStackBuilder.getIntentCount(), is(1));
+    }
+
+    private NavController createNavController() {
+        NavController navController = new NavController(InstrumentationRegistry.getTargetContext());
+        TestNavigator navigator = new TestNavigator();
+        navController.getNavigatorProvider().addNavigator(navigator);
+        return navController;
+    }
+}
diff --git a/navigation/runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.java b/navigation/runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.java
new file mode 100644
index 0000000..db4101b
--- /dev/null
+++ b/navigation/runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.v4.util.Pair;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import androidx.navigation.test.R;
+import androidx.navigation.testing.TestNavigatorProvider;
+
+@SmallTest
+public class NavInflaterTest {
+    private Instrumentation mInstrumentation;
+
+    @Before
+    public void getInstrumentation() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testInflateSimple() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        NavInflater navInflater = new NavInflater(context, new TestNavigatorProvider(context));
+        NavGraph graph = navInflater.inflate(R.navigation.nav_simple);
+
+        assertThat(graph, is(notNullValue(NavGraph.class)));
+        assertThat(graph.getStartDestination(), is(R.id.start_test));
+    }
+
+    @Test
+    public void testInflateDeepLinkWithApplicationId() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        NavInflater navInflater = new NavInflater(context, new TestNavigatorProvider(context));
+        NavGraph graph = navInflater.inflate(R.navigation.nav_simple);
+
+        assertThat(graph, is(notNullValue(NavGraph.class)));
+        Uri expectedUri = Uri.parse("android-app://"
+                + mInstrumentation.getTargetContext().getPackageName() + "/test");
+        Pair<NavDestination, Bundle> result = graph.matchDeepLink(expectedUri);
+        assertThat(result, is(notNullValue()));
+        assert result != null;
+        assertThat(result.first, is(notNullValue(NavDestination.class)));
+        assertThat(result.first.getId(), is(R.id.second_test));
+    }
+
+    @Test
+    public void testDefaultArgumentsInteger() {
+        Bundle defaultArguments = inflateDefaultArgumentsFromGraph();
+
+        assertThat(defaultArguments.getInt("test_int"), is(12));
+    }
+
+    @Test
+    public void testDefaultArgumentsDimen() {
+        Bundle defaultArguments = inflateDefaultArgumentsFromGraph();
+        Context context = InstrumentationRegistry.getTargetContext();
+        int expectedValue = context.getResources().getDimensionPixelSize(R.dimen.test_dimen_arg);
+
+        assertThat(defaultArguments.getInt("test_dimen"), is(expectedValue));
+    }
+
+    @Test
+    public void testDefaultArgumentsFloat() {
+        Bundle defaultArguments = inflateDefaultArgumentsFromGraph();
+
+        assertThat(defaultArguments.getFloat("test_float"), is(3.14f));
+    }
+
+    @Test
+    public void testDefaultArgumentsReference() {
+        Bundle defaultArguments = inflateDefaultArgumentsFromGraph();
+        Context context = InstrumentationRegistry.getTargetContext();
+        int expectedValue = context.getColor(R.color.test_reference_arg);
+
+        assertThat(defaultArguments.getInt("test_reference"), is(expectedValue));
+    }
+
+    private Bundle inflateDefaultArgumentsFromGraph() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        NavInflater navInflater = new NavInflater(context, new TestNavigatorProvider(context));
+        NavGraph graph = navInflater.inflate(R.navigation.nav_default_arguments);
+
+        NavDestination startDestination = graph.findNode(graph.getStartDestination());
+        Bundle defaultArguments = startDestination.getDefaultArguments();
+
+        assertThat(defaultArguments, is(notNullValue(Bundle.class)));
+        return defaultArguments;
+    }
+}
diff --git a/navigation/runtime/src/androidTest/res/navigation/nav_arguments.xml b/navigation/runtime/src/androidTest/res/navigation/nav_arguments.xml
new file mode 100644
index 0000000..c264db2
--- /dev/null
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_arguments.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/start_test">
+
+    <test android:id="@+id/start_test">
+        <action android:id="@+id/second" app:destination="@+id/second_test" />
+    </test>
+
+    <test android:id="@+id/second_test">
+        <argument android:name="test_no_default_value" />
+        <argument android:name="test_default_value" android:defaultValue="default" />
+        <argument android:name="test_overridden_value" android:defaultValue="default" />
+    </test>
+</navigation>
diff --git a/navigation/runtime/src/androidTest/res/navigation/nav_default_arguments.xml b/navigation/runtime/src/androidTest/res/navigation/nav_default_arguments.xml
new file mode 100644
index 0000000..495d9e0
--- /dev/null
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_default_arguments.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/start_test">
+
+    <test android:id="@+id/start_test">
+        <argument android:name="test_int" android:defaultValue="12" />
+        <argument android:name="test_dimen" android:defaultValue="@dimen/test_dimen_arg" />
+        <argument android:name="test_float" android:defaultValue="3.14" />
+        <argument android:name="test_reference" android:defaultValue="@color/test_reference_arg" />
+    </test>
+</navigation>
diff --git a/navigation/runtime/src/androidTest/res/navigation/nav_invalid_start_destination.xml b/navigation/runtime/src/androidTest/res/navigation/nav_invalid_start_destination.xml
new file mode 100644
index 0000000..1d17431
--- /dev/null
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_invalid_start_destination.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/second_test">
+
+    <test android:id="@+id/start_test" />
+
+    <navigation android:id="@+id/embedded_nav">
+        <test android:id="@+id/second_test" />
+    </navigation>
+</navigation>
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/runtime/src/androidTest/res/navigation/nav_missing_start_destination.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/runtime/src/androidTest/res/navigation/nav_missing_start_destination.xml
index 16b1d0c..5e2a82a 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_missing_start_destination.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+<navigation xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <test android:id="@+id/start_test" />
+
+</navigation>
diff --git a/navigation/runtime/src/androidTest/res/navigation/nav_nested_start_destination.xml b/navigation/runtime/src/androidTest/res/navigation/nav_nested_start_destination.xml
new file mode 100644
index 0000000..03d80cb
--- /dev/null
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_nested_start_destination.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/nested">
+
+    <navigation
+        android:id="@+id/nested"
+        app:startDestination="@+id/nested_test">
+
+        <test android:id="@+id/nested_test"/>
+    </navigation>
+</navigation>
\ No newline at end of file
diff --git a/navigation/runtime/src/androidTest/res/navigation/nav_simple.xml b/navigation/runtime/src/androidTest/res/navigation/nav_simple.xml
new file mode 100644
index 0000000..ff71fc3
--- /dev/null
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_simple.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/start_test">
+
+    <test android:id="@+id/start_test">
+        <action android:id="@+id/second" app:destination="@+id/second_test" />
+    </test>
+
+    <test android:id="@+id/second_test">
+        <action android:id="@+id/self" app:destination="@+id/second_test"
+            app:launchSingleTop="true" />
+        <action android:id="@+id/finish" app:popUpTo="@id/start_test" />
+        <action android:id="@+id/finish_self" app:popUpTo="@id/second_test"
+            app:popUpToInclusive="true" />
+        <deepLink app:uri="android-app://androidx.navigation.test/test" />
+    </test>
+</navigation>
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/runtime/src/androidTest/res/navigation/nav_start_destination.xml
similarity index 66%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/runtime/src/androidTest/res/navigation/nav_start_destination.xml
index 660dbcd..971ebb4 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/runtime/src/androidTest/res/navigation/nav_start_destination.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            app:startDestination="@+id/start_test">
+
+    <test android:id="@+id/start_test" />
+</navigation>
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/runtime/src/androidTest/res/values/colors.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/runtime/src/androidTest/res/values/colors.xml
index 16b1d0c..7e64182 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/runtime/src/androidTest/res/values/colors.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+
+<resources>
+    <color name="test_reference_arg">#F00</color>
+</resources>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/runtime/src/androidTest/res/values/dimens.xml
similarity index 76%
rename from car/res/drawable/car_button_ripple_background_day.xml
rename to navigation/runtime/src/androidTest/res/values/dimens.xml
index 16b1d0c..ce1a380 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/runtime/src/androidTest/res/values/dimens.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+
+<resources>
+    <dimen name="test_dimen_arg">48dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/runtime/src/main/AndroidManifest.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/runtime/src/main/AndroidManifest.xml
index 16b1d0c..db6466c 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/runtime/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.navigation">
+
+</manifest>
diff --git a/navigation/runtime/src/main/java/androidx/navigation/ActivityNavigator.java b/navigation/runtime/src/main/java/androidx/navigation/ActivityNavigator.java
new file mode 100644
index 0000000..f8f9092
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/ActivityNavigator.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * ActivityNavigator implements cross-activity navigation.
+ */
+@Navigator.Name("activity")
+public class ActivityNavigator extends Navigator<ActivityNavigator.Destination> {
+    private static final String EXTRA_NAV_SOURCE =
+            "android-support-navigation:ActivityNavigator:source";
+    private static final String EXTRA_NAV_CURRENT =
+            "android-support-navigation:ActivityNavigator:current";
+
+    private Context mContext;
+    private Activity mHostActivity;
+
+    public ActivityNavigator(@NonNull Context context) {
+        mContext = context;
+        while (context instanceof ContextWrapper) {
+            if (context instanceof Activity) {
+                mHostActivity = (Activity) context;
+                break;
+            }
+            context = ((ContextWrapper) context).getBaseContext();
+        }
+    }
+
+    @NonNull
+    Context getContext() {
+        return mContext;
+    }
+
+    @NonNull
+    @Override
+    public Destination createDestination() {
+        return new Destination(this);
+    }
+
+    @Override
+    public boolean popBackStack() {
+        if (mHostActivity != null) {
+            int destId = 0;
+            final Intent intent = mHostActivity.getIntent();
+            if (intent != null) {
+                destId = intent.getIntExtra(EXTRA_NAV_SOURCE, 0);
+            }
+            mHostActivity.finish();
+            dispatchOnNavigatorNavigated(destId, BACK_STACK_DESTINATION_POPPED);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void navigate(@NonNull Destination destination, @Nullable Bundle args,
+            @Nullable NavOptions navOptions) {
+        if (destination.getIntent() == null) {
+            throw new IllegalStateException("Destination " + destination.getId()
+                    + " does not have an Intent set.");
+        }
+        Intent intent = new Intent(destination.getIntent());
+        if (args != null) {
+            intent.putExtras(args);
+            String dataPattern = destination.getDataPattern();
+            if (!TextUtils.isEmpty(dataPattern)) {
+                // Fill in the data pattern with the args to build a valid URI
+                StringBuffer data = new StringBuffer();
+                Pattern fillInPattern = Pattern.compile("\\{(.+?)\\}");
+                Matcher matcher = fillInPattern.matcher(dataPattern);
+                while (matcher.find()) {
+                    String argName = matcher.group(1);
+                    if (args.containsKey(argName)) {
+                        matcher.appendReplacement(data, "");
+                        data.append(Uri.encode(args.getString(argName)));
+                    } else {
+                        throw new IllegalArgumentException("Could not find " + argName + " in "
+                                + args + " to fill data pattern " + dataPattern);
+                    }
+                }
+                matcher.appendTail(data);
+                intent.setData(Uri.parse(data.toString()));
+            }
+        }
+        if (navOptions != null && navOptions.shouldClearTask()) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+        if (navOptions != null && navOptions.shouldLaunchDocument()
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+        } else if (!(mContext instanceof Activity)) {
+            // If we're not launching from an Activity context we have to launch in a new task.
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        if (navOptions != null && navOptions.shouldLaunchSingleTop()) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        }
+        if (mHostActivity != null) {
+            final Intent hostIntent = mHostActivity.getIntent();
+            if (hostIntent != null) {
+                final int hostCurrentId = hostIntent.getIntExtra(EXTRA_NAV_CURRENT, 0);
+                if (hostCurrentId != 0) {
+                    intent.putExtra(EXTRA_NAV_SOURCE, hostCurrentId);
+                }
+            }
+        }
+        final int destId = destination.getId();
+        intent.putExtra(EXTRA_NAV_CURRENT, destId);
+        NavOptions.addPopAnimationsToIntent(intent, navOptions);
+        mContext.startActivity(intent);
+        if (navOptions != null && mHostActivity != null) {
+            int enterAnim = navOptions.getEnterAnim();
+            int exitAnim = navOptions.getExitAnim();
+            if (enterAnim != -1 || exitAnim != -1) {
+                enterAnim = enterAnim != -1 ? enterAnim : 0;
+                exitAnim = exitAnim != -1 ? exitAnim : 0;
+                mHostActivity.overridePendingTransition(enterAnim, exitAnim);
+            }
+        }
+
+        // You can't pop the back stack from the caller of a new Activity,
+        // so we don't add this navigator to the controller's back stack
+        dispatchOnNavigatorNavigated(destId, BACK_STACK_UNCHANGED);
+    }
+
+    /**
+     * NavDestination for activity navigation
+     */
+    public static class Destination extends NavDestination {
+        private Intent mIntent;
+        private String mDataPattern;
+
+        /**
+         * Construct a new activity destination. This destination is not valid until you set the
+         * Intent via {@link #setIntent(Intent)} or one or more of the other set method.
+         *
+         *
+         * @param navigatorProvider The {@link NavController} which this destination
+         *                          will be associated with.
+         */
+        public Destination(@NonNull NavigatorProvider navigatorProvider) {
+            this(navigatorProvider.getNavigator(ActivityNavigator.class));
+        }
+
+        /**
+         * Construct a new activity destination. This destination is not valid until you set the
+         * Intent via {@link #setIntent(Intent)} or one or more of the other set method.
+         *
+         * @param activityNavigator The {@link ActivityNavigator} which this destination
+         *                          will be associated with. Generally retrieved via a
+         *                          {@link NavController}'s
+         *                          {@link NavigatorProvider#getNavigator(Class)} method.
+         */
+        public Destination(@NonNull Navigator<? extends Destination> activityNavigator) {
+            super(activityNavigator);
+        }
+
+        @Override
+        public void onInflate(@NonNull Context context, @NonNull AttributeSet attrs) {
+            super.onInflate(context, attrs);
+            TypedArray a = context.getResources().obtainAttributes(attrs,
+                    R.styleable.ActivityNavigator);
+            String cls = a.getString(R.styleable.ActivityNavigator_android_name);
+            if (!TextUtils.isEmpty(cls)) {
+                // TODO Replace with ComponentName.createRelative() when minSdkVersion is 23
+                if (cls.charAt(0) == '.') {
+                    cls = context.getPackageName() + cls;
+                }
+                setComponentName(new ComponentName(context, cls));
+            }
+            setAction(a.getString(R.styleable.ActivityNavigator_action));
+            String data = a.getString(R.styleable.ActivityNavigator_data);
+            if (data != null) {
+                setData(Uri.parse(data));
+            }
+            setDataPattern(a.getString(R.styleable.ActivityNavigator_dataPattern));
+            a.recycle();
+        }
+
+        /**
+         * Set the Intent to start when navigating to this destination.
+         * @param intent Intent to associated with this destination.
+         * @return this {@link Destination}
+         */
+        public Destination setIntent(Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Gets the Intent associated with this destination.
+         * @return
+         */
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        /**
+         * Set an explicit {@link ComponentName} to navigate to.
+         *
+         * @param name The component name of the Activity to start.
+         * @return this {@link Destination}
+         */
+        public Destination setComponentName(ComponentName name) {
+            if (mIntent == null) {
+                mIntent = new Intent();
+            }
+            mIntent.setComponent(name);
+            return this;
+        }
+
+        /**
+         * Get the explicit {@link ComponentName} associated with this destination, if any
+         * @return
+         */
+        public ComponentName getComponent() {
+            if (mIntent == null) {
+                return null;
+            }
+            return mIntent.getComponent();
+        }
+
+        /**
+         * Sets the action sent when navigating to this destination.
+         * @param action The action string to use.
+         * @return this {@link Destination}
+         */
+        public Destination setAction(String action) {
+            if (mIntent == null) {
+                mIntent = new Intent();
+            }
+            mIntent.setAction(action);
+            return this;
+        }
+
+        /**
+         * Get the action used to start the Activity, if any
+         */
+        public String getAction() {
+            if (mIntent == null) {
+                return null;
+            }
+            return mIntent.getAction();
+        }
+
+        /**
+         * Sets a static data URI that is sent when navigating to this destination.
+         *
+         * <p>To use a dynamic URI that changes based on the arguments passed in when navigating,
+         * use {@link #setDataPattern(String)}, which will take precedence when arguments are
+         * present.</p>
+         *
+         * @param data A static URI that should always be used.
+         * @see #setDataPattern(String)
+         * @return this {@link Destination}
+         */
+        public Destination setData(Uri data) {
+            if (mIntent == null) {
+                mIntent = new Intent();
+            }
+            mIntent.setData(data);
+            return this;
+        }
+
+        /**
+         * Get the data URI used to start the Activity, if any
+         */
+        public Uri getData() {
+            if (mIntent == null) {
+                return null;
+            }
+            return mIntent.getData();
+        }
+
+        /**
+         * Sets a dynamic data URI pattern that is sent when navigating to this destination.
+         *
+         * <p>If a non-null arguments Bundle is present when navigating, any segments in the form
+         * <code>{argName}</code> will be replaced with a URI encoded string from the arguments.</p>
+         * @param dataPattern A URI pattern with segments in the form of <code>{argName}</code> that
+         *                    will be replaced with URI encoded versions of the Strings in the
+         *                    arguments Bundle.
+         * @see #setData
+         * @return this {@link Destination}
+         */
+        public Destination setDataPattern(String dataPattern) {
+            mDataPattern = dataPattern;
+            return this;
+        }
+
+        /**
+         * Gets the dynamic data URI pattern, if any
+         */
+        public String getDataPattern() {
+            return mDataPattern;
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/androidx/navigation/NavController.java b/navigation/runtime/src/main/java/androidx/navigation/NavController.java
new file mode 100644
index 0000000..cacb9fd
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/NavController.java
@@ -0,0 +1,699 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NavigationRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.TaskStackBuilder;
+import android.support.v4.util.Pair;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * NavController manages app navigation within a {@link NavHost}.
+ *
+ * <p>Apps will generally obtain a controller directly from a host, or by using one of the utility
+ * methods on the {@link Navigation} class rather than create a controller directly.</p>
+ *
+ * <p>Navigation flows and destinations are determined by the
+ * {@link NavGraph navigation graph} owned by the controller. These graphs are typically
+ * {@link #getNavInflater() inflated} from an Android resource, but, like views, they can also
+ * be constructed or combined programmatically or for the case of dynamic navigation structure.
+ * (For example, if the navigation structure of the application is determined by live data obtained'
+ * from a remote server.)</p>
+ */
+public class NavController {
+    private static final String KEY_GRAPH_ID = "android-support-nav:controller:graphId";
+    private static final String KEY_BACK_STACK_IDS = "android-support-nav:controller:backStackIds";
+    static final String KEY_DEEP_LINK_IDS = "android-support-nav:controller:deepLinkIds";
+    static final String KEY_DEEP_LINK_EXTRAS =
+            "android-support-nav:controller:deepLinkExtras";
+    /**
+     * The {@link Intent} that triggered a deep link to the current destination.
+     */
+    public static final String KEY_DEEP_LINK_INTENT =
+            "android-support-nav:controller:deepLinkIntent";
+
+    private final Context mContext;
+    private Activity mActivity;
+    private NavInflater mInflater;
+    private NavGraph mGraph;
+    private int mGraphId;
+    private int[] mBackStackToRestore;
+
+    private final Deque<NavDestination> mBackStack = new ArrayDeque<>();
+
+    private final SimpleNavigatorProvider mNavigatorProvider = new SimpleNavigatorProvider() {
+        @Nullable
+        @Override
+        public Navigator<? extends NavDestination> addNavigator(@NonNull String name,
+                @NonNull Navigator<? extends NavDestination> navigator) {
+            Navigator<? extends NavDestination> previousNavigator =
+                    super.addNavigator(name, navigator);
+            if (previousNavigator != navigator) {
+                if (previousNavigator != null) {
+                    previousNavigator.removeOnNavigatorNavigatedListener(mOnNavigatedListener);
+                }
+                navigator.addOnNavigatorNavigatedListener(mOnNavigatedListener);
+            }
+            return previousNavigator;
+        }
+    };
+
+    private final Navigator.OnNavigatorNavigatedListener mOnNavigatedListener =
+            new Navigator.OnNavigatorNavigatedListener() {
+                @Override
+                public void onNavigatorNavigated(Navigator navigator, @IdRes int destId,
+                        @Navigator.BackStackEffect int backStackEffect) {
+                    if (destId != 0) {
+                        NavDestination newDest = findDestination(destId);
+                        if (newDest == null) {
+                            throw new IllegalArgumentException("Navigator " + navigator
+                                    + " reported navigation to unknown destination id "
+                                    + NavDestination.getDisplayName(mContext, destId));
+                        }
+                        switch (backStackEffect) {
+                            case Navigator.BACK_STACK_DESTINATION_POPPED:
+                                while (!mBackStack.isEmpty()
+                                        && mBackStack.peekLast().getId() != destId) {
+                                    mBackStack.removeLast();
+                                }
+                                break;
+                            case Navigator.BACK_STACK_DESTINATION_ADDED:
+                                mBackStack.add(newDest);
+                                break;
+                            case Navigator.BACK_STACK_UNCHANGED:
+                                // Don't update the back stack and don't dispatchOnNavigated
+                                return;
+                        }
+                        dispatchOnNavigated(newDest);
+                    }
+                }
+            };
+
+    private final CopyOnWriteArrayList<OnNavigatedListener> mOnNavigatedListeners =
+            new CopyOnWriteArrayList<>();
+
+    /**
+     * OnNavigatorNavigatedListener receives a callback when the associated controller
+     * navigates to a new destination.
+     */
+    public interface OnNavigatedListener {
+        /**
+         * onNavigatorNavigated is called when the controller navigates to a new destination.
+         * This navigation may be to a destination that has not been seen before, or one that
+         * was previously on the back stack. This method is called after navigation is complete,
+         * but associated transitions may still be playing.
+         *
+         * @param controller the controller that navigated
+         * @param destination the new destination
+         */
+        void onNavigated(@NonNull NavController controller, @NonNull NavDestination destination);
+    }
+
+    /**
+     * Constructs a new controller for a given {@link Context}. Controllers should not be
+     * used outside of their context and retain a hard reference to the context supplied.
+     * If you need a global controller, pass {@link Context#getApplicationContext()}.
+     *
+     * <p>Apps should generally not construct controllers, instead obtain a relevant controller
+     * directly from a navigation host via {@link NavHost#getNavController()} or by using one of
+     * the utility methods on the {@link Navigation} class.</p>
+     *
+     * <p>Note that controllers that are not constructed with an {@link Activity} context
+     * (or a wrapped activity context) will only be able to navigate to
+     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK new tasks} or
+     * {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT new document tasks} when
+     * navigating to new activities.</p>
+     *
+     * @param context context for this controller
+     */
+    public NavController(@NonNull Context context) {
+        mContext = context;
+        while (context instanceof ContextWrapper) {
+            if (context instanceof Activity) {
+                mActivity = (Activity) context;
+                break;
+            }
+            context = ((ContextWrapper) context).getBaseContext();
+        }
+        mNavigatorProvider.addNavigator(new NavGraphNavigator(mContext));
+        mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
+    }
+
+    @NonNull
+    Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Retrieve the NavController's {@link NavigatorProvider}. All {@link Navigator Navigators} used
+     * to construct the {@link NavGraph navigation graph} for this nav controller should be added
+     * to this navigator provider before the graph is constructed.
+     * <p>
+     * Generally, the Navigators are set for you by the {@link NavHost} hosting this NavController
+     * and you do not need to manually interact with the navigator provider.
+     * </p>
+     * @return The {@link NavigatorProvider} used by this NavController.
+     */
+    @NonNull
+    public NavigatorProvider getNavigatorProvider() {
+        return mNavigatorProvider;
+    }
+
+    /**
+     * Adds an {@link OnNavigatedListener} to this controller to receive events when
+     * the controller navigates to a new destination.
+     *
+     * <p>The current destination, if any, will be immediately sent to your listener.</p>
+     *
+     * @param listener the listener to receive events
+     */
+    public void addOnNavigatedListener(@NonNull OnNavigatedListener listener) {
+        // Inform the new listener of our current state, if any
+        if (!mBackStack.isEmpty()) {
+            listener.onNavigated(this, mBackStack.peekLast());
+        }
+        mOnNavigatedListeners.add(listener);
+    }
+
+    /**
+     * Removes an {@link OnNavigatedListener} from this controller. It will no longer
+     * receive navigation events.
+     *
+     * @param listener the listener to remove
+     */
+    public void removeOnNavigatedListener(@NonNull OnNavigatedListener listener) {
+        mOnNavigatedListeners.remove(listener);
+    }
+
+    /**
+     * Attempts to pop the controller's back stack. Analogous to when the user presses
+     * the system {@link android.view.KeyEvent#KEYCODE_BACK Back} button when the associated
+     * navigation host has focus.
+     *
+     * @return true if the stack was popped, false otherwise
+     */
+    public boolean popBackStack() {
+        if (mBackStack.isEmpty()) {
+            throw new IllegalArgumentException("NavController back stack is empty");
+        }
+        boolean popped = false;
+        while (!mBackStack.isEmpty()) {
+            popped = mBackStack.removeLast().getNavigator().popBackStack();
+            if (popped) {
+                break;
+            }
+        }
+        return popped;
+    }
+
+
+    /**
+     * Attempts to pop the controller's back stack back to a specific destination.
+     *
+     * @param destinationId The topmost destination to retain
+     * @param inclusive Whether the given destination should also be popped.
+     *
+     * @return true if the stack was popped at least once, false otherwise
+     */
+    public boolean popBackStack(@IdRes int destinationId, boolean inclusive) {
+        if (mBackStack.isEmpty()) {
+            throw new IllegalArgumentException("NavController back stack is empty");
+        }
+        ArrayList<NavDestination> destinationsToRemove = new ArrayList<>();
+        Iterator<NavDestination> iterator = mBackStack.descendingIterator();
+        while (iterator.hasNext()) {
+            NavDestination destination = iterator.next();
+            if (inclusive || destination.getId() != destinationId) {
+                destinationsToRemove.add(destination);
+            }
+            if (destination.getId() == destinationId) {
+                break;
+            }
+        }
+        boolean popped = false;
+        iterator = destinationsToRemove.iterator();
+        while (iterator.hasNext()) {
+            NavDestination destination = iterator.next();
+            // Skip destinations already removed by a previous popBackStack operation
+            while (!mBackStack.isEmpty() && mBackStack.peekLast().getId() != destination.getId()) {
+                if (iterator.hasNext()) {
+                    destination = iterator.next();
+                } else {
+                    destination = null;
+                    break;
+                }
+            }
+            if (destination != null) {
+                popped = destination.getNavigator().popBackStack() || popped;
+            }
+        }
+        return popped;
+    }
+
+    /**
+     * Attempts to navigate up in the navigation hierarchy. Suitable for when the
+     * user presses the "Up" button marked with a left (or start)-facing arrow in the upper left
+     * (or starting) corner of the app UI.
+     *
+     * <p>The intended behavior of Up differs from {@link #popBackStack() Back} when the user
+     * did not reach the current destination from the application's own task. e.g. if the user
+     * is viewing a document or link in the current app in an activity hosted on another app's
+     * task where the user clicked the link. In this case the current activity (determined by the
+     * context used to create this NavController) will be {@link Activity#finish() finished} and
+     * the user will be taken to an appropriate destination in this app on its own task.</p>
+     *
+     * @return true if navigation was successful, false otherwise
+     */
+    public boolean navigateUp() {
+        if (mBackStack.size() == 1) {
+            // If there's only one entry, then we've deep linked into a specific destination
+            // on another task so we need to find the parent and start our task from there
+            NavDestination currentDestination = getCurrentDestination();
+            int destId = currentDestination.getId();
+            NavGraph parent = currentDestination.getParent();
+            while (parent != null) {
+                if (parent.getStartDestination() != destId) {
+                    TaskStackBuilder parentIntents = new NavDeepLinkBuilder(NavController.this)
+                            .setDestination(parent.getId())
+                            .createTaskStackBuilder();
+                    parentIntents.startActivities();
+                    if (mActivity != null) {
+                        mActivity.finish();
+                    }
+                    return true;
+                }
+                destId = parent.getId();
+                parent = parent.getParent();
+            }
+            // We're already at the startDestination of the graph so there's no 'Up' to go to
+            return false;
+        } else {
+            return popBackStack();
+        }
+    }
+
+    void dispatchOnNavigated(NavDestination destination) {
+        for (OnNavigatedListener listener : mOnNavigatedListeners) {
+            listener.onNavigated(this, destination);
+        }
+    }
+
+    /**
+     * Sets the {@link NavGraph navigation graph} as specified in the application manifest.
+     *
+     * <p>Applications may declare a graph resource in their manifest instead of declaring
+     * or passing this data to each host or controller:</p>
+     *
+     * <pre class="prettyprint">
+     *     <meta-data android:name="android.nav.graph" android:resource="@xml/my_nav_graph" />
+     * </pre>
+     *
+     * <p>The inflated graph can be retrieved via {@link #getGraph()}.</p>
+     *
+     * @see NavInflater#METADATA_KEY_GRAPH
+     * @see NavInflater#inflateMetadataGraph()
+     * @see #getGraph
+     */
+    public void setMetadataGraph() {
+        setGraph(getNavInflater().inflateMetadataGraph());
+    }
+
+    /**
+     * Returns the {@link NavInflater inflater} for this controller.
+     *
+     * @return inflater for loading navigation resources
+     */
+    @NonNull
+    public NavInflater getNavInflater() {
+        if (mInflater == null) {
+            mInflater = new NavInflater(mContext, mNavigatorProvider);
+        }
+        return mInflater;
+    }
+
+    /**
+     * Sets the {@link NavGraph navigation graph} to the specified resource.
+     * Any current navigation graph data will be replaced.
+     *
+     * <p>The inflated graph can be retrieved via {@link #getGraph()}.</p>
+     *
+     * @param graphResId resource id of the navigation graph to inflate
+     *
+     * @see #getNavInflater()
+     * @see #setGraph(NavGraph)
+     * @see #getGraph
+     */
+    public void setGraph(@NavigationRes int graphResId) {
+        mGraph = getNavInflater().inflate(graphResId);
+        mGraphId = graphResId;
+        onGraphCreated();
+    }
+
+    /**
+     * Sets the {@link NavGraph navigation graph} to the specified graph.
+     * Any current navigation graph data will be replaced.
+     *
+     * <p>The graph can be retrieved later via {@link #getGraph()}.</p>
+     *
+     * @param graph graph to set
+     * @see #setGraph(int)
+     * @see #getGraph
+     */
+    public void setGraph(@NonNull NavGraph graph) {
+        mGraph = graph;
+        mGraphId = 0;
+        onGraphCreated();
+    }
+
+    private void onGraphCreated() {
+        if (mBackStackToRestore != null) {
+            for (int destinationId : mBackStackToRestore) {
+                NavDestination node = findDestination(destinationId);
+                if (node == null) {
+                    throw new IllegalStateException("unknown destination during restore: "
+                            + mContext.getResources().getResourceName(destinationId));
+                }
+                mBackStack.add(node);
+            }
+            mBackStackToRestore = null;
+        }
+        if (mGraph != null && mBackStack.isEmpty()) {
+            boolean deepLinked = mActivity != null && onHandleDeepLink(mActivity.getIntent());
+            if (!deepLinked) {
+                // Navigate to the first destination in the graph
+                // if we haven't deep linked to a destination
+                mGraph.navigate(null, null);
+            }
+        }
+    }
+
+    /**
+     * Checks the given Intent for a Navigation deep link and navigates to the deep link if present.
+     * This is called automatically for you the first time you set the graph if you've passed in an
+     * {@link Activity} as the context when constructing this NavController, but should be manually
+     * called if your Activity receives new Intents in {@link Activity#onNewIntent(Intent)}.
+     * <p>
+     * The types of Intents that are supported include:
+     * <ul>
+     *     <ol>Intents created by {@link NavDeepLinkBuilder} or
+     *     {@link #createDeepLink()}. This assumes that the current graph shares
+     *     the same hierarchy to get to the deep linked destination as when the deep link was
+     *     constructed.</ol>
+     *     <ol>Intents that include a {@link Intent#getData() data Uri}. This Uri will be checked
+     *     against the Uri patterns added via {@link NavDestination#addDeepLink(String)}.</ol>
+     * </ul>
+     * <p>The {@link #getGraph() navigation graph} should be set before calling this method.</p>
+     * @param intent The Intent that may contain a valid deep link
+     * @return True if the navigation controller found a valid deep link and navigated to it.
+     * @see NavDestination#addDeepLink(String)
+     */
+    public boolean onHandleDeepLink(@Nullable Intent intent) {
+        if (intent == null) {
+            return false;
+        }
+        Bundle extras = intent.getExtras();
+        int[] deepLink = extras != null ? extras.getIntArray(KEY_DEEP_LINK_IDS) : null;
+        Bundle bundle = extras != null ? extras.getBundle(KEY_DEEP_LINK_EXTRAS) : null;
+        if ((deepLink == null || deepLink.length == 0) && intent.getData() != null) {
+            Pair<NavDestination, Bundle> matchingDeepLink = mGraph.matchDeepLink(intent.getData());
+            if (matchingDeepLink != null) {
+                deepLink = matchingDeepLink.first.buildDeepLinkIds();
+                bundle = matchingDeepLink.second;
+            }
+        }
+        if (deepLink == null || deepLink.length == 0) {
+            return false;
+        }
+        if (bundle == null) {
+            bundle = new Bundle();
+        }
+        bundle.putParcelable(KEY_DEEP_LINK_INTENT, intent);
+        int flags = intent.getFlags();
+        if ((flags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
+                && (flags & Intent.FLAG_ACTIVITY_CLEAR_TASK) == 0) {
+            // Someone called us with NEW_TASK, but we don't know what state our whole
+            // task stack is in, so we need to manually restart the whole stack to
+            // ensure we're in a predictably good state.
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            TaskStackBuilder taskStackBuilder = TaskStackBuilder
+                    .create(mContext)
+                    .addNextIntentWithParentStack(intent);
+            taskStackBuilder.startActivities();
+            if (mActivity != null) {
+                mActivity.finish();
+            }
+            return true;
+        }
+        if ((flags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+            // Start with a cleared task starting at our root when we're on our own task
+            if (!mBackStack.isEmpty()) {
+                navigate(mGraph.getStartDestination(), bundle, new NavOptions.Builder()
+                        .setClearTask(true).setEnterAnim(0).setExitAnim(0).build());
+            }
+            while (mBackStack.size() < deepLink.length) {
+                int destinationId = deepLink[mBackStack.size()];
+                NavDestination node = findDestination(destinationId);
+                if (node == null) {
+                    throw new IllegalStateException("unknown destination during deep link: "
+                            + NavDestination.getDisplayName(mContext, destinationId));
+                }
+                node.navigate(bundle,
+                        new NavOptions.Builder().setEnterAnim(0).setExitAnim(0).build());
+            }
+            return true;
+        }
+        // Assume we're on another apps' task and only start the final destination
+        NavGraph graph = mGraph;
+        for (int i = 0; i < deepLink.length; i++) {
+            int destinationId = deepLink[i];
+            NavDestination node = i == 0 ? mGraph : graph.findNode(destinationId);
+            if (node == null) {
+                throw new IllegalStateException("unknown destination during deep link: "
+                        + NavDestination.getDisplayName(mContext, destinationId));
+            }
+            if (i != deepLink.length - 1) {
+                // We're not at the final NavDestination yet, so keep going through the chain
+                graph = (NavGraph) node;
+            } else {
+                // Navigate to the last NavDestination, clearing any existing destinations
+                node.navigate(bundle, new NavOptions.Builder()
+                        .setClearTask(true).setEnterAnim(0).setExitAnim(0).build());
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Gets the topmost navigation graph associated with this NavController.
+     *
+     * @see #setGraph(int)
+     * @see #setGraph(NavGraph)
+     * @see #setMetadataGraph()
+     */
+    public NavGraph getGraph() {
+        return mGraph;
+    }
+
+    /**
+     * Gets the current destination.
+     */
+    public NavDestination getCurrentDestination() {
+        return mBackStack.peekLast();
+    }
+
+    private NavDestination findDestination(@IdRes int destinationId) {
+        if (mGraph == null) {
+            return null;
+        }
+        if (mGraph.getId() == destinationId) {
+            return mGraph;
+        }
+        NavDestination currentNode = mBackStack.isEmpty() ? mGraph : mBackStack.peekLast();
+        NavGraph currentGraph = currentNode instanceof NavGraph
+                ? (NavGraph) currentNode
+                : currentNode.getParent();
+        return currentGraph.findNode(destinationId);
+    }
+
+    /**
+     * Navigate to a destination from the current navigation graph. This supports both navigating
+     * via an {@link NavDestination#getAction(int) action} and directly navigating to a destination.
+     *
+     * @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
+     *              navigate to
+     */
+    public final void navigate(@IdRes int resId) {
+        navigate(resId, null);
+    }
+
+    /**
+     * Navigate to a destination from the current navigation graph. This supports both navigating
+     * via an {@link NavDestination#getAction(int) action} and directly navigating to a destination.
+     *
+     * @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
+     *              navigate to
+     * @param args arguments to pass to the destination
+     */
+    public final void navigate(@IdRes int resId, @Nullable Bundle args) {
+        navigate(resId, args, null);
+    }
+
+    /**
+     * Navigate to a destination from the current navigation graph. This supports both navigating
+     * via an {@link NavDestination#getAction(int) action} and directly navigating to a destination.
+     *
+     * @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
+     *              navigate to
+     * @param args arguments to pass to the destination
+     * @param navOptions special options for this navigation operation
+     */
+    public void navigate(@IdRes int resId, @Nullable Bundle args, @Nullable NavOptions navOptions) {
+        NavDestination currentNode = mBackStack.isEmpty() ? mGraph : mBackStack.peekLast();
+        if (currentNode == null) {
+            throw new IllegalStateException("no current navigation node");
+        }
+        @IdRes int destId = resId;
+        final NavAction navAction = currentNode.getAction(resId);
+        if (navAction != null) {
+            if (navOptions == null) {
+                navOptions = navAction.getNavOptions();
+            }
+            destId = navAction.getDestinationId();
+        }
+        if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != 0) {
+            popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
+            return;
+        }
+
+        if (destId == 0) {
+            throw new IllegalArgumentException("Destination id == 0 can only be used"
+                    + " in conjunction with navOptions.popUpTo != 0");
+        }
+
+        NavDestination node = findDestination(destId);
+        if (node == null) {
+            final String dest = NavDestination.getDisplayName(mContext, destId);
+            throw new IllegalArgumentException("navigation destination " + dest
+                    + (navAction != null
+                    ? " referenced from action " + NavDestination.getDisplayName(mContext, resId)
+                    : "")
+                    + " is unknown to this NavController");
+        }
+        if (navOptions != null) {
+            if (navOptions.shouldClearTask()) {
+                // Start with a clean slate
+                popBackStack(0, true);
+                mBackStack.clear();
+            } else if (navOptions.getPopUpTo() != 0) {
+                popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
+            }
+        }
+        node.navigate(args, navOptions);
+    }
+
+    /**
+     * Navigate via the given {@link NavDirections}
+     *
+     * @param directions directions that describe this navigation operation
+     */
+    public void navigate(@NonNull NavDirections directions) {
+        navigate(directions.getActionId(), directions.getArguments());
+    }
+
+    /**
+     * Navigate via the given {@link NavDirections}
+     *
+     * @param directions directions that describe this navigation operation
+     */
+    public void navigate(@NonNull NavDirections directions, @Nullable NavOptions navOptions) {
+        navigate(directions.getActionId(), directions.getArguments(), navOptions);
+    }
+    /**
+     * Create a deep link to a destination within this NavController.
+     *
+     * @return a {@link NavDeepLinkBuilder} suitable for constructing a deep link
+     */
+    @NonNull
+    public NavDeepLinkBuilder createDeepLink() {
+        return new NavDeepLinkBuilder(this);
+    }
+
+    /**
+     * Saves all navigation controller state to a Bundle.
+     *
+     * <p>State may be restored from a bundle returned from this method by calling
+     * {@link #restoreState(Bundle)}. Saving controller state is the responsibility
+     * of a {@link NavHost}.</p>
+     *
+     * @return saved state for this controller
+     */
+    @Nullable
+    public Bundle saveState() {
+        Bundle b = null;
+        if (mGraphId != 0) {
+            b = new Bundle();
+            b.putInt(KEY_GRAPH_ID, mGraphId);
+        }
+        if (!mBackStack.isEmpty()) {
+            if (b == null) {
+                b = new Bundle();
+            }
+            int[] backStack = new int[mBackStack.size()];
+            int index = 0;
+            for (NavDestination destination : mBackStack) {
+                backStack[index++] = destination.getId();
+            }
+            b.putIntArray(KEY_BACK_STACK_IDS, backStack);
+        }
+        return b;
+    }
+
+    /**
+     * Restores all navigation controller state from a bundle.
+     *
+     * <p>State may be saved to a bundle by calling {@link #saveState()}.
+     * Restoring controller state is the responsibility of a {@link NavHost}.</p>
+     *
+     * @param navState state bundle to restore
+     */
+    public void restoreState(@Nullable Bundle navState) {
+        if (navState == null) {
+            return;
+        }
+
+        mGraphId = navState.getInt(KEY_GRAPH_ID);
+        mBackStackToRestore = navState.getIntArray(KEY_BACK_STACK_IDS);
+        if (mGraphId != 0) {
+            // Set the graph right away, onGraphCreated will re-add the back stack
+            // from mBackStackToRestore
+            setGraph(mGraphId);
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.java b/navigation/runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.java
new file mode 100644
index 0000000..d2a4480
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NavigationRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.TaskStackBuilder;
+
+import java.util.ArrayDeque;
+
+/**
+ * Class used to construct deep links to a particular destination in a {@link NavGraph}.
+ *
+ * <p>When this deep link is triggered:
+ * <ol>
+ *     <li>The task is cleared.</li>
+ *     <li>The destination and all of its parents will be on the back stack.</li>
+ *     <li>Calling {@link NavController#navigateUp()} will navigate to the parent of the
+ *     destination.</li>
+ * </ol></p>
+ *
+ * The parent of the destination is the {@link NavGraph#getStartDestination() start destination}
+ * of the containing {@link NavGraph navigation graph}. In the cases where the destination is
+ * the start destination of its containing navigation graph, the start destination of its
+ * grandparent is used.
+ * <p>
+ * You can construct an instance directly with {@link #NavDeepLinkBuilder(Context)} or build one
+ * using an existing {@link NavController} via {@link NavController#createDeepLink()}.
+ */
+public class NavDeepLinkBuilder {
+    private final Context mContext;
+    private final Intent mIntent;
+
+    private NavGraph mGraph;
+    private int mDestId;
+
+    /**
+     * Construct a new NavDeepLinkBuilder.
+     *
+     * If the context passed in here is not an {@link Activity}, this method will use
+     * {@link android.content.pm.PackageManager#getLaunchIntentForPackage(String)} as the
+     * default activity to launch, if available.
+     *
+     * @param context Context used to create deep links
+     * @see #setComponentName
+     */
+    public NavDeepLinkBuilder(@NonNull Context context) {
+        mContext = context;
+        if (mContext instanceof Activity) {
+            mIntent = new Intent(mContext, mContext.getClass());
+        } else {
+            Intent launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(
+                    mContext.getPackageName());
+            mIntent = launchIntent != null ? launchIntent : new Intent();
+        }
+        mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    }
+
+    /**
+     * @see NavController#createDeepLink()
+     */
+    NavDeepLinkBuilder(@NonNull NavController navController) {
+        this(navController.getContext());
+        mGraph = navController.getGraph();
+    }
+
+    /**
+     * Sets an explicit Activity to be started by the deep link created by this class.
+     *
+     * @param activityClass The Activity to start. This Activity should have a {@link NavController}
+     *                      which uses the same {@link NavGraph} used to construct this
+     *                      deep link.
+     * @return this object for chaining
+     */
+    @NonNull
+    public NavDeepLinkBuilder setComponentName(@NonNull Class<? extends Activity> activityClass) {
+        return setComponentName(new ComponentName(mContext, activityClass));
+    }
+
+    /**
+     * Sets an explicit Activity to be started by the deep link created by this class.
+     *
+     * @param componentName The Activity to start. This Activity should have a {@link NavController}
+     *                      which uses the same {@link NavGraph} used to construct this
+     *                      deep link.
+     * @return this object for chaining
+     */
+    @NonNull
+    public NavDeepLinkBuilder setComponentName(@NonNull ComponentName componentName) {
+        mIntent.setComponent(componentName);
+        return this;
+    }
+
+    /**
+     * Sets the graph that contains the {@link #setDestination(int) deep link destination}.
+     *
+     * @param navGraphId ID of the {@link NavGraph} containing the deep link destination
+     * @return this object for chaining
+     */
+    @NonNull
+    public NavDeepLinkBuilder setGraph(@NavigationRes int navGraphId) {
+        return setGraph(new NavInflater(mContext, new PermissiveNavigatorProvider(mContext))
+                .inflate(navGraphId));
+    }
+
+    /**
+     * Sets the graph that contains the {@link #setDestination(int) deep link destination}.
+     *
+     * @param navGraph The {@link NavGraph} containing the deep link destination
+     * @return this object for chaining
+     */
+    @NonNull
+    public NavDeepLinkBuilder setGraph(@NonNull NavGraph navGraph) {
+        mGraph = navGraph;
+        if (mDestId != 0) {
+            fillInIntent();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the destination id to deep link to.
+     *
+     * @param destId destination ID to deep link to.
+     * @return this object for chaining
+     */
+    @NonNull
+    public NavDeepLinkBuilder setDestination(@IdRes int destId) {
+        mDestId = destId;
+        if (mGraph != null) {
+            fillInIntent();
+        }
+        return this;
+    }
+
+    private void fillInIntent() {
+        NavDestination node = null;
+        ArrayDeque<NavDestination> possibleDestinations = new ArrayDeque<>();
+        possibleDestinations.add(mGraph);
+        while (!possibleDestinations.isEmpty() && node == null) {
+            NavDestination destination = possibleDestinations.poll();
+            if (destination.getId() == mDestId) {
+                node = destination;
+            } else if (destination instanceof NavGraph) {
+                for (NavDestination child : (NavGraph) destination) {
+                    possibleDestinations.add(child);
+                }
+            }
+        }
+        if (node == null) {
+            final String dest = NavDestination.getDisplayName(mContext, mDestId);
+            throw new IllegalArgumentException("navigation destination " + dest
+                    + " is unknown to this NavController");
+        }
+        mIntent.putExtra(NavController.KEY_DEEP_LINK_IDS, node.buildDeepLinkIds());
+    }
+
+    /**
+     * Set optional arguments to send onto the destination
+     * @param args arguments to pass to the destination
+     * @return this object for chaining
+     */
+    @NonNull
+    public NavDeepLinkBuilder setArguments(@Nullable Bundle args) {
+        mIntent.putExtra(NavController.KEY_DEEP_LINK_EXTRAS, args);
+        return this;
+    }
+
+    /**
+     * Construct the full {@link TaskStackBuilder task stack} needed to deep link to the given
+     * destination.
+     * <p>
+     * You must have {@link #setGraph set a NavGraph} and {@link #setDestination set a destination}
+     * before calling this method.
+     * </p>
+     *
+     * @return a {@link TaskStackBuilder} which can be used to
+     * {@link TaskStackBuilder#startActivities() send the deep link} or
+     * {@link TaskStackBuilder#getPendingIntent(int, int) create a PendingIntent} to deep link to
+     * the given destination.
+     */
+    @NonNull
+    public TaskStackBuilder createTaskStackBuilder() {
+        if (mIntent.getIntArrayExtra(NavController.KEY_DEEP_LINK_IDS) == null) {
+            if (mGraph == null) {
+                throw new IllegalStateException("You must call setGraph() "
+                        + "before constructing the deep link");
+            } else {
+                throw new IllegalStateException("You must call setDestination() "
+                        + "before constructing the deep link");
+            }
+        }
+        // We create a copy of the Intent to ensure the Intent does not have itself
+        // as an extra. This also prevents developers from modifying the internal Intent
+        // via taskStackBuilder.editIntentAt()
+        TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(mContext)
+                .addNextIntentWithParentStack(new Intent(mIntent));
+        for (int index = 0; index < taskStackBuilder.getIntentCount(); index++) {
+            // Attach the original Intent to each Activity so that they can know
+            // they were constructed in response to a deep link
+            taskStackBuilder.editIntentAt(index)
+                    .putExtra(NavController.KEY_DEEP_LINK_INTENT, mIntent);
+        }
+        return taskStackBuilder;
+    }
+
+    /**
+     * Construct a {@link PendingIntent} to the {@link #setDestination(int) deep link destination}.
+     * <p>
+     * This constructs the entire {@link #createTaskStackBuilder() task stack} needed.
+     * <p>
+     * You must have {@link #setGraph set a NavGraph} and {@link #setDestination set a destination}
+     * before calling this method.
+     * </p>
+     *
+     * @return a PendingIntent constructed with
+     * {@link TaskStackBuilder#getPendingIntent(int, int)} to deep link to the
+     * given destination
+     */
+    @NonNull
+    public PendingIntent createPendingIntent() {
+        return createTaskStackBuilder()
+                .getPendingIntent(mDestId, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+
+    /**
+     * A {@link NavigatorProvider} that only parses the basics: {@link NavGraph navigation graphs}
+     * and {@link NavDestination destinations}, effectively only getting the base destination
+     * information.
+     */
+    private static class PermissiveNavigatorProvider extends SimpleNavigatorProvider {
+        /**
+         * A Navigator that only parses the {@link NavDestination} attributes.
+         */
+        private final Navigator<NavDestination> mDestNavigator = new Navigator<NavDestination>() {
+            @Override
+            public NavDestination createDestination() {
+                return new NavDestination(this);
+            }
+
+            @Override
+            public void navigate(NavDestination destination, Bundle args, NavOptions navOptions) {
+                throw new IllegalStateException("navigate is not supported");
+            }
+
+            @Override
+            public boolean popBackStack() {
+                throw new IllegalStateException("popBackStack is not supported");
+            }
+        };
+
+        PermissiveNavigatorProvider(Context context) {
+            addNavigator(new NavGraphNavigator(context));
+        }
+
+        @NonNull
+        @Override
+        public Navigator<? extends NavDestination> getNavigator(@NonNull String name) {
+            try {
+                return super.getNavigator(name);
+            } catch (IllegalStateException e) {
+                return mDestNavigator;
+            }
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/androidx/navigation/NavHost.java b/navigation/runtime/src/main/java/androidx/navigation/NavHost.java
new file mode 100644
index 0000000..ffa44db
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/NavHost.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.view.View;
+
+/**
+ * A host is a single context or container for navigation via a {@link NavController}.
+ * <p>
+ * Navigation hosts must:
+ * <ul>
+ * <li>Handle {@link NavController#saveState() saving} and
+ * {@link NavController#restoreState(Bundle) restoring} their controller's state</li>
+ * <li>Call {@link Navigation#setViewNavController(View, NavController)} on their root view</li>
+ * </ul>
+ */
+public interface NavHost {
+
+    /**
+     * Returns the {@link NavController navigation controller} for this navigation host.
+     *
+     * @return this host's navigation controller
+     */
+    @NonNull
+    NavController getNavController();
+}
diff --git a/navigation/runtime/src/main/java/androidx/navigation/NavInflater.java b/navigation/runtime/src/main/java/androidx/navigation/NavInflater.java
new file mode 100644
index 0000000..e6c62dd
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/NavInflater.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.support.annotation.NavigationRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Class which translates a navigation XML file into a {@link NavGraph}
+ */
+public class NavInflater {
+    /**
+     * Metadata key for defining an app's default navigation graph.
+     *
+     * <p>Applications may declare a graph resource in their manifest instead of declaring
+     * or passing this data to each host or controller:</p>
+     *
+     * <pre class="prettyprint">
+     *     <meta-data android:name="android.nav.graph" android:resource="@xml/my_nav_graph" />
+     * </pre>
+     *
+     * <p>A graph resource declared in this manner can be inflated into a controller by calling
+     * {@link NavController#setMetadataGraph()} or directly via {@link #inflateMetadataGraph()}.
+     * Navigation host implementations should do this automatically
+     * if no navigation resource is otherwise supplied during host configuration.</p>
+     */
+    public static final String METADATA_KEY_GRAPH = "android.nav.graph";
+
+    private static final String TAG_ARGUMENT = "argument";
+    private static final String TAG_DEEP_LINK = "deepLink";
+    private static final String TAG_ACTION = "action";
+    private static final String TAG_INCLUDE = "include";
+    private static final String APPLICATION_ID_PLACEHOLDER = "${applicationId}";
+
+    private static final ThreadLocal<TypedValue> sTmpValue = new ThreadLocal<>();
+
+    private Context mContext;
+    private NavigatorProvider mNavigatorProvider;
+
+    public NavInflater(@NonNull Context c, @NonNull NavigatorProvider navigatorProvider) {
+        mContext = c;
+        mNavigatorProvider = navigatorProvider;
+    }
+
+    /**
+     * Inflates {@link NavGraph navigation graph} as specified in the application manifest.
+     *
+     * <p>Applications may declare a graph resource in their manifest instead of declaring
+     * or passing this data to each host or controller:</p>
+     *
+     * <pre class="prettyprint">
+     *     <meta-data android:name="android.nav.graph" android:resource="@xml/my_nav_graph" />
+     * </pre>
+     *
+     * @see #METADATA_KEY_GRAPH
+     */
+    @Nullable
+    public NavGraph inflateMetadataGraph() {
+        final Bundle metaData = mContext.getApplicationInfo().metaData;
+        if (metaData != null) {
+            final int resid = metaData.getInt(METADATA_KEY_GRAPH);
+            if (resid != 0) {
+                return inflate(resid);
+            }
+        }
+        return null;
+    }
+    /**
+     * Inflate a NavGraph from the given XML resource id.
+     *
+     * @param graphResId
+     * @return
+     */
+    @SuppressLint("ResourceType")
+    public NavGraph inflate(@NavigationRes int graphResId) {
+        Resources res = mContext.getResources();
+        XmlResourceParser parser = res.getXml(graphResId);
+        final AttributeSet attrs = Xml.asAttributeSet(parser);
+        try {
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                // Empty loop
+            }
+            if (type != XmlPullParser.START_TAG) {
+                throw new XmlPullParserException("No start tag found");
+            }
+
+            String rootElement = parser.getName();
+            NavDestination destination = inflate(res, parser, attrs);
+            if (!(destination instanceof NavGraph)) {
+                throw new IllegalArgumentException("Root element <" + rootElement + ">"
+                        + " did not inflate into a NavGraph");
+            }
+            return (NavGraph) destination;
+        } catch (Exception e) {
+            throw new RuntimeException("Exception inflating "
+                    + res.getResourceName(graphResId) + " line "
+                    + parser.getLineNumber(), e);
+        } finally {
+            parser.close();
+        }
+    }
+
+    private NavDestination inflate(Resources res, XmlResourceParser parser, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+        Navigator navigator = mNavigatorProvider.getNavigator(parser.getName());
+        final NavDestination dest = navigator.createDestination();
+
+        dest.onInflate(mContext, attrs);
+
+        final int innerDepth = parser.getDepth() + 1;
+        int type;
+        int depth;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth
+                || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if (depth > innerDepth) {
+                continue;
+            }
+
+            final String name = parser.getName();
+            if (TAG_ARGUMENT.equals(name)) {
+                inflateArgument(res, dest, attrs);
+            } else if (TAG_DEEP_LINK.equals(name)) {
+                inflateDeepLink(res, dest, attrs);
+            } else if (TAG_ACTION.equals(name)) {
+                inflateAction(res, dest, attrs);
+            } else if (TAG_INCLUDE.equals(name) && dest instanceof NavGraph) {
+                final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavInclude);
+                final int id = a.getResourceId(R.styleable.NavInclude_graph, 0);
+                ((NavGraph) dest).addDestination(inflate(id));
+                a.recycle();
+            } else if (dest instanceof NavGraph) {
+                ((NavGraph) dest).addDestination(inflate(res, parser, attrs));
+            }
+        }
+
+        return dest;
+    }
+
+    private void inflateArgument(Resources res, NavDestination dest, AttributeSet attrs)
+            throws XmlPullParserException {
+        final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavArgument);
+        String name = a.getString(R.styleable.NavArgument_android_name);
+
+        TypedValue value = sTmpValue.get();
+        if (value == null) {
+            value = new TypedValue();
+            sTmpValue.set(value);
+        }
+        if (a.getValue(R.styleable.NavArgument_android_defaultValue, value)) {
+            switch (value.type) {
+                case TypedValue.TYPE_STRING:
+                    dest.getDefaultArguments().putString(name, value.string.toString());
+                    break;
+                case TypedValue.TYPE_DIMENSION:
+                    dest.getDefaultArguments().putInt(name,
+                            (int) value.getDimension(res.getDisplayMetrics()));
+                    break;
+                case TypedValue.TYPE_FLOAT:
+                    dest.getDefaultArguments().putFloat(name, value.getFloat());
+                    break;
+                case TypedValue.TYPE_REFERENCE:
+                    dest.getDefaultArguments().putInt(name, value.data);
+                    break;
+                default:
+                    if (value.type >= TypedValue.TYPE_FIRST_INT
+                            && value.type <= TypedValue.TYPE_LAST_INT) {
+                        dest.getDefaultArguments().putInt(name, value.data);
+                    } else {
+                        throw new XmlPullParserException("unsupported argument type " + value.type);
+                    }
+            }
+        }
+        a.recycle();
+    }
+
+    private void inflateDeepLink(Resources res, NavDestination dest, AttributeSet attrs) {
+        final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavDeepLink);
+        String uri = a.getString(R.styleable.NavDeepLink_uri);
+        if (TextUtils.isEmpty(uri)) {
+            throw new IllegalArgumentException("Every <" + TAG_DEEP_LINK
+                    + "> must include an app:uri");
+        }
+        uri = uri.replace(APPLICATION_ID_PLACEHOLDER, mContext.getPackageName());
+        dest.addDeepLink(uri);
+        a.recycle();
+    }
+
+    private void inflateAction(Resources res, NavDestination dest, AttributeSet attrs) {
+        final TypedArray a = res.obtainAttributes(attrs, R.styleable.NavAction);
+        final int id = a.getResourceId(R.styleable.NavAction_android_id, 0);
+        final int destId = a.getResourceId(R.styleable.NavAction_destination, 0);
+        NavAction action = new NavAction(destId);
+
+        NavOptions.Builder builder = new NavOptions.Builder();
+        builder.setLaunchSingleTop(a.getBoolean(R.styleable.NavAction_launchSingleTop, false));
+        builder.setLaunchDocument(a.getBoolean(R.styleable.NavAction_launchDocument, false));
+        builder.setClearTask(a.getBoolean(R.styleable.NavAction_clearTask, false));
+        builder.setPopUpTo(a.getResourceId(R.styleable.NavAction_popUpTo, 0),
+                a.getBoolean(R.styleable.NavAction_popUpToInclusive, false));
+        builder.setEnterAnim(a.getResourceId(R.styleable.NavAction_enterAnim, -1));
+        builder.setExitAnim(a.getResourceId(R.styleable.NavAction_exitAnim, -1));
+        builder.setPopEnterAnim(a.getResourceId(R.styleable.NavAction_popEnterAnim, -1));
+        builder.setPopExitAnim(a.getResourceId(R.styleable.NavAction_popExitAnim, -1));
+        action.setNavOptions(builder.build());
+
+        dest.putAction(id, action);
+        a.recycle();
+    }
+}
diff --git a/navigation/runtime/src/main/java/androidx/navigation/Navigation.java b/navigation/runtime/src/main/java/androidx/navigation/Navigation.java
new file mode 100644
index 0000000..f1e3683
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/Navigation.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewParent;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Entry point for navigation operations.
+ *
+ * <p>This class provides utilities for finding a relevant {@link NavController} instance from
+ * various common places in your application, or for performing navigation in response to
+ * UI events.</p>
+ */
+public class Navigation {
+    // No instances. Static utilities only.
+    private Navigation() {
+    }
+
+    /**
+     * Find a {@link NavController} given the id of a View and its containing
+     * {@link Activity}. This is a convenience wrapper around {@link #findNavController(View)}.
+     *
+     * <p>This method will locate the {@link NavController} associated with this view.
+     * This is automatically populated for the id of a {@link NavHost} and its children.</p>
+     *
+     * @param activity The Activity hosting the view
+     * @param viewId The id of the view to search from
+     * @return the {@link NavController} associated with the view referenced by id
+     */
+    @Nullable
+    public static NavController findNavController(@NonNull Activity activity, @IdRes int viewId) {
+        return Navigation.findNavController(activity.findViewById(viewId));
+    }
+
+    /**
+     * Gets the {@link NavController} given the id of a View and its containing
+     * {@link Activity}. This is a convenience wrapper around {@link #getNavController(View)}.
+     *
+     * <p>This method will locate the {@link NavController} associated with this view.
+     * This is automatically populated for the id of a {@link NavHost} and its children.</p>
+     *
+     * @param activity The Activity hosting the view
+     * @param viewId The id of the view to search from
+     * @return the {@link NavController} associated with the view referenced by id
+     * @throws IllegalStateException if the given viewId does not correspond with a
+     * {@link NavHost} or is not within a NavHost.
+     */
+    @NonNull
+    public static NavController getNavController(@NonNull Activity activity, @IdRes int viewId) {
+        NavController navController = findNavController(activity, viewId);
+        if (navController == null) {
+            throw new IllegalStateException("Activity " + activity
+                    + " does not have a NavController set on " + viewId);
+        }
+        return navController;
+    }
+
+    /**
+     * Find a {@link NavController} given a local {@link View}.
+     *
+     * <p>This method will locate the {@link NavController} associated with this view.
+     * This is automatically populated for views that are managed by a {@link NavHost}
+     * and is intended for use by various {@link android.view.View.OnClickListener listener}
+     * interfaces.</p>
+     *
+     * @param view the view to search from
+     * @return the locally scoped {@link NavController} to the given view
+     */
+    @Nullable
+    public static NavController findNavController(@NonNull View view) {
+        while (view != null) {
+            NavController controller = getViewNavController(view);
+            if (controller != null) {
+                return controller;
+            }
+            ViewParent parent = view.getParent();
+            view = parent instanceof View ? (View) parent : null;
+        }
+        return null;
+    }
+
+    /**
+     * Gets the {@link NavController} given a local {@link View}.
+     *
+     * <p>This method will locate the {@link NavController} associated with this view.
+     * This is automatically populated for views that are managed by a {@link NavHost}
+     * and is intended for use by various {@link android.view.View.OnClickListener listener}
+     * interfaces.</p>
+     *
+     * @param view the view to search from
+     * @return the locally scoped {@link NavController} to the given view
+     * @throws IllegalStateException if the given view does not correspond with a
+     * {@link NavHost} or is not within a NavHost.
+     */
+    @NonNull
+    public static NavController getNavController(@NonNull View view) {
+        NavController navController = findNavController(view);
+        if (navController == null) {
+            throw new IllegalStateException("View " + view + " does not have a NavController set");
+        }
+        return navController;
+    }
+
+    /**
+     * Create an {@link android.view.View.OnClickListener} for navigating
+     * to a destination. This supports both navigating via an
+     * {@link NavDestination#getAction(int) action} and directly navigating to a destination.
+     *
+     * @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
+     *              navigate to when the view is clicked
+     * @return a new click listener for setting on an arbitrary view
+     */
+    @NonNull
+    public static View.OnClickListener createNavigateOnClickListener(@IdRes final int resId) {
+        return createNavigateOnClickListener(resId, null);
+    }
+
+    /**
+     * Create an {@link android.view.View.OnClickListener} for navigating
+     * to a destination. This supports both navigating via an
+     * {@link NavDestination#getAction(int) action} and directly navigating to a destination.
+     *
+     * @param resId an {@link NavDestination#getAction(int) action} id or a destination id to
+     *              navigate to when the view is clicked
+     * @param args arguments to pass to the final destination
+     * @return a new click listener for setting on an arbitrary view
+     */
+    @NonNull
+    public static View.OnClickListener createNavigateOnClickListener(@IdRes final int resId,
+            @Nullable final Bundle args) {
+        return new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                getNavController(view).navigate(resId, args);
+            }
+        };
+    }
+
+    /**
+     * Associates a NavController with the given View, allowing developers to use
+     * {@link #getNavController(View)} and {@link #getNavController(Activity, int)} with that
+     * View or any of its children to retrieve the NavController.
+     * <p>
+     * This is generally called for you by the hosting {@link NavHost}.
+     * @param view View that should be associated with the given NavController
+     * @param controller The controller you wish to later retrieve via
+     *                   {@link #getNavController(View)}
+     */
+    public static void setViewNavController(@NonNull View view,
+            @Nullable NavController controller) {
+        view.setTag(R.id.nav_controller_view_tag, controller);
+    }
+
+    static NavController getViewNavController(View view) {
+        Object tag = view.getTag(R.id.nav_controller_view_tag);
+        NavController controller = null;
+        if (tag instanceof WeakReference) {
+            controller = ((WeakReference<NavController>) tag).get();
+        } else if (tag instanceof NavController) {
+            controller = (NavController) tag;
+        }
+        return controller;
+    }
+}
diff --git a/navigation/runtime/src/main/java/androidx/navigation/package-info.java b/navigation/runtime/src/main/java/androidx/navigation/package-info.java
new file mode 100644
index 0000000..8814964
--- /dev/null
+++ b/navigation/runtime/src/main/java/androidx/navigation/package-info.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Navigation is a framework for navigating between 'destinations' within an Android
+ * application that provides a consistent API whether destinations are implemented as
+ * {@link android.support.v4.app.Fragment Fragments}, {@link android.app.Activity Activities}, or
+ * other components.
+ * <p>
+ * There are 3 major components in Navigation.
+ * <ul>
+ *     <li>{@link androidx.navigation.NavGraph}: A navigation graph encapsulates a set
+ *     of {@link androidx.navigation.NavDestination destinations}. It can be created by
+ *     inflating a navigation XML file, by constructing it programmatically,
+ *     or a combination of the two.
+ *     </li>
+ *     <li>{@link androidx.navigation.NavController}: This is the main entry
+ *     point for interacting with the Navigation Graph, translating calls to
+ *     {@link androidx.navigation.NavController#navigate(int)},
+ *     {@link androidx.navigation.NavController#popBackStack()}, and
+ *     {@link androidx.navigation.NavController#navigateUp()} into the appropriate operations.
+ *     </li>
+ *     <li>{@link androidx.navigation.NavHost}:
+ *     {@link androidx.navigation.NavController},
+ *     {@link androidx.navigation.fragment.FragmentNavigator.Destination fragment destinations}.
+ *     </li>
+ * </ul>
+ * Below is an example of working with a NavController.
+ * <pre class="prettyprint">
+ * // File: HomeFragment.java
+ * public void onViewCreated(View view, {@literal @}Nullable Bundle savedInstanceState) {
+ *   // For example purposes, assume our layout created in onCreateView has a Button
+ *   // that should navigate the user to a destination
+ *   Button b = view.findViewById(R.id.view_details);
+ *
+ *   // Retrieve the NavController from any View within a NavHost
+ *   final NavController navController = Navigation.findNavController(this);
+ *
+ *   // And set the listener
+ *   b.setOnClickListener(() -%gt; navController.navigate(R.id.details));
+ *
+ *   // Or use the convenience method in Navigation to combine all of the previous steps
+ *   b.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.details));
+ * }
+ * </pre>
+ * <p>
+ * Please see the documentation of individual classes for details.
+ */
+package androidx.navigation;
diff --git a/navigation/runtime/src/main/res/values/attrs.xml b/navigation/runtime/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..b36e607
--- /dev/null
+++ b/navigation/runtime/src/main/res/values/attrs.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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>
+    <declare-styleable name="ActivityNavigator">
+        <attr name="android:name" />
+        <attr name="action" format="string" />
+        <attr name="data" format="string" />
+        <attr name="dataPattern" format="string" />
+    </declare-styleable>
+
+    <declare-styleable name="NavInclude">
+        <attr name="graph" format="reference" />
+    </declare-styleable>
+</resources>
diff --git a/car/res/drawable/car_button_ripple_background.xml b/navigation/runtime/src/main/res/values/ids.xml
similarity index 75%
copy from car/res/drawable/car_button_ripple_background.xml
copy to navigation/runtime/src/main/res/values/ids.xml
index 13d0a49..d789f1c 100644
--- a/car/res/drawable/car_button_ripple_background.xml
+++ b/navigation/runtime/src/main/res/values/ids.xml
@@ -1,19 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
+  ~ Copyright (C) 2017 The Android Open Source Project
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
   ~ You may obtain a copy of the License at
-  ~
   ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
   ~ Unless required by applicable law or agreed to in writing, software
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background" />
+<resources>
+    <item type="id" name="nav_controller_view_tag" />
+</resources>
\ No newline at end of file
diff --git a/navigation/safe-args-generator/build.gradle b/navigation/safe-args-generator/build.gradle
new file mode 100644
index 0000000..1c4616b
--- /dev/null
+++ b/navigation/safe-args-generator/build.gradle
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.SupportConfig
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+apply plugin: androidx.build.SupportKotlinLibraryPlugin
+
+
+sourceSets {
+    test.java.srcDirs += 'src/tests/kotlin'
+}
+
+dependencies {
+    compile(XPP3)
+    compile(XMLPULL)
+    compile(XPP3)
+    compile(XMLPULL)
+    compile(KOTLIN_STDLIB)
+
+    compile(JAVAPOET)
+
+    testCompile(JUNIT)
+    testCompile(GOOGLE_COMPILE_TESTING)
+    def logger = new com.android.build.gradle.internal.LoggerWrapper(project.logger)
+    def sdkHandler = new com.android.build.gradle.internal.SdkHandler(project, logger)
+    testCompile fileTree(dir: "${sdkHandler.sdkFolder}/platforms/android-$SupportConfig.CURRENT_SDK_VERSION/",
+            include : "android.jar")
+    testCompile fileTree(dir: "${new File(project(":navigation:navigation-common").buildDir, "libJar")}",
+            include : "*.jar")
+    testCompile files(org.gradle.internal.jvm.Jvm.current().getToolsJar())
+}
+
+tasks.findByName("test").doFirst {
+    // android.jar and xmlpull has the same classes, but android.jar has stubs instead of real
+    // implementation, so we move android.jar to end of classpath
+    def classpath = it.classpath.getFiles()
+    def androidJar = classpath.find { it.name == "android.jar" }
+    classpath.remove(androidJar)
+    classpath.add(androidJar)
+    it.classpath = files(classpath)
+}
+
+tasks.findByName("compileKotlin").dependsOn(":navigation:navigation-common:jarDebug")
+
+supportLibrary {
+    name = 'Android Navigation TypeSafe Arguments Generator'
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = '2017'
+    description = "Android Navigation TypeSafe Arguments Generator"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavArgumentResolver.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavArgumentResolver.kt
new file mode 100644
index 0000000..4f804a5
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavArgumentResolver.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.models.Argument
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.ResReference
+
+fun resolveArguments(rootDestination: Destination): Destination {
+    val destinations = mutableMapOf<ResReference, Destination>()
+
+    fun dfs(dest: Destination): Destination {
+        val nested = dest.nested.filter { it.id != null }.associateBy { it.id!! }
+        destinations.putAll(nested)
+        val resolvedActions = dest.actions.map { action ->
+            val actionDestination = destinations[action.destination]
+            if (actionDestination != null) {
+                action.copy(args = mergeArguments(action.args, actionDestination.args))
+            } else {
+                action
+            }
+        }
+        val result = dest.copy(nested = dest.nested.map(::dfs), actions = resolvedActions)
+        nested.keys.forEach { id -> destinations.remove(id) }
+        return result
+    }
+
+    return dfs(rootDestination)
+}
+
+private fun mergeArguments(args1: List<Argument>, args2: List<Argument>) =
+        args2.fold(args1) { result, arg ->
+            val duplicate = result.find { it.name == arg.name }
+            if (duplicate != null) {
+                if (duplicate.type != arg.type) {
+                    // TODO: needs context to print better exception
+                    throw IllegalArgumentException("Incompatible types ${duplicate.type} and " +
+                            "${arg.type} of argument ${arg.name}")
+                }
+                result
+            } else {
+                result + arg
+            }
+        }
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavParser.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavParser.kt
new file mode 100644
index 0000000..9c47b29
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavParser.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.models.Action
+import androidx.navigation.safe.args.generator.models.Argument
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.ResReference
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserFactory
+import java.io.File
+import java.io.FileReader
+
+private const val TAG_NAVIGATION = "navigation"
+private const val TAG_ACTION = "action"
+private const val TAG_ARGUMENT = "argument"
+
+private const val ATTRIBUTE_ID = "id"
+private const val ATTRIBUTE_DESTINATION = "destination"
+private const val ATTRIBUTE_DEFAULT_VALUE = "defaultValue"
+private const val ATTRIBUTE_NAME = "name"
+private const val ATTRIBUTE_TYPE = "type"
+
+private const val NAMESPACE_RES_AUTO = "http://schemas.android.com/apk/res-auto"
+private const val NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android"
+
+private fun parseDestination(
+        parser: XmlPullParser, rFilePackage: String,
+        applicationId: String): Destination {
+    val type = parser.name
+    val name = parser.attrValue(NAMESPACE_ANDROID, ATTRIBUTE_NAME) ?: ""
+    val idValue = parser.attrValue(NAMESPACE_ANDROID, ATTRIBUTE_ID)
+    val args = mutableListOf<Argument>()
+    val actions = mutableListOf<Action>()
+    val nested = mutableListOf<Destination>()
+    parser.traverseInnerStartTags {
+        when {
+            parser.name == TAG_ACTION -> actions.add(parseAction(parser, rFilePackage))
+            parser.name == TAG_ARGUMENT -> args.add(parseArgument(parser, rFilePackage))
+            type == TAG_NAVIGATION -> nested.add(parseDestination(parser, rFilePackage,
+                    applicationId))
+        }
+    }
+
+    val id = parseNullableId(idValue, rFilePackage)
+    val className = Destination.createName(id, name, applicationId)
+    if (className == null && (actions.isNotEmpty() || args.isNotEmpty())) {
+        throw IllegalArgumentException("Destination with arguments or action mush have " +
+                "either name either id attributes")
+    }
+    return Destination(id, className, type, args, actions, nested)
+}
+
+private fun parseArgument(parser: XmlPullParser, rFilePackage: String): Argument {
+    val name = parser.attrValueOrThrow(NAMESPACE_ANDROID, ATTRIBUTE_NAME)
+    val defaultValue = parser.attrValue(NAMESPACE_ANDROID, ATTRIBUTE_DEFAULT_VALUE)
+    val typeString = parser.attrValue(NAMESPACE_RES_AUTO, ATTRIBUTE_TYPE)
+    if (typeString == null && defaultValue != null) {
+        return inferArgument(name, defaultValue, rFilePackage)
+    }
+
+    val (type, defaultTypedValue) = when (typeString) {
+        "integer" -> NavType.INT to defaultValue?.let { parseIntValue(defaultValue) }
+        "reference" -> NavType.REFERENCE to defaultValue?.let {
+            ReferenceValue(parseReference(defaultValue, rFilePackage))
+        }
+        else -> NavType.STRING to defaultValue?.let { StringValue(it) }
+    }
+    return Argument(name, type, defaultTypedValue)
+}
+
+internal fun inferArgument(name: String, defaultValue: String, rFilePackage: String): Argument {
+    val reference = tryToParseReference(defaultValue, rFilePackage)
+    if (reference != null) {
+        return Argument(name, NavType.REFERENCE, ReferenceValue(reference))
+    }
+    val intValue = tryToParseIntValue(defaultValue)
+    if (intValue != null) {
+        return Argument(name, NavType.INT, intValue)
+    }
+    return Argument(name, NavType.STRING, StringValue(defaultValue))
+}
+
+private fun parseAction(parser: XmlPullParser, rFilePackage: String): Action {
+    val idValue = parser.attrValueOrThrow(NAMESPACE_ANDROID, ATTRIBUTE_ID)
+    val destValue = parser.attrValue(NAMESPACE_RES_AUTO, ATTRIBUTE_DESTINATION)
+    val args = mutableListOf<Argument>()
+    parser.traverseInnerStartTags {
+        if (parser.name == TAG_ARGUMENT) {
+            args.add(parseArgument(parser, rFilePackage))
+        }
+    }
+    return Action(parseId(idValue, rFilePackage),
+            parseNullableId(destValue, rFilePackage), args)
+}
+
+fun parseNavigationFile(navigationXml: File, rFilePackage: String,
+        applicationId: String): Destination {
+    FileReader(navigationXml).use { reader ->
+        val parser = XmlPullParserFactory.newInstance().newPullParser().apply {
+            setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true)
+            setInput(reader)
+        }
+        parser.traverseStartTags { true }
+        return parseDestination(parser, rFilePackage, applicationId)
+    }
+}
+
+// @[+][package:]id/resource_name -> package.R.id.resource_name
+private val RESOURCE_REGEX = Regex("^@[+]?(.+?:)?(.+?)/(.+)$")
+
+internal fun parseReference(xmlValue: String, rFilePackage: String): ResReference {
+    return tryToParseReference(xmlValue, rFilePackage) ?:
+            throw IllegalArgumentException("id should be in format: " +
+                    "@[+][package:]res_type/resource_name, but is: $xmlValue")
+}
+
+internal fun tryToParseReference(xmlValue: String, rFilePackage: String): ResReference? {
+    val matchEntire = RESOURCE_REGEX.matchEntire(xmlValue) ?: return null
+    val groups = matchEntire.groupValues
+    val resourceName = groups.last()
+    val resType = groups[groups.size - 2]
+    val packageName = if (groups[1].isNotEmpty()) groups[1].removeSuffix(":") else rFilePackage
+    return ResReference(packageName, resType, resourceName)
+}
+
+internal fun parseId(xmlId: String, rFilePackage: String): ResReference {
+    val ref = parseReference(xmlId, rFilePackage)
+    if (!ref.isId()) {
+        throw IllegalArgumentException("$xmlId was passed as id, but is ${ref.resType}")
+    }
+    return ref
+}
+
+internal fun parseNullableId(xmlId: String?, rFilePackage: String): ResReference? = xmlId?.let {
+    parseId(it, rFilePackage)
+}
+
+private fun tryToParseIntValue(value: String): IntValue? {
+    try {
+        if (value.startsWith("0x")) {
+            Integer.parseUnsignedInt(value.substring(2), 16)
+        } else {
+            Integer.parseInt(value)
+        }
+    } catch (ex: NumberFormatException) {
+        return null
+    }
+    return IntValue(value)
+}
+
+internal fun parseIntValue(value: String): IntValue {
+    return tryToParseIntValue(value)
+            ?: throw IllegalArgumentException("Failed to parse $value as int")
+}
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavSafeArgsGenerator.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavSafeArgsGenerator.kt
new file mode 100644
index 0000000..2e0c08a
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavSafeArgsGenerator.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.models.Destination
+import com.squareup.javapoet.JavaFile
+import java.io.File
+
+fun generateSafeArgs(
+        rFilePackage: String, applicationId: String,
+        navigationXml: File, outputDir: File): List<String> {
+    val rawDestination = parseNavigationFile(navigationXml, rFilePackage, applicationId)
+    val resolvedDestination = resolveArguments(rawDestination)
+    val javaFiles = mutableSetOf<JavaFile>()
+    fun writeJavaFiles(destination: Destination) {
+        if (destination.actions.isNotEmpty()) {
+            javaFiles.add(generateDirectionsJavaFile(destination))
+        }
+        if (destination.args.isNotEmpty()) {
+            javaFiles.add(generateArgsJavaFile(destination))
+        }
+        destination.nested.forEach(::writeJavaFiles)
+    }
+    writeJavaFiles(resolvedDestination)
+    javaFiles.forEach { javaFile -> javaFile.writeTo(outputDir) }
+    return javaFiles.map { javaFile -> "${javaFile.packageName}.${javaFile.typeSpec.name}" }
+}
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavWriter.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavWriter.kt
new file mode 100644
index 0000000..f072c54
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/NavWriter.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.ext.N
+import androidx.navigation.safe.args.generator.ext.S
+import androidx.navigation.safe.args.generator.ext.T
+import androidx.navigation.safe.args.generator.models.Action
+import androidx.navigation.safe.args.generator.models.Argument
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.accessor
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+private const val NAVIGATION_PACKAGE = "androidx.navigation"
+private val NAV_DIRECTION_CLASSNAME: ClassName = ClassName.get(NAVIGATION_PACKAGE, "NavDirections")
+private val BUNDLE_CLASSNAME: ClassName = ClassName.get("android.os", "Bundle")
+
+private class ClassWithArgsSpecs(val args: List<Argument>) {
+
+    fun fieldSpecs() = args.map { arg ->
+        FieldSpec.builder(arg.type.typeName(), arg.name)
+                .apply {
+                    addModifiers(Modifier.PRIVATE)
+                    if (arg.isOptional()) {
+                        initializer(arg.defaultValue!!.write())
+                    }
+                }
+                .build()
+    }
+
+    fun setters(thisClassName: ClassName) = args.map { (name, type) ->
+        MethodSpec.methodBuilder("set${name.capitalize()}")
+                .addModifiers(Modifier.PUBLIC)
+                .addParameter(type.typeName(), name)
+                .addStatement("this.$N = $N", name, name)
+                .addStatement("return this")
+                .returns(thisClassName)
+                .build()
+    }
+
+    fun constructor() = MethodSpec.constructorBuilder().apply {
+        addModifiers(Modifier.PUBLIC)
+        args.filterNot(Argument::isOptional).forEach { (argName, type) ->
+            addParameter(type.typeName(), argName)
+            addStatement("this.$N = $N", argName, argName)
+        }
+    }.build()
+
+    fun toBundleMethod(name: String) = MethodSpec.methodBuilder(name).apply {
+        addModifiers(Modifier.PUBLIC)
+        returns(BUNDLE_CLASSNAME)
+        val bundleName = "__outBundle"
+        addStatement("$T $N = new $T()", BUNDLE_CLASSNAME, bundleName, BUNDLE_CLASSNAME)
+        args.forEach { (argName, type) ->
+            addStatement("$N.$N($S, this.$N)", bundleName, type.bundlePutMethod(), argName, argName)
+        }
+        addStatement("return $N", bundleName)
+    }.build()
+
+    fun copyProperties(to: String, from: String) = CodeBlock.builder()
+            .apply {
+                args.forEach { arg -> addStatement("$to.${arg.name} = $from.${arg.name}") }
+            }
+            .build()
+
+    fun getters() = args.map { arg ->
+        MethodSpec.methodBuilder("get${arg.name.capitalize()}")
+                .addModifiers(Modifier.PUBLIC)
+                .addStatement("return $N", arg.name)
+                .returns(arg.type.typeName())
+                .build()
+    }
+}
+
+fun generateDestinationDirectionsTypeSpec(
+        className: ClassName,
+        destination: Destination): TypeSpec {
+    val actionTypes = destination.actions.map { action ->
+        action to generateDirectionsTypeSpec(action)
+    }
+
+    val getters = actionTypes
+            .map { (action, actionType) ->
+                val constructor = actionType.methodSpecs.find(MethodSpec::isConstructor)!!
+                val params = constructor.parameters.joinToString(", ") { param -> param.name }
+                val actionTypeName = ClassName.get("", actionType.name)
+                MethodSpec.methodBuilder(action.id.name)
+                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                        .addParameters(constructor.parameters)
+                        .returns(actionTypeName)
+                        .addStatement("return new $T($params)", actionTypeName)
+                        .build()
+            }
+
+    return TypeSpec.classBuilder(className)
+            .addModifiers(Modifier.PUBLIC)
+            .addTypes(actionTypes.map { (_, actionType) -> actionType })
+            .addMethods(getters)
+            .build()
+}
+
+fun generateDirectionsTypeSpec(action: Action): TypeSpec {
+    val specs = ClassWithArgsSpecs(action.args)
+
+    val getDestIdMethod = MethodSpec.methodBuilder("getActionId")
+            .addModifiers(Modifier.PUBLIC)
+            .returns(Int::class.java)
+            .addStatement("return $N", action.id.accessor())
+            .build()
+
+    val className = ClassName.get("", action.id.name.capitalize())
+    return TypeSpec.classBuilder(className)
+            .addSuperinterface(NAV_DIRECTION_CLASSNAME)
+            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+            .addFields(specs.fieldSpecs())
+            .addMethod(specs.constructor())
+            .addMethods(specs.setters(className))
+            .addMethod(specs.toBundleMethod("getArguments"))
+            .addMethod(getDestIdMethod)
+            .build()
+}
+
+internal fun generateArgsJavaFile(destination: Destination): JavaFile {
+    val destName = destination.name
+            ?: throw IllegalStateException("Destination with arguments must have name")
+    val className = ClassName.get(destName.packageName(), "${destName.simpleName()}Args")
+    val args = destination.args
+    val specs = ClassWithArgsSpecs(args)
+
+    val fromBundleMethod = MethodSpec.methodBuilder("fromBundle").apply {
+        addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+        val bundle = "bundle"
+        addParameter(BUNDLE_CLASSNAME, bundle)
+        returns(className)
+        val result = "result"
+        addStatement("$T $N = new $T()", className, result, className)
+        args.forEach { arg ->
+            beginControlFlow("if ($N.containsKey($S))", bundle, arg.name).apply {
+                addStatement("$N.$N = $N.$N($S)", result, arg.name, bundle,
+                        arg.type.bundleGetMethod(), arg.name)
+            }
+            if (!arg.isOptional()) {
+                nextControlFlow("else")
+                addStatement("throw new $T($S)", IllegalArgumentException::class.java,
+                        "Required argument \"${arg.name}\" is missing and does " +
+                                "not have an android:defaultValue")
+            }
+            endControlFlow()
+        }
+        addStatement("return $N", result)
+    }.build()
+
+    val constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build()
+
+    val copyConstructor = MethodSpec.constructorBuilder()
+            .addModifiers(Modifier.PUBLIC)
+            .addParameter(className, "original")
+            .addCode(specs.copyProperties("this", "original"))
+            .build()
+
+    val buildMethod = MethodSpec.methodBuilder("build")
+            .addModifiers(Modifier.PUBLIC)
+            .returns(className)
+            .addStatement("$T result = new $T()", className, className)
+            .addCode(specs.copyProperties("result", "this"))
+            .addStatement("return result")
+            .build()
+
+    val builderClassName = ClassName.get("", "Builder")
+    val builderTypeSpec = TypeSpec.classBuilder("Builder")
+            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+            .addFields(specs.fieldSpecs())
+            .addMethod(copyConstructor)
+            .addMethod(specs.constructor())
+            .addMethod(buildMethod)
+            .addMethods(specs.setters(builderClassName))
+            .addMethods(specs.getters())
+            .build()
+
+    val typeSpec = TypeSpec.classBuilder(className)
+            .addModifiers(Modifier.PUBLIC)
+            .addFields(specs.fieldSpecs())
+            .addMethod(constructor)
+            .addMethod(fromBundleMethod)
+            .addMethods(specs.getters())
+            .addMethod(specs.toBundleMethod("toBundle"))
+            .addType(builderTypeSpec)
+            .build()
+
+    return JavaFile.builder(className.packageName(), typeSpec).build()
+}
+
+fun generateDirectionsJavaFile(destination: Destination): JavaFile {
+    val destName = destination.name
+            ?: throw IllegalStateException("Destination with actions must have name")
+    val className = ClassName.get(destName.packageName(), "${destName.simpleName()}Directions")
+    val typeSpec = generateDestinationDirectionsTypeSpec(className, destination)
+    return JavaFile.builder(className.packageName(), typeSpec).build()
+}
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/Types.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/Types.kt
new file mode 100644
index 0000000..bc0db04
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/Types.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.ext.S
+import androidx.navigation.safe.args.generator.models.ResReference
+import androidx.navigation.safe.args.generator.models.accessor
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
+import com.squareup.javapoet.TypeName
+
+enum class NavType {
+
+    INT {
+        override fun typeName(): TypeName = TypeName.INT
+        override fun bundlePutMethod() = "putInt"
+        override fun bundleGetMethod() = "getInt"
+    },
+
+    STRING {
+        override fun typeName(): TypeName = ClassName.get(String::class.java)
+        override fun bundlePutMethod() = "putString"
+        override fun bundleGetMethod() = "getString"
+    },
+
+    REFERENCE {
+        // it is internally the same as INT, but we don't want to allow to
+        // assignment between int and reference args
+        override fun typeName(): TypeName = TypeName.INT
+
+        override fun bundlePutMethod() = "putInt"
+        override fun bundleGetMethod() = "getInt"
+    };
+
+    abstract fun typeName(): TypeName
+    abstract fun bundlePutMethod(): String
+    abstract fun bundleGetMethod(): String
+}
+
+sealed class WriteableValue {
+    abstract fun write(): CodeBlock
+}
+
+data class ReferenceValue(private val resReference: ResReference?) : WriteableValue() {
+    override fun write(): CodeBlock = CodeBlock.of(resReference.accessor())
+}
+
+data class StringValue(private val value: String) : WriteableValue() {
+    override fun write(): CodeBlock = CodeBlock.of(S, value)
+}
+
+// keeping value as String, it will help to preserve client format of it: hex, dec
+data class IntValue(private val value: String) : WriteableValue() {
+    override fun write(): CodeBlock = CodeBlock.of(value)
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/XmlPullParserExt.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/XmlPullParserExt.kt
new file mode 100644
index 0000000..cea993c
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/XmlPullParserExt.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import org.xmlpull.v1.XmlPullParser
+
+fun XmlPullParser.traverseStartTags(onStartTag: (XmlPullParser) -> Boolean) {
+    while (eventType != XmlPullParser.END_DOCUMENT) {
+        val processedLine = lineNumber
+        val processedColumn = columnNumber
+        if (eventType == XmlPullParser.START_TAG) {
+            if (onStartTag(this)) {
+                return
+            }
+        }
+        if (processedLine == lineNumber && processedColumn == columnNumber) {
+            // otherwise onStart already called next() and we need to try to process current node
+            next()
+        }
+    }
+}
+
+fun XmlPullParser.traverseInnerStartTags(onStartTag: (XmlPullParser) -> Unit) {
+    val innerDepth = depth + 1
+    next()
+    traverseStartTags {
+        if (innerDepth == it.depth) {
+            onStartTag(it)
+        }
+        it.depth < innerDepth
+    }
+}
+
+fun XmlPullParser.attrValue(namespace: String, name: String): String? =
+        (0 until this.attributeCount).find {
+            getAttributeNamespace(it) == namespace && name == getAttributeName(it)
+        }?.let { getAttributeValue(it) }
+
+fun XmlPullParser.attrValueOrThrow(namespace: String, name: String): String =
+        attrValue(namespace, name) ?:
+                throw IllegalStateException("attribute $namespace:$name is missing.")
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/ext/NavJavaPoet_ext.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/ext/NavJavaPoet_ext.kt
new file mode 100644
index 0000000..697998b
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/ext/NavJavaPoet_ext.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator.ext
+
+const val N = "\$N"
+const val T = "\$T"
+const val S = "\$S"
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Action.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Action.kt
new file mode 100644
index 0000000..0361296
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Action.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator.models
+
+data class Action(val id: ResReference,
+                  val destination: ResReference?,
+                  val args: List<Argument> = emptyList())
+
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Argument.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Argument.kt
new file mode 100644
index 0000000..fea1bdb
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Argument.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator.models
+
+import androidx.navigation.safe.args.generator.NavType
+import androidx.navigation.safe.args.generator.WriteableValue
+
+data class Argument(val name: String, val type: NavType, val defaultValue: WriteableValue? = null) {
+    fun isOptional() = defaultValue != null
+}
+
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Destination.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Destination.kt
new file mode 100644
index 0000000..0c74908
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/Destination.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator.models
+
+import com.squareup.javapoet.ClassName
+
+data class Destination(
+        val id: ResReference?,
+        val name: ClassName?,
+        val type: String,
+        val args: List<Argument>,
+        val actions: List<Action>,
+        val nested: List<Destination> = emptyList()) {
+
+    companion object {
+        fun createName(id: ResReference?, name: String, applicationId: String): ClassName? = when {
+            name.isNotEmpty() -> {
+                val specifiedPackage = name.substringBeforeLast('.', "")
+                val classPackage = if (name.startsWith(".")) {
+                    "$applicationId$specifiedPackage"
+                } else {
+                    specifiedPackage
+                }
+                ClassName.get(classPackage, name.substringAfterLast('.'))
+            }
+            id != null -> ClassName.get(id.packageName, id.name.capitalize())
+            else -> null
+        }
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/ResReference.kt b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/ResReference.kt
new file mode 100644
index 0000000..125fe18
--- /dev/null
+++ b/navigation/safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/models/ResReference.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator.models
+
+data class ResReference(val packageName: String, val resType: String, val name: String) {
+    fun isId() = resType == "id"
+}
+
+fun ResReference?.accessor() = this?.let { "$packageName.R.$resType.$name" } ?: "0"
+
diff --git a/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/DestinationTest.kt b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/DestinationTest.kt
new file mode 100644
index 0000000..2f2150d
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/DestinationTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.ResReference
+import com.squareup.javapoet.ClassName
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class DestinationTest {
+
+    @Test
+    fun fullName() {
+        val name = Destination.createName(id("main"), "foo.sub.ClassName", "some.app")
+        assertThat(name, `is`(ClassName.get("foo.sub", "ClassName")))
+    }
+
+    @Test
+    fun relativeWithSubpackage() {
+        val name = Destination.createName(id("main"), ".sub.ClassName", "some.app")
+        assertThat(name, `is`(ClassName.get("some.app.sub", "ClassName")))
+    }
+
+    @Test
+    fun fullNameNoPackage() {
+        val name = Destination.createName(id("main"), "ClassName", "some.app")
+        assertThat(name, `is`(ClassName.get("", "ClassName")))
+    }
+
+    @Test
+    fun idOnly() {
+        val name = Destination.createName(id("main"), "", "some.app")
+        assertThat(name, `is`(ClassName.get("foo.bar", "Main")))
+    }
+}
+
+private fun id(name: String) = ResReference("foo.bar", "id", name)
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavArgumentResolverTest.kt b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavArgumentResolverTest.kt
new file mode 100644
index 0000000..a4906d2
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavArgumentResolverTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.NavType.INT
+import androidx.navigation.safe.args.generator.NavType.STRING
+import androidx.navigation.safe.args.generator.models.Action
+import androidx.navigation.safe.args.generator.models.Argument
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.ResReference
+import com.squareup.javapoet.ClassName
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Assert.fail
+
+@RunWith(JUnit4::class)
+class NavArgumentResolverTest {
+
+    private fun id(id: String) = ResReference("a.b", "id", id)
+
+    private fun createTemplateDestination(name: String) =
+            Destination(
+                    id(name), ClassName.get("foo", "Fragment${name.capitalize()}"), "test",
+                    listOf(
+                            Argument("arg1", STRING),
+                            Argument("arg2", STRING, StringValue("foo"))
+                    ), emptyList())
+
+    @Test
+    fun test() {
+        val dest1Template = createTemplateDestination("first")
+        val dest2Template = createTemplateDestination("second")
+        val outerScopeAction = Action(id("toOuterScope"), id("outerScope"),
+                listOf(Argument("boo", STRING)))
+        val dest1 = dest1Template.copy(actions = listOf(Action(id("action1"), dest2Template.id),
+                outerScopeAction))
+        val dest2 = dest2Template.copy(actions = listOf(Action(id("action2"), dest1Template.id,
+                listOf(Argument("arg1", STRING, StringValue("actionValue")),
+                        Argument("actionArg", STRING)))))
+
+        val topLevel = Destination(null, null, "test",
+                emptyList(), emptyList(), listOf(dest1, dest2))
+
+        val resolveArguments = resolveArguments(topLevel)
+        assertThat(resolveArguments.nested.size, `is`(2))
+
+        val resolvedAction1 = Action(id("action1"), dest2Template.id, dest2.args)
+        assertThat(resolveArguments.nested[0].actions, `is`(listOf(resolvedAction1,
+                outerScopeAction)))
+
+        val resolvedAction2 = Action(id("action2"), dest1Template.id, listOf(
+                Argument("arg1", STRING, StringValue("actionValue")),
+                Argument("actionArg", STRING),
+                Argument("arg2", STRING, StringValue("foo"))
+        ))
+        assertThat(resolveArguments.nested[1].actions, `is`(listOf(resolvedAction2)))
+    }
+
+    @Test
+    fun testIncompatibleTypes() {
+        val dest1 = createTemplateDestination("first")
+        val invalidAction = Action(id("action"), dest1.id, listOf(
+                Argument("arg2", INT, IntValue("11")),
+                Argument("arg1", STRING)
+        ))
+
+        val topLevel = Destination(null, null, "test", emptyList(), listOf(invalidAction),
+                listOf(dest1))
+
+        try {
+            resolveArguments(topLevel)
+            fail()
+        } catch (ex: IllegalArgumentException) {
+            // expected error
+        }
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavGeneratorTest.kt b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavGeneratorTest.kt
new file mode 100644
index 0000000..48e3e3a
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavGeneratorTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.io.File
+
+@RunWith(JUnit4::class)
+class NavGeneratorTest {
+
+    @Suppress("MemberVisibilityCanPrivate")
+    @get:Rule
+    val workingDir = TemporaryFolder()
+
+    @Test
+    fun test() {
+        val javaNames = generateSafeArgs("foo", "foo.flavor",
+                File("src/tests/test-data/naive_test.xml"), workingDir.root)
+
+        val expectedSet = setOf(
+                "androidx.navigation.testapp.MainFragmentDirections",
+                "foo.flavor.NextFragmentDirections",
+                "androidx.navigation.testapp.MainFragmentArgs",
+                "foo.flavor.NextFragmentArgs"
+                )
+        assertThat(javaNames.toSet(), `is`(expectedSet))
+        javaNames.forEach { name ->
+            val file = File(workingDir.root, "${name.replace('.', File.separatorChar)}.java")
+            assertThat(file.exists(), `is`(true))
+        }
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavParserTest.kt b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavParserTest.kt
new file mode 100644
index 0000000..2164674
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavParserTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.NavType.INT
+import androidx.navigation.safe.args.generator.NavType.REFERENCE
+import androidx.navigation.safe.args.generator.NavType.STRING
+import androidx.navigation.safe.args.generator.models.Action
+import androidx.navigation.safe.args.generator.models.Argument
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.ResReference
+import com.squareup.javapoet.ClassName
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.io.File
+
+@RunWith(JUnit4::class)
+class NavParserTest {
+
+    @Test
+    fun test() {
+        val id: (String) -> ResReference = { id -> ResReference("a.b", "id", id) }
+        val navGraph = parseNavigationFile(File("src/tests/test-data/naive_test.xml"), "a.b",
+                "foo.app")
+
+        val nameFirst = ClassName.get("androidx.navigation.testapp", "MainFragment")
+        val nameNext = ClassName.get("foo.app", "NextFragment")
+        val expectedFirst = Destination(id("first_screen"), nameFirst, "fragment",
+                listOf(Argument("myarg1", STRING, StringValue("one"))),
+                listOf(Action(id("next"), id("next_fragment"), listOf(
+                        Argument("myarg2", STRING),
+                        Argument("randomArgument", STRING),
+                        Argument("intArgument", INT, IntValue("261"))
+                ))))
+
+        val expectedNext = Destination(id("next_fragment"), nameNext, "fragment",
+                listOf(Argument("myarg2", STRING)),
+                listOf(Action(id("next"), id("first_screen")),
+                        Action(id("finish"), null)))
+
+        val expectedGraph = Destination(null, null, "navigation", emptyList(), emptyList(),
+                listOf(expectedFirst, expectedNext))
+        assertThat(navGraph, `is`(expectedGraph))
+    }
+
+    @Test
+    fun testReferenceParsing() {
+        assertThat(parseReference("@+id/next", "a.b"), `is`(ResReference("a.b", "id", "next")))
+        assertThat(parseReference("@id/next", "a.b"), `is`(ResReference("a.b", "id", "next")))
+        assertThat(parseReference("@android:string/text", "a.b"),
+                `is`(ResReference("android", "string", "text")))
+        assertThat(parseReference("@android:id/text", "a.b"),
+                `is`(ResReference("android", "id", "text")))
+        assertThat(parseReference("@not.android:string/text", "a.b"),
+                `is`(ResReference("not.android", "string", "text")))
+    }
+
+    @Test
+    fun testIntValueParsing() {
+        val error = errorOf({ parseIntValue("foo") })
+        assertThat(error, instanceOf(IllegalArgumentException::class.java))
+        assertThat(parseIntValue("10"), `is`(IntValue("10")))
+        assertThat(parseIntValue("-10"), `is`(IntValue("-10")))
+        assertThat(parseIntValue("0xA"), `is`(IntValue("0xA")))
+        assertThat(parseIntValue("0xFFFFFFFF"), `is`(IntValue("0xFFFFFFFF")))
+        assertThat(errorOf({ parseIntValue("0x1FFFFFFFF") }),
+                instanceOf(IllegalArgumentException::class.java))
+    }
+
+    @Test
+    fun testArgInference() {
+        val infer = { value: String -> inferArgument("foo", value, "a.b") }
+        val intArg = { value: String -> Argument("foo", INT, IntValue(value)) }
+        val stringArg = { value: String -> Argument("foo", STRING, StringValue(value)) }
+        val referenceArg = { pName: String, type: String, value: String ->
+            Argument("foo", REFERENCE, ReferenceValue(ResReference(pName, type, value)))
+        }
+
+        assertThat(infer("spb"), `is`(stringArg("spb")))
+        assertThat(infer("10"), `is`(intArg("10")))
+        assertThat(infer("0x10"), `is`(intArg("0x10")))
+        assertThat(infer("@android:id/some_la"), `is`(referenceArg("android", "id", "some_la")))
+        assertThat(infer("@foo"), `is`(stringArg("@foo")))
+        assertThat(infer("@+id/foo"), `is`(referenceArg("a.b", "id", "foo")))
+        assertThat(infer("@foo:stuff"), `is`(stringArg("@foo:stuff")))
+        assertThat(infer("@/stuff"), `is`(stringArg("@/stuff")))
+        assertThat(infer("10101010100100"), `is`(stringArg("10101010100100")))
+    }
+
+    private fun errorOf(f: () -> Unit, message: String = ""): Exception {
+        try {
+            f()
+            Assert.fail(message)
+            throw Error()
+        } catch (e: Exception) {
+            return e
+        }
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavWriterTest.kt b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavWriterTest.kt
new file mode 100644
index 0000000..f4f3bc7
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/NavWriterTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.NavType.INT
+import androidx.navigation.safe.args.generator.NavType.STRING
+import androidx.navigation.safe.args.generator.NavType.REFERENCE
+
+import androidx.navigation.safe.args.generator.models.Action
+import androidx.navigation.safe.args.generator.models.Argument
+import androidx.navigation.safe.args.generator.models.Destination
+import androidx.navigation.safe.args.generator.models.ResReference
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubject
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import org.hamcrest.CoreMatchers
+import org.hamcrest.MatcherAssert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.io.File
+import java.nio.charset.Charset
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class WriterTest {
+
+    @get:Rule
+    @Suppress("MemberVisibilityCanBePrivate")
+    val workingDir = TemporaryFolder()
+
+    private fun load(fullClassName: String, folder: String): JavaFileObject {
+        val folderPath = "src/tests/test-data/${if (folder.isEmpty()) "" else folder + "/"}"
+        val split = fullClassName.split(".")
+        val code = File("$folderPath/${split.last()}.java").readText(Charset.defaultCharset())
+        return JavaFileObjects.forSourceString(fullClassName, code)
+    }
+
+    private fun id(id: String) = ResReference("a.b", "id", id)
+
+    private fun wrappedInnerClass(spec: TypeSpec): JavaFileObject {
+        val wrappedSpec = TypeSpec.classBuilder("BoringWrapper").addType(spec).build()
+        return toJavaFileObject(JavaFile.builder("a.b", wrappedSpec).build())
+    }
+
+    private fun toJavaFileObject(javaFile: JavaFile): JavaFileObject {
+        val destination = workingDir.newFolder()
+        javaFile.writeTo(destination)
+        val path = javaFile.packageName.replace('.', '/')
+        val generated = File(destination, "$path/${javaFile.typeSpec.name}.java")
+        MatcherAssert.assertThat(generated.exists(), CoreMatchers.`is`(true))
+        return JavaFileObjects.forResource(generated.toURI().toURL())
+    }
+
+    private fun toJavaFileObject(spec: TypeSpec) =
+            toJavaFileObject(JavaFile.builder("a.b", spec).build())
+
+    private fun assertCompilesWithoutError(javaFileObject: JavaFileObject) {
+        JavaSourcesSubject.assertThat(load("a.b.R", "a/b"), javaFileObject).compilesWithoutError()
+    }
+
+    private fun JavaSourcesSubject.parsesAs(fullClassName: String) =
+            this.parsesAs(load(fullClassName, "expected"))
+
+    @Test
+    fun testDirectionClassGeneration() {
+        val actionSpec = generateDirectionsTypeSpec(Action(id("next"), id("destA"),
+                listOf(
+                        Argument("main", STRING),
+                        Argument("mainInt", INT),
+                        Argument("optional", STRING, StringValue("bla")),
+                        Argument("optionalInt", INT, IntValue("239")))))
+        val actual = toJavaFileObject(actionSpec)
+        JavaSourcesSubject.assertThat(actual).parsesAs("a.b.Next")
+        // actions spec must be inner class to be compiled, because of static modifier on class
+        assertCompilesWithoutError(wrappedInnerClass(actionSpec))
+    }
+
+    @Test
+    fun testDirectionNoIdClassGeneration() {
+        val actionSpec = generateDirectionsTypeSpec(Action(id("finish"), null, emptyList()))
+        val actual = toJavaFileObject(actionSpec)
+        JavaSourcesSubject.assertThat(actual).parsesAs("a.b.Finish")
+        // actions spec must be inner class to be compiled, because of static modifier on class
+        assertCompilesWithoutError(wrappedInnerClass(actionSpec))
+    }
+
+    @Test
+    fun testDirectionsClassGeneration() {
+        val nextAction = Action(id("next"), id("destA"),
+                listOf(
+                        Argument("main", STRING),
+                        Argument("optional", STRING, StringValue("bla"))))
+
+        val prevAction = Action(id("previous"), id("destB"),
+                listOf(
+                        Argument("arg1", STRING),
+                        Argument("arg2", STRING)))
+
+        val dest = Destination(null, ClassName.get("a.b", "MainFragment"), "fragment", listOf(),
+                listOf(prevAction, nextAction))
+
+        val actual = toJavaFileObject(generateDirectionsJavaFile(dest))
+        JavaSourcesSubject.assertThat(actual).parsesAs("a.b.MainFragmentDirections")
+        assertCompilesWithoutError(actual)
+    }
+
+    @Test
+    fun testArgumentsClassGeneration() {
+        val dest = Destination(null, ClassName.get("a.b", "MainFragment"), "fragment", listOf(
+                Argument("main", STRING),
+                Argument("optional", INT, IntValue("-1")),
+                Argument("reference", REFERENCE, ReferenceValue(ResReference("a.b", "drawable",
+                        "background")))),
+                listOf())
+
+        val actual = toJavaFileObject(generateArgsJavaFile(dest))
+        JavaSourcesSubject.assertThat(actual).parsesAs("a.b.MainFragmentArgs")
+        assertCompilesWithoutError(actual)
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/WritableValueTest.kt b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/WritableValueTest.kt
new file mode 100644
index 0000000..4ab2627f
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/kotlin/androidx/navigation/safe/args/generator/WritableValueTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safe.args.generator
+
+import androidx.navigation.safe.args.generator.models.ResReference
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class WritableValueTest {
+
+    @Test
+    fun testIntWrite() {
+        assertThat(IntValue("-10").write().toString(), `is`("-10"))
+        assertThat(IntValue("11").write().toString(), `is`("11"))
+    }
+
+    @Test
+    fun testStringWrite() {
+        assertThat(StringValue("foo").write().toString(), `is`("\"foo\""))
+    }
+
+    @Test
+    fun testReferenceWrite() {
+        assertThat(ReferenceValue(ResReference("foo", "id", "bla")).write().toString(),
+                `is`("foo.R.id.bla"))
+        assertThat(ReferenceValue(null).write().toString(), `is`("0"))
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/test-data/IGNORE_CHECKSTYLE b/navigation/safe-args-generator/src/tests/test-data/IGNORE_CHECKSTYLE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/IGNORE_CHECKSTYLE
diff --git a/navigation/safe-args-generator/src/tests/test-data/a/b/R.java b/navigation/safe-args-generator/src/tests/test-data/a/b/R.java
new file mode 100644
index 0000000..8d2fc90
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/a/b/R.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package a.b;
+
+// fake R class to compile against for WriterTest
+public class R {
+
+    public static final class id {
+        public static final int finish = 0x7f060000;
+        public static final int previous = 0x7f060001;
+        public static final int next = 0x7f060002;
+    }
+
+    public static final class drawable {
+        public static int background = 0x7f090001;
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/test-data/expected/Finish.java b/navigation/safe-args-generator/src/tests/test-data/expected/Finish.java
new file mode 100644
index 0000000..07e5ca5
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/expected/Finish.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package a.b;
+
+import android.os.Bundle;
+import androidx.navigation.NavDirections;
+
+public static class Finish implements NavDirections {
+
+    public Finish() {
+    }
+
+    public Bundle getArguments() {
+        Bundle __outBundle = new Bundle();
+        return __outBundle;
+    }
+
+    public int getActionId() {
+        return a.b.R.id.finish;
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/test-data/expected/MainFragmentArgs.java b/navigation/safe-args-generator/src/tests/test-data/expected/MainFragmentArgs.java
new file mode 100644
index 0000000..b62ed9c
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/expected/MainFragmentArgs.java
@@ -0,0 +1,105 @@
+package a.b;
+
+import android.os.Bundle;
+import java.lang.IllegalArgumentException;
+import java.lang.String;
+
+public class MainFragmentArgs {
+    private String main;
+
+    private int optional = -1;
+
+    private int reference = a.b.R.drawable.background;
+
+    private MainFragmentArgs() {
+    }
+
+    public static MainFragmentArgs fromBundle(Bundle bundle) {
+        MainFragmentArgs result = new MainFragmentArgs();
+        if (bundle.containsKey("main")) {
+            result.main = bundle.getString("main");
+        } else {
+            throw new IllegalArgumentException("Required argument \"main\" is missing and does not have an android:defaultValue");
+        }
+        if (bundle.containsKey("optional")) {
+            result.optional = bundle.getInt("optional");
+        }
+        if (bundle.containsKey("reference")) {
+            result.reference = bundle.getInt("reference");
+        }
+        return result;
+    }
+
+    public String getMain() {
+        return main;
+    }
+
+    public int getOptional() {
+        return optional;
+    }
+
+    public int getReference() {
+        return reference;
+    }
+
+    public Bundle toBundle() {
+        Bundle __outBundle = new Bundle();
+        __outBundle.putString("main", this.main);
+        __outBundle.putInt("optional", this.optional);
+        __outBundle.putInt("reference", this.reference);
+        return __outBundle;
+    }
+
+    public static class Builder {
+        private String main;
+
+        private int optional = -1;
+
+        private int reference = a.b.R.drawable.background;
+
+        public Builder(MainFragmentArgs original) {
+            this.main = original.main;
+            this.optional = original.optional;
+            this.reference = original.reference;
+        }
+
+        public Builder(String main) {
+            this.main = main;
+        }
+
+        public MainFragmentArgs build() {
+            MainFragmentArgs result = new MainFragmentArgs();
+            result.main = this.main;
+            result.optional = this.optional;
+            result.reference = this.reference;
+            return result;
+        }
+
+        public Builder setMain(String main) {
+            this.main = main;
+            return this;
+        }
+
+        public Builder setOptional(int optional) {
+            this.optional = optional;
+            return this;
+        }
+
+        public Builder setReference(int reference) {
+            this.reference = reference;
+            return this;
+        }
+
+        public String getMain() {
+            return main;
+        }
+
+        public int getOptional() {
+            return optional;
+        }
+
+        public int getReference() {
+            return reference;
+        }
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/test-data/expected/MainFragmentDirections.java b/navigation/safe-args-generator/src/tests/test-data/expected/MainFragmentDirections.java
new file mode 100644
index 0000000..a4747fb
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/expected/MainFragmentDirections.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package a.b;
+
+import android.os.Bundle;
+import androidx.navigation.NavDirections;
+import java.lang.String;
+
+public class MainFragmentDirections {
+    public static Previous previous(String arg1, String arg2) {
+        return new Previous(arg1, arg2);
+    }
+
+    public static Next next(String main) {
+        return new Next(main);
+    }
+
+    public static class Previous implements NavDirections {
+        private String arg1;
+
+        private String arg2;
+
+        public Previous(String arg1, String arg2) {
+            this.arg1 = arg1;
+            this.arg2 = arg2;
+        }
+
+        public Previous setArg1(String arg1) {
+            this.arg1 = arg1;
+            return this;
+        }
+
+        public Previous setArg2(String arg2) {
+            this.arg2 = arg2;
+            return this;
+        }
+
+        public Bundle getArguments() {
+            Bundle __outBundle = new Bundle();
+            __outBundle.putString("arg1", this.arg1);
+            __outBundle.putString("arg2", this.arg2);
+            return __outBundle;
+        }
+
+        public int getActionId() {
+            return a.b.R.id.previous;
+        }
+    }
+
+    public static class Next implements NavDirections {
+        private String main;
+
+        private String optional = "bla";
+
+        public Next(String main) {
+            this.main = main;
+        }
+
+        public Next setMain(String main) {
+            this.main = main;
+            return this;
+        }
+
+        public Next setOptional(String optional) {
+            this.optional = optional;
+            return this;
+        }
+
+        public Bundle getArguments() {
+            Bundle __outBundle = new Bundle();
+            __outBundle.putString("main", this.main);
+            __outBundle.putString("optional", this.optional);
+            return __outBundle;
+        }
+
+        public int getActionId() {
+            return a.b.R.id.next;
+        }
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/test-data/expected/Next.java b/navigation/safe-args-generator/src/tests/test-data/expected/Next.java
new file mode 100644
index 0000000..5bd185b
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/expected/Next.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package a.b;
+
+import android.os.Bundle;
+import androidx.navigation.NavDirections;
+import java.lang.String;
+
+public static class Next implements NavDirections {
+    private String main;
+
+    private int mainInt;
+
+    private String optional = "bla";
+
+    private int optionalInt = 239;
+
+    public Next(String main, int mainInt) {
+        this.main = main;
+        this.mainInt = mainInt;
+    }
+
+    public Next setMain(String main) {
+        this.main = main;
+        return this;
+    }
+
+    public Next setMainInt(int mainInt) {
+        this.mainInt = mainInt;
+        return this;
+    }
+
+    public Next setOptional(String optional) {
+        this.optional = optional;
+        return this;
+    }
+
+    public Next setOptionalInt(int optionalInt) {
+        this.optionalInt = optionalInt;
+        return this;
+    }
+
+    public Bundle getArguments() {
+        Bundle __outBundle = new Bundle();
+        __outBundle.putString("main", this.main);
+        __outBundle.putInt("mainInt", this.mainInt);
+        __outBundle.putString("optional", this.optional);
+        __outBundle.putInt("optionalInt", this.optionalInt);
+        return __outBundle;
+    }
+
+    public int getActionId() {
+        return a.b.R.id.next;
+    }
+}
\ No newline at end of file
diff --git a/navigation/safe-args-generator/src/tests/test-data/naive_test.xml b/navigation/safe-args-generator/src/tests/test-data/naive_test.xml
new file mode 100644
index 0000000..7cef9b0
--- /dev/null
+++ b/navigation/safe-args-generator/src/tests/test-data/naive_test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/first_screen">
+    <fragment android:id="@+id/first_screen"
+              android:name="androidx.navigation.testapp.MainFragment">
+        <argument android:name="myarg1" android:defaultValue="one" />
+        <action android:id="@+id/next" app:destination="@+id/next_fragment">
+            <argument android:name="myarg2" app:type="string"/>
+            <argument android:name="randomArgument"/>
+            <argument android:name="intArgument" app:type="integer" android:defaultValue="261"/>
+        </action>
+    </fragment>
+    <fragment android:id="@+id/next_fragment"
+              android:name=".NextFragment">
+        <argument android:name="myarg2" />
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+        <action android:id="@+id/finish" app:popUpTo="@id/first_screen" />
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/navigation/safe-args-gradle-plugin/build.gradle b/navigation/safe-args-gradle-plugin/build.gradle
new file mode 100644
index 0000000..62dc6e2
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/build.gradle
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+import androidx.build.SupportConfig
+
+apply plugin: androidx.build.SupportKotlinLibraryPlugin
+apply plugin: 'java-gradle-plugin'
+
+ext.generatedResources = "$buildDir/generated/resources"
+
+sourceSets {
+    test.java.srcDirs += 'src/tests/kotlin'
+    test.resources.srcDirs += generatedResources
+}
+
+dependencies {
+    compile build_libs.gradle
+    compile project(":navigation:navigation-safe-args-generator")
+    compile gradleApi()
+    compile(GSON)
+    testCompile gradleTestKit()
+    testCompile(JUNIT)
+}
+
+task generateSdkResource() {
+    inputs.property("compileSdkVersion", SupportConfig.CURRENT_SDK_VERSION)
+    inputs.property("buildToolsVersion", SupportConfig.BUILD_TOOLS_VERSION)
+    outputs.dir(generatedResources)
+    doLast {
+        // Properties.write will have a timestamp, that invalidates the task,
+        // so we don't use it and write a file manually
+        new File(generatedResources, "sdk.prop").withWriter('UTF-8') { writer ->
+            writer.write("compileSdkVersion=$SupportConfig.CURRENT_SDK_VERSION\n")
+            writer.write("buildToolsVersion=$SupportConfig.BUILD_TOOLS_VERSION\n")
+        }
+    }
+}
+
+test {
+    testLogging { showStandardStreams = true }
+}
+
+tasks["compileTestJava"].dependsOn generateSdkResource
+
+gradlePlugin {
+    plugins {
+        safeargs {
+            id = "androidx.navigation.safeargs"
+            implementationClass = "androidx.navigation.safeargs.gradle.SafeArgsPlugin"
+        }
+    }
+}
+
+supportLibrary {
+    name = "Android Navigation TypeSafe Arguments Gradle Plugin"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2017"
+    description = "Android Navigation TypeSafe Arguments Gradle Plugin"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt b/navigation/safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
new file mode 100644
index 0000000..ee71478
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/ArgumentsGenerationTask.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safeargs.gradle
+
+import androidx.navigation.safe.args.generator.generateSafeArgs
+import com.android.build.gradle.internal.tasks.IncrementalTask
+import com.android.ide.common.resources.FileStatus
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import org.gradle.api.GradleException
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputDirectory
+import java.io.File
+
+private const val MAPPING_FILE = "file_mappings.json"
+
+open class ArgumentsGenerationTask : IncrementalTask() {
+    @get:Input
+    lateinit var rFilePackage: String
+
+    @get:Input
+    lateinit var applicationId: String
+
+    @get:OutputDirectory
+    lateinit var outputDir: File
+
+    @get:InputFiles
+    var navigationFiles: List<File> = emptyList()
+
+    private fun generateArgs(navFiles: Collection<File>, out: File) = navFiles.map { file ->
+        Mapping(file.relativeTo(project.projectDir).path,
+                generateSafeArgs(rFilePackage, applicationId, file, out))
+    }
+
+    private fun writeMappings(mappings: List<Mapping>) {
+        File(incrementalFolder, MAPPING_FILE).writer().use { Gson().toJson(mappings, it) }
+    }
+
+    private fun readMappings(): List<Mapping> {
+        val type = object : TypeToken<List<Mapping>>() {}.type
+        val mappingsFile = File(incrementalFolder, MAPPING_FILE)
+        if (mappingsFile.exists()) {
+            return mappingsFile.reader().use { Gson().fromJson(it, type) }
+        } else {
+            return emptyList()
+        }
+    }
+
+    override fun doFullTaskAction() {
+        if (outputDir.exists() && !outputDir.deleteRecursively()) {
+            project.logger.warn("Failed to clear directory for navigation arguments")
+        }
+        if (!outputDir.exists() && !outputDir.mkdirs()) {
+            throw GradleException("Failed to create directory for navigation arguments")
+        }
+        val mappings = generateArgs(navigationFiles, outputDir)
+        writeMappings(mappings)
+    }
+
+    override fun doIncrementalTaskAction(changedInputs: MutableMap<File, FileStatus>) {
+        super.doIncrementalTaskAction(changedInputs)
+        val oldMapping = readMappings()
+        val navFiles = changedInputs.filter { (_, status) -> status != FileStatus.REMOVED }.keys
+        val newMapping = generateArgs(navFiles, outputDir)
+        val newJavaFiles = newMapping.flatMap { it.javaFiles }.toSet()
+        val (modified, unmodified) = oldMapping.partition {
+            File(project.projectDir, it.navFile) in changedInputs
+        }
+        modified.flatMap { it.javaFiles }
+                .filter { name -> name !in newJavaFiles }
+                .forEach { javaName ->
+                    val fileName = "${javaName.replace('.', File.separatorChar)}.java"
+                    val file = File(outputDir, fileName)
+                    if (file.exists()) {
+                        file.delete()
+                    }
+                }
+        writeMappings(unmodified + newMapping)
+    }
+
+    override fun isIncremental() = true
+}
+
+private data class Mapping(val navFile: String, val javaFiles: List<String>)
diff --git a/navigation/safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt b/navigation/safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
new file mode 100644
index 0000000..686b6b9
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safeargs.gradle
+
+import com.android.build.gradle.AppExtension
+import com.android.build.gradle.api.BaseVariant
+import groovy.util.XmlSlurper
+import org.gradle.api.GradleException
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import java.io.File
+
+private const val PLUGIN_DIRNAME = "navigation-args"
+internal const val GENERATED_PATH = "generated/source/$PLUGIN_DIRNAME"
+internal const val INTERMEDIATES_PATH = "intermediates/$PLUGIN_DIRNAME"
+
+@Suppress("unused")
+class SafeArgsPlugin : Plugin<Project> {
+
+    override fun apply(project: Project) {
+        val appExtension = project.extensions.findByType(AppExtension::class.java)
+                ?: throw GradleException("safeargs plugin must be used with android plugin")
+        appExtension.applicationVariants.all { variant ->
+            val task = project.tasks.create("generateSafeArgs${variant.name.capitalize()}",
+                    ArgumentsGenerationTask::class.java) { task ->
+                task.rFilePackage = variant.rFilePackage()
+                task.applicationId = variant.applicationId
+                task.navigationFiles = navigationFiles(variant)
+                task.outputDir = File(project.buildDir, "$GENERATED_PATH/${variant.dirName}")
+                task.incrementalFolder = File(project.buildDir,
+                        "$INTERMEDIATES_PATH/${variant.dirName}")
+                task.variantName = variant.name
+            }
+            variant.registerJavaGeneratingTask(task, task.outputDir)
+        }
+    }
+}
+
+private fun navigationFiles(variant: BaseVariant) = variant.sourceSets
+        .flatMap { it.resDirectories }
+        .mapNotNull {
+            File(it, "navigation").let { navFolder ->
+                if (navFolder.exists() && navFolder.isDirectory) navFolder else null
+            }
+        }
+        .flatMap { navFolder -> navFolder.listFiles().asIterable() }
+        .groupBy { file -> file.name }
+        .map { entry -> entry.value.last() }
+
+private fun BaseVariant.rFilePackage(): String {
+    val mainSourceSet = sourceSets.find { it.name == "main" }
+    val sourceSet = mainSourceSet ?: sourceSets[0]
+    val manifest = sourceSet.manifestFile
+    val parsed = XmlSlurper(false, false).parse(manifest)
+    return parsed.getProperty("@package").toString()
+}
\ No newline at end of file
diff --git a/navigation/safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/PluginTest.kt b/navigation/safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/PluginTest.kt
new file mode 100644
index 0000000..f490951
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/PluginTest.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.safeargs.gradle
+
+import org.gradle.testkit.runner.BuildResult
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.not
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.io.File
+import java.util.Properties
+
+private const val MAIN_DIR = "androidx/navigation/testapp"
+
+private const val NEXT_DIRECTIONS = "$MAIN_DIR/NextFragmentDirections.java"
+private const val MAIN_DIRECTIONS = "$MAIN_DIR/MainFragmentDirections.java"
+private const val MODIFIED_NEXT_DIRECTIONS = "$MAIN_DIR/ModifiedNextFragmentDirections.java"
+private const val ADDITIONAL_DIRECTIONS = "$MAIN_DIR/AdditionalFragmentDirections.java"
+private const val FOO_DIRECTIONS = "safe/gradle/test/app/foo/FooFragmentDirections.java"
+
+private const val NAV_RESOURCES = "src/main/res/navigation"
+private const val SEC = 1000L
+
+// Does not work in the Android Studio
+@RunWith(JUnit4::class)
+class PluginTest {
+
+    @Suppress("MemberVisibilityCanPrivate")
+    @get:Rule
+    val testProjectDir = TemporaryFolder()
+
+    private var buildFile: File = File("")
+    private var compileSdkVersion = ""
+    private var buildToolsVersion = ""
+
+    private fun projectRoot(): File = testProjectDir.root
+
+    private fun assertGenerated(name: String) = assertExists(name, true)
+
+    private fun assertNotGenerated(name: String) = assertExists(name, false)
+
+    private fun assertExists(name: String, ex: Boolean): File {
+        val generatedFile = File(projectRoot(), "build/$GENERATED_PATH/$name")
+        assertThat(generatedFile.exists(), `is`(ex))
+        return generatedFile
+    }
+
+    private fun navResource(name: String) = File(projectRoot(), "$NAV_RESOURCES/$name")
+
+    private fun runGradle(vararg args: String) = GradleRunner.create()
+            .withProjectDir(projectRoot()).withPluginClasspath().withArguments(*args).build()
+
+    @Before
+    fun setup() {
+        projectRoot().mkdirs()
+        buildFile = File(projectRoot(), "build.gradle")
+        buildFile.createNewFile()
+        // copy local.properties
+        File("../../app-toolkit/local.properties").copyTo(File(projectRoot(),
+                "local.properties"), overwrite = true)
+        val stream = PluginTest::class.java.classLoader.getResourceAsStream("sdk.prop")
+        val properties = Properties()
+        properties.load(stream)
+        compileSdkVersion = properties.getProperty("compileSdkVersion")
+        buildToolsVersion = properties.getProperty("buildToolsVersion")
+        testData("app-project").copyRecursively(projectRoot())
+    }
+
+    private fun setupSimpleBuildGradle() {
+        buildFile.writeText("""
+            plugins {
+                id('com.android.application')
+                id('androidx.navigation.safeargs')
+            }
+
+            android {
+                compileSdkVersion $compileSdkVersion
+                buildToolsVersion "$buildToolsVersion"
+            }
+        """.trimIndent())
+    }
+
+    @Test
+    fun runGenerateTask() {
+        buildFile.writeText("""
+            plugins {
+                id('com.android.application')
+                id('androidx.navigation.safeargs')
+            }
+
+            android {
+                compileSdkVersion $compileSdkVersion
+                buildToolsVersion "$buildToolsVersion"
+                flavorDimensions "mode"
+                productFlavors {
+                    foo {
+                        dimension "mode"
+                        applicationIdSuffix ".foo"
+                    }
+                    notfoo {
+                        dimension "mode"
+                    }
+
+                }
+            }
+        """.trimIndent())
+
+        runGradle("generateSafeArgsNotfooDebug", "generateSafeArgsFooDebug")
+                .assertSuccessfulTask("generateSafeArgsNotfooDebug")
+                .assertSuccessfulTask("generateSafeArgsFooDebug")
+
+        assertGenerated("notfoo/debug/$NEXT_DIRECTIONS")
+        assertNotGenerated("foo/debug/$NEXT_DIRECTIONS")
+        assertGenerated("foo/debug/$FOO_DIRECTIONS")
+    }
+
+    @Test
+    fun incrementalAdd() {
+        setupSimpleBuildGradle()
+        runGradle("generateSafeArgsDebug").assertSuccessfulTask("generateSafeArgsDebug")
+        val nextLastMod = assertGenerated("debug/$NEXT_DIRECTIONS").lastModified()
+
+        testData("incremental-test-data/add_nav.xml").copyTo(navResource("add_nav.xml"))
+
+        // lastModified has one second precision on certain platforms and jdk versions
+        // so sleep for a second
+        Thread.sleep(SEC)
+        runGradle("generateSafeArgsDebug").assertSuccessfulTask("generateSafeArgsDebug")
+        assertGenerated("debug/$ADDITIONAL_DIRECTIONS")
+        val newNextLastMod = assertGenerated("debug/$NEXT_DIRECTIONS").lastModified()
+        assertThat(newNextLastMod, `is`(nextLastMod))
+    }
+
+    @Test
+    fun incrementalModify() {
+        setupSimpleBuildGradle()
+        testData("incremental-test-data/add_nav.xml").copyTo(navResource("add_nav.xml"))
+
+        runGradle("generateSafeArgsDebug").assertSuccessfulTask("generateSafeArgsDebug")
+        val mainLastMod = assertGenerated("debug/$MAIN_DIRECTIONS").lastModified()
+        val additionalLastMod = assertGenerated("debug/$ADDITIONAL_DIRECTIONS").lastModified()
+        assertGenerated("debug/$NEXT_DIRECTIONS")
+
+        testData("incremental-test-data/modified_nav.xml").copyTo(navResource("nav_test.xml"), true)
+
+        // lastModified has one second precision on certain platforms and jdk versions
+        // so sleep for a second
+        Thread.sleep(SEC)
+        runGradle("generateSafeArgsDebug").assertSuccessfulTask("generateSafeArgsDebug")
+        val newMainLastMod = assertGenerated("debug/$MAIN_DIRECTIONS").lastModified()
+        // main directions were regenerated
+        assertThat(newMainLastMod, not(mainLastMod))
+
+        // but additional directions weren't touched
+        val newAdditionalLastMod = assertGenerated("debug/$ADDITIONAL_DIRECTIONS").lastModified()
+        assertThat(newAdditionalLastMod, `is`(additionalLastMod))
+
+        assertGenerated("debug/$MODIFIED_NEXT_DIRECTIONS")
+        assertNotGenerated("debug/$NEXT_DIRECTIONS")
+    }
+
+    @Test
+    fun incrementalRemove() {
+        setupSimpleBuildGradle()
+        testData("incremental-test-data/add_nav.xml").copyTo(navResource("add_nav.xml"))
+
+        runGradle("generateSafeArgsDebug").assertSuccessfulTask("generateSafeArgsDebug")
+        val mainLastMod = assertGenerated("debug/$MAIN_DIRECTIONS").lastModified()
+        assertGenerated("debug/$ADDITIONAL_DIRECTIONS")
+
+        val wasRemoved = navResource("add_nav.xml").delete()
+        assertThat(wasRemoved, `is`(true))
+
+        // lastModified has one second precision on certain platforms and jdk versions
+        // so sleep for a second
+        Thread.sleep(SEC)
+        runGradle("generateSafeArgsDebug").assertSuccessfulTask("generateSafeArgsDebug")
+        val newMainLastMod = assertGenerated("debug/$MAIN_DIRECTIONS").lastModified()
+        // main directions weren't touched
+        assertThat(newMainLastMod, `is`(mainLastMod))
+
+        // but additional directions are removed
+        assertNotGenerated("debug/$ADDITIONAL_DIRECTIONS")
+    }
+}
+
+private fun testData(name: String) = File("src/test/test-data", name)
+
+private fun BuildResult.assertSuccessfulTask(name: String): BuildResult {
+    assertThat(task(":$name")!!.outcome, `is`(TaskOutcome.SUCCESS))
+    return this
+}
\ No newline at end of file
diff --git a/navigation/safe-args-gradle-plugin/src/test/test-data/IGNORE_CHECKSTYLE b/navigation/safe-args-gradle-plugin/src/test/test-data/IGNORE_CHECKSTYLE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/IGNORE_CHECKSTYLE
diff --git a/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/foo/res/navigation/nav_test.xml b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/foo/res/navigation/nav_test.xml
new file mode 100644
index 0000000..a3f8210
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/foo/res/navigation/nav_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/first_screen">
+    <fragment android:id="@+id/first_screen"
+              android:name="androidx.navigation.testapp.MainFragment">
+        <argument android:name="myarg1" android:defaultValue="one" />
+        <action android:id="@+id/next" app:destination="@+id/next_fragment">
+            <argument android:name="myarg2"/>
+            <argument android:name="randomArgument"/>
+        </action>
+    </fragment>
+    <fragment android:id="@+id/next_fragment"
+              android:name=".FooFragment">
+        <argument android:name="myarg2" />
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+        <action android:id="@+id/finish" app:popUpTo="@id/first_screen" />
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/AndroidManifest.xml
similarity index 68%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/AndroidManifest.xml
index 660dbcd..92b9c99 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright 2018 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="safe.gradle.test.app">
+    <application
+        android:allowBackup="true"
+        android:supportsRtl="true">
+     </application>
+</manifest>
diff --git a/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/java/foo/Bar.java b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/java/foo/Bar.java
new file mode 100644
index 0000000..9410272
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/java/foo/Bar.java
@@ -0,0 +1,5 @@
+package foo;
+
+class Bar {
+
+}
\ No newline at end of file
diff --git a/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/res/navigation/nav_test.xml b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/res/navigation/nav_test.xml
new file mode 100644
index 0000000..1e38990
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/app-project/src/main/res/navigation/nav_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/first_screen">
+    <fragment android:id="@+id/first_screen"
+              android:name="androidx.navigation.testapp.MainFragment">
+        <argument android:name="myarg1" android:defaultValue="one" />
+        <action android:id="@+id/next" app:destination="@+id/next_fragment">
+            <argument android:name="myarg2"/>
+            <argument android:name="randomArgument"/>
+        </action>
+    </fragment>
+    <fragment android:id="@+id/next_fragment"
+              android:name="androidx.navigation.testapp.NextFragment">
+        <argument android:name="myarg2" />
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+        <action android:id="@+id/finish" app:popUpTo="@id/first_screen" />
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/navigation/safe-args-gradle-plugin/src/test/test-data/incremental-test-data/add_nav.xml b/navigation/safe-args-gradle-plugin/src/test/test-data/incremental-test-data/add_nav.xml
new file mode 100644
index 0000000..695f195
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/incremental-test-data/add_nav.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/additional_screen">
+    <fragment android:id="@+id/additional_screen"
+              android:name="androidx.navigation.testapp.AdditionalFragment">
+        <argument android:name="bar" android:defaultValue="one" />
+        <action android:id="@+id/foo_action" app:destination="@+id/next_fragment">
+            <argument android:name="foo" />
+        </action>
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/navigation/safe-args-gradle-plugin/src/test/test-data/incremental-test-data/modified_nav.xml b/navigation/safe-args-gradle-plugin/src/test/test-data/incremental-test-data/modified_nav.xml
new file mode 100644
index 0000000..9c27169
--- /dev/null
+++ b/navigation/safe-args-gradle-plugin/src/test/test-data/incremental-test-data/modified_nav.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            xmlns:tools="http://schemas.android.com/tools"
+            app:startDestination="@+id/first_screen">
+    <fragment android:id="@+id/first_screen"
+              android:name="androidx.navigation.testapp.MainFragment">
+        <argument android:name="myarg1" android:defaultValue="one" />
+        <action android:id="@+id/next" app:destination="@+id/next_fragment">
+            <argument android:name="randomArgument"/>
+        </action>
+    </fragment>
+    <fragment android:id="@+id/next_fragment"
+              android:name="androidx.navigation.testapp.ModifiedNextFragment">
+        <action android:id="@+id/next" app:destination="@+id/first_screen"/>
+        <action android:id="@+id/finish" app:popUpTo="@id/first_screen" />
+    </fragment>
+</navigation>
\ No newline at end of file
diff --git a/navigation/testing/build.gradle b/navigation/testing/build.gradle
new file mode 100644
index 0000000..923da88
--- /dev/null
+++ b/navigation/testing/build.gradle
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+    api(project(":navigation:navigation-common"))
+
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(TEST_RUNNER)
+
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Testing"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2017"
+    description = "Android Navigation-Testing"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/testing/ktx/build.gradle b/navigation/testing/ktx/build.gradle
new file mode 100644
index 0000000..d37cbef
--- /dev/null
+++ b/navigation/testing/ktx/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    buildTypes {
+        debug {
+            testCoverageEnabled = false // Breaks Kotlin compiler.
+        }
+    }
+}
+
+dependencies {
+    api(project(":navigation:navigation-testing"))
+    // Ensure that the -ktx dependency graph mirrors the Java dependency graph
+    api(project(":navigation:navigation-common-ktx"))
+    api(KOTLIN_STDLIB)
+
+    testImplementation(JUNIT)
+    testImplementation(TEST_RUNNER)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation Testing Kotlin Extensions"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2018"
+    description = "Android Navigation-Testing-Ktx"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/testing/ktx/src/androidTest/AndroidManifest.xml
similarity index 75%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/testing/ktx/src/androidTest/AndroidManifest.xml
index 660dbcd..d963bb6 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/testing/ktx/src/androidTest/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.navigation.testing.ktx.test">
+
+</manifest>
diff --git a/navigation/testing/ktx/src/androidTest/java/androidx/navigation/testing/TestNavigatorDestinationBuilderTest.kt b/navigation/testing/ktx/src/androidTest/java/androidx/navigation/testing/TestNavigatorDestinationBuilderTest.kt
new file mode 100644
index 0000000..20bba5a
--- /dev/null
+++ b/navigation/testing/ktx/src/androidTest/java/androidx/navigation/testing/TestNavigatorDestinationBuilderTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testing
+
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import androidx.navigation.contains
+import androidx.navigation.get
+import androidx.navigation.navigation
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TestNavigatorDestinationBuilderTest {
+    private val provider = TestNavigatorProvider(InstrumentationRegistry.getTargetContext())
+
+    @Test
+    fun test() {
+        val graph = provider.navigation(startDestination = DESTINATION_ID) {
+            test(DESTINATION_ID)
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+    }
+
+    @Test
+    fun testWithBody() {
+        val graph = provider.navigation(startDestination = DESTINATION_ID) {
+            test(DESTINATION_ID) {
+                label = LABEL
+            }
+        }
+        assertTrue("Destination should be added to the graph",
+                DESTINATION_ID in graph)
+        assertEquals("Destination should have label set",
+                LABEL, graph[DESTINATION_ID].label)
+    }
+}
+
+private const val DESTINATION_ID = 1
+private const val LABEL = "Test"
diff --git a/car/res/drawable/car_button_ripple_background.xml b/navigation/testing/ktx/src/main/AndroidManifest.xml
similarity index 83%
copy from car/res/drawable/car_button_ripple_background.xml
copy to navigation/testing/ktx/src/main/AndroidManifest.xml
index 13d0a49..35a5d64 100644
--- a/car/res/drawable/car_button_ripple_background.xml
+++ b/navigation/testing/ktx/src/main/AndroidManifest.xml
@@ -14,6 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background" />
+<manifest package="androidx.navigation.testing.ktx"/>
diff --git a/navigation/testing/ktx/src/main/java/androidx/navigation/testing/TestNavigator.kt b/navigation/testing/ktx/src/main/java/androidx/navigation/testing/TestNavigator.kt
new file mode 100644
index 0000000..fc46653
--- /dev/null
+++ b/navigation/testing/ktx/src/main/java/androidx/navigation/testing/TestNavigator.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testing
+
+import android.os.Bundle
+
+/**
+ * Get the [TestNavigator] back stack as a [List] of [destination and argument pairs][Pair].
+ */
+val TestNavigator.backStack: List<Pair<TestNavigator.Destination, Bundle?>>
+    get() = mBackStack.map { Pair(it.first!!, it.second) }
diff --git a/navigation/testing/ktx/src/main/java/androidx/navigation/testing/TestNavigatorDestinationBuilder.kt b/navigation/testing/ktx/src/main/java/androidx/navigation/testing/TestNavigatorDestinationBuilder.kt
new file mode 100644
index 0000000..ce8ae41
--- /dev/null
+++ b/navigation/testing/ktx/src/main/java/androidx/navigation/testing/TestNavigatorDestinationBuilder.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE")
+
+package androidx.navigation.testing
+
+import android.support.annotation.IdRes
+import androidx.navigation.NavDestinationBuilder
+import androidx.navigation.NavDestinationDsl
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.get
+
+/**
+ * Construct a new [TestNavigator.Destination]
+ */
+inline fun NavGraphBuilder.test(@IdRes id: Int) = test(id) {}
+
+/**
+ * Construct a new [TestNavigator.Destination]
+ */
+inline fun NavGraphBuilder.test(
+        @IdRes id: Int,
+        block: TestNavigatorDestinationBuilder.() -> Unit
+) = destination(TestNavigatorDestinationBuilder(provider[TestNavigator::class], id).apply(block))
+
+/**
+ * DSL for constructing a new [TestNavigator.Destination]
+ */
+@NavDestinationDsl
+class TestNavigatorDestinationBuilder(
+        navigator: TestNavigator,
+        @IdRes id: Int
+) : NavDestinationBuilder<TestNavigator.Destination>(navigator, id)
diff --git a/navigation/testing/ktx/src/test/java/androidx/navigation/testing/TestNavigatorTest.kt b/navigation/testing/ktx/src/test/java/androidx/navigation/testing/TestNavigatorTest.kt
new file mode 100644
index 0000000..80cdb4f
--- /dev/null
+++ b/navigation/testing/ktx/src/test/java/androidx/navigation/testing/TestNavigatorTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testing
+
+import android.os.Bundle
+import android.support.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+@SmallTest
+class TestNavigatorTest {
+
+    @Test
+    fun backStack() {
+        val testNavigator = TestNavigator()
+        val destination = testNavigator.createDestination()
+        val args = Bundle()
+        testNavigator.navigate(destination, args, null)
+        assertEquals("TestNavigator back stack size is 1 after navigate",
+                1,
+                testNavigator.backStack.size)
+        val (foundDestination, foundArgs) = testNavigator.backStack.last()
+        assertEquals("last() returns last destination navigated to",
+                destination, foundDestination)
+        assertEquals("last() returns arguments Bundle",
+                args, foundArgs)
+    }
+}
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/testing/src/main/AndroidManifest.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/testing/src/main/AndroidManifest.xml
index 16b1d0c..248fd5a 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/testing/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.navigation.testing">
+
+</manifest>
diff --git a/navigation/testing/src/main/java/androidx/navigation/testing/TestNavigator.java b/navigation/testing/src/main/java/androidx/navigation/testing/TestNavigator.java
new file mode 100644
index 0000000..68a4994
--- /dev/null
+++ b/navigation/testing/src/main/java/androidx/navigation/testing/TestNavigator.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testing;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.util.Pair;
+
+import androidx.navigation.NavDestination;
+import androidx.navigation.NavOptions;
+import androidx.navigation.Navigator;
+
+import java.util.ArrayDeque;
+
+/**
+ * A simple Navigator that doesn't actually navigate anywhere, but does dispatch correctly
+ */
+@Navigator.Name("test")
+public class TestNavigator extends Navigator<TestNavigator.Destination> {
+
+    public final ArrayDeque<Pair<Destination, Bundle>> mBackStack = new ArrayDeque<>();
+
+    @NonNull
+    @Override
+    public Destination createDestination() {
+        return new Destination(this);
+    }
+
+    @Override
+    public void navigate(@NonNull Destination destination, Bundle args,
+            NavOptions navOptions) {
+        if (navOptions != null && navOptions.shouldLaunchSingleTop() && !mBackStack.isEmpty()
+                && mBackStack.peekLast().first.getId() == destination.getId()) {
+            mBackStack.pop();
+            mBackStack.add(new Pair<>(destination, args));
+            dispatchOnNavigatorNavigated(destination.getId(), BACK_STACK_UNCHANGED);
+        } else {
+            mBackStack.add(new Pair<>(destination, args));
+            dispatchOnNavigatorNavigated(destination.getId(), BACK_STACK_DESTINATION_ADDED);
+        }
+    }
+
+    @Override
+    public boolean popBackStack() {
+        boolean popped = mBackStack.pollLast() != null;
+        if (popped) {
+            dispatchOnNavigatorNavigated(mBackStack.isEmpty()
+                            ? 0
+                            : mBackStack.peekLast().first.getId(),
+                    BACK_STACK_DESTINATION_POPPED);
+        }
+        return popped;
+    }
+
+    /**
+     * A simple Test destination
+     */
+    public static class Destination extends NavDestination {
+        /**
+         * NavDestinations should be created via {@link Navigator#createDestination}.
+         */
+        Destination(@NonNull Navigator<? extends NavDestination> navigator) {
+            super(navigator);
+        }
+    }
+}
diff --git a/navigation/testing/src/main/java/androidx/navigation/testing/TestNavigatorProvider.java b/navigation/testing/src/main/java/androidx/navigation/testing/TestNavigatorProvider.java
new file mode 100644
index 0000000..5608632
--- /dev/null
+++ b/navigation/testing/src/main/java/androidx/navigation/testing/TestNavigatorProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.testing;
+
+import android.content.Context;
+
+import androidx.navigation.NavGraphNavigator;
+import androidx.navigation.SimpleNavigatorProvider;
+
+/**
+ * Simple NavigatorProvider that only supports &lt;navigation&gt; and &lt;test&gt; navigation
+ * elements.
+ */
+public class TestNavigatorProvider extends SimpleNavigatorProvider {
+
+    public TestNavigatorProvider(Context context) {
+        addNavigator(new NavGraphNavigator(context));
+        addNavigator(new TestNavigator());
+    }
+}
diff --git a/navigation/ui/build.gradle b/navigation/ui/build.gradle
new file mode 100644
index 0000000..deb21c0
--- /dev/null
+++ b/navigation/ui/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+    api(project(":navigation:navigation-runtime"))
+    api(NAV_SUPPORT_DESIGN)
+
+    testImplementation(JUNIT)
+    testImplementation(MOCKITO_CORE)
+    testImplementation(TEST_RUNNER)
+
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation UI"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2018"
+    description = "Android Navigation-UI"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/navigation/ui/ktx/build.gradle b/navigation/ui/ktx/build.gradle
new file mode 100644
index 0000000..6de2e66
--- /dev/null
+++ b/navigation/ui/ktx/build.gradle
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.SupportLibraryExtension
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    buildTypes {
+        debug {
+            testCoverageEnabled = false // Breaks Kotlin compiler.
+        }
+    }
+}
+
+dependencies {
+    api(project(":navigation:navigation-ui"))
+    // Ensure that the -ktx dependency graph mirrors the Java dependency graph
+    api(project(":navigation:navigation-runtime-ktx"))
+    api(KOTLIN_STDLIB)
+    androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
+}
+
+supportLibrary {
+    name = "Android Navigation UI Kotlin Extensions"
+    publish = true
+    mavenVersion = LibraryVersions.NAVIGATION
+    mavenGroup = LibraryGroups.NAVIGATION
+    inceptionYear = "2018"
+    description = "Android Navigation-UI-Ktx"
+    url = SupportLibraryExtension.ARCHITECTURE_URL
+}
diff --git a/car/res/drawable/car_button_ripple_background.xml b/navigation/ui/ktx/src/main/AndroidManifest.xml
similarity index 83%
copy from car/res/drawable/car_button_ripple_background.xml
copy to navigation/ui/ktx/src/main/AndroidManifest.xml
index 13d0a49..120cc3f 100644
--- a/car/res/drawable/car_button_ripple_background.xml
+++ b/navigation/ui/ktx/src/main/AndroidManifest.xml
@@ -14,6 +14,4 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background" />
+<manifest package="androidx.navigation.ui.ktx"/>
diff --git a/navigation/ui/ktx/src/main/java/androidx/navigation/ui/Activity.kt b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/Activity.kt
new file mode 100644
index 0000000..6e530c6
--- /dev/null
+++ b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/Activity.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.ui
+
+import android.support.v4.widget.DrawerLayout
+import android.support.v7.app.AppCompatActivity
+import androidx.navigation.NavController
+
+/**
+ * Sets up the ActionBar returned by [AppCompatActivity.getSupportActionBar] for use
+ * with a [NavController].
+ *
+ * By calling this method, the title in the action bar will automatically be updated when
+ * the destination changes (assuming there is a valid
+ * [label][androidx.navigation.NavDestination.getLabel]).
+ *
+ * The action bar will also display the Up button when you are on a non-root destination and
+ * the drawer icon when on the root destination, automatically animating between them.
+ * Call [DrawerLayout.navigateUp] to handle the Up button.
+ *
+ * @param navController The NavController whose navigation actions will be reflected
+ *                      in the title of the action bar.
+ * @param drawerLayout The DrawerLayout that should be toggled from the home button
+ */
+fun AppCompatActivity.setupActionBarWithNavController(
+        navController: NavController,
+        drawerLayout: DrawerLayout? = null
+) {
+    NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
+}
diff --git a/navigation/ui/ktx/src/main/java/androidx/navigation/ui/BottomNavigationView.kt b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/BottomNavigationView.kt
new file mode 100644
index 0000000..a23285b
--- /dev/null
+++ b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/BottomNavigationView.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.ui
+
+import android.support.design.widget.BottomNavigationView
+import androidx.navigation.NavController
+
+/**
+ * Sets up a [BottomNavigationView] for use with a [NavController]. This will call
+ * [android.view.MenuItem.onNavDestinationSelected] when a menu item is selected.
+ *
+ * The selected item in the NavigationView will automatically be updated when the destination
+ * changes.
+ */
+fun BottomNavigationView.setupWithNavController(navController: NavController) {
+    NavigationUI.setupWithNavController(this, navController)
+}
diff --git a/navigation/ui/ktx/src/main/java/androidx/navigation/ui/DrawerLayout.kt b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/DrawerLayout.kt
new file mode 100644
index 0000000..c87acdc
--- /dev/null
+++ b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/DrawerLayout.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.ui
+
+import android.support.v4.widget.DrawerLayout
+import androidx.navigation.NavController
+
+/**
+ * Handles the Up button by delegating its behavior to the given [NavController].
+ *
+ * This is equivalent to calling [NavController.navigateUp] if the [DrawerLayout] is null.
+ *
+ * @return True if the [NavController] was able to navigate up.
+ */
+fun DrawerLayout?.navigateUp(navController: NavController): Boolean =
+        NavigationUI.navigateUp(this, navController)
diff --git a/navigation/ui/ktx/src/main/java/androidx/navigation/ui/MenuItem.kt b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/MenuItem.kt
new file mode 100644
index 0000000..a989ee3
--- /dev/null
+++ b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/MenuItem.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.ui
+
+import android.view.MenuItem
+import androidx.navigation.NavController
+
+/**
+ * Attempt to navigate to the [NavDestination] associated with this [MenuItem].
+ *
+ * Importantly, it assumes the [menu item id][getItemId] matches a valid
+ * [action id][androidx.navigation.NavDestination.getAction] or
+ * [destination id][androidx.navigation.NavDestination.getId] to be navigated to.
+ *
+ * @return True if the [NavController] was able to navigate to the destination.
+ */
+fun MenuItem.onNavDestinationSelected(navController: NavController): Boolean =
+        NavigationUI.onNavDestinationSelected(this, navController)
diff --git a/navigation/ui/ktx/src/main/java/androidx/navigation/ui/NavigationView.kt b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/NavigationView.kt
new file mode 100644
index 0000000..71e1db5
--- /dev/null
+++ b/navigation/ui/ktx/src/main/java/androidx/navigation/ui/NavigationView.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.ui
+
+import android.support.design.widget.NavigationView
+import androidx.navigation.NavController
+
+/**
+ * Sets up a [NavigationView] for use with a [NavController]. This will call
+ * [android.view.MenuItem.onNavDestinationSelected] when a menu item is selected.
+ *
+ * The selected item in the NavigationView will automatically be updated when the destination
+ * changes.
+ */
+fun NavigationView.setupWithNavController(navController: NavController) {
+    NavigationUI.setupWithNavController(this, navController)
+}
diff --git a/car/res/drawable/car_button_ripple_background_day.xml b/navigation/ui/src/main/AndroidManifest.xml
similarity index 76%
copy from car/res/drawable/car_button_ripple_background_day.xml
copy to navigation/ui/src/main/AndroidManifest.xml
index 16b1d0c..4db5212 100644
--- a/car/res/drawable/car_button_ripple_background_day.xml
+++ b/navigation/ui/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_dark" />
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.navigation.ui">
+
+</manifest>
diff --git a/navigation/ui/src/main/java/androidx/navigation/ui/NavigationUI.java b/navigation/ui/src/main/java/androidx/navigation/ui/NavigationUI.java
new file mode 100644
index 0000000..c34dc65
--- /dev/null
+++ b/navigation/ui/src/main/java/androidx/navigation/ui/NavigationUI.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.navigation.ui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomNavigationView;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.graphics.drawable.DrawerArrowDrawable;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewParent;
+
+import androidx.navigation.NavController;
+import androidx.navigation.NavDestination;
+import androidx.navigation.NavOptions;
+
+/**
+ * Class which hooks up elements typically in the 'chrome' of your application such as global
+ * navigation patterns like a navigation drawer or bottom nav bar with your {@link NavController}.
+ */
+public class NavigationUI {
+
+    // No instances. Static utilities only.
+    private NavigationUI() {
+    }
+
+    /**
+     * Attempt to navigate to the {@link NavDestination} associated with the given MenuItem. This
+     * MenuItem should have been added via one of the helper methods in this class.
+     *
+     * <p>Importantly, it assumes the {@link MenuItem#getItemId() menu item id} matches a valid
+     * {@link NavDestination#getAction(int) action id} or
+     * {@link NavDestination#getId() destination id} to be navigated to.</p>
+     *
+     * @param item The selected MenuItem.
+     * @param navController The NavController that hosts the destination.
+     * @return True if the {@link NavController} was able to navigate to the destination
+     * associated with the given MenuItem.
+     */
+    public static boolean onNavDestinationSelected(@NonNull MenuItem item,
+            @NonNull NavController navController) {
+        return onNavDestinationSelected(item, navController, false);
+    }
+
+    private static boolean onNavDestinationSelected(@NonNull MenuItem item,
+            @NonNull NavController navController, boolean popUp) {
+        NavOptions.Builder builder = new NavOptions.Builder()
+                .setPopUpTo(navController.getGraph().getStartDestination(), false)
+                .setLaunchSingleTop(true)
+                .setEnterAnim(R.anim.nav_default_enter_anim)
+                .setExitAnim(R.anim.nav_default_exit_anim)
+                .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
+                .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
+        if (popUp) {
+            builder.setPopUpTo(navController.getGraph().getStartDestination(), false);
+        }
+        NavOptions options = builder.build();
+        try {
+            //TODO provide proper API instead of using Exceptions as Control-Flow.
+            navController.navigate(item.getItemId(), null, options);
+            return true;
+        } catch (IllegalArgumentException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Handles the Up button by delegating its behavior to the given NavController. This should
+     * generally be called from {@link AppCompatActivity#onSupportNavigateUp()}.
+     * <p>If you do not have a {@link DrawerLayout}, you should call
+     * {@link NavController#navigateUp()} directly.
+     *
+     * @param drawerLayout The DrawerLayout that should be opened if you are on the topmost level
+     *                     of the app.
+     * @param navController The NavController that hosts your content.
+     * @return True if the {@link NavController} was able to navigate up.
+     */
+    public static boolean navigateUp(@Nullable DrawerLayout drawerLayout,
+            @NonNull NavController navController) {
+        if (drawerLayout != null && navController.getCurrentDestination().getId()
+                == navController.getGraph().getStartDestination()) {
+            drawerLayout.openDrawer(GravityCompat.START);
+            return true;
+        } else {
+            return navController.navigateUp();
+        }
+    }
+
+    /**
+     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
+     * with a {@link NavController}.
+     *
+     * <p>By calling this method, the title in the action bar will automatically be updated when
+     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
+     *
+     * <p>The action bar will also display the Up button when you are on a non-root destination.
+     * Call {@link #navigateUp(DrawerLayout, NavController)} to handle the Up button.
+     *
+     * @param activity The activity hosting the action bar that should be kept in sync with changes
+     *                 to the NavController.
+     * @param navController The NavController that supplies the secondary menu. Navigation actions
+ *                      on this NavController will be reflected in the title of the action bar.
+     */
+    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
+            @NonNull NavController navController) {
+        setupActionBarWithNavController(activity, navController, null);
+    }
+
+    /**
+     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
+     * with a {@link NavController}.
+     *
+     * <p>By calling this method, the title in the action bar will automatically be updated when
+     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
+     *
+     * <p>The action bar will also display the Up button when you are on a non-root destination and
+     * the drawer icon when on the root destination, automatically animating between them.
+     * Call {@link #navigateUp(DrawerLayout, NavController)} to handle the Up button.
+     *  @param activity The activity hosting the action bar that should be kept in sync with changes
+     *                 to the NavController.
+     * @param navController The NavController whose navigation actions will be reflected
+     *                      in the title of the action bar.
+     * @param drawerLayout The DrawerLayout that should be toggled from the home button
+     */
+    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
+            @NonNull NavController navController,
+            @Nullable DrawerLayout drawerLayout) {
+        navController.addOnNavigatedListener(
+                new ActionBarOnNavigatedListener(activity, drawerLayout));
+    }
+
+    /**
+     * Sets up a {@link NavigationView} for use with a {@link NavController}. This will call
+     * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected.
+     * The selected item in the NavigationView will automatically be updated when the destination
+     * changes.
+     *
+     * @param navigationView The NavigationView that should be kept in sync with changes to the
+     *                       NavController.
+     * @param navController The NavController that supplies the primary and secondary menu.
+ *                      Navigation actions on this NavController will be reflected in the
+ *                      selected item in the NavigationView.
+     */
+    public static void setupWithNavController(@NonNull final NavigationView navigationView,
+            @NonNull final NavController navController) {
+        navigationView.setNavigationItemSelectedListener(
+                new NavigationView.OnNavigationItemSelectedListener() {
+                    @Override
+                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+                        boolean handled = onNavDestinationSelected(item, navController, true);
+                        if (handled) {
+                            ViewParent parent = navigationView.getParent();
+                            if (parent instanceof DrawerLayout) {
+                                ((DrawerLayout) parent).closeDrawer(navigationView);
+                            }
+                        }
+                        return handled;
+                    }
+                });
+        navController.addOnNavigatedListener(new NavController.OnNavigatedListener() {
+            @Override
+            public void onNavigated(@NonNull NavController controller,
+                    @NonNull NavDestination destination) {
+                int destinationId = destination.getId();
+                Menu menu = navigationView.getMenu();
+                for (int h = 0, size = menu.size(); h < size; h++) {
+                    MenuItem item = menu.getItem(h);
+                    item.setChecked(item.getItemId() == destinationId);
+                }
+            }
+        });
+    }
+
+    /**
+     * Sets up a {@link BottomNavigationView} for use with a {@link NavController}. This will call
+     * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected. The
+     * selected item in the BottomNavigationView will automatically be updated when the destination
+     * changes.
+     *
+     * @param bottomNavigationView The BottomNavigationView that should be kept in sync with
+     *                             changes to the NavController.
+     * @param navController The NavController that supplies the primary menu.
+ *                      Navigation actions on this NavController will be reflected in the
+ *                      selected item in the BottomNavigationView.
+     */
+    public static void setupWithNavController(
+            @NonNull final BottomNavigationView bottomNavigationView,
+            @NonNull final NavController navController) {
+        bottomNavigationView.setOnNavigationItemSelectedListener(
+                new BottomNavigationView.OnNavigationItemSelectedListener() {
+                    @Override
+                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+                        return onNavDestinationSelected(item, navController, true);
+                    }
+                });
+        navController.addOnNavigatedListener(new NavController.OnNavigatedListener() {
+            @Override
+            public void onNavigated(@NonNull NavController controller,
+                    @NonNull NavDestination destination) {
+                int destinationId = destination.getId();
+                Menu menu = bottomNavigationView.getMenu();
+                for (int h = 0, size = menu.size(); h < size; h++) {
+                    MenuItem item = menu.getItem(h);
+                    if (item.getItemId() == destinationId) {
+                        item.setChecked(true);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * The OnNavigatedListener specifically for keeping the ActionBar updated. This handles both
+     * updating the title and updating the Up Indicator transitioning between the
+     */
+    private static class ActionBarOnNavigatedListener implements NavController.OnNavigatedListener {
+        private final AppCompatActivity mActivity;
+        @Nullable
+        private final DrawerLayout mDrawerLayout;
+        private DrawerArrowDrawable mArrowDrawable;
+        private ValueAnimator mAnimator;
+
+        ActionBarOnNavigatedListener(
+                @NonNull AppCompatActivity activity, @Nullable DrawerLayout drawerLayout) {
+            mActivity = activity;
+            mDrawerLayout = drawerLayout;
+        }
+
+        @Override
+        public void onNavigated(@NonNull NavController controller,
+                @NonNull NavDestination destination) {
+            ActionBar actionBar = mActivity.getSupportActionBar();
+            CharSequence title = destination.getLabel();
+            if (!TextUtils.isEmpty(title)) {
+                actionBar.setTitle(title);
+            }
+            boolean isStartDestination =
+                    controller.getGraph().getStartDestination() == destination.getId();
+            actionBar.setDisplayHomeAsUpEnabled(mDrawerLayout != null || !isStartDestination);
+            setActionBarUpIndicator(mDrawerLayout != null && isStartDestination);
+        }
+
+        void setActionBarUpIndicator(boolean showAsDrawerIndicator) {
+            ActionBarDrawerToggle.Delegate delegate = mActivity.getDrawerToggleDelegate();
+            boolean animate = true;
+            if (mArrowDrawable == null) {
+                mArrowDrawable = new DrawerArrowDrawable(
+                        delegate.getActionBarThemedContext());
+                delegate.setActionBarUpIndicator(mArrowDrawable, 0);
+                // We're setting the initial state, so skip the animation
+                animate = false;
+            }
+            float endValue = showAsDrawerIndicator ? 0f : 1f;
+            if (animate) {
+                float startValue = mArrowDrawable.getProgress();
+                if (mAnimator != null) {
+                    mAnimator.cancel();
+                }
+                mAnimator = ObjectAnimator.ofFloat(mArrowDrawable, "progress",
+                        startValue, endValue);
+                mAnimator.start();
+            } else {
+                mArrowDrawable.setProgress(endValue);
+            }
+        }
+    }
+}
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/ui/src/main/res/anim/nav_default_enter_anim.xml
similarity index 69%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/ui/src/main/res/anim/nav_default_enter_anim.xml
index 660dbcd..ef3b3dc 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/ui/src/main/res/anim/nav_default_enter_anim.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <alpha
+        android:duration="@android:integer/config_mediumAnimTime"
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0"/>
+</set>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/ui/src/main/res/anim/nav_default_exit_anim.xml
similarity index 69%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/ui/src/main/res/anim/nav_default_exit_anim.xml
index 660dbcd..f18bbd5 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/ui/src/main/res/anim/nav_default_exit_anim.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <alpha
+        android:duration="@android:integer/config_mediumAnimTime"
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0"/>
+</set>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/ui/src/main/res/anim/nav_default_pop_enter_anim.xml
similarity index 69%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/ui/src/main/res/anim/nav_default_pop_enter_anim.xml
index 660dbcd..ef3b3dc 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/ui/src/main/res/anim/nav_default_pop_enter_anim.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <alpha
+        android:duration="@android:integer/config_mediumAnimTime"
+        android:fromAlpha="0.0"
+        android:toAlpha="1.0"/>
+</set>
\ No newline at end of file
diff --git a/car/res/drawable/car_button_ripple_background_inverse.xml b/navigation/ui/src/main/res/anim/nav_default_pop_exit_anim.xml
similarity index 69%
copy from car/res/drawable/car_button_ripple_background_inverse.xml
copy to navigation/ui/src/main/res/anim/nav_default_pop_exit_anim.xml
index 660dbcd..f18bbd5 100644
--- a/car/res/drawable/car_button_ripple_background_inverse.xml
+++ b/navigation/ui/src/main/res/anim/nav_default_pop_exit_anim.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2018 The Android Open Source Project
+  ~ Copyright (C) 2017 The Android Open 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,6 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ripple
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background_inverse" />
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <alpha
+        android:duration="@android:integer/config_mediumAnimTime"
+        android:fromAlpha="1.0"
+        android:toAlpha="0.0"/>
+</set>
\ No newline at end of file
diff --git a/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/AsyncListDifferTest.kt b/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/AsyncListDifferTest.kt
index 8d4415e..0e0656b 100644
--- a/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/AsyncListDifferTest.kt
+++ b/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/AsyncListDifferTest.kt
@@ -20,7 +20,6 @@
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotSame
 import org.junit.Assert.assertSame
-import org.junit.Assert.fail
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -58,7 +57,7 @@
     private val mMainThread = TestExecutor()
     private val mBackgroundThread = TestExecutor()
 
-    private fun <T> createDiffer(listUpdateCallback: ListUpdateCallback,
+    private fun <T> createHelper(listUpdateCallback: ListUpdateCallback,
             diffCallback: DiffUtil.ItemCallback<T>): AsyncListDiffer<T> {
         return AsyncListDiffer(listUpdateCallback,
                 AsyncDifferConfig.Builder<T>(diffCallback)
@@ -70,72 +69,72 @@
     @Test
     fun initialState() {
         val callback = mock(ListUpdateCallback::class.java)
-        val differ = createDiffer(callback, STRING_DIFF_CALLBACK)
-        assertEquals(0, differ.currentList.size)
+        val helper = createHelper(callback, STRING_DIFF_CALLBACK)
+        assertEquals(0, helper.currentList.size)
         verifyZeroInteractions(callback)
     }
 
     @Test(expected = IndexOutOfBoundsException::class)
     fun getEmpty() {
-        val differ = createDiffer(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
-        differ.currentList[0]
+        val helper = createHelper(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
+        helper.currentList[0]
     }
 
     @Test(expected = IndexOutOfBoundsException::class)
     fun getNegative() {
-        val differ = createDiffer(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
-        differ.submitList(listOf("a", "b"))
-        differ.currentList[-1]
+        val helper = createHelper(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
+        helper.submitList(listOf("a", "b"))
+        helper.currentList[-1]
     }
 
     @Test(expected = IndexOutOfBoundsException::class)
     fun getPastEnd() {
-        val differ = createDiffer(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
-        differ.submitList(listOf("a", "b"))
-        differ.currentList[2]
+        val helper = createHelper(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
+        helper.submitList(listOf("a", "b"))
+        helper.currentList[2]
     }
 
     @Test
     fun getCurrentList() {
-        val differ = createDiffer(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
+        val helper = createHelper(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
 
         // null is emptyList
-        assertSame(emptyList<String>(), differ.currentList)
+        assertSame(emptyList<String>(), helper.currentList)
 
         // other list is wrapped
         val list = listOf("a", "b")
-        differ.submitList(list)
-        assertEquals(list, differ.currentList)
-        assertNotSame(list, differ.currentList)
+        helper.submitList(list)
+        assertEquals(list, helper.currentList)
+        assertNotSame(list, helper.currentList)
 
         // null again, empty again
-        differ.submitList(null)
-        assertSame(emptyList<String>(), differ.currentList)
+        helper.submitList(null)
+        assertSame(emptyList<String>(), helper.currentList)
     }
 
     @Test(expected = UnsupportedOperationException::class)
     fun mutateCurrentListEmpty() {
-        val differ = createDiffer(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
-        differ.currentList[0] = ""
+        val helper = createHelper(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
+        helper.currentList[0] = ""
     }
 
     @Test(expected = UnsupportedOperationException::class)
     fun mutateCurrentListNonEmpty() {
-        val differ = createDiffer(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
-        differ.submitList(listOf("a"))
-        differ.currentList[0] = ""
+        val helper = createHelper(IGNORE_CALLBACK, STRING_DIFF_CALLBACK)
+        helper.submitList(listOf("a"))
+        helper.currentList[0] = ""
     }
 
     @Test
     fun submitListSimple() {
         val callback = mock(ListUpdateCallback::class.java)
-        val differ = createDiffer(callback, STRING_DIFF_CALLBACK)
+        val helper = createHelper(callback, STRING_DIFF_CALLBACK)
 
-        differ.submitList(listOf("a", "b"))
+        helper.submitList(listOf("a", "b"))
 
-        assertEquals(2, differ.currentList.size)
-        assertEquals("a", differ.currentList[0])
-        assertEquals("b", differ.currentList[1])
+        assertEquals(2, helper.currentList.size)
+        assertEquals("a", helper.currentList[0])
+        assertEquals("b", helper.currentList[1])
 
         verify(callback).onInserted(0, 2)
         verifyNoMoreInteractions(callback)
@@ -170,24 +169,24 @@
     @Test
     fun submitListUpdate() {
         val callback = mock(ListUpdateCallback::class.java)
-        val differ = createDiffer(callback, STRING_DIFF_CALLBACK)
+        val helper = createHelper(callback, STRING_DIFF_CALLBACK)
 
         // initial list (immediate)
-        differ.submitList(listOf("a", "b"))
+        helper.submitList(listOf("a", "b"))
         verify(callback).onInserted(0, 2)
         verifyNoMoreInteractions(callback)
         drain()
         verifyNoMoreInteractions(callback)
 
         // update (deferred)
-        differ.submitList(listOf("a", "b", "c"))
+        helper.submitList(listOf("a", "b", "c"))
         verifyNoMoreInteractions(callback)
         drain()
         verify(callback).onInserted(2, 1)
         verifyNoMoreInteractions(callback)
 
         // clear (immediate)
-        differ.submitList(null)
+        helper.submitList(null)
         verify(callback).onRemoved(0, 3)
         verifyNoMoreInteractions(callback)
         drain()
@@ -292,21 +291,12 @@
     companion object {
         private val STRING_DIFF_CALLBACK = object : DiffUtil.ItemCallback<String>() {
             override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
-                // items are the same if first char is the same
-                return oldItem[0] == newItem[0]
+                return oldItem == newItem
             }
 
             override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
                 return oldItem == newItem
             }
-
-            override fun getChangePayload(oldItem: String, newItem: String): Any? {
-                if (newItem.startsWith(oldItem)) {
-                    // new string is appended, return added portion on the end
-                    return newItem.subSequence(oldItem.length, newItem.length)
-                }
-                return null
-            }
         }
 
         private val IGNORE_CALLBACK = object : ListUpdateCallback {
diff --git a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
index 26020e0..91ca484 100644
--- a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
+++ b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/AsyncListDiffer.java
@@ -194,23 +194,19 @@
         // incrementing generation means any currently-running diffs are discarded when they finish
         final int runGeneration = ++mMaxScheduledGeneration;
 
-        // fast simple remove all
         if (newList == null) {
             //noinspection ConstantConditions
-            int countRemoved = mList.size();
+            mUpdateCallback.onRemoved(0, mList.size());
             mList = null;
             mReadOnlyList = Collections.emptyList();
-            // notify last, after list is updated
-            mUpdateCallback.onRemoved(0, countRemoved);
             return;
         }
 
-        // fast simple first insert
         if (mList == null) {
+            // fast simple first insert
+            mUpdateCallback.onInserted(0, newList.size());
             mList = newList;
             mReadOnlyList = Collections.unmodifiableList(newList);
-            // notify last, after list is updated
-            mUpdateCallback.onInserted(0, newList.size());
             return;
         }
 
@@ -286,9 +282,8 @@
     }
 
     private void latchList(@NonNull List<T> newList, @NonNull DiffUtil.DiffResult diffResult) {
-        mList = newList;
-        // notify last, after list is updated
-        mReadOnlyList = Collections.unmodifiableList(newList);
         diffResult.dispatchUpdatesTo(mUpdateCallback);
+        mList = newList;
+        mReadOnlyList = Collections.unmodifiableList(newList);
     }
 }
diff --git a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index 08196eb..b044710 100644
--- a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -11519,12 +11519,12 @@
             if (!mRunning) {
                 return;
             }
-            mRunning = false;
             onStop();
             mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
             mTargetView = null;
             mTargetPosition = RecyclerView.NO_POSITION;
             mPendingInitialRun = false;
+            mRunning = false;
             // trigger a cleanup
             mLayoutManager.onSmoothScrollerStopped(this);
             // clear references to avoid any potential leak by a custom smooth scroller