Initial commit of navigation controller

Initial structure of the navigation controller and XML based
navigation graph.

Test: navigation/integration-tests/testapp compiles and runs

Change-Id: I08023947b7c510cd9704bda42edefe1c50cc8430
diff --git a/.gitignore b/.gitignore
index 9c6e6eb..def1f7a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,4 @@
 **/gen
 *.iml
 **/out
-buildSrc/build
-lifecycle/common/build
\ No newline at end of file
+**/build
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index 2a132e0..b2681cf 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -78,6 +78,11 @@
 include ':room:integration-tests:testapp'
 project(':room:integration-tests:testapp').projectDir = new File(supportRoot, "room/integration-tests/testapp")
 
+include ':navigation:runtime'
+project(':navigation:runtime').projectDir = new File(supportRoot, "navigation/runtime")
+
+include ':navigation:integration-tests:testapp'
+project(':navigation:integration-tests:testapp').projectDir = new File(supportRoot, "navigation/integration-tests/testapp")
 /////////////////////////////
 //
 // SupportLib
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/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..5a9db3e
--- /dev/null
+++ b/navigation/integration-tests/testapp/build.gradle
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+project.ext.noDocs = true
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        applicationId "com.android.support.navigation.testapp"
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile project(":navigation:runtime")
+    compile libs.support.app_compat
+    androidTestCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    testCompile libs.junit
+}
+createAndroidCheckstyle(project)
+
+tasks['check'].dependsOn(tasks['connectedCheck'])
+
+uploadArchives.enabled = false
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..009926e
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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="com.android.support.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="com.android.support.navigation.testapp.MainActivity">
+            <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>
+        </activity>
+
+    </application>
+
+</manifest>
diff --git a/navigation/integration-tests/testapp/src/main/java/com/android/support/navigation/testapp/MainActivity.java b/navigation/integration-tests/testapp/src/main/java/com/android/support/navigation/testapp/MainActivity.java
new file mode 100644
index 0000000..7c2061c
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/com/android/support/navigation/testapp/MainActivity.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.android.support.navigation.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.navigation.app.nav.NavController;
+import android.support.navigation.app.nav.NavDestination;
+import android.support.navigation.app.nav.NavHostFragment;
+import android.support.v4.app.FragmentActivity;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * A simple activity demonstrating use of a NavHostFragment.
+ */
+public class MainActivity extends FragmentActivity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main_activity);
+
+        NavHostFragment host = (NavHostFragment) getSupportFragmentManager()
+                .findFragmentById(R.id.my_nav_host_fragment);
+
+
+        if (host != null) {
+            host.getNavController().addOnNavigatedListener(new NavController.OnNavigatedListener() {
+                @Override
+                public void onNavigated(NavController controller, NavDestination destination) {
+                    String dest = getResources().getResourceName(destination.getId());
+                    Toast.makeText(MainActivity.this, "Navigated to "
+                            + dest,
+                            Toast.LENGTH_SHORT).show();
+                    Log.d("adamp", "Navigated to " + dest, new Throwable());
+                }
+            });
+        }
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/java/com/android/support/navigation/testapp/MainFragment.java b/navigation/integration-tests/testapp/src/main/java/com/android/support/navigation/testapp/MainFragment.java
new file mode 100644
index 0000000..2e6750d
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/java/com/android/support/navigation/testapp/MainFragment.java
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.support.navigation.testapp;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.navigation.app.nav.Navigation;
+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;
+
+/**
+ * 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));
+
+        Bundle bundle = new Bundle();
+        bundle.putString("myarg", "foo");
+        Navigation.findController(this).navigate(R.id.next, bundle);
+    }
+}
diff --git a/navigation/integration-tests/testapp/src/main/res/layout/main_activity.xml b/navigation/integration-tests/testapp/src/main/res/layout/main_activity.xml
new file mode 100644
index 0000000..1140a2f
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/main_activity.xml
@@ -0,0 +1,32 @@
+<?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: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="android.support.navigation.app.nav.NavHostFragment"
+        app:navGraph="@xml/nav_main"
+        app:startDestination="@+id/launcher_home"
+        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>
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..e7f7060
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/layout/main_fragment.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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <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/values/colors.xml b/navigation/integration-tests/testapp/src/main/res/values/colors.xml
new file mode 100644
index 0000000..d0709b5
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/values/colors.xml
@@ -0,0 +1,18 @@
+<?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>
+    <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..35b98bd
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/values/strings.xml
@@ -0,0 +1,16 @@
+<?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>
+</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..7d8cf23
--- /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">
+        <!-- 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/navigation/integration-tests/testapp/src/main/res/xml/nav_main.xml b/navigation/integration-tests/testapp/src/main/res/xml/nav_main.xml
new file mode 100644
index 0000000..3fd3764
--- /dev/null
+++ b/navigation/integration-tests/testapp/src/main/res/xml/nav_main.xml
@@ -0,0 +1,28 @@
+<?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">
+    <destination android:id="@+id/launcher_home"
+        app:navigator="fragment"
+        app:nav_fragment="com.android.support.navigation.testapp.MainFragment">
+        <default-argument app:argname="myarg" app:argvalue="one" />
+        <action android:id="@+id/next" app:destination="@+id/next_fragment"/>
+    </destination>
+    <destination android:id="@+id/next_fragment"
+        app:navigator="fragment"
+        app:nav_fragment="com.android.support.navigation.testapp.MainFragment">
+        <default-argument app:argname="myarg" app:argvalue="two" />
+        <action android:id="@+id/next" app:destination="@+id/launcher_home"/>
+    </destination>
+</navigation>
\ No newline at end of file
diff --git a/navigation/runtime/build.gradle b/navigation/runtime/build.gradle
new file mode 100644
index 0000000..34c8638
--- /dev/null
+++ b/navigation/runtime/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * 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 com.android.builder.core.BuilderConstants
+apply plugin: 'com.android.library'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion tools.current_sdk
+    buildToolsVersion tools.build_tools_version
+
+    defaultConfig {
+        minSdkVersion flatfoot.min_sdk
+        targetSdkVersion tools.current_sdk
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
+dependencies {
+    compile libs.support.core_utils
+    compile libs.support.fragments
+
+    testCompile libs.junit
+    testCompile libs.mockito.all
+    testCompile(libs.test_runner) {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile(libs.espresso_core, {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: rootProject.ext.localMavenRepo)
+            pom.artifactId = "runtime"
+        }
+    }
+}
+
+android.libraryVariants.all { variant ->
+    def name = variant.buildType.name
+    def suffix = name.capitalize()
+    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
+        dependsOn variant.javaCompile
+        from variant.javaCompile.destinationDir
+        destinationDir new File(project.buildDir, "libJar")
+    }
+}
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/navigation/runtime/src/main/AndroidManifest.xml b/navigation/runtime/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3d744d9
--- /dev/null
+++ b/navigation/runtime/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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="com.android.support.navigation">
+
+</manifest>
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/ActivityNavigator.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/ActivityNavigator.java
new file mode 100644
index 0000000..4523c70
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/ActivityNavigator.java
@@ -0,0 +1,142 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.AttributeSet;
+
+/**
+ * ActivityNavigator implements cross-activity navigation.
+ */
+public class ActivityNavigator extends Navigator<ActivityNavigator.Params> {
+    public static final String NAME = "activity";
+
+    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(Context context) {
+        mContext = context;
+    }
+
+    public ActivityNavigator(Activity hostActivity) {
+        mContext = mHostActivity = hostActivity;
+    }
+
+    @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, true);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Params generateDefaultParams() {
+        return new Params();
+    }
+
+    @Override
+    public Params inflateParams(Context context, AttributeSet attrs) {
+        // TODO Implement me!
+        return generateDefaultParams();
+    }
+
+    @Override
+    public boolean navigate(NavDestination destination, Bundle args, NavOptions navOptions) {
+        final Params params = (Params) destination.getNavigatorParams();
+        Intent intent = new Intent(params.getIntent());
+        if (args != null) {
+            intent.getExtras().putAll(args);
+        }
+        if (navOptions.shouldClearTask()) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        }
+        if (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.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);
+        mContext.startActivity(intent);
+        dispatchOnNavigatorNavigated(destId, false);
+
+        // Always return false. 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.
+        return false;
+    }
+
+    /**
+     * Params for activity navigation
+     */
+    public static class Params extends Navigator.Params {
+        private Intent mIntent;
+
+        public void setIntent(Intent intent) {
+            mIntent = intent;
+        }
+
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        void setComponentName(ComponentName name) {
+            if (mIntent == null) {
+                mIntent = new Intent();
+            }
+            mIntent.setComponent(name);
+        }
+
+        ComponentName getComponent() {
+            if (mIntent == null) {
+                return null;
+            }
+            return mIntent.getComponent();
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/FragmentNavigator.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/FragmentNavigator.java
new file mode 100644
index 0000000..a86cacc0
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/FragmentNavigator.java
@@ -0,0 +1,247 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+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.text.TextUtils;
+import android.util.AttributeSet;
+
+import com.android.support.navigation.R;
+
+import java.util.HashMap;
+
+/**
+ * Navigator that navigates through {@link FragmentTransaction fragment transactions}. Every
+ * destination using this Navigator must set a valid Fragment class name with
+ * <code>app:nav_fragment</code>.
+ */
+public class FragmentNavigator extends Navigator<FragmentNavigator.Params> {
+    public static final String NAME = "fragment";
+
+    private Context mContext;
+    private FragmentManager mFragmentManager;
+    private int mContainerId;
+    private HashMap<String, Class<? extends Fragment>> mFragmentClasses = new HashMap<>();
+
+    private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
+            new FragmentManager.OnBackStackChangedListener() {
+                @Override
+                public void onBackStackChanged() {
+                    int newCount = mFragmentManager.getBackStackEntryCount();
+
+                    int destId = 0;
+                    StateFragment state = getState();
+                    if (state != null) {
+                        destId = state.mCurrentDestId;
+                    }
+                    dispatchOnNavigatorNavigated(destId, newCount == 0);
+                }
+            };
+
+    public FragmentNavigator(Context context, FragmentManager manager, int containerId) {
+        mContext = context;
+        mFragmentManager = manager;
+        mContainerId = containerId;
+
+        mFragmentManager.addOnBackStackChangedListener(mOnBackStackChangedListener);
+    }
+
+    @Override
+    public boolean popBackStack() {
+        return mFragmentManager.popBackStackImmediate();
+    }
+
+    @Override
+    public Params generateDefaultParams() {
+        return new Params();
+    }
+
+    @Override
+    public Params inflateParams(Context context, AttributeSet attrs) {
+        Params p = generateDefaultParams();
+        TypedArray a = context.getResources().obtainAttributes(attrs,
+                R.styleable.FragmentNavigatorParams);
+        p.setFragmentClass(getFragmentClassByName(a.getString(
+                R.styleable.FragmentNavigatorParams_nav_fragment)));
+        p.setFlow(a.getString(R.styleable.FragmentNavigatorParams_nav_flow));
+        a.recycle();
+        return p;
+    }
+
+    Class<? extends Fragment> getFragmentClassByName(String name) {
+        Class<? extends Fragment> clazz = mFragmentClasses.get(name);
+        if (clazz == null) {
+            try {
+                clazz = (Class<? extends Fragment>) Class.forName(name, true,
+                        mContext.getClassLoader());
+                mFragmentClasses.put(name, clazz);
+            } catch (ClassNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return clazz;
+    }
+
+    @Override
+    public boolean navigate(NavDestination destination, Bundle args,
+                            NavOptions navOptions) {
+        final Params params = (Params) destination.getNavigatorParams();
+        String flowName = navOptions != null ? navOptions.getFlowName() : null;
+        if (flowName == null) {
+            flowName = params.getFlow();
+        }
+
+        // If the first non-null back stack entry name we find matches our flow name, we're still
+        // part of the same flow and we should pass null as the name of this transaction.
+        // This way we can finish a whole flow back to its root by the flow name.
+        if (flowName != null) {
+            final int stackCount = mFragmentManager.getBackStackEntryCount();
+            for (int i = stackCount - 1; i >= 0; i--) {
+                final FragmentManager.BackStackEntry bse = mFragmentManager.getBackStackEntryAt(i);
+                final String bseName = bse.getName();
+                if (bseName != null) {
+                    if (TextUtils.equals(bseName, flowName)) {
+                        flowName = null;
+                    }
+                    break;
+                }
+            }
+        }
+        final Fragment frag = params.createFragment(args);
+        final FragmentTransaction ft = mFragmentManager.beginTransaction()
+                .replace(mContainerId, frag);
+
+        final StateFragment oldState = getState();
+        if (oldState != null) {
+            ft.remove(oldState);
+        }
+
+        final int destId = destination.getId();
+        final StateFragment newState = new StateFragment();
+        newState.mCurrentDestId = destId;
+        ft.add(newState, StateFragment.FRAGMENT_TAG);
+
+        final boolean initialNavigation = mFragmentManager.getFragments().isEmpty();
+        if (!initialNavigation) {
+            ft.addToBackStack(flowName);
+        } else {
+            ft.postOnCommit(new Runnable() {
+                @Override
+                public void run() {
+                    dispatchOnNavigatorNavigated(destId, false);
+                }
+            });
+        }
+        ft.commit();
+
+        return true;
+    }
+
+    private StateFragment getState() {
+        return (StateFragment) mFragmentManager.findFragmentByTag(StateFragment.FRAGMENT_TAG);
+    }
+
+    /**
+     * Params specific to {@link FragmentNavigator}
+     */
+    public static class Params extends Navigator.Params {
+        private Class<? extends Fragment> mFragmentClass;
+        private String mFlow;
+
+        public Params() {
+        }
+
+        @Override
+        public void copyFrom(Navigator.Params other) {
+            super.copyFrom(other);
+            if (other instanceof Params) {
+                setFragmentClass(((Params) other).getFragmentClass());
+                setFlow(((Params) other).getFlow());
+            }
+        }
+
+        public void setFragmentClass(Class<? extends Fragment> clazz) {
+            mFragmentClass = clazz;
+        }
+
+        public Class<? extends Fragment> getFragmentClass() {
+            return mFragmentClass;
+        }
+
+        public String getFlow() {
+            return mFlow;
+        }
+
+        public void setFlow(String flowName) {
+            mFlow = flowName;
+        }
+
+        Fragment createFragment(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/runtime/src/main/java/android/support/navigation/app/nav/NavController.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavController.java
new file mode 100644
index 0000000..73db2ac
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavController.java
@@ -0,0 +1,527 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.XmlRes;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * NavController manages app navigation within a host.
+ *
+ * <p>A host is a single context or container for navigation, e.g. a {@link NavHostFragment}.
+ * Navigation hosts are responsible for {@link #saveState() saving} and
+ * {@link #restoreState(Bundle) restoring} their controller's state. 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 implements NavigatorProvider {
+    /**
+     * 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 #addMetadataGraph()}. 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 KEY_GRAPH_ID = "android-support-nav:controller:graphId";
+    private static final String KEY_START_DEST_ID = "android-support-nav:controller:startDestId";
+    private static final String KEY_CUR_DEST_ID = "android-support-nav:controller:curDestId";
+
+    private Context mContext;
+    private NavInflater mInflater;
+    private NavGraph mGraph;
+    private int mGraphId;
+    private NavDestination mCurrentNode;
+    private int mStartDestId;
+
+    private final HashMap<String, Navigator> mNavigators = new HashMap<>();
+    private final ArrayList<Navigator> mNavigatorBackStack = new ArrayList<>();
+
+    private final Navigator.OnNavigatorNavigatedListener mOnNavigatedListener =
+            new Navigator.OnNavigatorNavigatedListener() {
+                @Override
+                public void onNavigatorNavigated(Navigator navigator, @IdRes int destId,
+                                                 boolean isBackStackEmpty) {
+                    if (destId != 0) {
+                        NavDestination newDest = mGraph.findNode(destId);
+                        if (newDest == null) {
+                            throw new IllegalArgumentException("Navigator " + navigator
+                                    + " reported navigation to unknown destination id "
+                                    + mContext.getResources().getResourceName(destId));
+                        }
+                        mCurrentNode = newDest;
+                        dispatchOnNavigated(newDest);
+                    }
+                    if (isBackStackEmpty) {
+                onNavigatorBackStackEmpty(navigator);
+                    }
+                }
+            };
+
+    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(NavController controller, 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 such as
+     * {@link NavHostFragment#getNavController() NavHostFragment} 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(Context context) {
+        mContext = context;
+        final ActivityNavigator activityNavigator = new ActivityNavigator(mContext);
+        mNavigators.put(ActivityNavigator.NAME, activityNavigator);
+    }
+
+    /**
+     * Constructs a new controller for a given {@link Activity}.
+     *
+     * <p>Apps should generally not construct controllers, instead obtain a relevant controller
+     * directly from a navigation host such as
+     * {@link NavHostFragment#getNavController() NavHostFragment} or by using one of
+     * the utility methods on the {@link Navigation} class.</p>
+     *
+     * @param activity activity for this controller
+     */
+    public NavController(Activity activity) {
+        mContext = activity;
+        final ActivityNavigator activityNavigator = new ActivityNavigator(activity);
+        mNavigators.put(ActivityNavigator.NAME, activityNavigator);
+
+        // The host activity is always at the root.
+        mNavigatorBackStack.add(activityNavigator);
+    }
+
+    /**
+     * Adds an {@link OnNavigatedListener} to this controller to receive events when
+     * the controller navigates to a new destination.
+     *
+     * @param listener the listener to receive events
+     */
+    public void addOnNavigatedListener(OnNavigatedListener listener) {
+        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(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 (mNavigatorBackStack.isEmpty()) {
+            throw new IllegalArgumentException("NavController back stack is empty");
+        }
+        return mNavigatorBackStack.get(mNavigatorBackStack.size() - 1).popBackStack();
+    }
+
+    /**
+     * 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 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() {
+        // TODO check current task; if we're in one from a viewer context on another task, jump.
+        return popBackStack();
+    }
+
+    void dispatchOnNavigated(NavDestination destination) {
+        for (OnNavigatedListener listener : mOnNavigatedListeners) {
+            listener.onNavigated(this, destination);
+        }
+    }
+
+    @Override
+    public Navigator getNavigator(String name) {
+        if (TextUtils.isEmpty(name)) {
+            throw new IllegalArgumentException("navigator name cannot be null");
+        }
+
+        return mNavigators.get(name);
+    }
+
+    @Override
+    public void addNavigator(String name, Navigator navigator) {
+        navigator.addOnNavigatorNavigatedListener(mOnNavigatedListener);
+        mNavigators.put(name, navigator);
+    }
+
+    /**
+     * Removes a registered {@link Navigator} by name.
+     *
+     * @param name name of the navigator to remove
+     */
+    public void removeNavigator(String name) {
+        final Navigator removed = mNavigators.remove(name);
+        if (removed != null) {
+            removed.removeOnNavigatorNavigatedListener(mOnNavigatedListener);
+        }
+    }
+
+    /**
+     * Add a {@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
+     */
+    public void addMetadataGraph() {
+        final Bundle metaData = mContext.getApplicationInfo().metaData;
+        if (metaData != null) {
+            final int resid = metaData.getInt(METADATA_KEY_GRAPH);
+            if (resid != 0) {
+                addGraph(resid);
+            }
+        }
+    }
+
+    /**
+     * Returns the {@link NavInflater inflater} for this controller.
+     *
+     * @return inflater for loading navigation resources
+     */
+    public NavInflater getNavInflater() {
+        if (mInflater == null) {
+            mInflater = new NavInflater(mContext, this);
+        }
+        return mInflater;
+    }
+
+    /**
+     * Sets the {@link NavGraph navigation graph} to the specified resource.
+     * Any current navigation graph data will be replaced.
+     *
+     * @param resid resource id of the navigation graph to inflate
+     *
+     * @see #getNavInflater()
+     * @see #setGraph(NavGraph)
+     */
+    public void setGraph(@XmlRes int resid) {
+        mGraph = getNavInflater().inflate(resid);
+        mGraphId = resid;
+    }
+
+    /**
+     * Sets the {@link NavGraph navigation graph} to the specified resource.
+     * Any current navigation graph data will be replaced.
+     *
+     * @param graph graph to set
+     * @see #setGraph(int)
+     */
+    public void setGraph(NavGraph graph) {
+        mGraph = graph;
+        mGraphId = 0;
+    }
+
+    /**
+     * Adds the contents of a navigation resource to the current navigation graph.
+     *
+     * @param resid resource id of the navigation graph to inflate
+     *
+     * @see #getNavInflater()
+     * @see #setGraph(NavGraph)
+     */
+    public void addGraph(@XmlRes int resid) {
+        NavInflater inflater = getNavInflater();
+        NavGraph newGraph = inflater.inflate(resid);
+        if (mGraph != null) {
+            mGraph.addAll(newGraph);
+            mGraphId = 0;
+        } else {
+            mGraph = newGraph;
+            mGraphId = resid;
+        }
+    }
+
+    /**
+     * Adds the contents of a navigation resource to the current navigation graph.
+     *
+     * @param graph graph to merge into this controller's graph
+     */
+    public void addGraph(NavGraph graph) {
+        if (mGraph == null) {
+            mGraph = new NavGraph();
+        }
+        mGraph.addAll(graph);
+        mGraphId = 0;
+    }
+
+    /**
+     * Sets the starting navigation destination for this controller.
+     *
+     * @param resid destination id to set
+     */
+    public void setStartDestination(@IdRes int resid) {
+        mStartDestId = resid;
+        if (mCurrentNode == null) {
+            navigateTo(resid);
+        }
+    }
+
+    /**
+     * Navigate directly to a destination.
+     *
+     * <p>Requests navigation to the given destination id from the current navigation graph.
+     * Apps should generally prefer {@link #navigate(int) navigating by action} when possible.</p>
+     *
+     * @param resid destination id to navigate to
+     */
+    public final void navigateTo(@IdRes int resid) {
+        navigateTo(resid, null);
+    }
+
+    /**
+     * Navigate directly to a destination.
+     *
+     * <p>Requests navigation to the given destination id from the current navigation graph.
+     * Apps should generally prefer {@link #navigate(int) navigating by action} when possible.</p>
+     *
+     * @param resid destination id to navigate to
+     * @param args arguments to pass to the destination
+     */
+    public final void navigateTo(@IdRes int resid, Bundle args) {
+        navigateTo(resid, args, null);
+    }
+
+    /**
+     * Navigate directly to a destination.
+     *
+     * <p>Requests navigation to the given destination id from the current navigation graph.
+     * Apps should generally prefer {@link #navigate(int) navigating by action} when possible.</p>
+     *
+     * @param resid destination id to navigate to
+     * @param args arguments to pass to the destination
+     * @param navOptions special options for this navigation operation
+     */
+    public void navigateTo(@IdRes int resid, Bundle args, NavOptions navOptions) {
+        NavDestination node = mGraph.findNode(resid);
+        if (node == null) {
+            final String dest = mContext.getResources().getResourceName(resid);
+            throw new IllegalArgumentException("navigation destination " + dest
+                    + " is unknown to this NavController");
+        }
+        node.navigate(args, navOptions);
+    }
+
+    /**
+     * Navigate via an action defined on the current destination.
+     *
+     * <p>Requests navigation to the given {@link NavDestination#getActionDestination(int) action},
+     * appropriate for the current location, e.g. "next" or "home."</p>
+     *
+     * @param action navigation action to invoke
+     */
+    public void navigate(@IdRes int action) {
+        navigate(action, null);
+    }
+
+    /**
+     * Navigate via an action defined on the current destination.
+     *
+     * <p>Requests navigation to the given {@link NavDestination#getActionDestination(int) action},
+     * appropriate for the current location, e.g. "next" or "home."</p>
+     *
+     * @param action navigation action to invoke
+     * @param args arguments to pass to the destination
+     */
+    public void navigate(@IdRes int action, Bundle args) {
+        navigate(action, args, null);
+    }
+
+    /**
+     * Navigate via an action defined on the current destination.
+     *
+     * <p>Requests navigation to the given {@link NavDestination#getActionDestination(int) action},
+     * appropriate for the current location, e.g. "next" or "home."</p>
+     *
+     * @param action navigation action to invoke
+     * @param args arguments to pass to the destination
+     * @param navOptions special options for this navigation operation
+     */
+    public void navigate(@IdRes int action, Bundle args, NavOptions navOptions) {
+        if (mCurrentNode == null) {
+            throw new IllegalStateException("no current navigation node");
+        }
+        final int dest = mCurrentNode.getActionDestination(action);
+        if (dest == 0) {
+            final Resources res = mContext.getResources();
+            throw new IllegalStateException("no destination defined from "
+                    + res.getResourceName(mCurrentNode.getId())
+                    + " for action " + res.getResourceName(action));
+        }
+        navigateTo(dest, args, navOptions);
+    }
+
+    /**
+     * 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 navigation host, e.g. {@link NavHostFragment}.</p>
+     *
+     * @return saved state for this controller
+     */
+    public Bundle saveState() {
+        Bundle b = null;
+        if (mGraphId != 0) {
+            b = new Bundle();
+            b.putInt(KEY_GRAPH_ID, mGraphId);
+        }
+        if (mStartDestId != 0) {
+            if (b == null) {
+                b = new Bundle();
+            }
+            b.putInt(KEY_START_DEST_ID, mStartDestId);
+        }
+        if (mCurrentNode != null) {
+            if (b == null) {
+                b = new Bundle();
+            }
+            b.putInt(KEY_CUR_DEST_ID, mCurrentNode.getId());
+        }
+        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 navigation host,
+     * e.g. {@link NavHostFragment}.</p>
+     *
+     * @param navState state bundle to restore
+     */
+    public void restoreState(Bundle navState) {
+        if (navState == null) {
+            return;
+        }
+
+        mGraphId = navState.getInt(KEY_GRAPH_ID);
+        if (mGraphId != 0) {
+            setGraph(mGraphId);
+        }
+        mStartDestId = navState.getInt(KEY_START_DEST_ID);
+
+        // Restore the current location first, or setStartDestination will perform navigation
+        // if mCurrentNode is null.
+        final int loc = navState.getInt(KEY_CUR_DEST_ID);
+        if (loc != 0) {
+            NavDestination node = mGraph.findNode(loc);
+            if (node == null) {
+                throw new IllegalStateException("unknown current destination during restore: "
+                        + mContext.getResources().getResourceName(loc));
+            }
+            mCurrentNode = node;
+        }
+        if (mStartDestId != 0) {
+            setStartDestination(mStartDestId);
+        }
+    }
+
+    void onNavigatorBackStackEmpty(Navigator emptyNavigator) {
+        if (mNavigatorBackStack.isEmpty()) {
+            throw new IllegalStateException("Navigator " + emptyNavigator
+                    + " reported empty, but this NavController has no back stack!");
+        }
+        // If a navigator's back stack is empty, it can't have any presence in our
+        // navigator stack either. Remove all instances of it.
+        for (int i = mNavigatorBackStack.size() - 1; i >= 0; i--) {
+            final Navigator n = mNavigatorBackStack.get(i);
+            if (n == emptyNavigator) {
+                mNavigatorBackStack.remove(i);
+            }
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavDestination.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavDestination.java
new file mode 100644
index 0000000..19d1f5c
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavDestination.java
@@ -0,0 +1,242 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.util.SparseIntArray;
+
+import com.android.support.navigation.R;
+
+/**
+ * NavDestination represents one node within an overall navigation graph.
+ *
+ * <p>Each destination has a {@link Navigator}. The navigator determines valid
+ * {@link Navigator.Params parameters} that can be {@link #setNavigatorParams(Navigator.Params) set}
+ * for each destination, and how those parameters will be {@link NavInflater inflated} from
+ * a resource.</p>
+ *
+ * <p>Destinations declare a set of {@link #putActionDestination(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 {
+    private int mId;
+    private Navigator mNavigator;
+    private Navigator.Params mNavParams;
+    private Bundle mDefaultArgs;
+    private SparseIntArray mActions;
+    private int mFlowId;
+
+    /**
+     * Called when inflating a destination from a resource.
+     *
+     * @param context local context performing inflation
+     * @param attrs attrs to parse during inflation
+     * @param inflater inflater currently performing the inflation
+     */
+    public void onInflate(Context context, AttributeSet attrs, NavInflater inflater) {
+        final TypedArray a = context.getResources().obtainAttributes(attrs,
+                R.styleable.NavDestination);
+        setId(a.getResourceId(R.styleable.NavDestination_android_id, 0));
+        setNavigator(inflater.getNavigator(a.getString(R.styleable.NavDestination_navigator)));
+        a.recycle();
+
+        setNavigatorParams(mNavigator.inflateParams(context, attrs));
+    }
+
+    /**
+     * Returns the destination's unique ID. This should be an ID resource generated by
+     * the Android resource system.
+     *
+     * @return this destination's ID
+     */
+    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(int id) {
+        mId = id;
+    }
+
+    /**
+     * Sets the destination's {@link Navigator}.
+     *
+     * <p>The {@link #getNavigatorParams() current params} object will be checked for validity
+     * by the new navigator. If the params are no longer valid, the new navigator will be asked
+     * to convert the old params.</p>
+     *
+     * @param navigator navigator to set
+     */
+    public void setNavigator(Navigator navigator) {
+        if (navigator == mNavigator) {
+            return;
+        }
+        mNavigator = navigator;
+        // Apply the navigator params validity checks for the new navigator
+        setNavigatorParams(mNavParams);
+    }
+
+    /**
+     * Returns the destination's {@link Navigator}.
+     *
+     * @return this destination's navigator
+     */
+    public Navigator getNavigator() {
+        return mNavigator;
+    }
+
+    /**
+     * Sets the destination's {@link Navigator.Params}.
+     *
+     * <p>The params object will be checked for validity by the
+     * {@link #getNavigator() current navigator}. If the params are not valid, the navigator
+     * will be asked to convert them.</p>
+     *
+     * @param params params to set
+     */
+    public void setNavigatorParams(Navigator.Params params) {
+        if (mNavigator != null && !mNavigator.checkParams(params)) {
+            Navigator.Params newParams = mNavigator.generateDefaultParams();
+            newParams.copyFrom(params);
+            params = newParams;
+        }
+        mNavParams = params;
+    }
+
+    /**
+     * Returns the destination's {@link Navigator.Params}.
+     *
+     * @return this destination's params
+     */
+    public Navigator.Params getNavigatorParams() {
+        return mNavParams;
+    }
+
+    /**
+     * 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(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(Bundle args) {
+        getDefaultArguments().putAll(args);
+    }
+
+    /**
+     * Returns the destination ID for a given action.
+     *
+     * @param id action ID to fetch
+     * @return destination ID mapped to the given action id, or 0 if none
+     */
+    public @IdRes int getActionDestination(@IdRes int id) {
+        if (mActions == null) {
+            return 0;
+        }
+        return mActions.get(id);
+    }
+
+    /**
+     * Sets a destination ID for an action ID.
+     *
+     * @param actionId action ID to bind
+     * @param destId destination ID for the given action
+     */
+    public void putActionDestination(@IdRes int actionId, @IdRes int destId) {
+        if (actionId == 0) {
+            throw new IllegalArgumentException("cannot setActionDestination for actionId 0");
+        }
+        if (mActions == null) {
+            mActions = new SparseIntArray();
+        }
+        mActions.put(actionId, destId);
+    }
+
+    /**
+     * Unsets the destination ID for an action ID.
+     *
+     * @param actionId action ID to remove
+     */
+    public void removeActionDestination(@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(Bundle args, NavOptions navOptions) {
+        Bundle finalArgs = null;
+        Bundle defaultArgs = getDefaultArguments();
+        if (defaultArgs != null) {
+            finalArgs = new Bundle();
+            finalArgs.putAll(defaultArgs);
+        }
+        if (args != null) {
+            if (finalArgs == null) {
+                finalArgs = new Bundle();
+            }
+            finalArgs.putAll(args);
+        }
+        mNavigator.navigate(this, finalArgs, navOptions);
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavGraph.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavGraph.java
new file mode 100644
index 0000000..bbb4a3f
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavGraph.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 android.support.navigation.app.nav;
+
+import android.support.annotation.IdRes;
+import android.util.SparseArray;
+
+/**
+ * NavGraph is a collection of {@link NavDestination} nodes fetchable by ID.
+ */
+public class NavGraph {
+    private final SparseArray<NavDestination> mNodes = new SparseArray<>();
+
+    /**
+     * Adds a destination to the collection.
+     *
+     * @param node destination to add
+     */
+    public void addDestination(NavDestination node) {
+        mNodes.put(node.getId(), node);
+    }
+
+    /**
+     * Finds a destination in the collection by ID.
+     *
+     * @param resid ID to locate
+     * @return the node with ID resid
+     */
+    public NavDestination findNode(@IdRes int resid) {
+        return mNodes.get(resid);
+    }
+
+    /**
+     * Add all destinations from another collection to this one.
+     *
+     * @param other collection of destinations to add
+     */
+    public void addAll(NavGraph other) {
+        for (int i = 0, size = other.mNodes.size(); i < size; i++) {
+            mNodes.put(other.mNodes.keyAt(i), other.mNodes.valueAt(i));
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavHostFragment.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavHostFragment.java
new file mode 100644
index 0000000..25704fb
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavHostFragment.java
@@ -0,0 +1,279 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.XmlRes;
+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 com.android.support.navigation.R;
+
+/**
+ * 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="android.support.navigation.app.nav.NavHostFragment"
+ *                 app:navGraph="@xml/nav_sample"
+ *                 app:startDestination="@+id/launcher_home"
+ *                 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#findController(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 {
+    private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId";
+    private static final String KEY_START_DEST_ID = "android-support-nav:fragment:startDestId";
+    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";
+
+    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 graphRes resource id of the navigation graph to inflate
+     * @return a new NavHostFragment instance
+     */
+    public static NavHostFragment create(@XmlRes int graphRes) {
+        return create(graphRes, 0);
+    }
+
+    /**
+     * Create a new NavHostFragment instance with an inflated {@link NavGraph} resource
+     * and a starting destination id.
+     *
+     * @param graphRes resource id of the navigation graph to inflate
+     * @param startDestinationRes id of the initial destination
+     * @return a new NavHostFragment instance
+     */
+    public static NavHostFragment create(@XmlRes int graphRes, @IdRes int startDestinationRes) {
+        Bundle b = null;
+        if (graphRes != 0) {
+            b = new Bundle();
+            b.putInt(KEY_GRAPH_ID, graphRes);
+        }
+        if (startDestinationRes != 0) {
+            if (b == null) {
+                b = new Bundle();
+            }
+            b.putInt(KEY_START_DEST_ID, startDestinationRes);
+        }
+
+        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
+     */
+    @Nullable
+    public NavController getNavController() {
+        return mNavController;
+    }
+
+    /**
+     * Set a {@link NavGraph} for this navigation host's {@link NavController} by resource id.
+     * The existing graph will be replaced.
+     *
+     * @param graphRes resource id of the navigation graph to inflate
+     */
+    public void setGraph(@XmlRes int graphRes) {
+        if (mNavController == null) {
+            Bundle args = getArguments();
+            if (args == null) {
+                args = new Bundle();
+            }
+            args.putInt(KEY_GRAPH_ID, graphRes);
+            setArguments(args);
+        } else {
+            mNavController.setGraph(graphRes);
+        }
+    }
+
+    /**
+     * Set a starting destination id for this navigation host.
+     * If this host has not navigated to a destination yet, the host will navigate to the start
+     * destination. The initial navigation to the starting destination is not considered part of the
+     * {@link #getNavController() host controller's} back stack.
+     *
+     * @param destRes id of the initial destination
+     */
+    public void setStartDestination(@IdRes int destRes) {
+        if (mNavController == null) {
+            Bundle args = getArguments();
+            if (args == null) {
+                args = new Bundle();
+            }
+            args.putInt(KEY_START_DEST_ID, destRes);
+            setArguments(args);
+        } else {
+            mNavController.setStartDestination(destRes);
+        }
+    }
+
+    @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();
+        final Activity activity = getActivity();
+
+        if (activity != null) {
+            mNavController = new NavController(activity);
+        } else {
+            mNavController = new NavController(context);
+        }
+        mNavController.addNavigator(FragmentNavigator.NAME,
+                new FragmentNavigator(context, getChildFragmentManager(), getId()));
+
+        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.getInt(KEY_GRAPH_ID);
+            final int destid = args.getInt(KEY_START_DEST_ID);
+            if (graphid != 0) {
+                mNavController.setGraph(graphid);
+            } else {
+                mNavController.addMetadataGraph();
+            }
+            if (destid != 0) {
+                mNavController.setStartDestination(destid);
+            }
+        }
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        return new FrameLayout(inflater.getContext());
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        if (!(view instanceof ViewGroup)) {
+            throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
+        }
+        Navigation.setViewNavController(view, 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 int destid = a.getResourceId(R.styleable.NavHostFragment_startDestination, 0);
+        final boolean defaultHost = a.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false);
+
+        if (graphid != 0) {
+            setGraph(graphid);
+        }
+        if (destid != 0) {
+            setStartDestination(destid);
+        }
+        if (defaultHost) {
+            mDefaultNavHost = true;
+            if (isAdded()) {
+                getFragmentManager().beginTransaction().setPrimaryNavigationFragment(this).commit();
+            }
+        }
+        a.recycle();
+    }
+
+    @Override
+    public void onSaveInstanceState(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/runtime/src/main/java/android/support/navigation/app/nav/NavInflater.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavInflater.java
new file mode 100644
index 0000000..2bc674a
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavInflater.java
@@ -0,0 +1,191 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.support.annotation.XmlRes;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import com.android.support.navigation.R;
+
+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 {
+    private static final String TAG_ROOT = "navigation";
+    private static final String TAG_DESTINATION = "destination";
+    private static final String TAG_ARGUMENT = "default-argument";
+    private static final String TAG_ACTION = "action";
+
+    private static final ThreadLocal<TypedValue> sTmpValue = new ThreadLocal<>();
+
+    private Context mContext;
+    private NavigatorProvider mNavigatorProvider;
+
+    public NavInflater(Context c, NavigatorProvider navigatorProvider) {
+        mContext = c;
+        mNavigatorProvider = navigatorProvider;
+    }
+
+    /**
+     * Retrieve a Navigator with the given name from the {@link NavigatorProvider} used to
+     * construct this class.
+     *
+     * @param name
+     * @return
+     */
+    public Navigator getNavigator(String name) {
+        return mNavigatorProvider.getNavigator(name);
+    }
+
+    /**
+     * Inflate a NavGraph from the given XML resource id.
+     *
+     * @param navres
+     * @return
+     */
+    public NavGraph inflate(@XmlRes int navres) {
+        Resources res = mContext.getResources();
+        XmlResourceParser parser = res.getXml(navres);
+        NavGraph graph = new NavGraph();
+
+        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");
+            }
+
+            if (!TAG_ROOT.equals(parser.getName())) {
+                throw new XmlPullParserException("Expected root element <" + TAG_ROOT + ">");
+            }
+
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+
+                if (TAG_DESTINATION.equals(parser.getName())) {
+                    final NavDestination dest;
+                    try {
+                        dest = inflateDestination(res, parser, attrs);
+                    } catch (Exception e) {
+                        throw new RuntimeException("Exception inflating "
+                                + res.getResourceName(navres) + " line "
+                                + parser.getLineNumber(), e);
+                    }
+                    graph.addDestination(dest);
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            parser.close();
+        }
+        return graph;
+    }
+
+    private NavDestination inflateDestination(Resources res, XmlResourceParser parser,
+                                              AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+        final NavDestination dest = new NavDestination();
+
+        dest.onInflate(mContext, attrs, this);
+
+        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_ACTION.equals(name)) {
+                inflateAction(res, dest, 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_argname);
+
+        TypedValue value = sTmpValue.get();
+        if (value == null) {
+            value = new TypedValue();
+            sTmpValue.set(value);
+        }
+        a.getValue(R.styleable.NavArgument_argvalue, 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 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);
+        dest.putActionDestination(id, destId);
+        a.recycle();
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavOptions.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavOptions.java
new file mode 100644
index 0000000..aa21fd2
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavOptions.java
@@ -0,0 +1,146 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.os.Bundle;
+
+/**
+ * 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_LAUNCH_MODE = "launchMode";
+    private static final String KEY_FLOW_NAME = "flowName";
+
+    private int mLaunchMode;
+    private String mFlowName;
+
+    NavOptions(int launchMode, String flowName) {
+        mLaunchMode = launchMode;
+        mFlowName = flowName;
+    }
+
+    boolean shouldLaunchSingleTop() {
+        return (mLaunchMode & LAUNCH_SINGLE_TOP) != 0;
+    }
+
+    boolean shouldLaunchDocument() {
+        return (mLaunchMode & LAUNCH_DOCUMENT) != 0;
+    }
+
+    boolean shouldClearTask() {
+        return (mLaunchMode & LAUNCH_CLEAR_TASK) != 0;
+    }
+
+    public String getFlowName() {
+        return mFlowName;
+    }
+
+    Bundle toBundle() {
+        return new Bundle();
+    }
+
+    static NavOptions fromBundle(Bundle b) {
+        return new NavOptions(b.getInt(KEY_LAUNCH_MODE, 0),
+                b.getString(KEY_FLOW_NAME));
+    }
+
+    /**
+     * Builder for constructing new instances of NavOptions.
+     */
+    public static class Builder {
+        int mLaunchMode;
+        String mFlowName;
+        String mEndFlow;
+
+        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
+         */
+        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
+         */
+        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
+         */
+        public Builder setClearTask(boolean clearTask) {
+            if (clearTask) {
+                mLaunchMode |= LAUNCH_CLEAR_TASK;
+            } else {
+                mLaunchMode &= ~LAUNCH_CLEAR_TASK;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the flow name for this target.
+         * @param flowName
+         * @return
+         */
+        public Builder setFlowName(String flowName) {
+            mFlowName = flowName;
+            return this;
+        }
+
+        /**
+         * @return a constructed NavOptions
+         */
+        public NavOptions build() {
+            return new NavOptions(mLaunchMode, mFlowName);
+        }
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/Navigation.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/Navigation.java
new file mode 100644
index 0000000..adfb846
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/Navigation.java
@@ -0,0 +1,180 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.v4.app.Fragment;
+import android.view.View;
+import android.view.ViewParent;
+
+import com.android.support.navigation.R;
+
+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 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 #findController(View)}.</p>
+     *
+     * @param fragment the locally scoped Fragment for navigation
+     * @return the locally scoped {@link NavController} for navigating from this {@link Fragment}
+     */
+    public static NavController findController(Fragment fragment) {
+        if (fragment == null) {
+            return null;
+        }
+
+        Fragment findFragment = fragment;
+        while (findFragment != null) {
+            if (findFragment instanceof NavHostFragment) {
+                return ((NavHostFragment) findFragment).getNavController();
+            }
+            findFragment = findFragment.getParentFragment();
+        }
+
+        // Try looking for one associated with the view instead, if applicable
+        return findController(fragment.getView());
+    }
+
+    /**
+     * 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 NavHostFragment}
+     * 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
+     */
+    public static NavController findController(View view) {
+        if (view == null) {
+            return null;
+        }
+        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;
+    }
+
+    /**
+     * Create an {@link android.view.View.OnClickListener} for navigating via an action.
+     *
+     * @param actionId navigation action to take when the view is clicked
+     * @return a new click listener for setting on an arbitrary view
+     */
+    public static View.OnClickListener createNavigateOnClickListener(@IdRes final int actionId) {
+        return createNavigateOnClickListener(actionId, null);
+    }
+
+    /**
+     * Create an {@link android.view.View.OnClickListener} for navigating via an action.
+     *
+     * @param actionId navigation action to take 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
+     */
+    public static View.OnClickListener createNavigateOnClickListener(@IdRes final int actionId,
+                                                                     final Bundle args) {
+        return new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                final NavController controller = findController(view);
+                if (controller != null) {
+                    controller.navigate(actionId, args);
+                } else {
+                    throw new IllegalStateException(
+                            "OnClickListener#onClick could not find NavController for view "
+                            + view);
+                }
+            }
+        };
+    }
+
+    /**
+     * Create an {@link android.view.View.OnClickListener} for navigating
+     * to an explicit destination.
+     *
+     * @param destId destination to navigate to when the view is clicked
+     * @return a new click listener for setting on an arbitrary view
+     */
+    public static View.OnClickListener createNavigateToOnClickListener(@IdRes final int destId) {
+        return createNavigateToOnClickListener(destId, null);
+    }
+
+    /**
+     * Create an {@link android.view.View.OnClickListener} for navigating
+     * to an explicit destination.
+     *
+     * @param destId destination 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
+     */
+    public static View.OnClickListener createNavigateToOnClickListener(@IdRes final int destId,
+                                                                       final Bundle args) {
+        return new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                final NavController controller = findController(view);
+                if (controller != null) {
+                    controller.navigateTo(destId, args);
+                } else {
+                    throw new IllegalStateException(
+                            "OnClickListener#onClick could not find NavController for view "
+                            + view);
+                }
+            }
+        };
+    }
+
+    static void setViewNavController(View view, 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/android/support/navigation/app/nav/Navigator.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/Navigator.java
new file mode 100644
index 0000000..56cfb5a
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/Navigator.java
@@ -0,0 +1,173 @@
+/*
+ * 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 android.support.navigation.app.nav;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.util.AttributeSet;
+
+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} and
+ * {@link FragmentNavigator} knows how to navigate by replacing fragments within a container.</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>
+ *
+ * @param <P> the subclass of {@link Params} unique to the Navigator subclass
+ */
+public abstract class Navigator<P extends Navigator.Params> {
+    private final CopyOnWriteArrayList<OnNavigatorNavigatedListener> mOnNavigatedListeners =
+            new CopyOnWriteArrayList<>();
+
+    /**
+     * Create and return a {@link Params navigator params} object with default values
+     * for this navigator.
+     * @return a new {@link Params} with default values
+     */
+    public abstract P generateDefaultParams();
+
+    /**
+     * Inflate a {@link Params} object from a resource.
+     *
+     * <p>Parses the navigator params from a {@link NavDestination destination} node
+     * of a navigation graph resource. Navigator param attributes should have the prefix
+     * {@code nav_}.</p>
+     *
+     * @param context Context used to resolve attrs
+     * @param attrs attrs to parse
+     * @return a new {@link Params} instance parsed from attrs
+     */
+    public abstract P inflateParams(Context context, AttributeSet attrs);
+
+    /**
+     * Check if a {@link Params} object is valid for this navigator.
+     *
+     * <p>Returns {@code true} if the given params object is of the right type and
+     * properties are in range. If this method returns false, callers may use
+     * {@link #generateDefaultParams()} to obtain valid params instead.</p>
+     *
+     * @param params params to check for validity
+     * @return {@code true} if the given params are valid
+     */
+    public boolean checkParams(Navigator.Params params) {
+        return true;
+    }
+
+    /**
+     * 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>
+     *
+     * @param destination destination node to navigate to
+     * @param args arguments to use for navigation
+     * @param navOptions additional options for navigation
+     * @return true if navigation created a back stack entry that should be tracked
+     */
+    public abstract boolean navigate(NavDestination destination, Bundle args,
+                                     NavOptions navOptions);
+
+    /**
+     * Attempt to pop this navigator's back stack, performing the appropriate navigation.
+     *
+     * <p>Implementations should {@link #dispatchOnNavigatorNavigated(int, boolean)} to notify
+     * listeners of the resulting navigation destination and return {@link 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(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(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 isBackStackEmpty true if this navigator's back stack is empty after this navigation
+     */
+    public final void dispatchOnNavigatorNavigated(@IdRes int destId, boolean isBackStackEmpty) {
+        for (OnNavigatorNavigatedListener listener : mOnNavigatedListeners) {
+            listener.onNavigatorNavigated(this, destId, isBackStackEmpty);
+        }
+    }
+
+    /**
+     * Base class for navigator parameters.
+     *
+     * <p>Subclasses of {@link Navigator} should also subclass this class to hold any special
+     * data about a {@link NavDestination} 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.</p>
+     */
+    public static class Params {
+        /**
+         * Copy all valid fields from the given Params into this instance
+         * @param other the Params to copy all fields from
+         */
+        public void copyFrom(Params other) {
+        }
+    }
+
+    /**
+     * 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 isBackStackEmpty
+         */
+        void onNavigatorNavigated(Navigator navigator, @IdRes int destId, boolean isBackStackEmpty);
+    }
+}
diff --git a/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavigatorProvider.java b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavigatorProvider.java
new file mode 100644
index 0000000..1ae1e2a
--- /dev/null
+++ b/navigation/runtime/src/main/java/android/support/navigation/app/nav/NavigatorProvider.java
@@ -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.
+ */
+
+package android.support.navigation.app.nav;
+
+/**
+ * A NavigationProvider stores a set of {@link Navigator}s that are valid ways to navigate
+ * to a destination.
+ */
+public interface NavigatorProvider {
+    /**
+     * Retrieves a registered {@link Navigator} by name.
+     *
+     * @param name name of the navigator to return
+     * @return the registered navigator with the given name
+     *
+     * @see #addNavigator(String, Navigator)
+     */
+    Navigator getNavigator(String name);
+
+    /**
+     * 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
+     */
+    void addNavigator(String name, Navigator navigator);
+}
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..b047e13d
--- /dev/null
+++ b/navigation/runtime/src/main/res/values/attrs.xml
@@ -0,0 +1,40 @@
+<?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="NavDestination">
+        <attr name="android:id"/>
+        <attr name="navigator" format="string"/>
+    </declare-styleable>
+
+    <declare-styleable name="FragmentNavigatorParams">
+        <attr name="nav_fragment" format="string" />
+        <attr name="nav_flow" format="string" />
+    </declare-styleable>
+
+    <declare-styleable name="NavArgument">
+        <attr name="argname" format="string" />
+        <attr name="argvalue" format="string|dimension|integer|float|reference" />
+    </declare-styleable>
+
+    <declare-styleable name="NavAction">
+        <attr name="android:id" />
+        <attr name="destination" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="NavHostFragment">
+        <attr name="navGraph" format="reference" />
+        <attr name="startDestination" format="reference" />
+        <attr name="defaultNavHost" format="boolean" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/navigation/runtime/src/main/res/values/ids.xml b/navigation/runtime/src/main/res/values/ids.xml
new file mode 100644
index 0000000..d1571a9
--- /dev/null
+++ b/navigation/runtime/src/main/res/values/ids.xml
@@ -0,0 +1,17 @@
+<?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>
+    <item type="id" name="nav_host_fragment_host_container" />
+    <item type="id" name="nav_controller_view_tag" />
+</resources>
\ No newline at end of file