Provide interfaces from car-ui-lib to support IME wide screen

Add test cases to paintbooth

Bug: 162268101
Bug: 170343703
Test: Manual
Change-Id: Ie84ef9901756fb06292fee423a0bdb721df4a6c4
Merged-In: Ie84ef9901756fb06292fee423a0bdb721df4a6c4
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/AlertDialogBuilder.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/AlertDialogBuilder.java
index 803bb29..e56fa6b 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/AlertDialogBuilder.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/AlertDialogBuilder.java
@@ -15,12 +15,19 @@
  */
 package com.android.car.ui;
 
+import static android.view.WindowInsets.Type.ime;
+
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TITLE_TO_CONTENT_AREA;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TO_CONTENT_AREA;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_ACTION;
+
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.text.InputFilter;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -28,6 +35,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.EditText;
 import android.widget.ImageView;
@@ -62,6 +70,34 @@
     private boolean mIconTinted;
     private boolean mAllowDismissButton = true;
     private boolean mHasSingleChoiceBodyButton = false;
+    private EditText mCarUiEditText;
+    private InputMethodManager mInputMethodManager;
+    private String mWideScreenTitle;
+    private String mWideScreenTitleDesc;
+    private ViewGroup mRoot;
+
+    // Whenever the IME is closed and opened again, the title and desc information needs to be
+    // passed to the IME to be rendered. If the information is not passed to the IME the content
+    // area of the IME will render nothing into the content area.
+    private final View.OnApplyWindowInsetsListener mOnApplyWindowInsetsListener = (v, insets) -> {
+        if (insets.isVisible(ime())) {
+            Bundle bundle = new Bundle();
+            String title = mWideScreenTitle != null ? mWideScreenTitle : mTitle.toString();
+            bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, title);
+            if (mWideScreenTitleDesc != null) {
+                bundle.putString(ADD_DESC_TO_CONTENT_AREA, mWideScreenTitleDesc);
+            }
+            mInputMethodManager.sendAppPrivateCommand(mCarUiEditText, WIDE_SCREEN_ACTION,
+                    bundle);
+        }
+        return insets;
+    };
+
+    private final AlertDialog.OnDismissListener mOnDismissListener = dialog -> {
+        if (mRoot != null) {
+            mRoot.setOnApplyWindowInsetsListener(null);
+        }
+    };
 
     public AlertDialogBuilder(Context context) {
         // Resource id specified as 0 uses the parent contexts resolved value for alertDialogTheme.
@@ -70,6 +106,8 @@
 
     public AlertDialogBuilder(Context context, int themeResId) {
         mBuilder = new AlertDialog.Builder(context, themeResId);
+        mInputMethodManager = (InputMethodManager)
+                context.getSystemService(Context.INPUT_METHOD_SERVICE);
         mContext = context;
     }
 
@@ -562,7 +600,6 @@
      * dismissed when an item is clicked. It will only be dismissed if clicked on a
      * button, if no buttons are supplied it's up to the user to dismiss the dialog.
      * @return This Builder object to allow for chaining of calls to set methods
-     *
      * @deprecated Use {@link #setSingleChoiceItems(CarUiRadioButtonListItemAdapter)} instead.
      */
     @Deprecated
@@ -619,19 +656,19 @@
         View contentView = LayoutInflater.from(mContext).inflate(
                 R.layout.car_ui_alert_dialog_edit_text, null);
 
-        EditText editText = CarUiUtils.requireViewByRefId(contentView, R.id.textbox);
-        editText.setText(prompt);
+        mCarUiEditText = CarUiUtils.requireViewByRefId(contentView, R.id.textbox);
+        mCarUiEditText.setText(prompt);
 
         if (textChangedListener != null) {
-            editText.addTextChangedListener(textChangedListener);
+            mCarUiEditText.addTextChangedListener(textChangedListener);
         }
 
         if (inputFilters != null) {
-            editText.setFilters(inputFilters);
+            mCarUiEditText.setFilters(inputFilters);
         }
 
         if (inputType != 0) {
-            editText.setInputType(inputType);
+            mCarUiEditText.setInputType(inputType);
         }
 
         mBuilder.setView(contentView);
@@ -668,6 +705,19 @@
         return this;
     }
 
+    /**
+     * Sets the title and desc related to the dialog within the IMS templates.
+     *
+     * @param title title to be set.
+     * @param desc description related to the dialog.
+     * @return this Builder object to allow for chaining of calls to set methods
+     */
+    public AlertDialogBuilder setEditTextTitleAndDescForWideScreen(String title, String desc) {
+        mWideScreenTitle = title;
+        mWideScreenTitleDesc = desc;
+
+        return this;
+    }
 
     /** Final steps common to both {@link #create()} and {@link #show()} */
     private void prepareDialog() {
@@ -723,9 +773,13 @@
         // wrap-around. Android will focus on the first view automatically when the dialog is shown,
         // and we want it to focus on the title instead of the FocusParkingView, so we put the
         // FocusParkingView at the end of dialog window.
-        ViewGroup root = (ViewGroup) alertDialog.getWindow().getDecorView().getRootView();
+        mRoot = (ViewGroup) alertDialog.getWindow().getDecorView().getRootView();
         FocusParkingView fpv = new FocusParkingView(mContext);
-        root.addView(fpv);
+        mRoot.addView(fpv);
+
+        // apply window insets listener to know when IME is visible so we can set title and desc.
+        mRoot.setOnApplyWindowInsetsListener(mOnApplyWindowInsetsListener);
+        setOnDismissListener(mOnDismissListener);
 
         return alertDialog;
     }
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/CarUiEditText.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/CarUiEditText.java
new file mode 100644
index 0000000..f451c1e
--- /dev/null
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/CarUiEditText.java
@@ -0,0 +1,121 @@
+/*
+ * 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.ui;
+
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_ITEM_ID;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SECONDARY_IMAGE_ID;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_CLEAR_DATA_ACTION;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Edit text supporting the callbacks from the IMS.This will be useful in widescreen IME mode to
+ * notify apps of specific action through Interface.
+ */
+public class CarUiEditText extends EditText {
+    /**
+     * Interface for {@link CarUiEditText} to support different actions and callbacks from IME
+     * when running in wide screen mode.
+     */
+    public interface PrivateImeCommandCallback {
+        /**
+         * Called when user clicks on an item in the search results.
+         *
+         * @param itemId the id of the item clicked. This will be the same id that was passed by the
+         *               application to the IMS template to display search results.
+         */
+        void onItemClicked(String itemId);
+
+        /**
+         * Called when user clicks on a secondary image within item in the search results.
+         *
+         * @param secondaryImageId the id of the secondary image clicked. This will be the same id
+         *                         that was passed by the application to the IMS template to display
+         *                         search results.
+         */
+        void onSecondaryImageClicked(String secondaryImageId);
+    }
+
+    private final Set<PrivateImeCommandCallback> mPrivateImeCommandCallback = new HashSet<>();
+
+    public CarUiEditText(Context context) {
+        super(context);
+    }
+
+    public CarUiEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CarUiEditText(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CarUiEditText(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public boolean onPrivateIMECommand(String action, Bundle data) {
+
+        if (WIDE_SCREEN_CLEAR_DATA_ACTION.equals(action)) {
+            // clear the text.
+            setText("");
+        }
+
+        if (data == null || mPrivateImeCommandCallback == null) {
+            return false;
+        }
+
+        if (data.getString(SEARCH_RESULT_ITEM_ID) != null) {
+            for (PrivateImeCommandCallback listener : mPrivateImeCommandCallback) {
+                listener.onItemClicked(data.getString(SEARCH_RESULT_ITEM_ID));
+            }
+        }
+
+        if (data.getString(SEARCH_RESULT_SECONDARY_IMAGE_ID) != null) {
+            for (PrivateImeCommandCallback listener : mPrivateImeCommandCallback) {
+                listener.onSecondaryImageClicked(
+                        data.getString(SEARCH_RESULT_SECONDARY_IMAGE_ID));
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Registers a new {@link PrivateImeCommandCallback} to the list of
+     * listeners.
+     */
+    public void registerOnPrivateImeCommandListener(PrivateImeCommandCallback listener) {
+        mPrivateImeCommandCallback.add(listener);
+    }
+
+    /**
+     * Unregisters an existing {@link PrivateImeCommandCallback} from the list
+     * of listeners.
+     */
+    public boolean unregisterOnPrivateImeCommandListener(PrivateImeCommandCallback listener) {
+        return mPrivateImeCommandCallback.remove(listener);
+    }
+}
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/imewidescreen/CarUiImeWideScreenController.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/imewidescreen/CarUiImeWideScreenController.java
index c6905e2..9856019 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/imewidescreen/CarUiImeWideScreenController.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/imewidescreen/CarUiImeWideScreenController.java
@@ -94,7 +94,7 @@
     public static final String WIDE_SCREEN_ACTION = "automotive_wide_screen";
     // Action name of action that will be used by IMS to notify the application to clear the data
     // in the EditText.
-    private static final String WIDE_SCREEN_CLEAR_DATA_ACTION = "automotive_wide_screen_clear_data";
+    public static final String WIDE_SCREEN_CLEAR_DATA_ACTION = "automotive_wide_screen_clear_data";
     // Key to provide the resource id for the icon that will be displayed in the input area. If
     // this is not provided applications icon will be used. Value format is int.
     public static final String WIDE_SCREEN_EXTRACTED_TEXT_ICON_RES_ID =
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/SearchView.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/SearchView.java
index 9506fe1..8bf0fee 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/SearchView.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/SearchView.java
@@ -15,10 +15,20 @@
  */
 package com.android.car.ui.toolbar;
 
+import static android.view.WindowInsets.Type.ime;
+
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_ITEM_ID;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_PRIMARY_IMAGE_RES_ID_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SECONDARY_IMAGE_ID;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SECONDARY_IMAGE_RES_ID_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SUB_TITLE_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_TITLE_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_ACTION;
 import static com.android.car.ui.utils.CarUiUtils.requireViewByRefId;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -34,9 +44,15 @@
 import androidx.annotation.NonNull;
 import androidx.constraintlayout.widget.ConstraintLayout;
 
+import com.android.car.ui.CarUiEditText;
 import com.android.car.ui.R;
+import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.recyclerview.CarUiListItem;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -50,6 +66,7 @@
     private final int mStartPaddingWithoutIcon;
     private final int mStartPadding;
     private final int mEndPadding;
+    private List<CarUiListItem> mWideScreenSearchItemList = new ArrayList<>();
     private Set<Toolbar.OnSearchListener> mSearchListeners = Collections.emptySet();
     private Set<Toolbar.OnSearchCompletedListener> mSearchCompletedListeners =
             Collections.emptySet();
@@ -82,7 +99,7 @@
         super(context, attrs, defStyleAttr);
 
         mInputMethodManager = (InputMethodManager)
-            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
         LayoutInflater inflater = LayoutInflater.from(context);
         inflater.inflate(R.layout.car_ui_toolbar_search_view, this, true);
@@ -130,6 +147,18 @@
         });
     }
 
+    /**
+     * Apply window inset listener to the search container.
+     */
+    void installWindowInsetsListener(View searchContainer) {
+        searchContainer.getRootView().setOnApplyWindowInsetsListener((v, insets) -> {
+            if (insets.isVisible(ime())) {
+                displaySearchWideScreen();
+            }
+            return insets;
+        });
+    }
+
     private boolean isEnter(KeyEvent event) {
         boolean result = false;
         if (event != null) {
@@ -180,6 +209,71 @@
     }
 
     /**
+     * Sets list of search item {@link CarUiListItem} to be displayed in the IMS
+     * template.
+     */
+    public void setSearchItemsForWideScreen(List<CarUiListItem> searchItems) {
+        mWideScreenSearchItemList = searchItems;
+        displaySearchWideScreen();
+    }
+
+    private void displaySearchWideScreen() {
+        if (mWideScreenSearchItemList.isEmpty()) {
+            return;
+        }
+        ArrayList<String> itemIdList = new ArrayList<>();
+        ArrayList<String> titleList = new ArrayList<>();
+        ArrayList<String> subTitleList = new ArrayList<>();
+        ArrayList<Integer> primaryImageResId = new ArrayList<>();
+        ArrayList<String> secondaryItemId = new ArrayList<>();
+        ArrayList<Integer> secondaryImageResId = new ArrayList<>();
+        for (CarUiListItem listItem : mWideScreenSearchItemList) {
+            if (listItem instanceof CarUiContentListItem) {
+                CarUiImeSearchListItem item = (CarUiImeSearchListItem) listItem;
+                itemIdList.add(item.getItemId() != null ? item.getItemId().toString() : null);
+                titleList.add(item.getTitle() != null ? item.getTitle().toString() : null);
+                subTitleList.add(item.getBody() != null ? item.getBody().toString() : null);
+                primaryImageResId.add(item.getIconResId());
+                secondaryItemId.add(item.getSupplementalIconId() != null
+                        ? item.getSupplementalIconId().toString() : null);
+                secondaryImageResId.add(item.getSupplementalIconResId());
+            }
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putStringArrayList(SEARCH_RESULT_ITEM_ID, itemIdList);
+        bundle.putStringArrayList(SEARCH_RESULT_TITLE_LIST, titleList);
+        bundle.putStringArrayList(SEARCH_RESULT_SUB_TITLE_LIST, subTitleList);
+        bundle.putIntegerArrayList(SEARCH_RESULT_PRIMARY_IMAGE_RES_ID_LIST, primaryImageResId);
+        bundle.putStringArrayList(SEARCH_RESULT_SECONDARY_IMAGE_ID, secondaryItemId);
+        bundle.putIntegerArrayList(SEARCH_RESULT_SECONDARY_IMAGE_RES_ID_LIST, secondaryImageResId);
+        mInputMethodManager.sendAppPrivateCommand(mSearchText, WIDE_SCREEN_ACTION, bundle);
+    }
+
+    /**
+     * Registers a new {@link CarUiEditText.PrivateImeCommandCallback} to the list of
+     * listeners.
+     */
+    public void registerOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener) {
+        if (mSearchText instanceof CarUiEditText) {
+            ((CarUiEditText) mSearchText).registerOnPrivateImeCommandListener(listener);
+        }
+    }
+
+    /**
+     * Unregisters an existing {@link CarUiEditText.PrivateImeCommandCallback} from the list
+     * of listeners.
+     */
+    public boolean unregisterOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener) {
+        if (mSearchText instanceof CarUiEditText) {
+            return ((CarUiEditText) mSearchText).unregisterOnPrivateImeCommandListener(listener);
+        }
+        return false;
+    }
+
+    /**
      * Sets the search hint.
      *
      * @param resId A string resource id of the search hint.
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/Toolbar.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/Toolbar.java
index 45e7dce..3278edd 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/Toolbar.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/Toolbar.java
@@ -30,7 +30,9 @@
 import androidx.annotation.StringRes;
 import androidx.annotation.XmlRes;
 
+import com.android.car.ui.CarUiEditText;
 import com.android.car.ui.R;
+import com.android.car.ui.recyclerview.CarUiListItem;
 
 import java.util.List;
 
@@ -651,6 +653,36 @@
         return mController.unregisterOnSearchListener(listener);
     }
 
+    /**
+     * Registers a new {@link CarUiEditText.PrivateImeCommandCallback} to the list of
+     * listeners.
+     */
+    @Override
+    public void registerOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener) {
+        mController.registerOnPrivateImeCommandListener(listener);
+    }
+
+    /**
+     * Unregisters an existing {@link CarUiEditText.PrivateImeCommandCallback} from the list
+     * of listeners.
+     */
+    @Override
+    public boolean unregisterOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener) {
+        return mController.unregisterOnPrivateImeCommandListener(listener);
+    }
+
+    /**
+     * Sets a list of search items to be displayed in the IME window when running as a wide screen
+     * mode. This should be called each time the list is updated. For example, when a user is typing
+     * in the input field and the list gets filtered this method should be invoked each time.
+     */
+    @Override
+    public void setSearchItemsForWideScreen(List<CarUiListItem> searchItems) {
+        mController.setSearchItemsForWideScreen(searchItems);
+    }
+
     /** Registers a new {@link OnSearchCompletedListener} to the list of listeners. */
     @Override
     public void registerOnSearchCompletedListener(OnSearchCompletedListener listener) {
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarController.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarController.java
index d3e3910..d88259f 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarController.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarController.java
@@ -24,6 +24,9 @@
 import androidx.annotation.StringRes;
 import androidx.annotation.XmlRes;
 
+import com.android.car.ui.CarUiEditText;
+import com.android.car.ui.recyclerview.CarUiListItem;
+
 import java.util.List;
 
 /**
@@ -255,8 +258,10 @@
      */
     void registerToolbarHeightChangeListener(Toolbar.OnHeightChangedListener listener);
 
-    /** Unregisters an existing {@link Toolbar.OnHeightChangedListener} from the list of
-     * listeners. */
+    /**
+     * Unregisters an existing {@link Toolbar.OnHeightChangedListener} from the list of
+     * listeners.
+     */
     boolean unregisterToolbarHeightChangeListener(Toolbar.OnHeightChangedListener listener);
 
     /** Registers a new {@link Toolbar.OnTabSelectedListener} to the list of listeners. */
@@ -271,11 +276,32 @@
     /** Unregisters an existing {@link Toolbar.OnSearchListener} from the list of listeners. */
     boolean unregisterOnSearchListener(Toolbar.OnSearchListener listener);
 
+    /**
+     * Registers a new {@link CarUiEditText.PrivateImeCommandCallback} to the list of
+     * listeners.
+     */
+    void registerOnPrivateImeCommandListener(CarUiEditText.PrivateImeCommandCallback listener);
+
+    /**
+     * Unregisters an existing {@link CarUiEditText.PrivateImeCommandCallback} from the list
+     * of listeners.
+     */
+    boolean unregisterOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener);
+
+    /**
+     * Sets list of search item {@link CarUiListItem} to be displayed in the IMS
+     * template.
+     */
+    void setSearchItemsForWideScreen(List<CarUiListItem> searchItems);
+
     /** Registers a new {@link Toolbar.OnSearchCompletedListener} to the list of listeners. */
     void registerOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener);
 
-    /** Unregisters an existing {@link Toolbar.OnSearchCompletedListener} from the list of
-     * listeners. */
+    /**
+     * Unregisters an existing {@link Toolbar.OnSearchCompletedListener} from the list of
+     * listeners.
+     */
     boolean unregisterOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener);
 
     /** Registers a new {@link Toolbar.OnBackListener} to the list of listeners. */
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarControllerImpl.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarControllerImpl.java
index c3ce075..f89d420 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarControllerImpl.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/toolbar/ToolbarControllerImpl.java
@@ -42,6 +42,7 @@
 import androidx.annotation.XmlRes;
 
 import com.android.car.ui.AlertDialogBuilder;
+import com.android.car.ui.CarUiEditText;
 import com.android.car.ui.R;
 import com.android.car.ui.recyclerview.CarUiContentListItem;
 import com.android.car.ui.recyclerview.CarUiListItem;
@@ -112,6 +113,7 @@
     private boolean mNavIconSpaceReserved;
     private boolean mLogoFillsNavIconSpace;
     private boolean mShowLogo;
+    private List<CarUiListItem> mSearchItems;
     private final ProgressBarController mProgressBar;
     private final MenuItem.Listener mOverflowItemListener = item -> {
         updateOverflowDialog(item);
@@ -713,6 +715,12 @@
                     ViewGroup.LayoutParams.MATCH_PARENT);
             mSearchViewContainer.addView(searchView, layoutParams);
 
+            searchView.installWindowInsetsListener(mSearchViewContainer);
+
+            if (mSearchItems != null) {
+                searchView.setSearchItemsForWideScreen(mSearchItems);
+            }
+
             mSearchView = searchView;
         }
 
@@ -863,6 +871,39 @@
         return mOnSearchListeners.remove(listener);
     }
 
+    /**
+     * Registers a new {@link CarUiEditText.PrivateImeCommandCallback} to the list of
+     * listeners.
+     */
+    @Override
+    public void registerOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener) {
+        if (mSearchView != null) {
+            mSearchView.registerOnPrivateImeCommandListener(listener);
+        }
+    }
+
+    /**
+     * Unregisters an existing {@link CarUiEditText.PrivateImeCommandCallback} from the list
+     * of listeners.
+     */
+    @Override
+    public boolean unregisterOnPrivateImeCommandListener(
+            CarUiEditText.PrivateImeCommandCallback listener) {
+        if (mSearchView != null) {
+            return mSearchView.unregisterOnPrivateImeCommandListener(listener);
+        }
+        return false;
+    }
+
+    @Override
+    public void setSearchItemsForWideScreen(List<CarUiListItem> searchItems) {
+        mSearchItems = searchItems;
+        if (mSearchView != null) {
+            mSearchView.setSearchItemsForWideScreen(searchItems);
+        }
+    }
+
     /** Registers a new {@link Toolbar.OnSearchCompletedListener} to the list of listeners. */
     @Override
     public void registerOnSearchCompletedListener(Toolbar.OnSearchCompletedListener listener) {
diff --git a/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_alert_dialog_edit_text.xml b/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_alert_dialog_edit_text.xml
index d654b2b..378b539 100644
--- a/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_alert_dialog_edit_text.xml
+++ b/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_alert_dialog_edit_text.xml
@@ -16,7 +16,7 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android">
-    <EditText
+    <com.android.car.ui.CarUiEditText
               android:id="@+id/textbox"
               android:layout_width="match_parent"
               android:layout_height="@dimen/car_ui_dialog_edittext_height"
diff --git a/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_preference_dialog_edittext.xml b/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_preference_dialog_edittext.xml
index 04c1c37..39b4112 100644
--- a/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_preference_dialog_edittext.xml
+++ b/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_preference_dialog_edittext.xml
@@ -33,7 +33,7 @@
         android:layout_marginEnd="@dimen/car_ui_preference_edit_text_dialog_message_margin_end"
         android:visibility="gone"/>
 
-    <EditText
+    <com.android.car.ui.CarUiEditText
         android:id="@android:id/edit"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_toolbar_search_view.xml b/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_toolbar_search_view.xml
index c3ad68d..1c82fff 100644
--- a/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_toolbar_search_view.xml
+++ b/car-ui-lib/car-ui-lib/src/main/res/layout/car_ui_toolbar_search_view.xml
@@ -19,7 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <EditText
+    <com.android.car.ui.CarUiEditText
         android:id="@+id/car_ui_toolbar_search_bar"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
diff --git a/car-ui-lib/paintbooth/AndroidManifest.xml b/car-ui-lib/paintbooth/AndroidManifest.xml
index c277ff5..9763b47 100644
--- a/car-ui-lib/paintbooth/AndroidManifest.xml
+++ b/car-ui-lib/paintbooth/AndroidManifest.xml
@@ -72,6 +72,12 @@
         android:exported="false"
         android:parentActivityName=".MainActivity"/>
     <activity
+        android:name=".widescreenime.WideScreenImeActivity"
+        android:windowSoftInputMode="stateHidden|adjustNothing"
+        android:exported="false"
+        android:parentActivityName=".MainActivity">
+    </activity>
+    <activity
         android:name=".toolbar.ToolbarActivity"
         android:exported="false"
         android:parentActivityName=".MainActivity">
diff --git a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/MainActivity.java b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/MainActivity.java
index 02b8088..bf50162 100644
--- a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/MainActivity.java
+++ b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/MainActivity.java
@@ -46,6 +46,7 @@
 import com.android.car.ui.paintbooth.toolbar.NoCarUiToolbarActivity;
 import com.android.car.ui.paintbooth.toolbar.OldToolbarActivity;
 import com.android.car.ui.paintbooth.toolbar.ToolbarActivity;
+import com.android.car.ui.paintbooth.widescreenime.WideScreenImeActivity;
 import com.android.car.ui.paintbooth.widgets.WidgetActivity;
 import com.android.car.ui.recyclerview.CarUiRecyclerView;
 import com.android.car.ui.toolbar.ToolbarController;
@@ -76,6 +77,7 @@
             new ActivityElement("Old toolbar sample", OldToolbarActivity.class),
             new ActivityElement("No CarUiToolbar sample", NoCarUiToolbarActivity.class),
             new ActivityElement("Widget sample", WidgetActivity.class),
+            new ActivityElement("Wide Screen IME", WideScreenImeActivity.class),
             new ActivityElement("ListItem sample", CarUiListItemActivity.class));
 
     private abstract static class ViewHolder extends RecyclerView.ViewHolder {
diff --git a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/VisibleBoundsSimulator.java b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/VisibleBoundsSimulator.java
index 63a2aed..b3dfeb1 100644
--- a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/VisibleBoundsSimulator.java
+++ b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/VisibleBoundsSimulator.java
@@ -106,15 +106,12 @@
         int screenHeight = displayMetrics.heightPixels;
         int screenWidth = displayMetrics.widthPixels;
         LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
-        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                 WindowManager.LayoutParams.WRAP_CONTENT,
                 WindowManager.LayoutParams.WRAP_CONTENT,
-                // WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY is a hidden api, so
-                // use its value here so we can still compile on gradle / google3
-                WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW + 26,
+                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                 PixelFormat.TRANSLUCENT);
 
         params.packageName = this.getPackageName();
diff --git a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/dialogs/DialogsActivity.java b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/dialogs/DialogsActivity.java
index fd70730..3d390fc 100644
--- a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/dialogs/DialogsActivity.java
+++ b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/dialogs/DialogsActivity.java
@@ -147,6 +147,7 @@
         new AlertDialogBuilder(this)
                 .setTitle("Standard Alert Dialog")
                 .setEditBox("Edit me please", null, null)
+                .setEditTextTitleAndDescForWideScreen("title", "desc from app")
                 .setPositiveButton("OK", (dialogInterface, i) -> {
                 })
                 .show();
diff --git a/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/widescreenime/WideScreenImeActivity.java b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/widescreenime/WideScreenImeActivity.java
new file mode 100644
index 0000000..09fb578
--- /dev/null
+++ b/car-ui-lib/paintbooth/src/main/java/com/android/car/ui/paintbooth/widescreenime/WideScreenImeActivity.java
@@ -0,0 +1,339 @@
+/*
+ * 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.ui.paintbooth.widescreenime;
+
+import static android.view.inputmethod.EditorInfo.IME_FLAG_NO_EXTRACT_UI;
+
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TITLE_TO_CONTENT_AREA;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TO_CONTENT_AREA;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_ERROR_DESC_TO_INPUT_AREA;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.REQUEST_RENDER_CONTENT_AREA;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_ITEM_ID;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_PRIMARY_IMAGE_RES_ID_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SECONDARY_IMAGE_ID;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SECONDARY_IMAGE_RES_ID_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_SUB_TITLE_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.SEARCH_RESULT_TITLE_LIST;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_ACTION;
+import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_EXTRACTED_TEXT_ICON_RES_ID;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.ui.CarUiEditText;
+import com.android.car.ui.baselayout.Insets;
+import com.android.car.ui.baselayout.InsetsChangedListener;
+import com.android.car.ui.core.CarUi;
+import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
+import com.android.car.ui.paintbooth.R;
+import com.android.car.ui.recyclerview.CarUiContentListItem;
+import com.android.car.ui.recyclerview.CarUiListItem;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
+import com.android.car.ui.toolbar.MenuItem;
+import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.ToolbarController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity that shows different scenarios for wide screen ime.
+ */
+public class WideScreenImeActivity extends AppCompatActivity implements InsetsChangedListener {
+
+    private final List<MenuItem> mMenuItems = new ArrayList<>();
+    private final List<Pair<CharSequence, View.OnFocusChangeListener>> mEditText =
+            new ArrayList<>();
+
+    private final ArrayList<String> mItemIdList = new ArrayList<>();
+    private final ArrayList<String> mTitleList = new ArrayList<>();
+    private final ArrayList<String> mSubTitleList = new ArrayList<>();
+    private final ArrayList<Integer> mPrimaryImageResId = new ArrayList<>();
+    private final ArrayList<String> mSecondaryItemId = new ArrayList<>();
+    private final ArrayList<Integer> mSecondaryImageResId = new ArrayList<>();
+
+    private InputMethodManager mInputMethodManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.car_ui_recycler_view_activity);
+
+        mInputMethodManager = (InputMethodManager)
+                getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        ToolbarController toolbarNonFinal = CarUi.getToolbar(this);
+        if (toolbarNonFinal == null) {
+            toolbarNonFinal = requireViewById(R.id.toolbar);
+        }
+        ToolbarController toolbar = toolbarNonFinal;
+        toolbar.setTitle(getTitle());
+        toolbar.setState(Toolbar.State.SUBPAGE);
+        toolbar.setLogo(R.drawable.ic_launcher);
+        toolbar.registerOnBackListener(
+                () -> {
+                    if (toolbar.getState() == Toolbar.State.SEARCH
+                            || toolbar.getState() == Toolbar.State.EDIT) {
+                        toolbar.setState(Toolbar.State.SUBPAGE);
+                        return true;
+                    }
+                    return false;
+                });
+
+        final int[] count = {1};
+        CarUiImeSearchListItem item = new CarUiImeSearchListItem(CarUiContentListItem.Action.ICON);
+        item.setItemId("Item Id" + count[0]);
+        item.setTitle("Title " + count[0]);
+        item.setBody("Sub title " + count[0]);
+        item.setIconResId(R.drawable.ic_launcher);
+        item.setSupplementalIconId("Image Id " + count[0]);
+        item.setSupplementalIconResId(R.drawable.ic_launcher);
+
+        List<CarUiListItem> searchItems = new ArrayList<>();
+
+        searchItems.add(item);
+
+        // initial list to display in search view.
+        toolbar.setSearchItemsForWideScreen(searchItems);
+
+        toolbar.registerOnSearchListener((query) -> {
+            count[0]++;
+            CarUiImeSearchListItem item1 = new CarUiImeSearchListItem(
+                    CarUiContentListItem.Action.ICON);
+            item1.setItemId("Item Id" + count[0]);
+            item1.setTitle("Title " + count[0]);
+            item1.setBody("Sub title " + count[0]);
+            item1.setIconResId(R.drawable.ic_launcher);
+            item1.setSupplementalIconId("Image Id " + count[0]);
+            item1.setSupplementalIconResId(R.drawable.ic_launcher);
+            searchItems.add(item1);
+
+            toolbar.setSearchItemsForWideScreen(searchItems);
+        });
+
+        mMenuItems.add(MenuItem.builder(this)
+                .setToSearch()
+                .setOnClickListener(i -> {
+                    toolbar.setState(Toolbar.State.SEARCH);
+                })
+                .build());
+
+        toolbar.setMenuItems(mMenuItems);
+
+        mEditText.add(Pair.create("Default Input Edit Text field", null));
+
+        mEditText.add(Pair.create("Add Desc to content area",
+                this::addDescToContentArea));
+
+        mEditText.add(Pair.create("Hide the content area",
+                this::hideContentArea));
+
+        mEditText.add(Pair.create("Hide extraction view",
+                this::hideExtractionView));
+
+        for (int i = 0; i < 7; i++) {
+            mItemIdList.add("itemId" + i);
+            mTitleList.add("Title " + i);
+            mSubTitleList.add("subtitle " + i);
+            mPrimaryImageResId.add(R.drawable.ic_launcher);
+            mSecondaryItemId.add("imageId" + i);
+            mSecondaryImageResId.add(R.drawable.ic_launcher);
+        }
+
+        mEditText.add(Pair.create("Show IME  list view", this::showImeListView));
+
+        mEditText.add(Pair.create("Add icon to extracted view", this::addIconToExtractedView));
+
+        mEditText.add(
+                Pair.create("Add error message to content area", this::addErrorDescToContentArea));
+
+        CarUiRecyclerView recyclerView = requireViewById(R.id.list);
+        recyclerView.setAdapter(mAdapter);
+    }
+
+    private void addIconToExtractedView(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putInt(WIDE_SCREEN_EXTRACTED_TEXT_ICON_RES_ID, R.drawable.car_ui_icon_edit);
+        mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+    }
+
+    private void addErrorDescToContentArea(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putString(ADD_ERROR_DESC_TO_INPUT_AREA, "Some error message");
+        bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, "Title");
+        bundle.putString(ADD_DESC_TO_CONTENT_AREA, "Description provided by the application");
+        mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+    }
+
+    private void showImeListView(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+
+        EditText editText = (EditText) view;
+        editText.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                mItemIdList.add("itemId " + s.toString());
+                mTitleList.add("Title " + s.toString());
+                mSubTitleList.add("subtitle ");
+                mPrimaryImageResId.add(R.drawable.ic_launcher);
+                mSecondaryItemId.add("imageId" + s.toString());
+                mSecondaryImageResId.add(R.drawable.ic_launcher);
+
+                bundle.putStringArrayList(SEARCH_RESULT_TITLE_LIST, mTitleList);
+                bundle.putStringArrayList(SEARCH_RESULT_SUB_TITLE_LIST, mSubTitleList);
+                bundle.putIntegerArrayList(SEARCH_RESULT_PRIMARY_IMAGE_RES_ID_LIST,
+                        mPrimaryImageResId);
+                mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+            }
+        });
+
+        bundle.putStringArrayList(SEARCH_RESULT_ITEM_ID, mItemIdList);
+        bundle.putStringArrayList(SEARCH_RESULT_TITLE_LIST, mTitleList);
+        bundle.putStringArrayList(SEARCH_RESULT_SUB_TITLE_LIST, mSubTitleList);
+        bundle.putStringArrayList(SEARCH_RESULT_SECONDARY_IMAGE_ID, mSecondaryItemId);
+        bundle.putIntegerArrayList(SEARCH_RESULT_PRIMARY_IMAGE_RES_ID_LIST, mPrimaryImageResId);
+        bundle.putIntegerArrayList(SEARCH_RESULT_SECONDARY_IMAGE_RES_ID_LIST, mSecondaryImageResId);
+        mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+    }
+
+    private void hideExtractionView(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            return;
+        }
+
+        EditText editText = (EditText) view;
+        editText.setImeOptions(IME_FLAG_NO_EXTRACT_UI);
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(REQUEST_RENDER_CONTENT_AREA, false);
+        mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+    }
+
+    private void addDescToContentArea(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, "Title");
+        bundle.putString(ADD_DESC_TO_CONTENT_AREA, "Description provided by the application");
+        mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+    }
+
+    private void hideContentArea(View view, boolean hasFocus) {
+        if (!hasFocus) {
+            return;
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(REQUEST_RENDER_CONTENT_AREA, false);
+        mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
+    }
+
+
+    private static class ViewHolder extends RecyclerView.ViewHolder {
+
+        private final CarUiEditText mEditText;
+
+        ViewHolder(View itemView) {
+            super(itemView);
+            mEditText = itemView.requireViewById(R.id.edit_text);
+            mEditText.registerOnPrivateImeCommandListener(
+                    new CarUiEditText.PrivateImeCommandCallback() {
+
+                        @Override
+                        public void onItemClicked(String itemId) {
+                        }
+
+                        @Override
+                        public void onSecondaryImageClicked(String secondaryImageId) {
+                        }
+                    });
+        }
+
+        public void bind(CharSequence title, View.OnFocusChangeListener listener) {
+            mEditText.setText(title);
+            mEditText.setOnFocusChangeListener(listener);
+        }
+    }
+
+    private final RecyclerView.Adapter<ViewHolder> mAdapter =
+            new RecyclerView.Adapter<ViewHolder>() {
+                @Override
+                public int getItemCount() {
+                    return mEditText.size();
+                }
+
+                @Override
+                public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
+                    View item =
+                            LayoutInflater.from(parent.getContext())
+                                    .inflate(R.layout.edit_text_list_item,
+                                            parent, false);
+
+                    return new ViewHolder(item);
+                }
+
+                @Override
+                public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+                    Pair<CharSequence, View.OnFocusChangeListener> pair = mEditText.get(position);
+                    holder.bind(pair.first, pair.second);
+                }
+            };
+
+    @Override
+    public void onCarUiInsetsChanged(@NonNull Insets insets) {
+        requireViewById(R.id.list)
+                .setPadding(0, insets.getTop(), 0, insets.getBottom());
+        requireViewById(android.R.id.content)
+                .setPadding(insets.getLeft(), 0, insets.getRight(), 0);
+    }
+}
diff --git a/car-ui-lib/paintbooth/src/main/res/layout/edit_text_list_item.xml b/car-ui-lib/paintbooth/src/main/res/layout/edit_text_list_item.xml
new file mode 100644
index 0000000..3475b14
--- /dev/null
+++ b/car-ui-lib/paintbooth/src/main/res/layout/edit_text_list_item.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+
+    <com.android.car.ui.CarUiEditText
+        android:id="@+id/edit_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:inputType="text"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="20dp"
+        android:text="Edit Text Box"/>
+</RelativeLayout>