Merge "Add rotary reference app" into rvc-dev am: eba5ca372f

Change-Id: I1ecaad4b01ca0819f80c9c3e3cf48cc38d6eb164
diff --git a/RotaryPlayground/Android.bp b/RotaryPlayground/Android.bp
new file mode 100644
index 0000000..002feae
--- /dev/null
+++ b/RotaryPlayground/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_app {
+    name: "RotaryPlayground",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    sdk_version: "system_current",
+
+    static_libs: [
+        "car-apps-common",
+    ],
+
+    owner: "google",
+    certificate: "shared",
+
+    optimize: {
+        enabled: false,
+    }
+}
diff --git a/RotaryPlayground/AndroidManifest.xml b/RotaryPlayground/AndroidManifest.xml
new file mode 100644
index 0000000..4aac33f
--- /dev/null
+++ b/RotaryPlayground/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.car.rotaryplayground">
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/Theme.App">
+        <activity
+            android:name=".RotaryActivity"
+            android:label="@string/app_name"
+            android:allowEmbedded="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/RotaryPlayground/res/drawable/ic_launcher.png b/RotaryPlayground/res/drawable/ic_launcher.png
new file mode 100644
index 0000000..5d5dc1a
--- /dev/null
+++ b/RotaryPlayground/res/drawable/ic_launcher.png
Binary files differ
diff --git a/RotaryPlayground/res/drawable/ic_launcher_background.xml b/RotaryPlayground/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..1cb2f28
--- /dev/null
+++ b/RotaryPlayground/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillColor="#26A69A"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeColor="#33FFFFFF"
+        android:strokeWidth="0.8" />
+</vector>
diff --git a/RotaryPlayground/res/drawable/ic_launcher_foreground.xml b/RotaryPlayground/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..ca1f579
--- /dev/null
+++ b/RotaryPlayground/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportHeight="108"
+    android:viewportWidth="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeColor="#00000000"
+        android:strokeWidth="1" />
+</vector>
diff --git a/RotaryPlayground/res/layout/rotary_activity.xml b/RotaryPlayground/res/layout/rotary_activity.xml
new file mode 100644
index 0000000..bbe970c
--- /dev/null
+++ b/RotaryPlayground/res/layout/rotary_activity.xml
@@ -0,0 +1,33 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/rotary_menu"
+        android:layout_width="@dimen/menu_width"
+        android:layout_height="match_parent" />
+
+    <FrameLayout
+        android:id="@+id/rotary_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:layout_width="0dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/RotaryPlayground/res/layout/rotary_cards.xml b/RotaryPlayground/res/layout/rotary_cards.xml
new file mode 100644
index 0000000..bdfa549
--- /dev/null
+++ b/RotaryPlayground/res/layout/rotary_cards.xml
@@ -0,0 +1,157 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_margin="16dp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+        <EditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true">
+        </EditText>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <!-- A FocusArea with normal buttons with basic attributes. Upon nudging into this area,
+             the default focus should land on the first button in this container. Rotating the
+             rotary controller clockwise will move the focus from top to bottom.
+             Counterclockwise to move the focus from bottom to up. Nudge any direction to leave
+             this focus area. -->
+        <LinearLayout
+            android:id="@+id/card_normal"
+            android:background="@color/card_background_color"
+            android:layout_margin="16dp"
+            android:layout_width="300dp"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+        </LinearLayout>
+
+        <!-- A FocusArea that contains a button with a default focus attribute. Upon nudging into
+             this area, the focus will land on the button with the default focus attribute.
+             Rotating the rotary controller clockwise will move the focus from top to bottom.
+             Counterclockwise to move the focus from bottom to up. Nudge any direction to leave
+             this focus area. -->
+        <LinearLayout
+            android:id="@+id/card_with_default_focus"
+            android:background="@color/card_background_color"
+            android:layout_margin="16dp"
+            android:layout_width="300dp"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+            <!-- TODO(b/154180719): Make this button the default focus in this FocusArea -->
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button (Default)" />
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button" />
+        </LinearLayout>
+
+        <!-- A FocusArea that contains buttons with explicit ordering. Upon nudging into
+             this area, button_one will be focused. Rotating the rotary controller clockwise will
+             move the focus to button_two -> button_three -> button_four. Counterclockwise will
+             move the focus in the opposite order. Nudge any direction to leave this focus area. -->
+        <!-- TODO(b/154180719): Add explicit ordering to the buttons -->
+        <LinearLayout
+            android:id="@+id/card_with_explicit_order"
+            android:background="@color/card_background_color"
+            android:layout_margin="16dp"
+            android:layout_width="300dp"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+            <Button
+                android:id="@+id/button_one"
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button 1" />
+            <Button
+                android:id="@+id/button_three"
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button 3" />
+            <Button
+                android:id="@+id/button_two"
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button 2" />
+            <Button
+                android:id="@+id/button_four"
+                android:layout_width="match_parent"
+                android:layout_height="50dp"
+                android:onClick="onRotaryCardsButtonClick"
+                android:tag="test_button"
+                android:text="Button 4" />
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/RotaryPlayground/res/layout/rotary_menu.xml b/RotaryPlayground/res/layout/rotary_menu.xml
new file mode 100644
index 0000000..40181bb
--- /dev/null
+++ b/RotaryPlayground/res/layout/rotary_menu.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <Button
+        android:id="@+id/cards_example"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_marginTop="32dp"
+        android:text="Card Examples" />
+    <Button
+        android:id="@+id/menu_item_2"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_marginTop="32dp"
+        android:text="Menu Item 2" />
+    <Button
+        android:id="@+id/menu_item_3"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_marginTop="32dp"
+        android:text="Menu Item 3" />
+    <Button
+        android:id="@+id/menu_item_4"
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_marginTop="32dp"
+        android:text="Menu Item 4" />
+</LinearLayout>
diff --git a/RotaryPlayground/res/values/colors.xml b/RotaryPlayground/res/values/colors.xml
new file mode 100644
index 0000000..6636918
--- /dev/null
+++ b/RotaryPlayground/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+    <color name="card_background_color">#37393d</color>
+</resources>
\ No newline at end of file
diff --git a/RotaryPlayground/res/values/dimens.xml b/RotaryPlayground/res/values/dimens.xml
new file mode 100644
index 0000000..dece8ec
--- /dev/null
+++ b/RotaryPlayground/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+    <!-- Rotary Menu values -->
+    <dimen name="menu_width">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/RotaryPlayground/res/values/strings.xml b/RotaryPlayground/res/values/strings.xml
new file mode 100644
index 0000000..8fabdf9
--- /dev/null
+++ b/RotaryPlayground/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <string name="app_name" translatable="false">Rotary Playground</string>
+</resources>
diff --git a/RotaryPlayground/res/values/themes.xml b/RotaryPlayground/res/values/themes.xml
new file mode 100644
index 0000000..02a0be9
--- /dev/null
+++ b/RotaryPlayground/res/values/themes.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<resources>
+    <style name="Theme.App" parent="android:Theme.DeviceDefault">
+        <item name="android:buttonStyle">@style/ButtonStyle</item>
+    </style>
+
+    <style name="ButtonStyle" parent="android:Widget.Button">
+        <item name="android:background">@android:color/transparent</item>
+        <item name="android:textColor">@android:color/white</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryActivity.java b/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryActivity.java
new file mode 100644
index 0000000..1b44ac6
--- /dev/null
+++ b/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.rotaryplayground;
+
+import android.os.Bundle;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import android.text.Html;
+import android.text.Spanned;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+/**
+ * The main activity for Rotary Playground
+ *
+ * This activity starts the menu fragment and handles event calls (e.g. android:onClick) from
+ * elements in fragments.
+ */
+public class RotaryActivity extends FragmentActivity {
+
+    private static final String TAG = "RotaryActivity";
+    private static final String MESSAGE = "Hello there!";
+
+    private Fragment mMenuFragment = null;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.rotary_activity);
+        showMenuFragment();
+    }
+
+    /** Event handler for buttons in res/layout/rotary_cards.xml */
+    public void onRotaryCardsButtonClick(View v) {
+        showToast(MESSAGE);
+    }
+
+    private void showMenuFragment() {
+        if (mMenuFragment == null) {
+            mMenuFragment = new RotaryMenu();
+        }
+        getSupportFragmentManager().beginTransaction()
+                .replace(R.id.rotary_menu, mMenuFragment)
+                .commit();
+    }
+
+    private void showToast(String message) {
+        Log.d(TAG, "showToast -> " + message);
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+    }
+}
\ No newline at end of file
diff --git a/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryCards.java b/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryCards.java
new file mode 100644
index 0000000..28f34a1
--- /dev/null
+++ b/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryCards.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.rotaryplayground;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+/** Fragment to demo a layout with cards that are FocusArea containers. */
+public class RotaryCards extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.rotary_cards, container, false);
+        return view;
+    }
+}
diff --git a/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryMenu.java b/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryMenu.java
new file mode 100644
index 0000000..272317d
--- /dev/null
+++ b/RotaryPlayground/src/com/android/car/rotaryplayground/RotaryMenu.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.rotaryplayground;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+/**
+ * Fragment for the menu.
+ *
+ * On focus of a menu item, the associated fragment will start in the R.id.rotary_content container.
+ */
+public class RotaryMenu extends Fragment {
+
+    private Fragment mRotaryCards = null;
+
+    private Button mCardExamplesButton;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.rotary_menu, container, false);
+
+        mCardExamplesButton = view.findViewById(R.id.cards_example);
+        mCardExamplesButton.setOnFocusChangeListener((v, hasFocus) -> showRotaryCards(hasFocus));
+        mCardExamplesButton.setOnClickListener(v -> showRotaryCards(/* hasFocus= */ true));
+
+        return view;
+    }
+
+    private void showRotaryCards(boolean hasFocus) {
+        if (!hasFocus) {
+            return; // do nothing if no focus.
+        }
+        if (mRotaryCards == null) {
+            mRotaryCards = new RotaryCards();
+        }
+        showContent(mRotaryCards);
+    }
+
+    private void showContent(Fragment fragment) {
+        getFragmentManager().beginTransaction()
+                .replace(R.id.rotary_content, fragment)
+                .commit();
+    }
+}
diff --git a/readme.md b/readme.md
index f3b7ee8..6b13001 100644
--- a/readme.md
+++ b/readme.md
@@ -22,6 +22,17 @@
 6. Exit and re-open Android Auto
 7. TestMediaApp should now be visible (click headphones icon in phone app to see app picker)
 
+### RotaryPlayground
+
+RotaryPlayground is a test and reference application for the AAOS Rotary framework to use with an external rotary input device.
+
+To buid and install RotaryPlayground into an AAOS device:
+
+```
+$ make RotaryPlayground
+$ adb install -r -g out/target/[path]/system/app/RotaryPlayground/RotaryPlayground.apk
+```
+
 ## Contributing
 
 ### Workstation setup