Create Multi-display Test App.
- copy the main activity code from EmbeddedKitchenSinkApp
- copy the touch input test from EmbeddedKitchenSinkApp
- write IME input test (modifed from EmbeddedKitchenSinkApp's Carboard test); copy Watchdog from GarageModeTestApp to log the IME internal state in TextView; test for Input Connetion at EditText view
- write Draw input test to test for continous touch input (only supports single touch for now)
Bug: 131720357
Test: Multi-display setup and "lunch hawk_md-userdebug"
Change-Id: I52bdf1eff5c7febc04914fdf7e0ee3732808658b
diff --git a/tests/MultiDisplayTest/Android.mk b/tests/MultiDisplayTest/Android.mk
new file mode 100644
index 0000000..c5c7ce0
--- /dev/null
+++ b/tests/MultiDisplayTest/Android.mk
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := MultiDisplayTest
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_STATIC_ANDROID_LIBRARIES += \
+ androidx.lifecycle_lifecycle-livedata \
+ androidx.lifecycle_lifecycle-viewmodel \
+ androidx.car_car-cluster
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/MultiDisplayTest/AndroidManifest.xml b/tests/MultiDisplayTest/AndroidManifest.xml
new file mode 100644
index 0000000..cb294b3
--- /dev/null
+++ b/tests/MultiDisplayTest/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Declare the contents of this Android application. The namespace
+ attribute brings in the Android platform namespace, and the package
+ supplies a unique name for the application. When writing your
+ own application, the package name must be changed from "com.example.*"
+ to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.car.multidisplaytest">
+ <application android:label="MD Test">
+ <activity android:name="MDTest"
+ android:label="@string/app_title_always"
+ android:documentLaunchMode="always"
+ android:theme="@style/MainActivityTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/tests/MultiDisplayTest/res/drawable/border.xml b/tests/MultiDisplayTest/res/drawable/border.xml
new file mode 100644
index 0000000..088c31b
--- /dev/null
+++ b/tests/MultiDisplayTest/res/drawable/border.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:bottomRightRadius="5dp"
+ android:bottomLeftRadius="5dp"
+ android:topLeftRadius="5dp"
+ android:topRightRadius="5dp"/>
+ <stroke
+ android:width="2dip"
+ android:color="#212121" />
+</shape>
\ No newline at end of file
diff --git a/tests/MultiDisplayTest/res/drawable/ic_close_white.xml b/tests/MultiDisplayTest/res/drawable/ic_close_white.xml
new file mode 100644
index 0000000..4187755
--- /dev/null
+++ b/tests/MultiDisplayTest/res/drawable/ic_close_white.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/tests/MultiDisplayTest/res/layout/activity_main.xml b/tests/MultiDisplayTest/res/layout/activity_main.xml
new file mode 100644
index 0000000..e741c32
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/activity_main.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- We use this container to place kitchen app fragments. It insets the fragment contents -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/menu_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="10dp"
+ android:background="@android:color/background_light"
+ android:text="Hide Test Menu"
+ android:textSize="30sp"
+ android:padding="15dp"/>
+
+ <FrameLayout
+ android:id="@+id/menu_content"
+ android:layout_alignParentStart="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/menu_button"
+ android:visibility="gone"/>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/menu"
+ android:layout_below="@id/menu_button"
+ android:layout_alignParentStart="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</RelativeLayout>
diff --git a/tests/MultiDisplayTest/res/layout/draw.xml b/tests/MultiDisplayTest/res/layout/draw.xml
new file mode 100644
index 0000000..4bb1fa9
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/draw.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#000000"
+ android:orientation="vertical" >
+
+ <com.google.android.car.multidisplaytest.draw.CanvasView
+ android:id="@+id/drawCanvas"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <Button
+ android:id="@+id/clearButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|center"
+ android:text="Clear Canvas" />
+</FrameLayout>
diff --git a/tests/MultiDisplayTest/res/layout/input_type_test.xml b/tests/MultiDisplayTest/res/layout/input_type_test.xml
new file mode 100644
index 0000000..c23926f
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/input_type_test.xml
@@ -0,0 +1,613 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:weightSum="2">
+ <Button
+ style="@style/Button"
+ android:id="@+id/clearButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="clear"/>
+ </LinearLayout>
+
+ <LinearLayout
+ style="@style/SectionContainer"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout_weight="1">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fillViewport="true">
+ <TextView
+ android:id="@+id/ime_watchdog"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:text="@string/no_results"/>
+ </ScrollView>
+ </LinearLayout>
+
+ <ScrollView
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:id="@+id/inputViewGroup"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Test Input Focus:"/>
+
+ <EditText
+ android:id="@+id/testEditText"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="text"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Plain Text:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="text"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Date:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="date"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Date Time:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="datetime"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Number:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Number Decimal:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="numberDecimal"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Number Password:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="numberPassword"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Number Signed:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="numberSigned"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Auto Complete:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textAutoComplete"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Auto Correct:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textAutoCorrect"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Cap Characters:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textCapCharacters"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Cap Sentences:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textCapSentences"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Cap Words:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textCapWords"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Email Address:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textEmailAddress"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Email Subject:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textEmailSubject"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Filter:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textFilter"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="IME Multiline:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textImeMultiLine"
+ android:singleLine="false"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Long Message:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textLongMessage"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="MultiLine:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textMultiLine"
+ android:singleLine="false"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="No Suggestions:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textNoSuggestions"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Password:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textPassword"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Person Name:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textPersonName"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Phonetic:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textPhonetic"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Postal Address:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textPostalAddress"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Short Message:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textShortMessage"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="URI:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textUri"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Visible Password:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textVisiblePassword"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Web Edit Text:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textWebEditText"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Web Email Address:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textWebEmailAddress"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/inputTypeMarginLeft"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Web Password:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textWebPassword"
+ android:singleLine="true"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Phone:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="phone"
+ android:singleLine="true"/>
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="@dimen/inputTypeTextSize"
+ android:text="Time:"/>
+
+ <EditText
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="6"
+ android:inputType="time"
+ android:singleLine="true"/>
+ </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/list_item.xml b/tests/MultiDisplayTest/res/layout/list_item.xml
new file mode 100644
index 0000000..4f97c2c
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/list_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:paddingTop="2dip"
+ android:paddingBottom="3dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textSize="24sp" />
diff --git a/tests/MultiDisplayTest/res/layout/menu_item.xml b/tests/MultiDisplayTest/res/layout/menu_item.xml
new file mode 100644
index 0000000..be681b6
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/menu_item.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dp">
+
+ <Button
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="30sp"/>
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/present_fragment.xml b/tests/MultiDisplayTest/res/layout/present_fragment.xml
new file mode 100644
index 0000000..7f64456
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/present_fragment.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Demonstrates an activity that shows content on secondary displays using
+ the android.app.Presentation class.
+ See corresponding Java code PresentationActivity.java. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Message to show to use. -->
+ <TextView android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:gravity="center_vertical|center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/presentation_introduction"/>
+
+ <!-- A checkbox to toggle between showing all displays or only presentation displays. -->
+ <CheckBox android:id="@+id/show_all_displays"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/presentation_show_all_displays" />
+
+ <!-- List that will show information about all connected displays. -->
+ <ListView android:id="@+id/display_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/tests/MultiDisplayTest/res/layout/touch_points.xml b/tests/MultiDisplayTest/res/layout/touch_points.xml
new file mode 100644
index 0000000..f2b2e1f
--- /dev/null
+++ b/tests/MultiDisplayTest/res/layout/touch_points.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <com.google.android.car.multidisplaytest.touch.TouchPointView
+ android:id="@+id/touch_point_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</RelativeLayout>
diff --git a/tests/MultiDisplayTest/res/values/dimens.xml b/tests/MultiDisplayTest/res/values/dimens.xml
new file mode 100644
index 0000000..99a2f85
--- /dev/null
+++ b/tests/MultiDisplayTest/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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>
+ <dimen name="inputTypeMarginLeft">50dp</dimen>
+ <dimen name="inputTypeTextSize">24sp</dimen>
+ <dimen name="overview_icon_size">72dp</dimen>
+</resources>
diff --git a/tests/MultiDisplayTest/res/values/strings.xml b/tests/MultiDisplayTest/res/values/strings.xml
new file mode 100644
index 0000000..24fb4f9
--- /dev/null
+++ b/tests/MultiDisplayTest/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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_title_always" translatable="false">MDTest</string>
+ <string name="section_header_watchdog" translatable="false">Watchdog</string>
+ <string name="no_results" translatable="false">No Results</string>
+ <string name="presentation_introduction" translatable="false">Fragment that uses a
+ Presentation and the DisplayManager to show content on other Displays.\n
+ Selecting a Display will open a Presentation on it</string>
+ <string name="presentation_show_all_displays" translatable="false">Show all displays</string>
+</resources>
diff --git a/tests/MultiDisplayTest/res/values/styles.xml b/tests/MultiDisplayTest/res/values/styles.xml
new file mode 100644
index 0000000..c15c870
--- /dev/null
+++ b/tests/MultiDisplayTest/res/values/styles.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="OverviewButton">
+ <item name="android:layout_width">@dimen/overview_icon_size</item>
+ <item name="android:layout_height">@dimen/overview_icon_size</item>
+ <item name="android:padding">6dp</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:clickable">true</item>
+ </style>
+
+ <style name="MainActivityTheme" parent="android:Theme.DeviceDefault.NoActionBar">
+ </style>
+
+ <style name="SectionContainer">
+ <!-- Customize your theme here. -->
+ <item name="android:background">@drawable/border</item>
+ <item name="android:padding">3dp</item>
+ <item name="android:layout_margin">5dp</item>
+ </style>
+
+ <style name="Button">
+ <item name="android:layout_margin">5dp</item>
+ </style>
+
+</resources>
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
new file mode 100644
index 0000000..57e2834
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.car.multidisplaytest.draw.DrawTestFragment;
+import com.google.android.car.multidisplaytest.ime.InputTestFragment;
+import com.google.android.car.multidisplaytest.touch.TouchTestFragment;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Mostly copied from EmbeddedKitchenSinkApp with modifications on Fragments
+ */
+public class MDTest extends FragmentActivity {
+ private static final String TAG = MDTest.class.getSimpleName();
+ private FragmentManager mFragmentManager;
+ private Button mMenuButton;
+ private RecyclerView mMenu;
+ private View mMenuContent;
+
+ private interface ClickHandler {
+ void onClick();
+ }
+
+ private abstract class MenuEntry implements ClickHandler {
+ abstract String getText();
+ }
+
+ private final class FragmentMenuEntry<T extends Fragment> extends MenuEntry {
+ private final class MenuFragment<T extends Fragment> {
+ private final Class<T> mClazz;
+ private T mMenuFragment = null;
+
+ MenuFragment(Class<T> clazz) {
+ mClazz = clazz;
+ }
+
+ T getFragment() {
+ if (mMenuFragment == null) {
+ try {
+ mMenuFragment = mClazz.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ Log.e(TAG, "unable to create fragment", e);
+ }
+ }
+ return mMenuFragment;
+ }
+ }
+
+ private final String mText;
+ private final MenuFragment<T> mFragment;
+
+ FragmentMenuEntry(String text, Class<T> clazz) {
+ mText = text;
+ mFragment = new MenuFragment<>(clazz);
+ }
+
+ @Override
+ String getText() {
+ return mText;
+ }
+
+ @Override
+ public void onClick() {
+ Fragment fragment = mFragment.getFragment();
+ if (fragment != null) {
+ mFragmentManager.beginTransaction()
+ .replace(R.id.menu_content, fragment)
+ .commit();
+ // MDTest.this.showFragment(fragment);
+ toggleMenuVisibility();
+ } else {
+ Log.e(TAG, "cannot show fragment for " + getText());
+ }
+ }
+ }
+
+ // list of test fragments
+ private final List<MenuEntry> mMenuEntries = Arrays.asList(
+ new FragmentMenuEntry("Touch test", TouchTestFragment.class),
+ new FragmentMenuEntry("IME test", InputTestFragment.class),
+ new FragmentMenuEntry("Draw test", DrawTestFragment.class)
+ );
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mMenuContent = findViewById(R.id.menu_content);
+
+ mMenu = findViewById(R.id.menu);
+ mMenu.setAdapter(new MenuAdapter(this));
+ mMenu.setLayoutManager(new GridLayoutManager(this, 3));
+
+ mMenuButton = findViewById(R.id.menu_button);
+ mMenuButton.setOnClickListener(view -> toggleMenuVisibility());
+ Log.i(TAG, "Creating MDTest activity view");
+ mFragmentManager = MDTest.this.getSupportFragmentManager();
+ onNewIntent(getIntent());
+ }
+
+ private void toggleMenuVisibility() {
+ boolean menuVisible = mMenu.getVisibility() == View.VISIBLE;
+ mMenu.setVisibility(menuVisible ? View.GONE : View.VISIBLE);
+ mMenuContent.setVisibility(menuVisible ? View.VISIBLE : View.GONE);
+ mMenuButton.setText(menuVisible ? "Show Test Menu" : "Hide Test Menu");
+ }
+
+ private final class MenuAdapter extends RecyclerView.Adapter<ItemViewHolder> {
+ private final LayoutInflater mLayoutInflator;
+
+ MenuAdapter(Context context) {
+ mLayoutInflator = LayoutInflater.from(context);
+ }
+
+ @Override
+ public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = mLayoutInflator.inflate(R.layout.menu_item, parent, false);
+ return new ItemViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ItemViewHolder holder, int position) {
+ holder.mTitle.setText(mMenuEntries.get(position).getText());
+ holder.mTitle.setOnClickListener(v -> mMenuEntries.get(position).onClick());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mMenuEntries.size();
+ }
+ }
+
+ private final class ItemViewHolder extends RecyclerView.ViewHolder {
+ private TextView mTitle;
+
+ ItemViewHolder(View itemView) {
+ super(itemView);
+ mTitle = itemView.findViewById(R.id.title);
+ }
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/CanvasView.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/CanvasView.java
new file mode 100644
index 0000000..594f654
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/CanvasView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest.draw;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class CanvasView extends View {
+ private static final float TOLERANCE = 5.0f;
+
+ private final Paint mPaint;
+ private final Path mPath;
+ private float mX;
+ private float mY;
+
+ public CanvasView(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ mPath = new Path();
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeJoin(Paint.Join.ROUND);
+ mPaint.setStrokeWidth(4f);
+ mPaint.setColor(Color.WHITE);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ downTouch(x, y);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ moveTouch(x, y);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_UP:
+ upTouch();
+ invalidate();
+ break;
+ }
+ return true;
+ }
+
+ public void clearCanvas() {
+ mPath.reset();
+ invalidate();
+ }
+
+ private void moveTouch(float x, float y) {
+ float dx = Math.abs(x - mX);
+ float dy = Math.abs(y - mY);
+ if (dx >= TOLERANCE || dy >= TOLERANCE) {
+ // use mid-point as the control point of the bezier curve to make a smooth line
+ // eg. when forming curve between three non-aligned consecutive points
+ mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
+ mX = x;
+ mY = y;
+ }
+ }
+
+ private void downTouch(float x, float y) {
+ mPath.moveTo(x, y);
+ mX = x;
+ mY = y;
+ }
+
+ private void upTouch() {
+ mPath.lineTo(mX, mY);
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/DrawTestFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/DrawTestFragment.java
new file mode 100644
index 0000000..b6c322a
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/draw/DrawTestFragment.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest.draw;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.multidisplaytest.R;
+
+public class DrawTestFragment extends Fragment {
+ private CanvasView mCanvas;
+ private Button mClearButton;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ View view = inflater.inflate(R.layout.draw, container, false);
+
+ mCanvas = view.findViewById(R.id.drawCanvas);
+ mClearButton = view.findViewById(R.id.clearButton);
+ setClearButtonListener();
+
+ return view;
+ }
+
+ private void setClearButtonListener() {
+ mClearButton.setOnClickListener(view -> mCanvas.clearCanvas());
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/ime/InputTestFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/ime/InputTestFragment.java
new file mode 100644
index 0000000..05751d6
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/ime/InputTestFragment.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest.ime;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.multidisplaytest.R;
+
+/**
+ * Modified from GarageModeTestApp;
+ * Including coping Watchdog.java and Logger.java
+ */
+public class InputTestFragment extends Fragment {
+ private static final String TAG = InputTestFragment.class.getSimpleName();
+
+ private Button mClearButton;
+ private EditText mTestEditText;
+ private InputMethodManager mInputManager;
+ private InputConnection mInputConnection;
+ private TextView mWatchdogTextView;
+ private ViewGroup mInputViewGroup;
+ private Watchdog mWatchdog;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ View view = inflater.inflate(R.layout.input_type_test, container, false);
+ setViewsFromFragment(view);
+ setListners();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ Log.d(TAG, "Resuming watchdog");
+
+ mWatchdog = new Watchdog(mWatchdogTextView);
+ mWatchdog.start();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ Log.d(TAG, "Pausing watchdog");
+
+ if (mWatchdog != null) {
+ mWatchdog.stop();
+ mWatchdog = null;
+ }
+ }
+
+ private void setViewsFromFragment(View view) {
+ mWatchdogTextView = view.findViewById(R.id.ime_watchdog);
+ mInputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ mInputViewGroup = view.findViewById(R.id.inputViewGroup);
+ mClearButton = view.findViewById(R.id.clearButton);
+ // Log this EditText view's input focus to test for input connection with IME
+ mTestEditText = view.findViewById(R.id.testEditText);
+ }
+
+ private void setListners() {
+ mClearButton.setOnClickListener(view -> onClearButtonClick());
+ mInputViewGroup.setOnTouchListener((view, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (mWatchdog != null) {
+ boolean activeState = mInputManager.isActive();
+ boolean acceptingState = mInputManager.isAcceptingText();
+ String logMessage = String.format("IME states: Active - %b, AcceptingText - %b",
+ activeState, acceptingState);
+ mWatchdog.logEvent(logMessage);
+ }
+ }
+ return true;
+ });
+
+ mTestEditText.setOnFocusChangeListener((view, hasFocus) -> {
+ if (mWatchdog != null) {
+ if (hasFocus) {
+ mWatchdog.logEvent("EditText view has input connection with IME");
+ } else {
+ mWatchdog.logEvent("EditText view doesn't have input connection with IME");
+ }
+ }
+ });
+ }
+
+ private void onClearButtonClick() {
+ if (mWatchdog != null) {
+ mWatchdog.logEvent("Clear botton test...");
+ mWatchdog.start();
+ }
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/ime/Watchdog.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/ime/Watchdog.java
new file mode 100644
index 0000000..80d63ee
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/ime/Watchdog.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest.ime;
+
+import android.os.Handler;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * Copied from GarageModeTestApp
+ * Use Handler to send and process messages in a queue
+ * Potentially use for multi-thread
+ */
+public final class Watchdog {
+ private static final String TAG = Watchdog.class.getSimpleName();
+ // wait before trying to get new message from the queue and post it
+ private static final Integer DELAY = 500; // in millisecond
+ private static final Integer MAXQSIZE = 10000;
+
+ private final TextView mView;
+ private final ArrayList<String> mEvents;
+
+ private Handler mWatchdogHandler;
+ private Runnable mRefreshLoop;
+
+ Watchdog(TextView view) {
+ mView = view;
+ mEvents = new ArrayList<>();
+ }
+
+ public void logEvent(String s) {
+ Date date = new Date();
+ SimpleDateFormat dateFormat = new SimpleDateFormat("[yyyy-MM-dd hh:mm:ss]");
+ mEvents.add(0, dateFormat.format(date) + " " + s);
+
+ if (mEvents.size() > MAXQSIZE) {
+ mEvents.remove(mEvents.size() - 1);
+ }
+ }
+
+ public synchronized void refresh() {
+ mView.setText(String.join("\n", mEvents));
+ }
+
+ public void start() {
+ Log.d(TAG, "Starting Watchdog");
+ mEvents.clear();
+ mWatchdogHandler = new Handler();
+ mRefreshLoop = () -> {
+ refresh();
+ mWatchdogHandler.postDelayed(mRefreshLoop, DELAY);
+ };
+ mWatchdogHandler.postDelayed(mRefreshLoop, DELAY);
+ }
+
+ public void stop() {
+ Log.d(TAG, "Stopping Watchdog");
+ mWatchdogHandler.removeCallbacks(mRefreshLoop);
+ mWatchdogHandler = null;
+ mRefreshLoop = null;
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/touch/TouchPointView.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/touch/TouchPointView.java
new file mode 100644
index 0000000..77980d0
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/touch/TouchPointView.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest.touch;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Copied from EmbeddedKitchenSinkApp
+ * Show touch points as circles; allow multiple touch points
+ * Set LOG_ONLY = false to test on physical device.
+ */
+public class TouchPointView extends View {
+ private static final String TAG = TouchPointView.class.getSimpleName();
+ private static final boolean LOG_ONLY = false;
+ private static final int[] COLORS = {
+ Color.RED,
+ Color.GREEN,
+ Color.BLUE,
+ Color.YELLOW,
+ Color.MAGENTA,
+ Color.BLACK,
+ Color.DKGRAY
+ };
+
+ private final List<Finger> mFingers;
+ private final Paint mPaint;
+
+ public TouchPointView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TouchPointView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mFingers = new ArrayList<Finger>();
+
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (LOG_ONLY) {
+ logTouchEvents(event);
+ return true;
+ }
+ mFingers.clear();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ invalidate();
+ return true;
+ }
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int pointerId = event.getPointerId(i);
+ int pointerIndex = event.findPointerIndex(pointerId);
+ Finger finger = new Finger();
+ finger.point = new Point((int) event.getX(pointerIndex),
+ (int) event.getY(pointerIndex));
+ finger.pointerId = pointerId;
+
+ mFingers.add(finger);
+ }
+ invalidate();
+ return true;
+ }
+
+ private void logTouchEvents(MotionEvent event) {
+ if (event.getActionMasked() != MotionEvent.ACTION_UP) {
+ return;
+ }
+
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int pointerId = event.getPointerId(i);
+ int pointerIndex = event.findPointerIndex(pointerId);
+ long downTime = event.getDownTime();
+ long eventTime = event.getEventTime();
+ Log.d(TAG, "TouchUp [x=" + event.getX(pointerIndex) + ", y=" + event.getY(pointerIndex)
+ + " , pointerId=" + pointerId + ", pointerIndex=" + pointerIndex + ", duration="
+ + (eventTime - downTime) + "]");
+ }
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ if (LOG_ONLY) {
+ return;
+ }
+ int radius = canvas.getWidth() / 100;
+ for (Finger finger: mFingers) {
+ Point point = finger.point;
+ int color = COLORS[finger.pointerId % COLORS.length];
+ mPaint.setColor(color);
+ canvas.drawCircle(point.x, point.y, radius, mPaint);
+ }
+ }
+
+ private static final class Finger {
+ public Point point;
+ public int pointerId;
+ }
+}
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/touch/TouchTestFragment.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/touch/TouchTestFragment.java
new file mode 100644
index 0000000..5341435
--- /dev/null
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/touch/TouchTestFragment.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.google.android.car.multidisplaytest.touch;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.multidisplaytest.R;
+
+public class TouchTestFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ View view = inflater.inflate(R.layout.touch_points, container, false);
+
+ return view;
+ }
+}