activityInjector;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (!overrideApplicationComponent(this)) {
+ DaggerCatalogApplicationComponent.builder().application(this).build().inject(this);
+ }
+ }
+
+ /**
+ * Replaces the application component by the one specified in AndroidManifest.xml metadata with
+ * key {@link #COMPONENT_OVERRIDE_KEY}. Returns {@code true} if the component was properly
+ * initialized and replaced, otherwise returns {@code false}.
+ *
+ * This assumes that the replacement component can be initialized exactly the same way as the
+ * default component.
+ *
+ *
Suppressing unchecked warnings because there is no way we have a statically typed class
+ * argument for instances of Class in this method.
+ */
+ @SuppressWarnings("unchecked")
+ private boolean overrideApplicationComponent(CatalogApplication catalogApplication) {
+ try {
+ ApplicationInfo applicationInfo =
+ getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
+ String className = applicationInfo.metaData.getString(COMPONENT_OVERRIDE_KEY);
+ if (className == null) {
+ // Fail early
+ Log.i(TAG, "Component override metadata not found, using default component.");
+ return false;
+ }
+ Log.i(TAG, className);
+ Object builderObject = Class.forName(className).getMethod("builder").invoke(null);
+ Class> builderClass = builderObject.getClass();
+ builderClass
+ .getMethod("application", Application.class)
+ .invoke(builderObject, catalogApplication);
+ Object component = builderClass.getMethod("build").invoke(builderObject);
+ component
+ .getClass()
+ .getMethod("inject", CatalogApplication.class)
+ .invoke(component, catalogApplication);
+ return true;
+ } catch (PackageManager.NameNotFoundException
+ | ClassNotFoundException
+ | NoSuchMethodException
+ | InvocationTargetException
+ | IllegalAccessException e) {
+ Log.e(TAG, "Component override failed with exception:", e);
+ }
+ return false;
+ }
+
+ @Override
+ public AndroidInjector activityInjector() {
+ return activityInjector;
+ }
+}
diff --git a/catalog/java/io/material/catalog/application/CatalogApplicationComponent.java b/catalog/java/io/material/catalog/application/CatalogApplicationComponent.java
new file mode 100644
index 000000000..fd253b750
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/CatalogApplicationComponent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.application;
+
+import android.app.Application;
+import dagger.BindsInstance;
+import dagger.android.support.AndroidSupportInjectionModule;
+import io.material.catalog.application.scope.ApplicationScope;
+import io.material.catalog.main.MainActivity;
+
+/** The Application's root component. */
+@ApplicationScope
+@dagger.Component(
+ modules = {
+ AndroidSupportInjectionModule.class,
+ MainActivity.Module.class,
+ }
+)
+public interface CatalogApplicationComponent {
+ void inject(CatalogApplication app);
+
+ /** The root component's builder. */
+ @dagger.Component.Builder
+ interface Builder {
+ @BindsInstance
+ CatalogApplicationComponent.Builder application(Application application);
+
+ CatalogApplicationComponent build();
+ }
+}
diff --git a/catalog/java/io/material/catalog/application/attrs/res/values/attrs.xml b/catalog/java/io/material/catalog/application/attrs/res/values/attrs.xml
new file mode 100644
index 000000000..768109a67
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/attrs/res/values/attrs.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/application/legacymultidex/LegacyMultidexCatalogApplication.java b/catalog/java/io/material/catalog/application/legacymultidex/LegacyMultidexCatalogApplication.java
new file mode 100644
index 000000000..a84d644c3
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/legacymultidex/LegacyMultidexCatalogApplication.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.application.legacymultidex;
+
+import android.content.Context;
+import android.support.multidex.MultiDex;
+import io.material.catalog.application.CatalogApplication;
+
+/**
+ * A version of {@link CatalogApplication} for development builds on older phones that uses the
+ * multidex support library for allowing multiple dex files.
+ */
+public class LegacyMultidexCatalogApplication extends CatalogApplication {
+ @Override
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ MultiDex.install(this);
+ }
+}
diff --git a/catalog/java/io/material/catalog/application/scope/ActivityScope.java b/catalog/java/io/material/catalog/application/scope/ActivityScope.java
new file mode 100644
index 000000000..3fae3234f
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/scope/ActivityScope.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.application.scope;
+
+import java.lang.annotation.Documented;
+import javax.inject.Scope;
+
+/** Dagger scope for dependencies that should only have a single instance created per activity. */
+@Scope
+@Documented
+public @interface ActivityScope {}
diff --git a/catalog/java/io/material/catalog/application/scope/ApplicationScope.java b/catalog/java/io/material/catalog/application/scope/ApplicationScope.java
new file mode 100644
index 000000000..25bdaf1c6
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/scope/ApplicationScope.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.application.scope;
+
+import java.lang.annotation.Documented;
+import javax.inject.Scope;
+
+/**
+ * Dagger scope for dependencies that should only have a single instance created for the entire
+ * application.
+ */
+@Scope
+@Documented
+public @interface ApplicationScope {}
diff --git a/catalog/java/io/material/catalog/application/scope/FragmentScope.java b/catalog/java/io/material/catalog/application/scope/FragmentScope.java
new file mode 100644
index 000000000..a98a60c5a
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/scope/FragmentScope.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.application.scope;
+
+import java.lang.annotation.Documented;
+import javax.inject.Scope;
+
+/** Dagger scope for dependencies that should only have a single instance created per fragment. */
+@Scope
+@Documented
+public @interface FragmentScope {}
diff --git a/catalog/java/io/material/catalog/application/theme/res/values/colors.xml b/catalog/java/io/material/catalog/application/theme/res/values/colors.xml
new file mode 100644
index 000000000..1bd51d7d9
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/theme/res/values/colors.xml
@@ -0,0 +1,28 @@
+
+
+
+ #FFFFFF
+ #FFFFFF
+
+ #212121
+ #535353
+ #000000
+
+ #30E57E
+ #33F286
+ #25B363
+
diff --git a/catalog/java/io/material/catalog/application/theme/res/values/strings.xml b/catalog/java/io/material/catalog/application/theme/res/values/strings.xml
new file mode 100644
index 000000000..0a6a85a10
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/theme/res/values/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Close demo
+
diff --git a/catalog/java/io/material/catalog/application/theme/res/values/styles.xml b/catalog/java/io/material/catalog/application/theme/res/values/styles.xml
new file mode 100644
index 000000000..591443805
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/theme/res/values/styles.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/application/theme/res/values/themes.xml b/catalog/java/io/material/catalog/application/theme/res/values/themes.xml
new file mode 100644
index 000000000..cc4d97620
--- /dev/null
+++ b/catalog/java/io/material/catalog/application/theme/res/values/themes.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable-xxxhdpi/ic_placeholder_circle_24.png b/catalog/java/io/material/catalog/assets/res/drawable-xxxhdpi/ic_placeholder_circle_24.png
new file mode 100644
index 000000000..097463494
Binary files /dev/null and b/catalog/java/io/material/catalog/assets/res/drawable-xxxhdpi/ic_placeholder_circle_24.png differ
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_add_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_add_24px.xml
new file mode 100644
index 000000000..3a21aec8c
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_add_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_animation_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_animation_24px.xml
new file mode 100644
index 000000000..c426ba1c2
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_animation_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_bottom_nav_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_bottom_nav_24px.xml
new file mode 100644
index 000000000..6ff099132
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_bottom_nav_24px.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_bottom_sheets_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_bottom_sheets_24px.xml
new file mode 100644
index 000000000..c9ebe1564
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_bottom_sheets_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_bottomappbar_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_bottomappbar_24px.xml
new file mode 100644
index 000000000..efde207da
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_bottomappbar_24px.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_buttons_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_buttons_24px.xml
new file mode 100644
index 000000000..c938a4e68
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_buttons_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_card_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_card_24px.xml
new file mode 100644
index 000000000..6e5d31795
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_card_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_chips_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_chips_24px.xml
new file mode 100644
index 000000000..b6a0979bb
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_chips_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_close_vd_theme_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_close_vd_theme_24px.xml
new file mode 100644
index 000000000..b184e46cd
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_close_vd_theme_24px.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_colors_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_colors_24px.xml
new file mode 100644
index 000000000..8b2ab1cdd
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_colors_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_colors_vd_theme_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_colors_vd_theme_24px.xml
new file mode 100644
index 000000000..03e066d45
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_colors_vd_theme_24px.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_components_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_components_24px.xml
new file mode 100644
index 000000000..6970d287c
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_components_24px.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_data_table_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_data_table_24px.xml
new file mode 100644
index 000000000..263f4ec2e
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_data_table_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_demo_play_circle_outline_vd_theme_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_demo_play_circle_outline_vd_theme_24px.xml
new file mode 100644
index 000000000..ce320e6f2
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_demo_play_circle_outline_vd_theme_24px.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_developer_guide_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_developer_guide_24px.xml
new file mode 100644
index 000000000..0ccc6d899
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_developer_guide_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_dialogs_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_dialogs_24px.xml
new file mode 100644
index 000000000..48ba85484
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_dialogs_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_expension_panels_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_expension_panels_24px.xml
new file mode 100644
index 000000000..a39f6e83e
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_expension_panels_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_fab_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_fab_24px.xml
new file mode 100644
index 000000000..cfc050ebf
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_fab_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_favorite_vd_theme_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_favorite_vd_theme_24px.xml
new file mode 100644
index 000000000..b892bf2a5
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_favorite_vd_theme_24px.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_feature_highlight_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_feature_highlight_24px.xml
new file mode 100644
index 000000000..1461e0be3
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_feature_highlight_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_github_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_github_24px.xml
new file mode 100644
index 000000000..393e57a6d
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_github_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_info_outline_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_info_outline_24px.xml
new file mode 100644
index 000000000..77eb4ab9a
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_info_outline_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_lists_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_lists_24px.xml
new file mode 100644
index 000000000..9399db86d
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_lists_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_logo_components_48px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_logo_components_48px.xml
new file mode 100644
index 000000000..54b956725
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_logo_components_48px.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_menu_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_menu_24px.xml
new file mode 100644
index 000000000..4b5cb2f94
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_menu_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_progress_activity_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_progress_activity_24px.xml
new file mode 100644
index 000000000..63bdc8ddd
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_progress_activity_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_responsive_layout_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_responsive_layout_24px.xml
new file mode 100644
index 000000000..b90d0bedf
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_responsive_layout_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_ripples_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_ripples_24px.xml
new file mode 100644
index 000000000..052775cf7
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_ripples_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_scrollable_header_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_scrollable_header_24px.xml
new file mode 100644
index 000000000..3599fa904
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_scrollable_header_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_search_vd_theme_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_search_vd_theme_24px.xml
new file mode 100644
index 000000000..97d3d2d71
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_search_vd_theme_24px.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_selection_controls_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_selection_controls_24px.xml
new file mode 100644
index 000000000..90e1837de
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_selection_controls_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_shadow_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_shadow_24px.xml
new file mode 100644
index 000000000..d66eccc62
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_shadow_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_side_navigation_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_side_navigation_24px.xml
new file mode 100644
index 000000000..e5d6123f9
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_side_navigation_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_sliders_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_sliders_24px.xml
new file mode 100644
index 000000000..1afbfc303
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_sliders_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_steppers_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_steppers_24px.xml
new file mode 100644
index 000000000..f090b9d4a
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_steppers_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_subheader_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_subheader_24px.xml
new file mode 100644
index 000000000..e0aca2709
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_subheader_24px.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_switches_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_switches_24px.xml
new file mode 100644
index 000000000..99a3b7ab4
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_switches_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_tabs_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_tabs_24px.xml
new file mode 100644
index 000000000..f8535c45b
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_tabs_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_text_field_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_text_field_24px.xml
new file mode 100644
index 000000000..e4e706f47
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_text_field_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_toast_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_toast_24px.xml
new file mode 100644
index 000000000..d194f4615
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_toast_24px.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_tooltip_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_tooltip_24px.xml
new file mode 100644
index 000000000..7a6f097f4
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_tooltip_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/assets/res/drawable/ic_topappbar_24px.xml b/catalog/java/io/material/catalog/assets/res/drawable/ic_topappbar_24px.xml
new file mode 100644
index 000000000..34e6f461c
--- /dev/null
+++ b/catalog/java/io/material/catalog/assets/res/drawable/ic_topappbar_24px.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/BottomAppBarFragment.java b/catalog/java/io/material/catalog/bottomappbar/BottomAppBarFragment.java
new file mode 100644
index 000000000..ca05fb8bc
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/BottomAppBarFragment.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomappbar;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+
+/** A landing fragment that links to Bottom App Bar demos for the Catalog app. */
+public class BottomAppBarFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_bottomappbar_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_bottomappbar_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new BottomAppBarMainDemoFragment();
+ }
+ };
+ }
+
+ /** The Dagger module for {@link BottomAppBarFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract BottomAppBarFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_bottomappbar_title, R.drawable.ic_bottomappbar_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new BottomAppBarFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/bottomappbar/BottomAppBarMainDemoFragment.java b/catalog/java/io/material/catalog/bottomappbar/BottomAppBarMainDemoFragment.java
new file mode 100644
index 000000000..edd221878
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/BottomAppBarMainDemoFragment.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomappbar;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import com.google.android.material.bottomappbar.BottomAppBar;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.navigation.NavigationView;
+import com.google.android.material.snackbar.Snackbar;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ToggleButton;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.OnBackPressedHandler;
+import io.material.catalog.themeswitcher.ThemeSwitcherHelper;
+
+/** A fragment that displays the main Bottom App Bar demos for the Catalog app. */
+public class BottomAppBarMainDemoFragment extends DemoFragment implements OnBackPressedHandler {
+
+ protected BottomAppBar bar;
+ protected CoordinatorLayout coordinatorLayout;
+
+ @Nullable private ThemeSwitcherHelper themeSwitcherHelper;
+ private BottomSheetBehavior bottomDrawerBehavior;
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ setHasOptionsMenu(true);
+
+ // The theme switcher helper is used in an adhoc way with the toolbar since the BottomAppBar is
+ // set as the action bar.
+ themeSwitcherHelper = new ThemeSwitcherHelper(getFragmentManager());
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.demo_primary, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ Snackbar.make(getView(), menuItem.getTitle(), Snackbar.LENGTH_SHORT).show();
+ return true;
+ }
+
+ @LayoutRes
+ public int getBottomAppBarContent() {
+ return R.layout.cat_bottomappbar_fragment;
+ }
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(getBottomAppBarContent(), viewGroup, false);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ toolbar.setTitle(getDefaultDemoTitle());
+ themeSwitcherHelper.onCreateOptionsMenu(toolbar.getMenu(), getActivity().getMenuInflater());
+ toolbar.setOnMenuItemClickListener(themeSwitcherHelper::onOptionsItemSelected);
+ toolbar.setNavigationOnClickListener(
+ v -> {
+ getActivity().onBackPressed();
+ });
+
+ coordinatorLayout = view.findViewById(R.id.coordinator_layout);
+ bar = view.findViewById(R.id.bar);
+ ((AppCompatActivity) getActivity()).setSupportActionBar(bar);
+
+ setUpBottomDrawer(view);
+
+ ImageButton fab = view.findViewById(R.id.fab);
+ fab.setOnClickListener(
+ v -> Snackbar.make(getView(), fab.getContentDescription(), Snackbar.LENGTH_SHORT).show());
+ NavigationView navigationView = view.findViewById(R.id.navigation_view);
+ navigationView.setNavigationItemSelectedListener(
+ item -> {
+ Snackbar.make(getView(), item.getTitle(), Snackbar.LENGTH_SHORT).show();
+ return false;
+ });
+
+ Button centerButton = view.findViewById(R.id.center);
+ Button endButton = view.findViewById(R.id.end);
+ ToggleButton attachToggle = view.findViewById(R.id.attach_toggle);
+ attachToggle.setChecked(bar.isFabAttached());
+ centerButton.setOnClickListener(
+ v -> bar.setFabAlignmentMode(BottomAppBar.FAB_ALIGNMENT_MODE_CENTER));
+ endButton.setOnClickListener(v -> bar.setFabAlignmentMode(BottomAppBar.FAB_ALIGNMENT_MODE_END));
+ attachToggle.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> bar.setFabAttached(isChecked));
+
+ return view;
+ }
+
+ @Override
+ public boolean onBackPressed() {
+ if (bottomDrawerBehavior.getState() != BottomSheetBehavior.STATE_HIDDEN) {
+ bottomDrawerBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+
+ protected void setUpBottomDrawer(View view) {
+ View bottomDrawer = coordinatorLayout.findViewById(R.id.bottom_drawer);
+ bottomDrawerBehavior = BottomSheetBehavior.from(bottomDrawer);
+ bottomDrawerBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
+
+ bar.setNavigationOnClickListener(
+ v -> bottomDrawerBehavior.setState(BottomSheetBehavior.STATE_HALF_EXPANDED));
+ bar.setNavigationIcon(R.drawable.ic_menu_24);
+ bar.replaceMenu(R.menu.demo_primary);
+ }
+}
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_3d_rotation_24.xml b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_3d_rotation_24.xml
new file mode 100644
index 000000000..90822b37d
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_3d_rotation_24.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_accelerator_24.xml b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_accelerator_24.xml
new file mode 100644
index 000000000..6702fafe2
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_accelerator_24.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_dashboard_24.xml b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_dashboard_24.xml
new file mode 100644
index 000000000..2c9f661b4
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_dashboard_24.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_menu_24.xml b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_menu_24.xml
new file mode 100644
index 000000000..3ab5062f5
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_menu_24.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_search_24.xml b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_search_24.xml
new file mode 100644
index 000000000..0ba7f6505
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/drawable/ic_search_24.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_content.xml b/catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_content.xml
new file mode 100644
index 000000000..57512656f
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_content.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_fragment.xml b/catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_fragment.xml
new file mode 100644
index 000000000..105845cf0
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_fragment.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/menu/demo_primary.xml b/catalog/java/io/material/catalog/bottomappbar/res/menu/demo_primary.xml
new file mode 100644
index 000000000..1adb130d3
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/menu/demo_primary.xml
@@ -0,0 +1,36 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/values/colors.xml b/catalog/java/io/material/catalog/bottomappbar/res/values/colors.xml
new file mode 100644
index 000000000..efdf7df6f
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/values/colors.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ #f8f9fa
+
+
diff --git a/catalog/java/io/material/catalog/bottomappbar/res/values/strings.xml b/catalog/java/io/material/catalog/bottomappbar/res/values/strings.xml
new file mode 100644
index 000000000..5772254bd
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomappbar/res/values/strings.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ Bottom App Bar
+
+ A bottom app bar provides a docked bar at the bottom of the screen for common application
+ actions. The bottom app bar includes a floating button for a primary action and a navigation bar
+ area for secondary actions.
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n
+Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor bibendum, vel congue leo egestas.\n\n
+Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque est.\n\n
+Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus convallis.\n\n
+Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim."
+
+ Center
+ Detached
+ Attached
+ End
+
+ 3D
+ Accelerator
+ Search
+ Dashboard
+ Show navigation drawer
+ FAB
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/BottomNavigationColorStylesDemoFragment.java b/catalog/java/io/material/catalog/bottomnav/BottomNavigationColorStylesDemoFragment.java
new file mode 100644
index 000000000..9e442f6a0
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/BottomNavigationColorStylesDemoFragment.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomnav;
+
+import io.material.catalog.R;
+
+/** A fragment that displays bottom navs that are colored by the provided styles. */
+public class BottomNavigationColorStylesDemoFragment extends BottomNavigationDemoFragment {
+
+ @Override
+ protected int getBottomNavsContent() {
+ return R.layout.cat_bottom_nav_color_styles;
+ }
+}
diff --git a/catalog/java/io/material/catalog/bottomnav/BottomNavigationDemoFragment.java b/catalog/java/io/material/catalog/bottomnav/BottomNavigationDemoFragment.java
new file mode 100644
index 000000000..e84c0bef9
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/BottomNavigationDemoFragment.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomnav;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.android.material.bottomnavigation.BottomNavigationView.OnNavigationItemSelectedListener;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.DemoUtils;
+import java.util.List;
+
+/** A base class that provides a demo screen structure for a single bottom nav demo. */
+public abstract class BottomNavigationDemoFragment extends DemoFragment {
+
+ private static final int MAX_BOTTOM_NAV_CHILDREN = 5;
+ private int numVisibleChildren = 3;
+ protected List bottomNavigationViews;
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_bottom_nav_fragment, viewGroup, false /* attachToRoot */);
+ initBottomNavs(layoutInflater, view);
+ initBottomNavDemoControls(view);
+
+ OnNavigationItemSelectedListener navigationItemListener =
+ new OnNavigationItemSelectedListener() {
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ handleAllBottomNavSelections(item.getItemId());
+
+ TextView page1Text = view.findViewById(R.id.page_1);
+ TextView page2Text = view.findViewById(R.id.page_2);
+ TextView page3Text = view.findViewById(R.id.page_3);
+ TextView page4Text = view.findViewById(R.id.page_4);
+ TextView page5Text = view.findViewById(R.id.page_5);
+
+ int itemId = item.getItemId();
+ page1Text.setVisibility(itemId == R.id.action_page_1 ? View.VISIBLE : View.GONE);
+ page2Text.setVisibility(itemId == R.id.action_page_2 ? View.VISIBLE : View.GONE);
+ page3Text.setVisibility(itemId == R.id.action_page_3 ? View.VISIBLE : View.GONE);
+ page4Text.setVisibility(itemId == R.id.action_page_4 ? View.VISIBLE : View.GONE);
+ page5Text.setVisibility(itemId == R.id.action_page_5 ? View.VISIBLE : View.GONE);
+ return false;
+ }
+ };
+ setBottomNavListeners(navigationItemListener);
+ return view;
+ }
+
+ private void handleAllBottomNavSelections(int itemId) {
+ for (BottomNavigationView bn : bottomNavigationViews) {
+ handleBottomNavItemSelections(bn, itemId);
+ }
+ }
+
+ private void handleBottomNavItemSelections(BottomNavigationView bn, int itemId) {
+ bn.getMenu().findItem(itemId).setChecked(true);
+ }
+
+ protected void initBottomNavDemoControls(View view) {
+ initAddNavItemButton(view.findViewById(R.id.add_button));
+ initRemoveNavItemButton(view.findViewById(R.id.remove_button));
+ }
+
+ private void initAddNavItemButton(Button addNavItemButton) {
+ addNavItemButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (numVisibleChildren < MAX_BOTTOM_NAV_CHILDREN) {
+ addNavItemsToBottomNavs();
+ numVisibleChildren++;
+ }
+ }
+ });
+ }
+
+ private void initRemoveNavItemButton(Button removeNavItemButton) {
+ removeNavItemButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (numVisibleChildren > 0) {
+ numVisibleChildren--;
+ removeNavItemsFromBottomNavs();
+ }
+ }
+ });
+ }
+
+ private void setBottomNavListeners(OnNavigationItemSelectedListener listener) {
+ for (BottomNavigationView bn : bottomNavigationViews) {
+ bn.setOnNavigationItemSelectedListener(listener);
+ }
+ }
+
+ private void removeNavItemsFromBottomNavs() {
+ adjustAllBottomNavItemsVisibilities(false);
+ }
+
+ private void addNavItemsToBottomNavs() {
+ adjustAllBottomNavItemsVisibilities(true);
+ }
+
+ private void adjustAllBottomNavItemsVisibilities(boolean visibility) {
+ for (BottomNavigationView bn : bottomNavigationViews) {
+ adjustBottomNavItemsVisibility(bn, visibility);
+ }
+ }
+
+ private void adjustBottomNavItemsVisibility(BottomNavigationView bn, boolean visibility) {
+ bn.getMenu().getItem(numVisibleChildren).setVisible(visibility);
+ }
+
+ private void initBottomNavs(LayoutInflater layoutInflater, View view) {
+ inflateBottomNavs(layoutInflater, view.findViewById(R.id.content));
+ inflateBottomNavDemoControls(layoutInflater, view.findViewById(R.id.demo_controls));
+ addBottomNavsToList(view);
+ }
+
+ private void inflateBottomNavDemoControls(LayoutInflater layoutInflater, ViewGroup content) {
+ @LayoutRes int demoControls = getBottomNavDemoControlsLayout();
+ if (demoControls != 0) {
+ content.addView(layoutInflater.inflate(getBottomNavDemoControlsLayout(), content, false));
+ }
+ }
+
+ private void inflateBottomNavs(LayoutInflater layoutInflater, ViewGroup content) {
+ content.addView(layoutInflater.inflate(getBottomNavsContent(), content, false));
+ }
+
+ private void addBottomNavsToList(View view) {
+ bottomNavigationViews = DemoUtils.findViewsWithType(view, BottomNavigationView.class);
+ }
+
+ @LayoutRes
+ protected int getBottomNavsContent() {
+ return R.layout.cat_bottom_nav;
+ }
+
+ @LayoutRes
+ protected int getBottomNavDemoControlsLayout() {
+ return 0;
+ }
+}
diff --git a/catalog/java/io/material/catalog/bottomnav/BottomNavigationFragment.java b/catalog/java/io/material/catalog/bottomnav/BottomNavigationFragment.java
new file mode 100644
index 000000000..b225565e3
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/BottomNavigationFragment.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomnav;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A landing fragment that links to bottom nav demos for the Catalog app. */
+public class BottomNavigationFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_bottom_nav_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_bottom_nav_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new BottomNavigationMainDemoFragment();
+ }
+ };
+ }
+
+ @Override
+ public List getAdditionalDemos() {
+ List additionalDemos = new ArrayList<>();
+ additionalDemos.add(
+ new Demo(R.string.cat_bottom_nav_label_visibility_demo_title) {
+ @Override
+ public Fragment createFragment() {
+ return new BottomNavigationLabelVisibilityDemoFragment();
+ }
+ });
+ additionalDemos.add(
+ new Demo(R.string.cat_bottom_nav_color_styles_demo_title) {
+ @Override
+ public Fragment createFragment() {
+ return new BottomNavigationColorStylesDemoFragment();
+ }
+ });
+ return additionalDemos;
+ }
+
+ /** The Dagger module for {@link BottomNavigationFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract BottomNavigationFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_bottom_nav_title, R.drawable.ic_bottom_nav_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new BottomNavigationFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/bottomnav/BottomNavigationLabelVisibilityDemoFragment.java b/catalog/java/io/material/catalog/bottomnav/BottomNavigationLabelVisibilityDemoFragment.java
new file mode 100644
index 000000000..49dc3bb5d
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/BottomNavigationLabelVisibilityDemoFragment.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomnav;
+
+import io.material.catalog.R;
+
+import com.google.android.material.bottomnavigation.BottomNavigationView;
+import com.google.android.material.bottomnavigation.LabelVisibilityMode;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+/** A fragment that displays controls for the bottom nav's label visibility. */
+public class BottomNavigationLabelVisibilityDemoFragment extends BottomNavigationDemoFragment {
+ @Override
+ protected void initBottomNavDemoControls(View view) {
+ super.initBottomNavDemoControls(view);
+ initLabelVisibilityModeButtons(view);
+ initIconSlider(view);
+ }
+
+ @Override
+ protected int getBottomNavDemoControlsLayout() {
+ return R.layout.cat_bottom_nav_label_visibility_controls;
+ }
+
+ private void setAllBottomNavsLabelVisibilityMode(@LabelVisibilityMode int labelVisibilityMode) {
+ for (BottomNavigationView bn : bottomNavigationViews) {
+ setBottomNavsLabelVisibilityMode(bn, labelVisibilityMode);
+ }
+ }
+
+ private void setBottomNavsLabelVisibilityMode(
+ BottomNavigationView bn, @LabelVisibilityMode int labelVisibilityMode) {
+ bn.setLabelVisibilityMode(labelVisibilityMode);
+ }
+
+ private void setAllBottomNavsIconSize(int size) {
+ for (BottomNavigationView bn : bottomNavigationViews) {
+ bn.setItemIconSize(size);
+ }
+ }
+
+ private void initLabelVisibilityModeButtons(View view) {
+ initLabelVisibilityModeButton(
+ view.findViewById(R.id.label_mode_auto_button), LabelVisibilityMode.LABEL_VISIBILITY_AUTO);
+ initLabelVisibilityModeButton(
+ view.findViewById(R.id.label_mode_selected_button),
+ LabelVisibilityMode.LABEL_VISIBILITY_SELECTED);
+ initLabelVisibilityModeButton(
+ view.findViewById(R.id.label_mode_labeled_button),
+ LabelVisibilityMode.LABEL_VISIBILITY_LABELED);
+ initLabelVisibilityModeButton(
+ view.findViewById(R.id.label_mode_unlabeled_button),
+ LabelVisibilityMode.LABEL_VISIBILITY_UNLABELED);
+ }
+
+ private void initLabelVisibilityModeButton(
+ Button labelVisibilityModeButton, @LabelVisibilityMode int labelVisibilityMode) {
+ labelVisibilityModeButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setAllBottomNavsLabelVisibilityMode(labelVisibilityMode);
+ }
+ });
+ }
+
+ private void initIconSlider(View view) {
+ SeekBar iconSizeSlider = view.findViewById(R.id.icon_size_slider);
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ TextView iconSizeTextView = view.findViewById(R.id.icon_size_text_view);
+ String iconSizeUnit = "dp";
+
+ iconSizeSlider.setOnSeekBarChangeListener(
+ new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ setAllBottomNavsIconSize(
+ (int)
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, progress, displayMetrics));
+ iconSizeTextView.setText(String.valueOf(progress).concat(iconSizeUnit));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+ }
+}
diff --git a/catalog/java/io/material/catalog/bottomnav/BottomNavigationMainDemoFragment.java b/catalog/java/io/material/catalog/bottomnav/BottomNavigationMainDemoFragment.java
new file mode 100644
index 000000000..9f62f1a64
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/BottomNavigationMainDemoFragment.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.bottomnav;
+
+/** A fragment that displays the main bottom nav demos for the Catalog app. */
+public class BottomNavigationMainDemoFragment extends BottomNavigationDemoFragment {}
diff --git a/catalog/java/io/material/catalog/bottomnav/res/drawable/ic_star_vd_theme_24.xml b/catalog/java/io/material/catalog/bottomnav/res/drawable/ic_star_vd_theme_24.xml
new file mode 100644
index 000000000..b95e6106d
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/drawable/ic_star_vd_theme_24.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav.xml b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav.xml
new file mode 100644
index 000000000..ea55398ed
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_color_styles.xml b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_color_styles.xml
new file mode 100644
index 000000000..c5cc475b3
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_color_styles.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_fragment.xml b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_fragment.xml
new file mode 100644
index 000000000..09d02415c
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_fragment.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_label_visibility_controls.xml b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_label_visibility_controls.xml
new file mode 100644
index 000000000..94513d427
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/layout/cat_bottom_nav_label_visibility_controls.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/menu/bottom_nav_menu.xml b/catalog/java/io/material/catalog/bottomnav/res/menu/bottom_nav_menu.xml
new file mode 100644
index 000000000..13de877ef
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/menu/bottom_nav_menu.xml
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/values/dimens.xml b/catalog/java/io/material/catalog/bottomnav/res/values/dimens.xml
new file mode 100644
index 000000000..007a4cfe3
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/values/dimens.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ 16dp
+
diff --git a/catalog/java/io/material/catalog/bottomnav/res/values/strings.xml b/catalog/java/io/material/catalog/bottomnav/res/values/strings.xml
new file mode 100644
index 000000000..7370cbf2f
--- /dev/null
+++ b/catalog/java/io/material/catalog/bottomnav/res/values/strings.xml
@@ -0,0 +1,44 @@
+
+
+
+
+ Bottom Navigation
+ Label Visibility Demo
+ Color Styles Demo
+
+ Bottom navigation bars make it easy to explore and switch between top-level views in a single
+ tap. This navigational element resides at the bottom of the screen, and provides direct access
+ for three to five destinations within an app.
+
+ Item %1$d
+ Page 1
+ Page 2
+ Page 3
+ Page 4
+ Page 5
+ Add nav item
+ Remove nav item
+ Icon size:
+ Label visibility mode:
+ Auto
+ Selected
+ Labeled
+ Unlabeled
+ Legacy Bottom Nav
+ Default Colored Bottom Nav
+ Colored Bottom Nav
+
diff --git a/catalog/java/io/material/catalog/button/ButtonsFragment.java b/catalog/java/io/material/catalog/button/ButtonsFragment.java
new file mode 100644
index 000000000..d377f6398
--- /dev/null
+++ b/catalog/java/io/material/catalog/button/ButtonsFragment.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.button;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+
+/** A landing fragment that links to button demos for the Catalog app. */
+public class ButtonsFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_buttons_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_buttons_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new ButtonsMainDemoFragment();
+ }
+ };
+ }
+
+ /** The Dagger module for {@link ButtonsFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract ButtonsFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_buttons_title, R.drawable.ic_buttons_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new ButtonsFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/button/ButtonsMainDemoFragment.java b/catalog/java/io/material/catalog/button/ButtonsMainDemoFragment.java
new file mode 100644
index 000000000..84ffe1065
--- /dev/null
+++ b/catalog/java/io/material/catalog/button/ButtonsMainDemoFragment.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.button;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.snackbar.BaseTransientBottomBar;
+import com.google.android.material.snackbar.Snackbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.DemoUtils;
+import java.util.List;
+
+/** A fragment that displays main button demos for the Catalog app. */
+public class ButtonsMainDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(getButtonsContent(), viewGroup, false /* attachToRoot */);
+
+ List buttons = DemoUtils.findViewsWithType(view, MaterialButton.class);
+
+ for (MaterialButton button : buttons) {
+ button.setOnClickListener(
+ v -> {
+ // Show a Snackbar with an action button, which should also have a MaterialButton style
+ Snackbar snackbar =
+ Snackbar.make(v, R.string.cat_button_clicked, BaseTransientBottomBar.LENGTH_LONG);
+ snackbar.setAction(
+ R.string.cat_snackbar_action_button_text,
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {}
+ });
+ snackbar.show();
+ });
+ }
+
+ return view;
+ }
+
+ @LayoutRes
+ protected int getButtonsContent() {
+ return R.layout.cat_buttons_fragment;
+ }
+}
diff --git a/catalog/java/io/material/catalog/button/res/layout/cat_buttons_fragment.xml b/catalog/java/io/material/catalog/button/res/layout/cat_buttons_fragment.xml
new file mode 100644
index 000000000..d7aed723a
--- /dev/null
+++ b/catalog/java/io/material/catalog/button/res/layout/cat_buttons_fragment.xml
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/button/res/values/strings.xml b/catalog/java/io/material/catalog/button/res/values/strings.xml
new file mode 100644
index 000000000..342f73598
--- /dev/null
+++ b/catalog/java/io/material/catalog/button/res/values/strings.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+ Buttons
+ Buttons
+
+ Buttons communicate the action that will occur when the user touches them.
+ They may display text, imagery, or both.
+ Flat buttons and raised buttons are the most commonly used types.
+
+ Enabled
+ Disabled
+ Material button
+ Unelevated button
+ Text button
+ Stroked button
+ Icon button
+ Icon text button
+ Filled buttons
+ Unfilled buttons
+
+ Button clicked
+ Done
+
diff --git a/catalog/java/io/material/catalog/card/CardFragment.java b/catalog/java/io/material/catalog/card/CardFragment.java
new file mode 100644
index 000000000..4f4eeae3e
--- /dev/null
+++ b/catalog/java/io/material/catalog/card/CardFragment.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.card;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+
+/** A landing fragment that links to card demos for the Catalog app. */
+public class CardFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_card_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_card_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new CardMainDemoFragment();
+ }
+ };
+ }
+
+ /** The Dagger module for {@link CardFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract CardFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_card_title, R.drawable.ic_card_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new CardFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/card/CardMainDemoFragment.java b/catalog/java/io/material/catalog/card/CardMainDemoFragment.java
new file mode 100644
index 000000000..bc806878e
--- /dev/null
+++ b/catalog/java/io/material/catalog/card/CardMainDemoFragment.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.card;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays main card demos for the Catalog app. */
+public class CardMainDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(getCardContent(), viewGroup, false /* attachToRoot */);
+
+ return view;
+ }
+
+ @LayoutRes
+ protected int getCardContent() {
+ return R.layout.cat_card_fragment;
+ }
+}
diff --git a/catalog/java/io/material/catalog/card/res/layout/cat_card_fragment.xml b/catalog/java/io/material/catalog/card/res/layout/cat_card_fragment.xml
new file mode 100644
index 000000000..e7b852590
--- /dev/null
+++ b/catalog/java/io/material/catalog/card/res/layout/cat_card_fragment.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/card/res/values/dimens.xml b/catalog/java/io/material/catalog/card/res/values/dimens.xml
new file mode 100644
index 000000000..c14d8452a
--- /dev/null
+++ b/catalog/java/io/material/catalog/card/res/values/dimens.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ 200dp
+
diff --git a/catalog/java/io/material/catalog/card/res/values/strings.xml b/catalog/java/io/material/catalog/card/res/values/strings.xml
new file mode 100644
index 000000000..79d5e68bd
--- /dev/null
+++ b/catalog/java/io/material/catalog/card/res/values/strings.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+ Cards
+ Material 1 CardView
+
+ Cards represent entry points into deeper levels of detail or navigation, such as an album in
+ Music or an upcoming vacation in Trips. They can be used in a grid, carousel, or feed to
+ contain groups of individual packaged information.
+
+
+ Cards on mobile default to 8dp margins. layout_margin needs to be set directly on a View in its
+ layout and will not work when included as part of a style.
+
+ Cards have 1dp elevation.
+
+ CardView from the Support Library works side-by-side with MaterialCardView. These cards default
+ to Material 1 styles and support fewer style attributes than MaterialCardView.
+
+
+ MaterialCardView uses the correct styles by default and makes use of additional style attributes
+ that are not provided in CardView. If you do not need to use the new style attributes, you can
+ use MaterialCardView in your layout, or apply a style tag to a CardView. We recommend using
+ the MaterialCardView class for the latest Material designs and maximum flexibility in your UI.
+
+
diff --git a/catalog/java/io/material/catalog/chip/ChipFragment.java b/catalog/java/io/material/catalog/chip/ChipFragment.java
new file mode 100644
index 000000000..5d938fada
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/ChipFragment.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.chip;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A landing fragment that links to Chip demos for the Catalog app. */
+public class ChipFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_chip_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_chip_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new ChipMainDemoFragment();
+ }
+ };
+ }
+
+ @Override
+ public List getAdditionalDemos() {
+ List additionalDemos = new ArrayList<>();
+ additionalDemos.add(
+ new Demo(R.string.cat_chip_group_demo_title) {
+ @Override
+ public Fragment createFragment() {
+ return new ChipGroupDemoFragment();
+ }
+ });
+ return additionalDemos;
+ }
+
+ /** The Dagger module for {@link ChipFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract ChipFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_chip_title, R.drawable.ic_chips_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new ChipFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/chip/ChipGroupDemoFragment.java b/catalog/java/io/material/catalog/chip/ChipGroupDemoFragment.java
new file mode 100644
index 000000000..a155a2080
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/ChipGroupDemoFragment.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.chip;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import com.google.android.material.chip.Chip;
+import com.google.android.material.chip.ChipGroup;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Switch;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays the ChipGroup demos for the Catalog app. */
+public class ChipGroupDemoFragment extends DemoFragment {
+
+ private Switch singleSelectionSwitch;
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_chip_group_fragment, viewGroup, false /* attachToRoot */);
+
+ ViewGroup content = view.findViewById(R.id.content);
+
+ singleSelectionSwitch = view.findViewById(R.id.single_selection);
+ ChipGroup reflowGroup = view.findViewById(R.id.reflow_group);
+ ChipGroup scrollGroup = view.findViewById(R.id.scroll_group);
+
+ singleSelectionSwitch.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> {
+ reflowGroup.setSingleSelection(isChecked);
+ scrollGroup.setSingleSelection(isChecked);
+
+ initChipGroup(reflowGroup);
+ initChipGroup(scrollGroup);
+ });
+ initChipGroup(reflowGroup);
+ initChipGroup(scrollGroup);
+
+ return view;
+ }
+
+ @LayoutRes
+ protected int getChipGroupItem(boolean singleSelection) {
+ return singleSelection
+ ? R.layout.cat_chip_group_item_choice
+ : R.layout.cat_chip_group_item_filter;
+ }
+
+ private void initChipGroup(ChipGroup chipGroup) {
+ chipGroup.removeAllViews();
+
+ boolean singleSelection = singleSelectionSwitch.isChecked();
+ String[] textArray = getResources().getStringArray(R.array.cat_chip_group_text_array);
+ for (String text : textArray) {
+ Chip chip =
+ (Chip) getLayoutInflater().inflate(getChipGroupItem(singleSelection), chipGroup, false);
+ chip.setChipText(text);
+ chipGroup.addView(chip);
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/chip/ChipMainDemoFragment.java b/catalog/java/io/material/catalog/chip/ChipMainDemoFragment.java
new file mode 100644
index 000000000..a496f1fae
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/ChipMainDemoFragment.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.chip;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import com.google.android.material.chip.Chip;
+import com.google.android.material.snackbar.BaseTransientBottomBar;
+import com.google.android.material.snackbar.Snackbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.DemoUtils;
+import java.util.List;
+
+/** A fragment that displays the main Chip demos for the Catalog app. */
+public class ChipMainDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(R.layout.cat_chip_fragment, viewGroup, false /* attachToRoot */);
+
+ ViewGroup content = view.findViewById(R.id.content);
+ View.inflate(getContext(), getChipContent(), content);
+
+ List chips = DemoUtils.findViewsWithType(view, Chip.class);
+ for (Chip chip : chips) {
+ chip.setOnCloseIconClickListener(
+ v -> {
+ Snackbar.make(view, "Clicked close icon.", BaseTransientBottomBar.LENGTH_SHORT).show();
+ });
+ }
+
+ return view;
+ }
+
+ @LayoutRes
+ protected int getChipContent() {
+ return R.layout.cat_chip_content;
+ }
+}
diff --git a/catalog/java/io/material/catalog/chip/res/color/close_icon_tint_list.xml b/catalog/java/io/material/catalog/chip/res/color/close_icon_tint_list.xml
new file mode 100644
index 000000000..d6ca2a448
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/color/close_icon_tint_list.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/layout/cat_chip_content.xml b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_content.xml
new file mode 100644
index 000000000..2e2bb8ec0
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_content.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/layout/cat_chip_fragment.xml b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_fragment.xml
new file mode 100644
index 000000000..7a57fa962
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_fragment.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_fragment.xml b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_fragment.xml
new file mode 100644
index 000000000..386d60b36
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_fragment.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_item_choice.xml b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_item_choice.xml
new file mode 100644
index 000000000..47ab5412d
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_item_choice.xml
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_item_filter.xml b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_item_filter.xml
new file mode 100644
index 000000000..de9477c6f
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_group_item_filter.xml
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/layout/cat_chip_text_field_fragment.xml b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_text_field_fragment.xml
new file mode 100644
index 000000000..6a01f567d
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/layout/cat_chip_text_field_fragment.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/values/strings.xml b/catalog/java/io/material/catalog/chip/res/values/strings.xml
new file mode 100644
index 000000000..681f2dbff
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/values/strings.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+ Chips
+
+ Chips are used when multiple interactive elements appear together in the same area, such as a
+ list of selectable movie times, a series of email contacts, or even an intelligent cross-app
+ action.
+
+
+ Hello, World!
+
+ Chip Group Demo
+ Single selection
+ Reflowing chip group
+ Scrolling chip group
+
+ Entry
+ Filter
+ Choice
+ Action
+
+ Standalone: [chip]
+ [chip]
+
+
+ - New York
+ - Los Angeles
+ - Chicago
+ - Houston
+ - Philadelphia
+ - Phoenix
+ - San Antonio
+ - San Diego
+ - Dallas
+ - San Jose
+ - Austin
+ - Jacksonville
+ - San Francisco
+
+
+
diff --git a/catalog/java/io/material/catalog/chip/res/xml/standalone_chip.xml b/catalog/java/io/material/catalog/chip/res/xml/standalone_chip.xml
new file mode 100644
index 000000000..deda54b6f
--- /dev/null
+++ b/catalog/java/io/material/catalog/chip/res/xml/standalone_chip.xml
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/draggable/DraggableCoordinatorLayout.java b/catalog/java/io/material/catalog/draggable/DraggableCoordinatorLayout.java
new file mode 100644
index 000000000..f5abe6c60
--- /dev/null
+++ b/catalog/java/io/material/catalog/draggable/DraggableCoordinatorLayout.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.material.catalog.draggable;
+
+import android.content.Context;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.v4.widget.ViewDragHelper;
+import android.support.v4.widget.ViewDragHelper.Callback;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A CoordinatorLayout whose children can be dragged. */
+public class DraggableCoordinatorLayout extends CoordinatorLayout {
+
+ private final ViewDragHelper viewDragHelper;
+ private final List draggableChildren = new ArrayList<>();
+
+ public DraggableCoordinatorLayout(Context context) {
+ this(context, null);
+ }
+
+ public DraggableCoordinatorLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ viewDragHelper = ViewDragHelper.create(this, dragCallback);
+ }
+
+ public void addDraggableChild(View child) {
+ if (child.getParent() != this) {
+ throw new IllegalArgumentException();
+ }
+ draggableChildren.add(child);
+ }
+
+ public void removeDraggableChild(View child) {
+ if (child.getParent() != this) {
+ throw new IllegalArgumentException();
+ }
+ draggableChildren.remove(child);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return viewDragHelper.shouldInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ viewDragHelper.processTouchEvent(ev);
+ return super.onTouchEvent(ev);
+ }
+
+ private final Callback dragCallback =
+ new Callback() {
+ @Override
+ public boolean tryCaptureView(View view, int i) {
+ return view.getVisibility() == VISIBLE && viewIsDraggableChild(view);
+ }
+
+ @Override
+ public int getViewHorizontalDragRange(View view) {
+ return view.getWidth();
+ }
+
+ @Override
+ public int getViewVerticalDragRange(View view) {
+ return view.getHeight();
+ }
+
+ @Override
+ public int clampViewPositionHorizontal(View view, int left, int dx) {
+ return left;
+ }
+
+ @Override
+ public int clampViewPositionVertical(View view, int top, int dy) {
+ return top;
+ }
+ };
+
+ private boolean viewIsDraggableChild(View view) {
+ return draggableChildren.isEmpty() || draggableChildren.contains(view);
+ }
+}
diff --git a/catalog/java/io/material/catalog/fab/FabFragment.java b/catalog/java/io/material/catalog/fab/FabFragment.java
new file mode 100644
index 000000000..a4812353c
--- /dev/null
+++ b/catalog/java/io/material/catalog/fab/FabFragment.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.fab;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+
+/** A landing fragment that links to FAB demos for the Catalog app. */
+public class FabFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_fab_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_fab_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new FabMainDemoFragment();
+ }
+ };
+ }
+
+ /** The Dagger module for {@link FabFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract FabFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_fab_title, R.drawable.ic_fab_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new FabFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/fab/FabMainDemoFragment.java b/catalog/java/io/material/catalog/fab/FabMainDemoFragment.java
new file mode 100644
index 000000000..4bde5aa3a
--- /dev/null
+++ b/catalog/java/io/material/catalog/fab/FabMainDemoFragment.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.fab;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.android.material.snackbar.BaseTransientBottomBar;
+import com.google.android.material.snackbar.Snackbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.DemoUtils;
+import java.util.List;
+
+/** A fragment that displays the main FAB demos for the Catalog app. */
+public class FabMainDemoFragment extends DemoFragment {
+
+ private boolean fabsShown = true;
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(R.layout.cat_fab_fragment, viewGroup, false /* attachToRoot */);
+
+ ViewGroup content = view.findViewById(R.id.content);
+ View.inflate(getContext(), getFabsContent(), content);
+
+ List fabs = DemoUtils.findViewsWithType(view, FloatingActionButton.class);
+
+ for (FloatingActionButton fab : fabs) {
+ fab.setOnClickListener(
+ v -> {
+ Snackbar.make(v, R.string.cat_fab_clicked, BaseTransientBottomBar.LENGTH_SHORT).show();
+ });
+ }
+
+ Button showHideFabs = view.findViewById(R.id.show_hide_fabs);
+ showHideFabs.setOnClickListener(
+ v -> {
+ for (FloatingActionButton fab : fabs) {
+ if (fabsShown) {
+ fab.hide();
+ showHideFabs.setText(R.string.show_fabs_label);
+ } else {
+ fab.show();
+ showHideFabs.setText(R.string.hide_fabs_label);
+ }
+ }
+ fabsShown = !fabsShown;
+ });
+
+ return view;
+ }
+
+ @LayoutRes
+ protected int getFabsContent() {
+ return R.layout.mtrl_fabs;
+ }
+}
diff --git a/catalog/java/io/material/catalog/fab/res/layout/cat_fab_fragment.xml b/catalog/java/io/material/catalog/fab/res/layout/cat_fab_fragment.xml
new file mode 100644
index 000000000..c62face44
--- /dev/null
+++ b/catalog/java/io/material/catalog/fab/res/layout/cat_fab_fragment.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/fab/res/layout/mtrl_fabs.xml b/catalog/java/io/material/catalog/fab/res/layout/mtrl_fabs.xml
new file mode 100644
index 000000000..8abde0506
--- /dev/null
+++ b/catalog/java/io/material/catalog/fab/res/layout/mtrl_fabs.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/fab/res/values/strings.xml b/catalog/java/io/material/catalog/fab/res/values/strings.xml
new file mode 100644
index 000000000..48279ccf1
--- /dev/null
+++ b/catalog/java/io/material/catalog/fab/res/values/strings.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+ Floating Action Button
+ The Floating Action Button (FAB) represents your product\'s primary action, and should be considered a direct extension of its brand.
+
+ Regular
+ Legacy
+ Theme
+
+ Hide FABs
+ Show FABs
+
+ FAB clicked
+
+
+ Regular FAB
+ Regular mini FAB
+ Legacy FAB
+ Legacy mini FAB
+ Themed FAB
+ Mini themed FAB
+
+
diff --git a/catalog/java/io/material/catalog/feature/Demo.java b/catalog/java/io/material/catalog/feature/Demo.java
new file mode 100644
index 000000000..9ff00ab08
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/Demo.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import io.material.catalog.R;
+
+import android.content.Intent;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+
+/** Represents a single demo. */
+public abstract class Demo {
+
+ @StringRes private final int titleResId;
+
+ public Demo() {
+ this(R.string.cat_demo_landing_row_demo_header);
+ }
+
+ public Demo(@StringRes int titleResId) {
+ this.titleResId = titleResId;
+ }
+
+ @StringRes
+ public final int getTitleResId() {
+ return titleResId;
+ }
+
+ @Nullable
+ public Fragment createFragment() {
+ return null;
+ }
+
+ @Nullable
+ public Intent createActivityIntent() {
+ return null;
+ }
+}
diff --git a/catalog/java/io/material/catalog/feature/DemoActivity.java b/catalog/java/io/material/catalog/feature/DemoActivity.java
new file mode 100644
index 000000000..75fbc7094
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/DemoActivity.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import dagger.android.AndroidInjection;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasFragmentInjector;
+import dagger.android.support.HasSupportFragmentInjector;
+import javax.inject.Inject;
+
+/** Base Activity class that provides a demo screen structure for a single demo. */
+public abstract class DemoActivity extends AppCompatActivity
+ implements HasFragmentInjector, HasSupportFragmentInjector {
+
+ public static final String EXTRA_DEMO_TITLE = "demo_title";
+
+ private Toolbar toolbar;
+ private ViewGroup demoContainer;
+
+ @Inject DispatchingAndroidInjector supportFragmentInjector;
+ @Inject DispatchingAndroidInjector frameworkFragmentInjector;
+
+ @Override
+ protected void onCreate(@Nullable Bundle bundle) {
+ safeInject();
+ super.onCreate(bundle);
+ setContentView(R.layout.cat_demo_activity);
+
+ toolbar = findViewById(R.id.toolbar);
+ demoContainer = findViewById(R.id.cat_demo_activity_container);
+
+ initDemoActionBar();
+ demoContainer.addView(onCreateDemoView(LayoutInflater.from(this), demoContainer, bundle));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @StringRes
+ public int getDemoTitleResId() {
+ return 0;
+ }
+
+ protected boolean shouldShowDefaultDemoActionBar() {
+ return true;
+ }
+
+ @Override
+ public AndroidInjector supportFragmentInjector() {
+ return supportFragmentInjector;
+ }
+
+ @Override
+ public AndroidInjector fragmentInjector() {
+ return frameworkFragmentInjector;
+ }
+
+ private void safeInject() {
+ try {
+ AndroidInjection.inject(this);
+ } catch (Exception e) {
+ // Ignore exception, not all DemoActivity subclasses need to inject
+ }
+ }
+
+ private void initDemoActionBar() {
+ if (shouldShowDefaultDemoActionBar()) {
+ setSupportActionBar(toolbar);
+ setDemoActionBarTitle(getSupportActionBar());
+ } else {
+ toolbar.setVisibility(View.GONE);
+ }
+ }
+
+ private void setDemoActionBarTitle(ActionBar actionBar) {
+ if (getDemoTitleResId() != 0) {
+ actionBar.setTitle(getDemoTitleResId());
+ } else {
+ actionBar.setTitle(getDefaultDemoTitle());
+ }
+ }
+
+ private String getDefaultDemoTitle() {
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ return extras.getString(EXTRA_DEMO_TITLE, "");
+ } else {
+ return "";
+ }
+ }
+
+ public abstract View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle);
+}
diff --git a/catalog/java/io/material/catalog/feature/DemoFragment.java b/catalog/java/io/material/catalog/feature/DemoFragment.java
new file mode 100644
index 000000000..2f9271b72
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/DemoFragment.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import io.material.catalog.R;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.support.AndroidSupportInjection;
+import dagger.android.support.HasSupportFragmentInjector;
+import io.material.catalog.themeswitcher.ThemeSwitcherHelper;
+import io.material.catalog.themeswitcher.ThemeSwitcherHelper.ThemeSwitcherFragment;
+import javax.inject.Inject;
+
+/** Base Fragment class that provides a demo screen structure for a single demo. */
+public abstract class DemoFragment extends Fragment
+ implements ThemeSwitcherFragment, HasSupportFragmentInjector {
+
+ public static final String ARG_DEMO_TITLE = "demo_title";
+
+ private Toolbar toolbar;
+ private ViewGroup demoContainer;
+ @Nullable private ThemeSwitcherHelper themeSwitcherHelper;
+
+ @Inject DispatchingAndroidInjector childFragmentInjector;
+
+ @Override
+ public void onAttach(Context context) {
+ safeInject();
+ super.onAttach(context);
+
+ themeSwitcherHelper = new ThemeSwitcherHelper(this);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(R.layout.cat_demo_fragment, viewGroup, false /* attachToRoot */);
+
+ toolbar = view.findViewById(R.id.toolbar);
+ demoContainer = view.findViewById(R.id.cat_demo_fragment_container);
+
+ initDemoActionBar();
+ demoContainer.addView(onCreateDemoView(layoutInflater, viewGroup, bundle));
+
+ return view;
+ }
+
+ @StringRes
+ public int getDemoTitleResId() {
+ return 0;
+ }
+
+ /**
+ * Whether this fragment wants to use the default demo action bar, or if the fragment wants to use
+ * its own Toolbar as the action bar.
+ */
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return true;
+ }
+
+ @Override
+ public AndroidInjector supportFragmentInjector() {
+ return childFragmentInjector;
+ }
+
+ private void safeInject() {
+ try {
+ AndroidSupportInjection.inject(this);
+ } catch (Exception e) {
+ // Ignore exception, not all DemoFragment subclasses need to inject
+ }
+ }
+
+ private void initDemoActionBar() {
+ if (shouldShowDefaultDemoActionBar()) {
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+ setDemoActionBarTitle(activity.getSupportActionBar());
+ } else {
+ toolbar.setVisibility(View.GONE);
+ }
+ }
+
+ private void setDemoActionBarTitle(ActionBar actionBar) {
+ if (getDemoTitleResId() != 0) {
+ actionBar.setTitle(getDemoTitleResId());
+ } else {
+ actionBar.setTitle(getDefaultDemoTitle());
+ }
+ }
+
+ protected String getDefaultDemoTitle() {
+ Bundle args = getArguments();
+ if (args != null) {
+ return args.getString(ARG_DEMO_TITLE, "");
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ themeSwitcherHelper.onCreateOptionsMenu(menu, menuInflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ return themeSwitcherHelper.onOptionsItemSelected(menuItem)
+ || super.onOptionsItemSelected(menuItem);
+ }
+
+ public abstract View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle);
+}
diff --git a/catalog/java/io/material/catalog/feature/DemoLandingFragment.java b/catalog/java/io/material/catalog/feature/DemoLandingFragment.java
new file mode 100644
index 000000000..ddd4de296
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/DemoLandingFragment.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import io.material.catalog.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.DimenRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.view.MarginLayoutParamsCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.TextView;
+import dagger.android.support.DaggerFragment;
+import java.util.Collections;
+import java.util.List;
+
+/** Base class that provides a landing screen structure for a single feature demo. */
+public abstract class DemoLandingFragment extends DaggerFragment {
+
+ private static final String FRAGMENT_DEMO_CONTENT = "fragment_demo_content";
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_demo_landing_fragment, viewGroup, false /* attachToRoot */);
+
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(view.findViewById(R.id.toolbar));
+ activity.getSupportActionBar().setTitle(getTitleResId());
+ activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ TextView descriptionTextView = view.findViewById(R.id.cat_demo_landing_description);
+ ViewGroup mainDemoContainer = view.findViewById(R.id.cat_demo_landing_main_demo_container);
+ ViewGroup additionalDemosSection =
+ view.findViewById(R.id.cat_demo_landing_additional_demos_section);
+ ViewGroup additionalDemosContainer =
+ view.findViewById(R.id.cat_demo_landing_additional_demos_container);
+
+ descriptionTextView.setText(getDescriptionResId());
+ addLinks(layoutInflater, view);
+ addDemoView(layoutInflater, mainDemoContainer, getMainDemo(), false);
+ List additionalDemos = getAdditionalDemos();
+ for (Demo additionalDemo : additionalDemos) {
+ addDemoView(layoutInflater, additionalDemosContainer, additionalDemo, true);
+ }
+ additionalDemosSection.setVisibility(additionalDemos.isEmpty() ? View.GONE : View.VISIBLE);
+
+ return view;
+ }
+
+ private void addLinks(LayoutInflater layoutInflater, View view) {
+ ViewGroup linksSection = view.findViewById(R.id.cat_demo_landing_links_section);
+ int linksArrayResId = getLinksArrayResId();
+ if (linksArrayResId != -1) {
+ String[] linksStringArray = getResources().getStringArray(linksArrayResId);
+ for (String linkString : linksStringArray) {
+ addLinkView(layoutInflater, linksSection, linkString);
+ }
+ linksSection.setVisibility(View.VISIBLE);
+ } else {
+ linksSection.setVisibility(View.GONE);
+ }
+ }
+
+ private void addLinkView(LayoutInflater layoutInflater, ViewGroup viewGroup, String linkString) {
+ TextView linkView =
+ (TextView) layoutInflater.inflate(R.layout.cat_demo_landing_link_entry, viewGroup, false);
+
+ linkView.setText(linkString);
+ viewGroup.addView(linkView);
+ }
+
+ private void addDemoView(
+ LayoutInflater layoutInflater, ViewGroup demoContainer, Demo demo, boolean isAdditional) {
+ View demoView = layoutInflater.inflate(R.layout.cat_demo_landing_row, demoContainer, false);
+
+ View rootView = demoView.findViewById(R.id.cat_demo_landing_row_root);
+ TextView titleTextView = demoView.findViewById(R.id.cat_demo_landing_row_title);
+ TextView subtitleTextView = demoView.findViewById(R.id.cat_demo_landing_row_subtitle);
+
+ rootView.setOnClickListener(v -> startDemo(demo));
+
+ titleTextView.setText(demo.getTitleResId());
+ subtitleTextView.setText(getDemoClassName(demo));
+
+ if (isAdditional) {
+ setMarginStart(titleTextView, R.dimen.cat_list_text_margin_from_icon_large);
+ setMarginStart(subtitleTextView, R.dimen.cat_list_text_margin_from_icon_large);
+ }
+
+ demoContainer.addView(demoView);
+ }
+
+ private String getDemoClassName(Demo demo) {
+ if (demo.createFragment() != null) {
+ return demo.createFragment().getClass().getSimpleName();
+ } else if (demo.createActivityIntent() != null) {
+ String className = demo.createActivityIntent().getComponent().getClassName();
+ return className.substring(className.lastIndexOf('.') + 1);
+ } else {
+ throw new IllegalStateException("Demo must implement createFragment or createActivityIntent");
+ }
+ }
+
+ private void startDemo(Demo demo) {
+ if (demo.createFragment() != null) {
+ startDemoFragment(demo.createFragment());
+ } else if (demo.createActivityIntent() != null) {
+ startDemoActivity(demo.createActivityIntent());
+ } else {
+ throw new IllegalStateException("Demo must implement createFragment or createActivityIntent");
+ }
+ }
+
+ private void startDemoFragment(Fragment fragment) {
+ Bundle args = new Bundle();
+ args.putString(DemoFragment.ARG_DEMO_TITLE, getString(getTitleResId()));
+ fragment.setArguments(args);
+ FeatureDemoUtils.startFragment(getActivity(), fragment, FRAGMENT_DEMO_CONTENT);
+ }
+
+ private void startDemoActivity(Intent intent) {
+ intent.putExtra(DemoActivity.EXTRA_DEMO_TITLE, getString(getTitleResId()));
+ startActivity(intent);
+ }
+
+ private void setMarginStart(View view, @DimenRes int marginResId) {
+ int margin = getResources().getDimensionPixelOffset(marginResId);
+ MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
+ MarginLayoutParamsCompat.setMarginStart(layoutParams, margin);
+ }
+
+ @StringRes
+ public abstract int getTitleResId();
+
+ @StringRes
+ public abstract int getDescriptionResId();
+
+ public abstract Demo getMainDemo();
+
+ @ArrayRes
+ public int getLinksArrayResId() {
+ return -1;
+ }
+
+ public List getAdditionalDemos() {
+ return Collections.emptyList();
+ }
+}
diff --git a/catalog/java/io/material/catalog/feature/DemoUtils.java b/catalog/java/io/material/catalog/feature/DemoUtils.java
new file mode 100644
index 000000000..062a26382
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/DemoUtils.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import android.view.View;
+import android.view.ViewGroup;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Utils for demos. */
+public abstract class DemoUtils {
+
+ public static List findViewsWithType(View root, Class type) {
+ List views = new ArrayList<>();
+ findViewsWithType(root, type, views);
+ return views;
+ }
+
+ private static void findViewsWithType(View view, Class type, List views) {
+ if (type.isInstance(view)) {
+ views.add(type.cast(view));
+ }
+
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ findViewsWithType(viewGroup.getChildAt(i), type, views);
+ }
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/feature/FeatureDemo.java b/catalog/java/io/material/catalog/feature/FeatureDemo.java
new file mode 100644
index 000000000..643b3d814
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/FeatureDemo.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import android.support.annotation.DrawableRes;
+import android.support.annotation.IntDef;
+import android.support.annotation.StringRes;
+import android.support.v4.app.Fragment;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Represents a single feature to demo. */
+public abstract class FeatureDemo {
+
+ /** Status flag that denotes the demo and component are ready for use. */
+ public static final int STATUS_READY = 0;
+
+ /** Status flag that denotes the demo and/or component is work in progress. */
+ public static final int STATUS_WIP = 1;
+
+ /** Status flag enum for this {@link FeatureDemo}. */
+ @IntDef({STATUS_READY, STATUS_WIP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {}
+
+ @StringRes private final int titleResId;
+ @DrawableRes private final int drawableResId;
+ @Status private final int status;
+
+ public FeatureDemo(@StringRes int titleResId, @DrawableRes int drawableResId) {
+ this(titleResId, drawableResId, STATUS_READY);
+ }
+
+ public FeatureDemo(
+ @StringRes int titleResId, @DrawableRes int drawableResId, @Status int status) {
+ this.titleResId = titleResId;
+ this.drawableResId = drawableResId;
+ this.status = status;
+ }
+
+ @StringRes
+ public int getTitleResId() {
+ return titleResId;
+ }
+
+ @DrawableRes
+ public int getDrawableResId() {
+ return drawableResId;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public abstract Fragment createFragment();
+}
diff --git a/catalog/java/io/material/catalog/feature/FeatureDemoUtils.java b/catalog/java/io/material/catalog/feature/FeatureDemoUtils.java
new file mode 100644
index 000000000..decc622bb
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/FeatureDemoUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+
+/** Utils for feature demos. */
+public abstract class FeatureDemoUtils {
+
+ private static final int MAIN_ACTIVITY_FRAGMENT_CONTAINER_ID = R.id.container;
+
+ public static void startFragment(FragmentActivity activity, Fragment fragment, String tag) {
+ activity
+ .getSupportFragmentManager()
+ .beginTransaction()
+ .setCustomAnimations(
+ R.anim.abc_grow_fade_in_from_bottom,
+ R.anim.abc_fade_out,
+ R.anim.abc_fade_in,
+ R.anim.abc_shrink_fade_out_from_bottom)
+ .replace(MAIN_ACTIVITY_FRAGMENT_CONTAINER_ID, fragment, tag)
+ .addToBackStack(null /* name */)
+ .commit();
+ }
+
+ public static Fragment getCurrentFragment(FragmentActivity activity) {
+ return activity
+ .getSupportFragmentManager()
+ .findFragmentById(MAIN_ACTIVITY_FRAGMENT_CONTAINER_ID);
+ }
+}
diff --git a/catalog/java/io/material/catalog/feature/OnBackPressedHandler.java b/catalog/java/io/material/catalog/feature/OnBackPressedHandler.java
new file mode 100644
index 000000000..77fc38f35
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/OnBackPressedHandler.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.feature;
+
+/** Interface that allows Catalog Fragments to handle the main Activity's onBackPressed() */
+public interface OnBackPressedHandler {
+ boolean onBackPressed();
+}
diff --git a/catalog/java/io/material/catalog/feature/res/layout/cat_demo_activity.xml b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_activity.xml
new file mode 100644
index 000000000..200285967
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_activity.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/feature/res/layout/cat_demo_fragment.xml b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_fragment.xml
new file mode 100644
index 000000000..3281dbe7a
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_fragment.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_fragment.xml b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_fragment.xml
new file mode 100644
index 000000000..cbaddc14e
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_fragment.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_link_entry.xml b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_link_entry.xml
new file mode 100644
index 000000000..52dcb0b98
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_link_entry.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_links_section.xml b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_links_section.xml
new file mode 100644
index 000000000..f882cf14f
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_links_section.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_row.xml b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_row.xml
new file mode 100644
index 000000000..1edf28a59
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/layout/cat_demo_landing_row.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/feature/res/values/dimens.xml b/catalog/java/io/material/catalog/feature/res/values/dimens.xml
new file mode 100644
index 000000000..c256b24de
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/values/dimens.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ 16dp
+ 72dp
+ 1dp
+ 40dp
+ 16dp
+ 24dp
+
diff --git a/catalog/java/io/material/catalog/feature/res/values/strings.xml b/catalog/java/io/material/catalog/feature/res/values/strings.xml
new file mode 100644
index 000000000..1b84d921f
--- /dev/null
+++ b/catalog/java/io/material/catalog/feature/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ Description
+ Resources
+ Additional Examples
+ Demo
+
diff --git a/catalog/java/io/material/catalog/font/FontFragment.java b/catalog/java/io/material/catalog/font/FontFragment.java
new file mode 100644
index 000000000..95460c05e
--- /dev/null
+++ b/catalog/java/io/material/catalog/font/FontFragment.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.font;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+
+/** A landing fragment that links to Font demos for the Catalog app. */
+public class FontFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_font_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_font_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new FontMainDemoFragment();
+ }
+ };
+ }
+
+ /** The Dagger module for {@link FontFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract FontFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_font_title, R.drawable.ic_text_field_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new FontFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/font/FontMainDemoFragment.java b/catalog/java/io/material/catalog/font/FontMainDemoFragment.java
new file mode 100644
index 000000000..a8b2ff32f
--- /dev/null
+++ b/catalog/java/io/material/catalog/font/FontMainDemoFragment.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.font;
+
+import io.material.catalog.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StyleRes;
+import com.google.android.material.resources.TextAppearance;
+import com.google.android.material.snackbar.BaseTransientBottomBar;
+import com.google.android.material.snackbar.Snackbar;
+import android.support.v4.widget.TextViewCompat;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import io.material.catalog.feature.DemoFragment;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A fragment that displays the Font Typographic Styles demo for the Catalog app. */
+public class FontMainDemoFragment extends DemoFragment {
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_font_styles_fragment, viewGroup, false /* attachToRoot */);
+
+ RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ recyclerView.addItemDecoration(
+ new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
+ recyclerView.setAdapter(new FontStyleAdapter(getContext()));
+
+ return view;
+ }
+
+ /**
+ * Returns an array resource containing a list of theme attributes that each reference a text
+ * appearance style. Each style will be displayed in a list.
+ */
+ @ArrayRes
+ protected int getFontStyles() {
+ return R.array.cat_font_styles_array;
+ }
+
+ @ArrayRes
+ protected int getFontStyleNames() {
+ return R.array.cat_font_style_names_array;
+ }
+
+ protected String convertFontFamilyToDescription(String fontFamily) {
+ if (fontFamily == null) {
+ return "Regular";
+ }
+ switch (fontFamily.toLowerCase()) {
+ case "sans-serif-light":
+ return "Light";
+ case "sans-serif":
+ return "Regular";
+ case "sans-serif-medium":
+ return "Medium";
+ default:
+ return fontFamily;
+ }
+ }
+
+ private class FontStyleAdapter extends Adapter {
+
+ private final List styles = new ArrayList<>();
+ private final List names = new ArrayList<>();
+ private final List attributeNames = new ArrayList<>();
+
+ public FontStyleAdapter(Context context) {
+ TypedArray stylesArray = getResources().obtainTypedArray(getFontStyles());
+ TypedArray namesArray = getResources().obtainTypedArray(getFontStyleNames());
+
+ TypedValue value = new TypedValue();
+ for (int i = 0; i < stylesArray.length(); i++) {
+ // 1. Get the attribute from the array: ?attr/textAppearanceHeadline1
+ stylesArray.getValue(i, value);
+ int attribute = value.data;
+
+ // 2. Get the style from the attribute: @style/TextAppearance.MaterialComponents.Headline1
+ TypedArray a = context.obtainStyledAttributes(new int[] {attribute});
+ int style = a.getResourceId(0, 0);
+ a.recycle();
+
+ styles.add(style);
+ names.add(namesArray.getString(i));
+ attributeNames.add(context.getResources().getResourceEntryName(attribute));
+ }
+ stylesArray.recycle();
+ namesArray.recycle();
+ }
+
+ @NonNull
+ @Override
+ public FontStyleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new FontStyleViewHolder(parent);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull FontStyleViewHolder viewHolder, int i) {
+ viewHolder.bind(styles.get(i), names.get(i), attributeNames.get(i));
+ }
+
+ @Override
+ public int getItemCount() {
+ return styles.size();
+ }
+ }
+
+ private class FontStyleViewHolder extends ViewHolder {
+ private final TextView nameView;
+ private final TextView descriptionView;
+ private final ImageView infoView;
+
+ private String attributeName;
+
+ public FontStyleViewHolder(ViewGroup parent) {
+ super(
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.cat_font_styles_item, parent, false));
+
+ nameView = itemView.findViewById(R.id.name);
+ descriptionView = itemView.findViewById(R.id.description);
+ infoView = itemView.findViewById(R.id.info);
+
+ infoView.setOnClickListener(
+ view ->
+ Snackbar.make(
+ view,
+ view.getContext().getString(R.string.cat_font_style_message, attributeName),
+ BaseTransientBottomBar.LENGTH_LONG)
+ .show());
+ }
+
+ public void bind(@StyleRes int style, String name, String attributeName) {
+ this.attributeName = attributeName;
+
+ nameView.setText(name);
+ descriptionView.setText(createDescription(name, style));
+
+ TextViewCompat.setTextAppearance(nameView, style);
+ }
+
+ @SuppressWarnings("RestrictTo")
+ private String createDescription(String name, @StyleRes int style) {
+ TextAppearance textAppearance = new TextAppearance(itemView.getContext(), style);
+ return name
+ + " - "
+ + convertFontFamilyToDescription(textAppearance.fontFamily)
+ + " "
+ + pxToSp(textAppearance.textSize)
+ + "sp";
+ }
+
+ private int pxToSp(float px) {
+ return (int) (px / itemView.getResources().getDisplayMetrics().scaledDensity);
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/font/res/layout/cat_font_styles_fragment.xml b/catalog/java/io/material/catalog/font/res/layout/cat_font_styles_fragment.xml
new file mode 100644
index 000000000..88214b164
--- /dev/null
+++ b/catalog/java/io/material/catalog/font/res/layout/cat_font_styles_fragment.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/font/res/layout/cat_font_styles_item.xml b/catalog/java/io/material/catalog/font/res/layout/cat_font_styles_item.xml
new file mode 100644
index 000000000..aba8f7aa3
--- /dev/null
+++ b/catalog/java/io/material/catalog/font/res/layout/cat_font_styles_item.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/font/res/values/arrays.xml b/catalog/java/io/material/catalog/font/res/values/arrays.xml
new file mode 100644
index 000000000..ee21fa5c5
--- /dev/null
+++ b/catalog/java/io/material/catalog/font/res/values/arrays.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+ - ?attr/textAppearanceHeadline1
+ - ?attr/textAppearanceHeadline2
+ - ?attr/textAppearanceHeadline3
+ - ?attr/textAppearanceHeadline4
+ - ?attr/textAppearanceHeadline5
+ - ?attr/textAppearanceHeadline6
+ - ?attr/textAppearanceSubtitle1
+ - ?attr/textAppearanceSubtitle2
+ - ?attr/textAppearanceBody1
+ - ?attr/textAppearanceBody2
+ - ?attr/textAppearanceCaption
+ - ?attr/textAppearanceButton
+ - ?attr/textAppearanceOverline
+
+
+
+ - Headline 1
+ - Headline 2
+ - Headline 3
+ - Headline 4
+ - Headline 5
+ - Headline 6
+ - Subtitle 1
+ - Subtitle 2
+ - Body 1
+ - Body 2
+ - Caption
+ - Button
+ - Overline
+
+
+
diff --git a/catalog/java/io/material/catalog/font/res/values/strings.xml b/catalog/java/io/material/catalog/font/res/values/strings.xml
new file mode 100644
index 000000000..d35ac84a8
--- /dev/null
+++ b/catalog/java/io/material/catalog/font/res/values/strings.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+ Fonts
+
+ Roboto is the standard typeface on Android.
+
+ Roboto has been refined extensively to work across the wider set of supported platforms. It is
+ slightly wider and rounder, giving it greater clarity and making it more optimistic.
+
+
+ Use ?attr/%s in your layout.
+
+
diff --git a/catalog/java/io/material/catalog/main/MainActivity.java b/catalog/java/io/material/catalog/main/MainActivity.java
new file mode 100644
index 000000000..263755a01
--- /dev/null
+++ b/catalog/java/io/material/catalog/main/MainActivity.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.main;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.MenuItem;
+import dagger.android.ContributesAndroidInjector;
+import dagger.android.support.DaggerAppCompatActivity;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.feature.FeatureDemoUtils;
+import io.material.catalog.feature.OnBackPressedHandler;
+import io.material.catalog.tableofcontents.TocFragment;
+import io.material.catalog.tableofcontents.TocModule;
+import io.material.catalog.themeswitcher.ThemeOverlayUtils;
+import io.material.catalog.themeswitcher.ThemeSwitcherHelper.ThemeSwitcherActivity;
+
+/**
+ * The main launcher activity for the Catalog, capable of displaying a number of different screens
+ * via Fragments.
+ */
+public class MainActivity extends DaggerAppCompatActivity implements ThemeSwitcherActivity {
+
+ TocFragment tocFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ ThemeOverlayUtils.applyThemeOverlays(this);
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.cat_main_activity);
+
+ if (savedInstanceState == null) {
+ tocFragment = new TocFragment();
+ getSupportFragmentManager().beginTransaction().add(R.id.container, tocFragment).commit();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Let the fragment handle options first
+ Fragment currentFragment = FeatureDemoUtils.getCurrentFragment(this);
+ if (currentFragment != null && currentFragment.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (handleFragmentOnBackPressed()) {
+ return;
+ }
+ super.onBackPressed();
+ }
+
+ private boolean handleFragmentOnBackPressed() {
+ Fragment currentFragment = FeatureDemoUtils.getCurrentFragment(this);
+ return currentFragment instanceof OnBackPressedHandler
+ && ((OnBackPressedHandler) currentFragment).onBackPressed();
+ }
+
+ /** The Dagger module for {@link MainActivity} dependencies */
+ @dagger.Module
+ public abstract static class Module {
+ @ActivityScope
+ @ContributesAndroidInjector(modules = {TocModule.class})
+ abstract MainActivity contributeMainActivity();
+ }
+}
diff --git a/catalog/java/io/material/catalog/main/res/layout/cat_main_activity.xml b/catalog/java/io/material/catalog/main/res/layout/cat_main_activity.xml
new file mode 100644
index 000000000..fe16cd085
--- /dev/null
+++ b/catalog/java/io/material/catalog/main/res/layout/cat_main_activity.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/res/mipmap-anydpi-v26/ic_launcher.xml b/catalog/java/io/material/catalog/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..1684f2f84
--- /dev/null
+++ b/catalog/java/io/material/catalog/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/res/mipmap-anydpi-v26/ic_launcher_round.xml b/catalog/java/io/material/catalog/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..1684f2f84
--- /dev/null
+++ b/catalog/java/io/material/catalog/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher.png b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..8c7faa446
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_background.png b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 000000000..a102a1df4
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_foreground.png b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..43f21abac
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_round.png b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..d8f34cb0d
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher.png b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..25d19f156
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_background.png b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 000000000..c72ba5359
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_foreground.png b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..89824ab61
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_round.png b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..d3366f584
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher.png b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..90011cb2e
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_background.png b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..aea95900f
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_foreground.png b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..6e8ec0705
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_round.png b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..66d0d895e
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher.png b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..1a3f364dd
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_background.png b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..2602e8769
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_foreground.png b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..0a6eb6f92
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_round.png b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..7503f0535
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher.png b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..11491e744
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_background.png b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..6c3ba050c
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..09c55efa8
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_round.png b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..8057a7081
Binary files /dev/null and b/catalog/java/io/material/catalog/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/catalog/java/io/material/catalog/res/values/strings.xml b/catalog/java/io/material/catalog/res/values/strings.xml
new file mode 100644
index 000000000..2a70da175
--- /dev/null
+++ b/catalog/java/io/material/catalog/res/values/strings.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ Material Components Catalog
+
diff --git a/catalog/java/io/material/catalog/tableofcontents/GridDividerDecoration.java b/catalog/java/io/material/catalog/tableofcontents/GridDividerDecoration.java
new file mode 100644
index 000000000..971d5a77b
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/GridDividerDecoration.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tableofcontents;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.support.annotation.ColorInt;
+import android.support.annotation.Px;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ItemDecoration;
+import android.view.View;
+
+/**
+ * An {@link ItemDecoration} that adds Material-style dividers between grid items. This is meant to
+ * be used with {@link GridLayoutManager} and only supports vertical orientation.
+ *
+ * This decoration will draw both horizontal and vertical lines along the edges of each view. It
+ * will only draw dividers that are internal to the grid, meaning it will not draw lines for the
+ * outermost left, top, right, or bottom edges.
+ */
+public class GridDividerDecoration extends RecyclerView.ItemDecoration {
+
+ private final int spanCount;
+ private final Paint dividerPaint;
+ private final Rect bounds = new Rect();
+
+ public GridDividerDecoration(@Px int dividerSize, @ColorInt int dividerColor, int spanCount) {
+ this.dividerPaint = new Paint();
+ this.dividerPaint.setColor(dividerColor);
+ this.dividerPaint.setStrokeWidth(dividerSize);
+ this.dividerPaint.setStyle(Style.STROKE);
+ this.dividerPaint.setAntiAlias(true);
+ this.spanCount = spanCount;
+ }
+
+ @Override
+ public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ drawHorizontal(c, parent);
+ drawVertical(c, parent);
+ }
+
+ private void drawHorizontal(Canvas canvas, RecyclerView parent) {
+ final int itemCount = parent.getAdapter().getItemCount();
+ final int childCount = parent.getChildCount();
+ final int lastRowChildCount = getLastRowChildCount(itemCount);
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+
+ if (isChildInLastRow(parent, child, itemCount, lastRowChildCount)) {
+ continue;
+ }
+
+ parent.getDecoratedBoundsWithMargins(child, bounds);
+ final int y = bounds.bottom;
+ final int startX = bounds.left;
+ final int stopX = bounds.right;
+ canvas.drawLine(startX, y, stopX, y, dividerPaint);
+ }
+ }
+
+ private void drawVertical(Canvas canvas, RecyclerView parent) {
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+
+ if (isChildInLastColumn(parent, child)) {
+ continue;
+ }
+
+ parent.getDecoratedBoundsWithMargins(child, bounds);
+ final int x = bounds.right;
+ final int startY = bounds.top;
+ final int stopY = bounds.bottom;
+ canvas.drawLine(x, startY, x, stopY, dividerPaint);
+ }
+ }
+
+ private int getLastRowChildCount(int itemCount) {
+ final int gridChildRemainder = itemCount % spanCount;
+ return gridChildRemainder == 0 ? spanCount : gridChildRemainder;
+ }
+
+ private boolean isChildInLastRow(
+ RecyclerView parent, View child, int itemCount, int lastRowChildCount) {
+ return parent.getChildAdapterPosition(child) >= itemCount - lastRowChildCount;
+ }
+
+ private boolean isChildInLastColumn(RecyclerView parent, View child) {
+ return parent.getChildAdapterPosition(child) % spanCount == spanCount - 1;
+ }
+}
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java b/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java
new file mode 100644
index 000000000..5edb1c357
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/TocAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tableofcontents;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.widget.RecyclerView.Adapter;
+import android.view.ViewGroup;
+import io.material.catalog.feature.FeatureDemo;
+import java.util.List;
+
+/** Handles individual items in the catalog table of contents. */
+class TocAdapter extends Adapter {
+
+ private final FragmentActivity activity;
+ private final List featureDemos;
+
+ TocAdapter(FragmentActivity activity, List featureDemos) {
+ this.activity = activity;
+ this.featureDemos = featureDemos;
+ }
+
+ @Override
+ public TocViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ return new TocViewHolder(activity, viewGroup);
+ }
+
+ @Override
+ public void onBindViewHolder(TocViewHolder tocViewHolder, int i) {
+ tocViewHolder.bind(activity, featureDemos.get(i));
+ }
+
+ @Override
+ public int getItemCount() {
+ return featureDemos.size();
+ }
+}
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocFragment.java b/catalog/java/io/material/catalog/tableofcontents/TocFragment.java
new file mode 100644
index 000000000..4df243fd9
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/TocFragment.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tableofcontents;
+
+import io.material.catalog.R;
+
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.math.MathUtils;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import dagger.android.support.DaggerFragment;
+import io.material.catalog.feature.FeatureDemo;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.inject.Inject;
+
+/** Initial table of contents screen for the catalog app. */
+public class TocFragment extends DaggerFragment {
+
+ private static final int GRID_SPAN_COUNT_MIN = 1;
+ private static final int GRID_SPAN_COUNT_MAX = 4;
+
+ @Inject Set featureDemos;
+ @Inject TocResourceProvider tocResourceProvider;
+
+ private AppBarLayout appBarLayout;
+ private View gridTopDivider;
+ private RecyclerView recyclerView;
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(R.layout.cat_toc_fragment, viewGroup, false /* attachToRoot */);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+ activity.getSupportActionBar().setDisplayShowTitleEnabled(false);
+
+ ViewGroup content = view.findViewById(R.id.content);
+ View.inflate(getContext(), tocResourceProvider.getHeaderContent(), content);
+
+ appBarLayout = view.findViewById(R.id.cat_toc_app_bar_layout);
+ gridTopDivider = view.findViewById(R.id.cat_toc_grid_top_divider);
+ recyclerView = view.findViewById(R.id.cat_toc_grid);
+
+ if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+ addGridTopDividerVisibilityListener();
+ } else {
+ gridTopDivider.setVisibility(View.VISIBLE);
+ }
+
+ final int gridSpanCount = calculateGridSpanCount();
+
+ recyclerView.setLayoutManager(new GridLayoutManager(getContext(), gridSpanCount));
+ recyclerView.addItemDecoration(
+ new GridDividerDecoration(
+ getResources().getDimensionPixelSize(R.dimen.cat_toc_grid_divider_size),
+ ContextCompat.getColor(getContext(), R.color.cat_toc_grid_divider_color),
+ gridSpanCount));
+
+ List featureList = new ArrayList<>(featureDemos);
+ // Sort features alphabetically
+ Collator collator = Collator.getInstance();
+ Collections.sort(
+ featureList,
+ (feature1, feature2) ->
+ collator.compare(
+ getContext().getString(feature1.getTitleResId()),
+ getContext().getString(feature2.getTitleResId())));
+
+ TocAdapter tocAdapter = new TocAdapter(getActivity(), featureList);
+ recyclerView.setAdapter(tocAdapter);
+
+ return view;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+
+ ((AppCompatActivity) getActivity()).setSupportActionBar(null);
+ }
+
+ private void addGridTopDividerVisibilityListener() {
+ appBarLayout.addOnOffsetChangedListener(
+ new OnOffsetChangedListener() {
+ @Override
+ public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
+ if (Math.abs(verticalOffset) == appBarLayout.getTotalScrollRange()) {
+ // CTL is collapsed, hide top divider
+ gridTopDivider.setVisibility(View.GONE);
+ } else {
+ // CTL is expanded or expanding, show top divider
+ gridTopDivider.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ }
+
+ private int calculateGridSpanCount() {
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int displayWidth = displayMetrics.widthPixels;
+ int itemSize = getResources().getDimensionPixelSize(R.dimen.cat_toc_item_size);
+ int gridSpanCount = displayWidth / itemSize;
+ return MathUtils.clamp(gridSpanCount, GRID_SPAN_COUNT_MIN, GRID_SPAN_COUNT_MAX);
+ }
+}
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocModule.java b/catalog/java/io/material/catalog/tableofcontents/TocModule.java
new file mode 100644
index 000000000..26040f022
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/TocModule.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tableofcontents;
+
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.bottomappbar.BottomAppBarFragment;
+import io.material.catalog.bottomnav.BottomNavigationFragment;
+import io.material.catalog.button.ButtonsFragment;
+import io.material.catalog.card.CardFragment;
+import io.material.catalog.chip.ChipFragment;
+import io.material.catalog.fab.FabFragment;
+import io.material.catalog.font.FontFragment;
+import io.material.catalog.tabs.TabsFragment;
+import io.material.catalog.textfield.TextFieldFragment;
+import io.material.catalog.themeswitcher.ThemeSwitcherDialogFragment;
+import io.material.catalog.themeswitcher.ThemeSwitcherResourceProvider;
+import io.material.catalog.topappbar.TopAppBarFragment;
+import io.material.catalog.transformation.TransformationFragment;
+
+/**
+ * The Dagger module for {@link TocFragment} dependencies.
+ *
+ */
+@dagger.Module(
+ includes = {
+ BottomAppBarFragment.Module.class,
+ ButtonsFragment.Module.class,
+ BottomNavigationFragment.Module.class,
+ CardFragment.Module.class,
+ ChipFragment.Module.class,
+ FabFragment.Module.class,
+ FontFragment.Module.class,
+ TabsFragment.Module.class,
+ TextFieldFragment.Module.class,
+ TopAppBarFragment.Module.class,
+ TransformationFragment.Module.class,
+ }
+)
+public abstract class TocModule {
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract TocFragment contributeTocFragment();
+
+ @Provides
+ static TocResourceProvider provideTocResourceProvider() {
+ return new TocResourceProvider();
+ }
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract ThemeSwitcherDialogFragment contributeThemeSwitcherDialogFragment();
+
+ @Provides
+ static ThemeSwitcherResourceProvider provideThemeSwitcherResourceProvider() {
+ return new ThemeSwitcherResourceProvider();
+ }
+}
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocResourceProvider.java b/catalog/java/io/material/catalog/tableofcontents/TocResourceProvider.java
new file mode 100644
index 000000000..b1896e5d3
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/TocResourceProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tableofcontents;
+
+import io.material.catalog.R;
+
+import android.support.annotation.LayoutRes;
+
+/** A helper class that facilitates overriding of certain resources in the Catalog app. */
+public class TocResourceProvider {
+
+ @LayoutRes
+ protected int getHeaderContent() {
+ return R.layout.cat_toc_header;
+ }
+}
diff --git a/catalog/java/io/material/catalog/tableofcontents/TocViewHolder.java b/catalog/java/io/material/catalog/tableofcontents/TocViewHolder.java
new file mode 100644
index 000000000..dfe0582c2
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/TocViewHolder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tableofcontents;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import io.material.catalog.feature.FeatureDemo;
+import io.material.catalog.feature.FeatureDemoUtils;
+
+/** Creates the UI for a single item in the catalog table of contents. */
+class TocViewHolder extends ViewHolder {
+
+ private static final String FRAGMENT_CONTENT = "fragment_content";
+
+ private final TextView titleView;
+ private final ImageView imageView;
+ private final TextView statusWipLabelView;
+
+ private FragmentActivity activity;
+ private FeatureDemo featureDemo;
+
+ TocViewHolder(FragmentActivity activity, ViewGroup viewGroup) {
+ super(
+ LayoutInflater.from(activity)
+ .inflate(R.layout.cat_toc_item, viewGroup, false /* attachToRoot */));
+
+ titleView = itemView.findViewById(R.id.cat_toc_title);
+ imageView = itemView.findViewById(R.id.cat_toc_image);
+ statusWipLabelView = itemView.findViewById(R.id.cat_toc_status_wip_label);
+ }
+
+ void bind(FragmentActivity activity, FeatureDemo featureDemo) {
+ this.activity = activity;
+ this.featureDemo = featureDemo;
+
+ titleView.setText(featureDemo.getTitleResId());
+ imageView.setImageResource(featureDemo.getDrawableResId());
+ itemView.setOnClickListener(clickListener);
+ statusWipLabelView.setVisibility(
+ featureDemo.getStatus() == FeatureDemo.STATUS_WIP ? View.VISIBLE : View.GONE);
+ }
+
+ private final OnClickListener clickListener =
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FeatureDemoUtils.startFragment(activity, featureDemo.createFragment(), FRAGMENT_CONTENT);
+ }
+ };
+}
diff --git a/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_fragment.xml b/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_fragment.xml
new file mode 100644
index 000000000..6042add88
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_fragment.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_header.xml b/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_header.xml
new file mode 100644
index 000000000..6dea5cf6a
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_header.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_item.xml b/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_item.xml
new file mode 100644
index 000000000..1bab627a6
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/res/layout/cat_toc_item.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tableofcontents/res/values/colors.xml b/catalog/java/io/material/catalog/tableofcontents/res/values/colors.xml
new file mode 100644
index 000000000..9cee71fed
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/res/values/colors.xml
@@ -0,0 +1,22 @@
+
+
+
+ #E0E0E0
+ @color/cat_toc_grid_divider_color
+ #FFFFFF
+ #E0E0E0
+
diff --git a/catalog/java/io/material/catalog/tableofcontents/res/values/dimens.xml b/catalog/java/io/material/catalog/tableofcontents/res/values/dimens.xml
new file mode 100644
index 000000000..ffc4f12be
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/res/values/dimens.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ 128dp
+ 128dp
+ 16dp
+ 1dp
+ 180dp
+
diff --git a/catalog/java/io/material/catalog/tableofcontents/res/values/strings.xml b/catalog/java/io/material/catalog/tableofcontents/res/values/strings.xml
new file mode 100644
index 000000000..1a26f63c5
--- /dev/null
+++ b/catalog/java/io/material/catalog/tableofcontents/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ Material Components
+
+ WIP
+
diff --git a/catalog/java/io/material/catalog/tabs/TabItemContentFragment.java b/catalog/java/io/material/catalog/tabs/TabItemContentFragment.java
new file mode 100644
index 000000000..3ac8c28a6
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/TabItemContentFragment.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tabs;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/** Fragment to display layout for each tab item in tabs demo for the Catalog app. */
+public class TabItemContentFragment extends Fragment {
+ private static final String TAB_NUMBER = "tab_number";
+
+ public TabItemContentFragment() {}
+
+ public static TabItemContentFragment newInstance(int tabNumber) {
+ TabItemContentFragment fragment = new TabItemContentFragment();
+ Bundle bundle = new Bundle();
+ bundle.putInt(TAB_NUMBER, tabNumber);
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ inflater.inflate(R.layout.tab_item_content_fragment, viewGroup, false /* attachToRoot */);
+ TextView textView = (TextView) view.findViewById(R.id.tab_number_textview);
+ int tabNumber = getArguments().getInt(TAB_NUMBER, -1);
+ textView.setText(
+ String.format(getContext().getString(R.string.cat_tab_item_content), tabNumber));
+ return view;
+ }
+}
diff --git a/catalog/java/io/material/catalog/tabs/TabsControllableDemoFragment.java b/catalog/java/io/material/catalog/tabs/TabsControllableDemoFragment.java
new file mode 100644
index 000000000..7480f5651
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/TabsControllableDemoFragment.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tabs;
+
+import io.material.catalog.R;
+
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import com.google.android.material.tabs.TabLayout;
+import android.support.v4.view.ViewPager;
+import android.support.v7.widget.SwitchCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.RadioButton;
+import android.widget.Spinner;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.DemoUtils;
+import java.util.List;
+
+/** The main fragment that displays tabs demos for the Catalog app. */
+public class TabsControllableDemoFragment extends DemoFragment {
+
+ private static final int TAB_COUNT = 3;
+ @DrawableRes private static final int ICON_DRAWABLE_RES = R.drawable.ic_tabs_24px;
+ @StringRes private static final int LABEL_STRING_RES = R.string.cat_tab_item_label;
+
+ private boolean showIcons = true;
+ private boolean showLabels = true;
+ private List tabLayouts;
+ private ViewPager pager;
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_tabs_controllable_fragment, viewGroup, false /* attachToRoot */);
+
+ ViewGroup content = view.findViewById(R.id.content);
+ View tabsContent = layoutInflater.inflate(getTabsContent(), content, false /* attachToRoot */);
+ content.addView(tabsContent, 0);
+
+ tabLayouts = DemoUtils.findViewsWithType(view, TabLayout.class);
+ pager = view.findViewById(R.id.viewpager);
+
+ setupViewPager();
+ setAllTabLayoutIcons(ICON_DRAWABLE_RES);
+ setAllTabLayoutText(LABEL_STRING_RES);
+
+ SwitchCompat iconsToggle = view.findViewById(R.id.toggle_icons_switch);
+ iconsToggle.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ showIcons = isChecked;
+ setAllTabLayoutIcons(ICON_DRAWABLE_RES);
+ }
+ });
+
+ SwitchCompat labelsToggle = view.findViewById(R.id.toggle_labels_switch);
+ labelsToggle.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ showLabels = isChecked;
+ setAllTabLayoutText(LABEL_STRING_RES);
+ }
+ });
+
+ RadioButton tabGravityFillButton = view.findViewById(R.id.tabs_gravity_fill_button);
+ tabGravityFillButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setAllTabLayoutGravity(TabLayout.GRAVITY_FILL);
+ }
+ });
+
+ RadioButton tabGravityCenterButton = view.findViewById(R.id.tabs_gravity_center_button);
+ tabGravityCenterButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setAllTabLayoutGravity(TabLayout.GRAVITY_CENTER);
+ }
+ });
+
+ SwitchCompat inlineToggle = view.findViewById(R.id.toggle_inline_switch);
+ inlineToggle.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setAllTabLayoutInline(isChecked);
+ }
+ });
+
+ Spinner selectedIndicatorSpinner = (Spinner) view.findViewById(R.id.selector_spinner);
+ ArrayAdapter adapter =
+ ArrayAdapter.createFromResource(
+ selectedIndicatorSpinner.getContext(),
+ getSelectedIndicatorDrawableTitles(),
+ android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ selectedIndicatorSpinner.setAdapter(adapter);
+
+ selectedIndicatorSpinner.setOnItemSelectedListener(
+ new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ setAllTabLayoutSelectedIndicators(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ setAllTabLayoutSelectedIndicators(0);
+ }
+ });
+
+ return view;
+ }
+
+ @ArrayRes
+ protected int getSelectedIndicatorDrawableTitles() {
+ return R.array.cat_tabs_selected_indicator_drawable_titles;
+ }
+
+ @ArrayRes
+ protected int getSelectedIndicatorDrawables() {
+ return R.array.cat_tabs_selected_indicator_drawables;
+ }
+
+ @ArrayRes
+ protected int getSelectedIndicatorDrawableGravities() {
+ return R.array.cat_tabs_selected_indicator_drawable_gravities;
+ }
+
+ @LayoutRes
+ protected int getTabsContent() {
+ return R.layout.cat_tabs_controllable_content;
+ }
+
+ private void setupViewPager() {
+ pager.setAdapter(new TabsPagerAdapter(getFragmentManager(), getContext(), TAB_COUNT));
+ for (TabLayout tabLayout : tabLayouts) {
+ tabLayout.setupWithViewPager(pager);
+ }
+ }
+
+ private void setAllTabLayoutIcons(@DrawableRes int iconResId) {
+ for (TabLayout tabLayout : tabLayouts) {
+ setTabLayoutIcons(tabLayout, iconResId);
+ }
+ }
+
+ private void setTabLayoutIcons(TabLayout tabLayout, @DrawableRes int iconResId) {
+ for (int i = 0; i < tabLayout.getTabCount(); i++) {
+ if (showIcons) {
+ tabLayout.getTabAt(i).setIcon(iconResId);
+ } else {
+ tabLayout.getTabAt(i).setIcon(null);
+ }
+ }
+ }
+
+ private void setAllTabLayoutText(@StringRes int stringResId) {
+ for (TabLayout tabLayout : tabLayouts) {
+ setTabLayoutText(tabLayout, stringResId);
+ }
+ }
+
+ private void setTabLayoutText(TabLayout tabLayout, @StringRes int stringResId) {
+ for (int i = 0; i < tabLayout.getTabCount(); i++) {
+ if (showLabels) {
+ // Convert tab index (zero-based) to readable tab label starting at 1.
+ tabLayout.getTabAt(i).setText(getResources().getString(stringResId, i + 1));
+ } else {
+ tabLayout.getTabAt(i).setText(null);
+ }
+ }
+ }
+
+ private void setAllTabLayoutGravity(int gravity) {
+ for (TabLayout tabLayout : tabLayouts) {
+ tabLayout.setTabGravity(gravity);
+ }
+ }
+
+ private void setAllTabLayoutInline(boolean inline) {
+ for (TabLayout tabLayout : tabLayouts) {
+ tabLayout.setInlineLabel(inline);
+ }
+ }
+
+ private void setAllTabLayoutSelectedIndicators(int position) {
+ TypedArray drawables = getResources().obtainTypedArray(getSelectedIndicatorDrawables());
+ @DrawableRes int drawableResId = drawables.getResourceId(position, 0);
+ drawables.recycle();
+
+ TypedArray drawableGravities =
+ getResources().obtainTypedArray(getSelectedIndicatorDrawableGravities());
+ int drawableGravity = drawableGravities.getInt(position, 0);
+ drawableGravities.recycle();
+
+ for (TabLayout tabLayout : tabLayouts) {
+ tabLayout.setSelectedTabIndicator(drawableResId);
+ tabLayout.setSelectedTabIndicatorGravity(drawableGravity);
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/tabs/TabsFragment.java b/catalog/java/io/material/catalog/tabs/TabsFragment.java
new file mode 100644
index 000000000..18f7eff25
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/TabsFragment.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tabs;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A landing fragment for the Catalog app that links to tabs demos. */
+public class TabsFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_tabs_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_tabs_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new TabsMainDemoFragment();
+ }
+ };
+ }
+
+ @Override
+ public List getAdditionalDemos() {
+ List additionalDemos = new ArrayList<>();
+ additionalDemos.add(
+ new Demo(R.string.cat_tabs_controllable_demo_title) {
+ @Override
+ public Fragment createFragment() {
+ return new TabsControllableDemoFragment();
+ }
+ });
+ additionalDemos.add(
+ new Demo(R.string.cat_tabs_scrollable_demo_title) {
+ @Override
+ public Fragment createFragment() {
+ return new TabsScrollableDemoFragment();
+ }
+ });
+ return additionalDemos;
+ }
+
+ /** The Dagger module for {@link TabsFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract TabsFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_tabs_title, R.drawable.ic_tabs_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new TabsFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/tabs/TabsMainDemoFragment.java b/catalog/java/io/material/catalog/tabs/TabsMainDemoFragment.java
new file mode 100644
index 000000000..2d5ba3844
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/TabsMainDemoFragment.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tabs;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays the main tabs demo for the Catalog app. */
+public class TabsMainDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ return layoutInflater.inflate(getTabsContent(), viewGroup, false /* attachToRoot */);
+ }
+
+ @LayoutRes
+ protected int getTabsContent() {
+ return R.layout.cat_tabs_main_content;
+ }
+}
diff --git a/catalog/java/io/material/catalog/tabs/TabsPagerAdapter.java b/catalog/java/io/material/catalog/tabs/TabsPagerAdapter.java
new file mode 100644
index 000000000..2efef9613
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/TabsPagerAdapter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tabs;
+
+import io.material.catalog.R;
+
+import android.content.Context;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+
+/** Pager adapter to control displaying of tab item pages in tabs demo for the Catalog app. */
+public class TabsPagerAdapter extends FragmentPagerAdapter {
+
+ private Context context;
+ private int numTabs;
+
+ public TabsPagerAdapter(FragmentManager manager, Context context, int numTabs) {
+ super(manager);
+ this.context = context;
+ this.numTabs = numTabs;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return TabItemContentFragment.newInstance(getReadableTabPosition(position));
+ }
+
+ @Override
+ public int getCount() {
+ return numTabs;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return String.format(
+ context.getString(R.string.cat_tab_item_label), getReadableTabPosition(position));
+ }
+
+ /**
+ * Convert zero-based numbering of tabs into readable numbering of tabs starting at 1.
+ *
+ * @param position - Zero-based tab position
+ * @return Readable tab position
+ */
+ private int getReadableTabPosition(int position) {
+ return position + 1;
+ }
+}
diff --git a/catalog/java/io/material/catalog/tabs/TabsScrollableDemoFragment.java b/catalog/java/io/material/catalog/tabs/TabsScrollableDemoFragment.java
new file mode 100644
index 000000000..c5a61fbb0
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/TabsScrollableDemoFragment.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.tabs;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays a scrollable tabs demo for the Catalog app. */
+public class TabsScrollableDemoFragment extends DemoFragment {
+
+ @Nullable
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ return layoutInflater.inflate(getTabsContent(), viewGroup, false /* attachToRoot */);
+ }
+
+ @LayoutRes
+ protected int getTabsContent() {
+ return R.layout.cat_tabs_scrollable_content;
+ }
+}
diff --git a/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_box_indicator.xml b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_box_indicator.xml
new file mode 100644
index 000000000..c8550d297
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_box_indicator.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_line_indicator.xml b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_line_indicator.xml
new file mode 100644
index 000000000..644112c8f
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_line_indicator.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_oval_indicator.xml b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_oval_indicator.xml
new file mode 100644
index 000000000..c9a923fc6
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_oval_indicator.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_pill_indicator.xml b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_pill_indicator.xml
new file mode 100644
index 000000000..d980d5505
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_pill_indicator.xml
@@ -0,0 +1,37 @@
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_rounded_line_indicator.xml b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_rounded_line_indicator.xml
new file mode 100644
index 000000000..63ff6bb89
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/drawable/cat_tabs_rounded_line_indicator.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_controllable_content.xml b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_controllable_content.xml
new file mode 100644
index 000000000..28e73ee5e
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_controllable_content.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_controllable_fragment.xml b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_controllable_fragment.xml
new file mode 100644
index 000000000..638b31005
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_controllable_fragment.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_main_content.xml b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_main_content.xml
new file mode 100644
index 000000000..3cfbd049a
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_main_content.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_scrollable_content.xml b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_scrollable_content.xml
new file mode 100644
index 000000000..08b28b849
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/layout/cat_tabs_scrollable_content.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/layout/tab_item_content_fragment.xml b/catalog/java/io/material/catalog/tabs/res/layout/tab_item_content_fragment.xml
new file mode 100644
index 000000000..d1a77a26e
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/layout/tab_item_content_fragment.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/values/arrays.xml b/catalog/java/io/material/catalog/tabs/res/values/arrays.xml
new file mode 100644
index 000000000..2a8f53b92
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/values/arrays.xml
@@ -0,0 +1,54 @@
+
+
+
+
+ - @drawable/cat_tabs_line_indicator
+ - @drawable/cat_tabs_line_indicator
+ - @drawable/cat_tabs_line_indicator
+ - @drawable/cat_tabs_pill_indicator
+ - @drawable/cat_tabs_oval_indicator
+ - @drawable/cat_tabs_box_indicator
+ - @drawable/cat_tabs_rounded_line_indicator
+
+
+
+ - @string/cat_tabs_selected_indicator_line_bottom
+ - @string/cat_tabs_selected_indicator_line_top
+ - @string/cat_tabs_selected_indicator_line_center
+ - @string/cat_tabs_selected_indicator_pill
+ - @string/cat_tabs_selected_indicator_oval
+ - @string/cat_tabs_selected_indicator_box
+ - @string/cat_tabs_selected_indicator_rounded_line
+
+
+
+
+ - 0
+
+ - 2
+
+ - 1
+
+ - 3
+
+ - 3
+
+ - 3
+
+ - 0
+
+
diff --git a/catalog/java/io/material/catalog/tabs/res/values/dimens.xml b/catalog/java/io/material/catalog/tabs/res/values/dimens.xml
new file mode 100644
index 000000000..272f2d59b
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/values/dimens.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ 16dp
+ 8dp
+
diff --git a/catalog/java/io/material/catalog/tabs/res/values/strings.xml b/catalog/java/io/material/catalog/tabs/res/values/strings.xml
new file mode 100644
index 000000000..b477d86af
--- /dev/null
+++ b/catalog/java/io/material/catalog/tabs/res/values/strings.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ Tabs
+
+ Tabs enable content organization at a high level, such as switching between views, data sets, or
+ functional aspects of an app.\n\nPresent tabs as a single row above their associated content.
+ Tab labels should succinctly describe the content within.\n\nTabs allow navigation between
+ different top-level groups of content, such as different sections of a newspaper, different
+ music genres, or different types of documents.
+
+ Tab %d
+ Swipe here - Tab %d
+ Show icons
+ Show labels
+ Gravity
+ Fill
+ Center
+ Inline
+ Indicator
+ Legacy Tabs
+ Material Tabs
+ Material Tabs (Colored)
+ Controllable Tabs Demo
+ Scrollable Tabs Demo
+
+
+ Line (bottom)
+ Line (top)
+ Line (center)
+ Pill
+ Oval
+ Box
+ Rounded line
+
+
+ All
+ Shopping
+ News
+ Maps
+ Images
+ Updates
+ Settings
+ Plugins
+ More
+
+
diff --git a/catalog/java/io/material/catalog/textfield/TextFieldControllableDemoFragment.java b/catalog/java/io/material/catalog/textfield/TextFieldControllableDemoFragment.java
new file mode 100644
index 000000000..a2fdf0bc8
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/TextFieldControllableDemoFragment.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.textfield;
+
+import io.material.catalog.R;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import com.google.android.material.textfield.TextInputLayout;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays the main text field demos for the Catalog app. */
+public class TextFieldControllableDemoFragment extends DemoFragment {
+ private int colorIndex = 0;
+ private int[] colors =
+ new int[] {
+ Color.BLUE, Color.RED, Color.GREEN, Color.DKGRAY,
+ };
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_textfield_controllable_fragment, viewGroup, false /* attachToRoot */);
+
+ // Initialize text inputs.
+ TextInputLayout textInputDemoBoxOutline = view.findViewById(R.id.text_input_demo_box_outline);
+
+ TextInputLayout textInputError = view.findViewById(R.id.text_input_error);
+ TextInputLayout textInputLabel = view.findViewById(R.id.text_input_label);
+ TextInputLayout textInputCounterMax = view.findViewById(R.id.text_input_counter_max);
+
+ // Initialize button for changing the outline box color.
+ Button changeColorButton = view.findViewById(R.id.button_change_color);
+ changeColorButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ textInputDemoBoxOutline.setBoxStrokeColor(getNextColor());
+ }
+ });
+
+ // Initialize button for toggling the error text visibility.
+ Button toggleErrorButton = view.findViewById(R.id.button_toggle_error);
+ toggleErrorButton.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (textInputDemoBoxOutline.getError() == null) {
+ if (textInputError.getEditText().length() == 0) {
+ textInputDemoBoxOutline.setError(
+ getResources().getString(R.string.cat_textfield_error));
+ } else {
+ textInputDemoBoxOutline.setError(textInputError.getEditText().getText());
+ }
+ toggleErrorButton.setText(
+ getResources().getString(R.string.cat_textfield_hide_error_text));
+ } else {
+ textInputDemoBoxOutline.setError(null);
+ toggleErrorButton.setText(
+ getResources().getString(R.string.cat_textfield_show_error_text));
+ }
+ }
+ });
+
+ // Initialize button for updating the label text.
+ view.findViewById(R.id.button_update_label_text)
+ .setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!checkTextInputIsNull(textInputLabel)) {
+ textInputDemoBoxOutline.setHint(textInputLabel.getEditText().getText());
+ }
+ }
+ });
+
+ // Initialize button for updating the error text.
+ view.findViewById(R.id.button_update_error_text)
+ .setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!checkTextInputIsNull(textInputError)) {
+ textInputDemoBoxOutline.setError(textInputError.getEditText().getText());
+ toggleErrorButton.setText(
+ getResources().getString(R.string.cat_textfield_hide_error_text));
+ }
+ }
+ });
+
+ // Initialize button for updating the counter max.
+ view.findViewById(R.id.button_counter_max)
+ .setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!checkTextInputIsNull(textInputCounterMax)) {
+ textInputDemoBoxOutline.setCounterMaxLength(
+ Integer.parseInt(textInputCounterMax.getEditText().getText().toString()));
+ }
+ }
+ });
+ return view;
+ }
+
+ private boolean checkTextInputIsNull(TextInputLayout textInputLayout) {
+ if (textInputLayout.getEditText().getText() == null
+ || textInputLayout.getEditText().length() == 0) {
+ textInputLayout.setError(
+ getResources().getString(R.string.cat_textfield_null_input_error_text));
+ return true;
+ }
+ textInputLayout.setError(null);
+ return false;
+ }
+
+ private int getNextColor() {
+ colorIndex = (colorIndex + 1) % colors.length;
+ return colors[colorIndex];
+ }
+}
diff --git a/catalog/java/io/material/catalog/textfield/TextFieldFragment.java b/catalog/java/io/material/catalog/textfield/TextFieldFragment.java
new file mode 100644
index 000000000..ed02ffc40
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/TextFieldFragment.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.textfield;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A landing fragment that links to text field demos for the Catalog app. */
+public class TextFieldFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_textfield_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_textfield_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new TextFieldMainDemoFragment();
+ }
+ };
+ }
+
+ @Override
+ public List getAdditionalDemos() {
+ List additionalDemos = new ArrayList<>();
+ additionalDemos.add(
+ new Demo(R.string.cat_textfield_controllable_demo_title) {
+ @Override
+ public Fragment createFragment() {
+ return new TextFieldControllableDemoFragment();
+ }
+ });
+ return additionalDemos;
+ }
+
+ /** The Dagger module for {@link TextFieldFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract TextFieldFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_textfield_title, R.drawable.ic_text_field_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new TextFieldFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/textfield/TextFieldMainDemoFragment.java b/catalog/java/io/material/catalog/textfield/TextFieldMainDemoFragment.java
new file mode 100644
index 000000000..be8c2222a
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/TextFieldMainDemoFragment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.textfield;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays the main text field demos for the Catalog app. */
+public class TextFieldMainDemoFragment extends DemoFragment {
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ return layoutInflater.inflate(getTextfieldsContent(), viewGroup, false /* attachToRoot */);
+ }
+
+ @LayoutRes
+ protected int getTextfieldsContent() {
+ return R.layout.cat_textfield_content;
+ }
+}
diff --git a/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_content.xml b/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_content.xml
new file mode 100644
index 000000000..da2ee101f
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_content.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_controllable_fragment.xml b/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_controllable_fragment.xml
new file mode 100644
index 000000000..62d42135a
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_controllable_fragment.xml
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_fragment.xml b/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_fragment.xml
new file mode 100644
index 000000000..c14fdf0bb
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/res/layout/cat_textfield_fragment.xml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/catalog/java/io/material/catalog/textfield/res/values/dimens.xml b/catalog/java/io/material/catalog/textfield/res/values/dimens.xml
new file mode 100644
index 000000000..ef91258f0
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/res/values/dimens.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ 16dp
+ 48dp
+
diff --git a/catalog/java/io/material/catalog/textfield/res/values/strings.xml b/catalog/java/io/material/catalog/textfield/res/values/strings.xml
new file mode 100644
index 000000000..5573e0df2
--- /dev/null
+++ b/catalog/java/io/material/catalog/textfield/res/values/strings.xml
@@ -0,0 +1,54 @@
+
+
+
+
+ Text Field
+ Controllable Text Field Demo
+
+ Text fields allow users to input, edit, and select text. Text fields typically reside in forms
+ but can appear in other places, like dialog boxes and search.
+
+
+ Filled text field
+ Dense filled text field
+
+ Dense filled password text field
+
+ Outline text field
+ Dense outline text field
+
+ Sample text field
+ Customize sample text field
+
+ Enter values below to customize the sample text field.
+
+ Label
+ Password
+ Error
+ Label text
+ Error text
+ Change color
+ Character counter max
+ Update label
+ Update error
+ Update character counter
+ Enter a value
+ Show error
+ Hide error
+ Disable
+ Enable
+
diff --git a/catalog/java/io/material/catalog/themeswitcher/ThemeOverlayUtils.java b/catalog/java/io/material/catalog/themeswitcher/ThemeOverlayUtils.java
new file mode 100644
index 000000000..083daf6bf
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/ThemeOverlayUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.themeswitcher;
+
+import android.app.Activity;
+import android.support.annotation.StyleRes;
+import java.util.Arrays;
+
+/** Utils for theme overlays. */
+public abstract class ThemeOverlayUtils {
+
+ @StyleRes private static int[] themeOverlays = new int[0];
+
+ public static void setThemeOverlays(Activity activity, @StyleRes int... themeOverlays) {
+ if (!Arrays.equals(ThemeOverlayUtils.themeOverlays, themeOverlays)) {
+ ThemeOverlayUtils.themeOverlays = themeOverlays;
+ activity.recreate();
+ }
+ }
+
+ public static void clearThemeOverlays(Activity activity) {
+ setThemeOverlays(activity);
+ }
+
+ @StyleRes
+ public static int[] getThemeOverlays() {
+ return themeOverlays;
+ }
+
+ public static void applyThemeOverlays(Activity activity) {
+ for (int themeOverlay : themeOverlays) {
+ activity.setTheme(themeOverlay);
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherDialogFragment.java b/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherDialogFragment.java
new file mode 100644
index 000000000..f25e50b4f
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherDialogFragment.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.themeswitcher;
+
+import io.material.catalog.R;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.ArrayRes;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.StyleRes;
+import android.support.annotation.StyleableRes;
+import android.support.v4.widget.CompoundButtonCompat;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import dagger.android.support.DaggerAppCompatDialogFragment;
+import javax.inject.Inject;
+
+/**
+ * Theme switcher dialog that allows the user to choose a primary or secondary palette to overlay
+ * above the app theme.
+ */
+public class ThemeSwitcherDialogFragment extends DaggerAppCompatDialogFragment {
+
+ @StyleableRes
+ private static final int[] PRIMARY_THEME_OVERLAY_ATTRS = {
+ R.attr.colorPrimary, R.attr.colorPrimaryLight, R.attr.colorPrimaryDark
+ };
+
+ @StyleableRes
+ private static final int[] SECONDARY_THEME_OVERLAY_ATTRS = {
+ R.attr.colorSecondary, R.attr.colorSecondaryLight, R.attr.colorSecondaryDark
+ };
+
+ @Inject ThemeSwitcherResourceProvider resourceProvider;
+ private RadioGroup primaryGroup;
+ private RadioGroup secondaryGroup;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle bundle) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder
+ .setTitle(R.string.mtrl_theme_switcher_title)
+ .setView(onCreateDialogView(getActivity().getLayoutInflater()))
+ .setPositiveButton(
+ R.string.mtrl_theme_switcher_save,
+ (dialog, id) -> {
+ dialog.dismiss();
+ applyThemeOverlays();
+ })
+ .setNegativeButton(R.string.mtrl_theme_switcher_cancel, null);
+ return builder.create();
+ }
+
+ private View onCreateDialogView(LayoutInflater layoutInflater) {
+ View view = layoutInflater.inflate(R.layout.mtrl_theme_switcher_dialog, null);
+
+ int[] currentThemeOverlays = ThemeOverlayUtils.getThemeOverlays();
+
+ primaryGroup = view.findViewById(R.id.primary_colors);
+ initializeColors(
+ primaryGroup,
+ resourceProvider.getPrimaryColors(),
+ resourceProvider.getPrimaryColorsContentDescription(),
+ PRIMARY_THEME_OVERLAY_ATTRS,
+ currentThemeOverlays.length >= 2 ? currentThemeOverlays[0] : 0);
+
+ secondaryGroup = view.findViewById(R.id.secondary_colors);
+ initializeColors(
+ secondaryGroup,
+ resourceProvider.getSecondaryColors(),
+ resourceProvider.getSecondaryColorsContentDescription(),
+ SECONDARY_THEME_OVERLAY_ATTRS,
+ currentThemeOverlays.length >= 2 ? currentThemeOverlays[1] : 0);
+
+ return view;
+ }
+
+ private void applyThemeOverlays() {
+ ColorPalette primaryPalette =
+ (ColorPalette) getDialog().findViewById(primaryGroup.getCheckedRadioButtonId()).getTag();
+ ColorPalette secondaryPalette =
+ (ColorPalette) getDialog().findViewById(secondaryGroup.getCheckedRadioButtonId()).getTag();
+
+ ThemeOverlayUtils.setThemeOverlays(
+ getActivity(), primaryPalette.themeOverlay, secondaryPalette.themeOverlay);
+ }
+
+ private void initializeColors(
+ RadioGroup group,
+ @ArrayRes int colors,
+ @ArrayRes int colorContentDescriptions,
+ @StyleableRes int[] themeOverlayAttrs,
+ @StyleRes int currentThemeOverlay) {
+ TypedArray colorsArray = getResources().obtainTypedArray(colors);
+ TypedArray contentDescriptionArray = getResources().obtainTypedArray(colorContentDescriptions);
+ if (colorsArray.length() != contentDescriptionArray.length()) {
+ throw new IllegalArgumentException(
+ "Color array length doesn't match its content description array length.");
+ }
+
+ for (int i = 0; i < colorsArray.length(); i++) {
+ @StyleRes int paletteThemeOverlay = colorsArray.getResourceId(i, 0);
+ ColorPalette palette = new ColorPalette(paletteThemeOverlay, themeOverlayAttrs);
+
+ RadioButton button = new RadioButton(getContext());
+ CompoundButtonCompat.setButtonTintList(
+ button, ColorStateList.valueOf(convertToDisplay(palette.main)));
+ button.setTag(palette);
+ button.setContentDescription(contentDescriptionArray.getString(i));
+
+ group.addView(button);
+
+ if (palette.themeOverlay == currentThemeOverlay || (i == 0 && currentThemeOverlay == 0)) {
+ group.check(button.getId());
+ }
+ }
+
+ colorsArray.recycle();
+ }
+
+ @ColorInt
+ private int convertToDisplay(@ColorInt int color) {
+ return color == Color.WHITE ? Color.BLACK : color;
+ }
+
+ private class ColorPalette {
+ @StyleRes private final int themeOverlay;
+
+ @ColorInt private final int main;
+ @ColorInt private final int light;
+ @ColorInt private final int dark;
+
+ @SuppressLint("ResourceType")
+ public ColorPalette(@StyleRes int themeOverlay, @StyleableRes int[] themeOverlayAttrs) {
+ this.themeOverlay = themeOverlay;
+
+ TypedArray a = getContext().obtainStyledAttributes(themeOverlay, themeOverlayAttrs);
+ main = a.getColor(0, Color.TRANSPARENT);
+ light = a.getColor(1, Color.TRANSPARENT);
+ dark = a.getColor(2, Color.TRANSPARENT);
+ a.recycle();
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherHelper.java b/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherHelper.java
new file mode 100644
index 000000000..dfa4b56ca
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.themeswitcher;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+/** Helper class for demos to support theme switcher functionality. */
+public class ThemeSwitcherHelper {
+ private final FragmentManager fragmentManager;
+ private final boolean enabled;
+
+ public ThemeSwitcherHelper(F fragment) {
+ fragmentManager = fragment.getFragmentManager();
+ enabled =
+ fragment.shouldShowDefaultDemoActionBar()
+ && fragment.getActivity() instanceof ThemeSwitcherActivity;
+
+ if (enabled) {
+ fragment.setHasOptionsMenu(true);
+ }
+ }
+
+ public ThemeSwitcherHelper(FragmentManager fragmentManager) {
+ this.fragmentManager = fragmentManager;
+ this.enabled = true;
+ }
+
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ if (enabled) {
+ menuInflater.inflate(R.menu.mtrl_theme_switcher_menu, menu);
+ }
+ }
+
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ if (enabled) {
+ if (menuItem.getItemId() == R.id.theme_switcher) {
+ showThemeSwitcher();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void showThemeSwitcher() {
+ new ThemeSwitcherDialogFragment().show(fragmentManager, "theme-switcher");
+ }
+
+ /** Implement this interface to whitelist an Activity for theme switcher support. */
+ public interface ThemeSwitcherActivity {}
+
+ /** Implement this interface to allow a Fragment to be used with {@link ThemeSwitcherHelper}. */
+ public interface ThemeSwitcherFragment {
+
+ /** If this is true, then the demo's default action bar comes with theme switcher support. */
+ boolean shouldShowDefaultDemoActionBar();
+ }
+}
diff --git a/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherResourceProvider.java b/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherResourceProvider.java
new file mode 100644
index 000000000..70a946f59
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/ThemeSwitcherResourceProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.themeswitcher;
+
+import io.material.catalog.R;
+
+import android.support.annotation.ArrayRes;
+
+/** A helper class that facilitates overriding of theme switcher resources in the Catalog app. */
+public class ThemeSwitcherResourceProvider {
+
+ @ArrayRes
+ public int getPrimaryColors() {
+ return R.array.mtrl_primary_palettes;
+ }
+
+ @ArrayRes
+ public int getSecondaryColors() {
+ return R.array.mtrl_secondary_palettes;
+ }
+
+ @ArrayRes
+ public int getPrimaryColorsContentDescription() {
+ return R.array.mtrl_palettes_content_description;
+ }
+
+ @ArrayRes
+ public int getSecondaryColorsContentDescription() {
+ return R.array.mtrl_palettes_content_description;
+ }
+}
diff --git a/catalog/java/io/material/catalog/themeswitcher/res/layout/mtrl_theme_switcher_dialog.xml b/catalog/java/io/material/catalog/themeswitcher/res/layout/mtrl_theme_switcher_dialog.xml
new file mode 100644
index 000000000..a341df0a1
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/res/layout/mtrl_theme_switcher_dialog.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/themeswitcher/res/menu/mtrl_theme_switcher_menu.xml b/catalog/java/io/material/catalog/themeswitcher/res/menu/mtrl_theme_switcher_menu.xml
new file mode 100644
index 000000000..b4caa1ca1
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/res/menu/mtrl_theme_switcher_menu.xml
@@ -0,0 +1,24 @@
+
+
+
diff --git a/catalog/java/io/material/catalog/themeswitcher/res/values/arrays.xml b/catalog/java/io/material/catalog/themeswitcher/res/values/arrays.xml
new file mode 100644
index 000000000..e9f928894
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/res/values/arrays.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+ - @style/ThemeOverlay.PrimaryPalette.Default
+ - @style/ThemeOverlay.PrimaryPalette.Red
+ - @style/ThemeOverlay.PrimaryPalette.Purple
+ - @style/ThemeOverlay.PrimaryPalette.Indigo
+ - @style/ThemeOverlay.PrimaryPalette.Blue
+ - @style/ThemeOverlay.PrimaryPalette.Green
+ - @style/ThemeOverlay.PrimaryPalette.Yellow
+ - @style/ThemeOverlay.PrimaryPalette.Orange
+ - @style/ThemeOverlay.PrimaryPalette.Brown
+ - @style/ThemeOverlay.PrimaryPalette.Grey
+ - @style/ThemeOverlay.PrimaryPalette.BlueGrey
+
+
+
+ - @style/ThemeOverlay.SecondaryPalette.Default
+ - @style/ThemeOverlay.SecondaryPalette.Red
+ - @style/ThemeOverlay.SecondaryPalette.Purple
+ - @style/ThemeOverlay.SecondaryPalette.Indigo
+ - @style/ThemeOverlay.SecondaryPalette.Blue
+ - @style/ThemeOverlay.SecondaryPalette.Green
+ - @style/ThemeOverlay.SecondaryPalette.Yellow
+ - @style/ThemeOverlay.SecondaryPalette.Orange
+ - @style/ThemeOverlay.SecondaryPalette.Brown
+ - @style/ThemeOverlay.SecondaryPalette.Grey
+ - @style/ThemeOverlay.SecondaryPalette.BlueGrey
+
+
+
+ - @string/mtrl_default_color
+ - @string/mtrl_red
+ - @string/mtrl_purple
+ - @string/mtrl_indigo
+ - @string/mtrl_blue
+ - @string/mtrl_green
+ - @string/mtrl_yellow
+ - @string/mtrl_orange
+ - @string/mtrl_brown
+ - @string/mtrl_grey
+ - @string/mtrl_blue_grey
+
+
+
diff --git a/catalog/java/io/material/catalog/themeswitcher/res/values/strings.xml b/catalog/java/io/material/catalog/themeswitcher/res/values/strings.xml
new file mode 100644
index 000000000..088bae179
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/res/values/strings.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ Theme Switcher
+ Save
+ Cancel
+
+ Default color
+ Red
+ Purple
+ Indigo
+ Blue
+ Green
+ Yellow
+ Orange
+ Brown
+ Grey
+ Blue grey
+
diff --git a/catalog/java/io/material/catalog/themeswitcher/res/values/themes_overlays_primary.xml b/catalog/java/io/material/catalog/themeswitcher/res/values/themes_overlays_primary.xml
new file mode 100644
index 000000000..b851c328e
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/res/values/themes_overlays_primary.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/themeswitcher/res/values/themes_overlays_secondary.xml b/catalog/java/io/material/catalog/themeswitcher/res/values/themes_overlays_secondary.xml
new file mode 100644
index 000000000..110be3ab4
--- /dev/null
+++ b/catalog/java/io/material/catalog/themeswitcher/res/values/themes_overlays_secondary.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingDemoFragment.java
new file mode 100644
index 000000000..93f2c77f6
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarCollapsingDemoFragment.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays a collapsing Top App Bar demo for the Catalog app. */
+public class TopAppBarCollapsingDemoFragment extends DemoFragment {
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(R.layout.cat_topappbar_collapsing_fragment, viewGroup, false);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+
+ return view;
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java
new file mode 100644
index 000000000..1a7b6373b
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarFragment.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A landing fragment that links to Top App Bar demos for the Catalog app. */
+public class TopAppBarFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_topappbar_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_topappbar_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarMainDemoFragment();
+ }
+ };
+ }
+
+ @Override
+ public List getAdditionalDemos() {
+ List additionalDemos = new ArrayList<>();
+ additionalDemos.add(
+ new Demo(R.string.cat_topappbar_scrolling_title) {
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarScrollingDemoFragment();
+ }
+ });
+ additionalDemos.add(
+ new Demo(R.string.cat_topappbar_collapsing_title) {
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarCollapsingDemoFragment();
+ }
+ });
+ return additionalDemos;
+ }
+
+ /** The Dagger module for {@link TopAppBarFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract TopAppBarFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(R.string.cat_topappbar_title, R.drawable.ic_topappbar_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new TopAppBarFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarMainDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarMainDemoFragment.java
new file mode 100644
index 000000000..8b6a36999
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarMainDemoFragment.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays the main Top App Bar demo for the Catalog app. */
+public class TopAppBarMainDemoFragment extends DemoFragment {
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(R.layout.cat_topappbar_fragment, viewGroup, false);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.cat_topappbar_menu, menu);
+ super.onCreateOptionsMenu(menu, menuInflater);
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java b/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java
new file mode 100644
index 000000000..12440724f
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/TopAppBarScrollingDemoFragment.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.topappbar;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.feature.DemoFragment;
+
+/** A fragment that displays a scrolling Top App Bar demo for the Catalog app. */
+public class TopAppBarScrollingDemoFragment extends DemoFragment {
+
+ @Override
+ public void onCreate(@Nullable Bundle bundle) {
+ super.onCreate(bundle);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view = layoutInflater.inflate(R.layout.cat_topappbar_scrolling_fragment, viewGroup, false);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.setSupportActionBar(toolbar);
+
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ menuInflater.inflate(R.menu.cat_topappbar_menu, menu);
+ super.onCreateOptionsMenu(menu, menuInflater);
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_fragment.xml
new file mode 100644
index 000000000..27720373e
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_collapsing_fragment.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_filler_text_view.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_filler_text_view.xml
new file mode 100644
index 000000000..63e22d1fd
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_filler_text_view.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_fragment.xml
new file mode 100644
index 000000000..815df8de4
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_fragment.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_scrolling_fragment.xml b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_scrolling_fragment.xml
new file mode 100644
index 000000000..224c90e46
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/layout/cat_topappbar_scrolling_fragment.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml b/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml
new file mode 100644
index 000000000..4f680eb5c
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/menu/cat_topappbar_menu.xml
@@ -0,0 +1,35 @@
+
+
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/values/dimens.xml b/catalog/java/io/material/catalog/topappbar/res/values/dimens.xml
new file mode 100644
index 000000000..a8de4ecc5
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/values/dimens.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ 192dp
+
diff --git a/catalog/java/io/material/catalog/topappbar/res/values/strings.xml b/catalog/java/io/material/catalog/topappbar/res/values/strings.xml
new file mode 100644
index 000000000..658cf1377
--- /dev/null
+++ b/catalog/java/io/material/catalog/topappbar/res/values/strings.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ Top App Bar
+ The Top App Bar, formerly known as the action bar in Android, is a special kind of toolbar that’s used for branding, navigation, search, and actions.
+ Scrolling Demo
+ Collapsing Demo
+
+
+ \t\tLorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.
+ \n\n\t\tSuspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor bibendum, vel congue leo egestas.
+ \n\n\t\tSed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque est.
+ \n\n\t\tAenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus convallis.
+ \n\n\t\tPhasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
+
+ Regular Title
+ Scrolling Title
+ Collapsing Title
+
+ Search
+ Favorite
+ Settings
+ Help & feedback
+
diff --git a/catalog/java/io/material/catalog/transformation/TransformationFragment.java b/catalog/java/io/material/catalog/transformation/TransformationFragment.java
new file mode 100644
index 000000000..c72d649cb
--- /dev/null
+++ b/catalog/java/io/material/catalog/transformation/TransformationFragment.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.transformation;
+
+import io.material.catalog.R;
+
+import android.support.v4.app.Fragment;
+import dagger.Provides;
+import dagger.android.ContributesAndroidInjector;
+import dagger.multibindings.IntoSet;
+import io.material.catalog.application.scope.ActivityScope;
+import io.material.catalog.application.scope.FragmentScope;
+import io.material.catalog.feature.Demo;
+import io.material.catalog.feature.DemoLandingFragment;
+import io.material.catalog.feature.FeatureDemo;
+
+/** A landing fragment that links to transformation demos for the Catalog app. */
+public class TransformationFragment extends DemoLandingFragment {
+
+ @Override
+ public int getTitleResId() {
+ return R.string.cat_transformation_title;
+ }
+
+ @Override
+ public int getDescriptionResId() {
+ return R.string.cat_transformation_description;
+ }
+
+ @Override
+ public Demo getMainDemo() {
+ return new Demo() {
+ @Override
+ public Fragment createFragment() {
+ return new TransformationMainDemoFragment();
+ }
+ };
+ }
+
+ /** The Dagger module for {@link TransformationFragment} dependencies. */
+ @dagger.Module
+ public abstract static class Module {
+
+ @FragmentScope
+ @ContributesAndroidInjector
+ abstract TransformationFragment contributeInjector();
+
+ @IntoSet
+ @Provides
+ @ActivityScope
+ static FeatureDemo provideFeatureDemo() {
+ return new FeatureDemo(
+ R.string.cat_transformation_title,
+ R.drawable.ic_animation_24px) {
+ @Override
+ public Fragment createFragment() {
+ return new TransformationFragment();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog/java/io/material/catalog/transformation/TransformationMainDemoFragment.java b/catalog/java/io/material/catalog/transformation/TransformationMainDemoFragment.java
new file mode 100644
index 000000000..b532f1e27
--- /dev/null
+++ b/catalog/java/io/material/catalog/transformation/TransformationMainDemoFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.material.catalog.transformation;
+
+import io.material.catalog.R;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import io.material.catalog.draggable.DraggableCoordinatorLayout;
+import io.material.catalog.feature.DemoFragment;
+import io.material.catalog.feature.OnBackPressedHandler;
+
+/** A fragment that displays the main transformation demos for the Catalog app. */
+public class TransformationMainDemoFragment extends DemoFragment implements OnBackPressedHandler {
+
+ private FloatingActionButton fab;
+
+ @Override
+ public View onCreateDemoView(
+ LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) {
+ View view =
+ layoutInflater.inflate(
+ R.layout.cat_transformation_fragment, viewGroup, false /* attachToRoot */);
+
+ Toolbar toolbar = view.findViewById(R.id.toolbar);
+ fab = view.findViewById(R.id.fab);
+ View sheet = view.findViewById(R.id.sheet);
+ View scrim = view.findViewById(R.id.scrim);
+
+ ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
+
+ fab.setOnClickListener(v -> fab.setExpanded(!fab.isExpanded()));
+ scrim.setOnClickListener(v -> fab.setExpanded(false));
+
+ DraggableCoordinatorLayout container = (DraggableCoordinatorLayout) view;
+ container.addDraggableChild(fab);
+ container.addDraggableChild(sheet);
+
+ return view;
+ }
+
+ @Override
+ public boolean shouldShowDefaultDemoActionBar() {
+ return false;
+ }
+
+ @Override
+ public boolean onBackPressed() {
+ if (fab.isExpanded()) {
+ fab.setExpanded(false);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/catalog/java/io/material/catalog/transformation/res/layout/cat_transformation_fragment.xml b/catalog/java/io/material/catalog/transformation/res/layout/cat_transformation_fragment.xml
new file mode 100644
index 000000000..fd2b12511
--- /dev/null
+++ b/catalog/java/io/material/catalog/transformation/res/layout/cat_transformation_fragment.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/catalog/java/io/material/catalog/transformation/res/values/strings.xml b/catalog/java/io/material/catalog/transformation/res/values/strings.xml
new file mode 100644
index 000000000..949c1da91
--- /dev/null
+++ b/catalog/java/io/material/catalog/transformation/res/values/strings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ Transformation
+
+ Material can make surfaces feel alive by multiplying and dividing them, and changing their shape
+ and size.
+
+ Close
+ FAB
+
diff --git a/docs/building-from-source.md b/docs/building-from-source.md
index 2a865a762..1355ba717 100644
--- a/docs/building-from-source.md
+++ b/docs/building-from-source.md
@@ -36,6 +36,7 @@ file.
## Useful Links
- [Getting Started](getting-started.md)
- [Contributing](contributing.md)
+- [Catalog App](catalog-app.md)
- [Class
documentation](https://developer.android.com/reference/com/google/android/material/classes)
- [MDC-Android on Stack
diff --git a/docs/catalog-app.md b/docs/catalog-app.md
new file mode 100644
index 000000000..dbfe966f2
--- /dev/null
+++ b/docs/catalog-app.md
@@ -0,0 +1,33 @@
+
+
+# Catalog App
+
+Featuring demos covering a large range of topics, the Material Components
+Catalog demonstrates how Material Design components and principles behave on
+real devices across different API levels. The Material Components Catalog also
+provides working code examples for developers.
+
+To try out the MDC Catalog app, you can either run the `catalog` module in
+Android Studio or run the following Gradle command:
+
+```sh
+./gradlew :catalog:installDebug
+```
+
+## Useful Links
+- [Getting Started](getting-started.md)
+- [Contributing](contributing.md)
+- [Building From Source](building-from-source.md)
+- [Class
+ documentation](https://developer.android.com/reference/com/google/android/material/classes)
+- [MDC-Android on Stack
+ Overflow](https://www.stackoverflow.com/questions/tagged/material-components+android)
+- [Android Developer’s
+ Guide](https://developer.android.com/training/material/index.html)
+- [Material.io](https://www.material.io)
+- [Material Design Guidelines](https://material.google.com)
diff --git a/docs/contributing.md b/docs/contributing.md
index 3645ea63b..b906bd31d 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -72,6 +72,7 @@ We follow the
## Useful Links
- [Getting Started](getting-started.md)
- [Building From Source](building-from-source.md)
+- [Catalog App](catalog-app.md)
- [Class
documentation](https://developer.android.com/reference/com/google/android/material/classes)
- [MDC-Android on Stack
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 72f4b09aa..4f065c3c1 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -161,6 +161,7 @@ the [directory structure](directorystructure.md) before getting started.
## Useful Links
- [Contributing](contributing.md)
- [Building From Source](building-from-source.md)
+- [Catalog App](catalog-app.md)
- [Class
documentation](https://developer.android.com/reference/com/google/android/material/classes)
- [MDC-Android on Stack
diff --git a/settings.gradle b/settings.gradle
index 9cb986d6a..5a51daab3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -12,6 +12,8 @@ include ':tests:javatests:com:google:android:material:animation'
// TODO: uncomment when backlayer is included in top-level build
//include ':tests:javatests:com:google:android:material:backlayer'
+include ':catalog'
+
include ':demos:java:io:material:demo:shrine'
include ':demos:java:io:material:demo:shrine:filters'
include ':demos:java:io:material:demo:shrine:products'