[Autofill Assistant] Add carousel chips in model.

Change-Id: I9b12db58728f9e18b54dc270d300005219ea2d77
Reviewed-on: https://chromium-review.googlesource.com/c/1426780
Commit-Queue: Jordan Demeulenaere <jdemeulenaere@chromium.org>
Reviewed-by: Stephane Zermatten <szermatt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625171}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index 821a46e..b05ea01 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -79,8 +79,8 @@
                 mAssistantView, mActivity.getResources().getDisplayMetrics());
         mHeaderCoordinator = new AssistantHeaderCoordinator(
                 mActivity, mBottomBarCoordinator.getView(), mModel.getHeaderModel());
-        mCarouselCoordinator = new AssistantCarouselCoordinator(
-                mActivity, mBottomBarCoordinator::onChildVisibilityChanged);
+        mCarouselCoordinator = new AssistantCarouselCoordinator(mActivity,
+                mModel.getCarouselModel(), mBottomBarCoordinator::onChildVisibilityChanged);
         mDetailsCoordinator = new AssistantDetailsCoordinator(
                 mActivity, mBottomBarCoordinator::onChildVisibilityChanged);
         mPaymentRequestCoordinator = new AssistantPaymentRequestCoordinator(
@@ -115,6 +115,7 @@
      * visible will still be shown for a few seconds before shutting down. Optionally replace
      * the status message with a generic error message iff {@code showGiveUpMessage} is true.
      */
+    // TODO(crbug.com/806868): Move this method to native.
     public void gracefulShutdown(boolean showGiveUpMessage) {
         mIsShuttingDownGracefully = true;
 
@@ -125,7 +126,7 @@
         mOverlayCoordinator.hide();
         mDetailsCoordinator.setVisible(false);
         mPaymentRequestCoordinator.setVisible(false);
-        mCarouselCoordinator.setVisible(false);
+        mModel.getCarouselModel().clearChips();
 
         if (showGiveUpMessage) {
             mHeaderCoordinator.setStatusMessage(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
index 42d3f64..9c5f16d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantCarouselModel;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 
 /**
@@ -14,9 +15,15 @@
 @JNINamespace("autofill_assistant")
 class AssistantModel {
     private final AssistantHeaderModel mHeaderModel = new AssistantHeaderModel();
+    private final AssistantCarouselModel mCarouselModel = new AssistantCarouselModel();
 
     @CalledByNative
     public AssistantHeaderModel getHeaderModel() {
         return mHeaderModel;
     }
+
+    @CalledByNative
+    public AssistantCarouselModel getCarouselModel() {
+        return mCarouselModel;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 8c3b791..b027f06f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -11,7 +11,6 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
-import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetails;
 import org.chromium.chrome.browser.autofill_assistant.payment.AutofillAssistantPaymentRequest.SelectedPaymentInformation;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
@@ -165,25 +164,6 @@
     }
 
     @CalledByNative
-    private void onSetChips(int[] types, String[] texts) {
-        assert types.length == texts.length;
-        List<AssistantChip> chips = new ArrayList<>();
-        for (int i = 0; i < types.length; i++) {
-            int index = i;
-            chips.add(new AssistantChip(types[i], texts[i], () -> {
-                mCoordinator.getCarouselCoordinator().clearChips();
-                safeNativeOnChipSelected(index);
-            }));
-        }
-        mCoordinator.getCarouselCoordinator().setChips(chips);
-    }
-
-    @CalledByNative
-    private void onClearChips() {
-        mCoordinator.getCarouselCoordinator().clearChips();
-    }
-
-    @CalledByNative
     private void onRequestPaymentInformation(String defaultEmail, boolean requestShipping,
             boolean requestPayerName, boolean requestPayerPhone, boolean requestPayerEmail,
             int shippingType, String title, String[] supportedBasicCardNetworks) {
@@ -284,11 +264,6 @@
     }
     private native void nativeOnUserInteractionInsideTouchableArea(long nativeUiControllerAndroid);
 
-    void safeNativeOnChipSelected(int index) {
-        if (mNativeUiController != 0) nativeOnChipSelected(mNativeUiController, index);
-    }
-    private native void nativeOnChipSelected(long nativeUiControllerAndroid, int index);
-
     void safeNativeOnGetPaymentInformation(boolean succeed,
             @Nullable PersonalDataManager.CreditCard card,
             @Nullable PersonalDataManager.AutofillProfile address, @Nullable String payerName,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselCoordinator.java
index ae40685..4e2184ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselCoordinator.java
@@ -6,18 +6,17 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.support.annotation.Nullable;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.TypedValue;
 import android.view.View;
 
-import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.ListObservable;
+import org.chromium.ui.modelutil.ListObservable.ListObserver;
 import org.chromium.ui.modelutil.RecyclerViewAdapter;
 import org.chromium.ui.modelutil.SimpleRecyclerViewMcp;
 
-import java.util.Collections;
-import java.util.List;
-
 /**
  * Coordinator responsible for suggesting chips to the user.
  */
@@ -30,9 +29,9 @@
 
     private final LinearLayoutManager mLayoutManager;
     private final RecyclerView mView;
-    private final ListModel<AssistantChip> mModel = new ListModel<>();
 
-    public AssistantCarouselCoordinator(Context context, Runnable onVisibilityChanged) {
+    public AssistantCarouselCoordinator(
+            Context context, AssistantCarouselModel model, Runnable onVisibilityChanged) {
         mOnVisibilityChanged = onVisibilityChanged;
 
         mLayoutManager = new LinearLayoutManager(
@@ -45,9 +44,41 @@
         mView.getItemAnimator().setMoveDuration(0);
         mView.getItemAnimator().setRemoveDuration(0);
         mView.setAdapter(new RecyclerViewAdapter<>(
-                new SimpleRecyclerViewMcp<>(
-                        mModel, AssistantChip::getType, AssistantChipViewHolder::bind),
+                new SimpleRecyclerViewMcp<>(model.getChipsModel(), AssistantChip::getType,
+                        AssistantChipViewHolder::bind),
                 AssistantChipViewHolder::create));
+
+        // Listen for changes on REVERSE_LAYOUT.
+        model.addObserver((source, propertyKey) -> {
+            if (AssistantCarouselModel.REVERSE_LAYOUT == propertyKey) {
+                mLayoutManager.setReverseLayout(model.get(AssistantCarouselModel.REVERSE_LAYOUT));
+            } else {
+                assert false : "Unhandled property detected in AssistantCarouselCoordinator!";
+            }
+        });
+
+        // Listen for changes on chips, and set visibility accordingly.
+        model.getChipsModel().addObserver(new ListObserver<Void>() {
+            @Override
+            public void onItemRangeInserted(ListObservable source, int index, int count) {
+                onChipsChanged();
+            }
+
+            @Override
+            public void onItemRangeRemoved(ListObservable source, int index, int count) {
+                onChipsChanged();
+            }
+
+            @Override
+            public void onItemRangeChanged(
+                    ListObservable<Void> source, int index, int count, @Nullable Void payload) {
+                onChipsChanged();
+            }
+
+            private void onChipsChanged() {
+                setVisible(model.getChipsModel().size() > 0);
+            }
+        });
     }
 
     /**
@@ -70,35 +101,6 @@
         }
     }
 
-    /**
-     * Set the chips to show.
-     */
-    public void setChips(List<AssistantChip> chips) {
-        if (chips.isEmpty()) {
-            setVisible(false);
-        } else {
-            setVisible(true);
-        }
-        mLayoutManager.setReverseLayout(shouldReverseLayout(chips));
-        mModel.set(chips);
-    }
-
-    /**
-     * Remove all chips shown to the user.
-     */
-    public void clearChips() {
-        setChips(Collections.emptyList());
-    }
-
-    private boolean shouldReverseLayout(List<AssistantChip> chips) {
-        for (int i = 0; i < chips.size(); i++) {
-            if (chips.get(i).getType() != AssistantChipType.CHIP_ASSISTIVE) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private class SpaceItemDecoration extends RecyclerView.ItemDecoration {
         private final int mInnerSpacePx;
         private final int mOuterSpacePx;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselDelegate.java
new file mode 100644
index 0000000..4fdc0ac
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselDelegate.java
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant.carousel;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+@JNINamespace("autofill_assistant")
+class AssistantCarouselDelegate {
+    private long mNativeAssistantCarouselDelegate;
+
+    @CalledByNative
+    private static AssistantCarouselDelegate create(long nativeAssistantCarouselDelegate) {
+        return new AssistantCarouselDelegate(nativeAssistantCarouselDelegate);
+    }
+
+    private AssistantCarouselDelegate(long nativeAssistantCarouselDelegate) {
+        mNativeAssistantCarouselDelegate = nativeAssistantCarouselDelegate;
+    }
+
+    void onChipSelected(int index) {
+        if (mNativeAssistantCarouselDelegate != 0) {
+            nativeOnChipSelected(mNativeAssistantCarouselDelegate, index);
+        }
+    }
+
+    @CalledByNative
+    private void clearNativePtr() {
+        mNativeAssistantCarouselDelegate = 0;
+    }
+
+    private native void nativeOnChipSelected(long nativeAssistantCarouselDelegate, int index);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselModel.java
new file mode 100644
index 0000000..05cfcdf9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselModel.java
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill_assistant.carousel;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.ui.modelutil.ListModel;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * State for the carousel of the Autofill Assistant.
+ */
+@JNINamespace("autofill_assistant")
+public class AssistantCarouselModel extends PropertyModel {
+    static final WritableBooleanPropertyKey REVERSE_LAYOUT = new WritableBooleanPropertyKey();
+
+    private final ListModel<AssistantChip> mChipsModel = new ListModel<>();
+
+    public AssistantCarouselModel() {
+        super(REVERSE_LAYOUT);
+    }
+
+    public ListModel<AssistantChip> getChipsModel() {
+        return mChipsModel;
+    }
+
+    @CalledByNative
+    private void setChips(int[] types, String[] texts, AssistantCarouselDelegate delegate) {
+        assert types.length == texts.length;
+        List<AssistantChip> chips = new ArrayList<>();
+        boolean reverseLayout = false;
+        for (int i = 0; i < types.length; i++) {
+            int index = i;
+            int type = types[i];
+
+            if (type != AssistantChipType.CHIP_ASSISTIVE) {
+                reverseLayout = true;
+            }
+
+            chips.add(new AssistantChip(type, texts[i], () -> {
+                clearChips();
+                delegate.onChipSelected(index);
+            }));
+        }
+
+        mChipsModel.set(chips);
+        set(REVERSE_LAYOUT, reverseLayout);
+    }
+
+    @CalledByNative
+    public void clearChips() {
+        mChipsModel.set(Collections.emptyList());
+    }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 850f4ec6..369cea74 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -148,6 +148,8 @@
   "java/src/org/chromium/chrome/browser/autofill_assistant/FeedbackContext.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/TouchEventFilterView.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselCoordinator.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselDelegate.java",
+  "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselModel.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChip.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantChipViewHolder.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/details/AssistantDetails.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 89c5d74..a490f32 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -143,7 +143,9 @@
                         AssistantChipType.CHIP_ASSISTIVE, "chip 0", () -> {/* do nothing */}),
                 new AssistantChip(AssistantChipType.CHIP_ASSISTIVE, "chip 1", mRunnableMock));
         ThreadUtils.runOnUiThreadBlocking(
-                () -> assistantCoordinator.getCarouselCoordinator().setChips(chips));
+                ()
+                        -> assistantCoordinator.getModel().getCarouselModel().getChipsModel().set(
+                                chips));
         RecyclerView chipsViewContainer = assistantCoordinator.getCarouselCoordinator().getView();
         Assert.assertEquals(2, chipsViewContainer.getAdapter().getItemCount());
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ffc2f7a..45bbb43 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2022,6 +2022,8 @@
       "android/android_theme_resources.h",
       "android/app_hooks.cc",
       "android/app_hooks.h",
+      "android/autofill_assistant/assistant_carousel_delegate.cc",
+      "android/autofill_assistant/assistant_carousel_delegate.h",
       "android/autofill_assistant/assistant_header_delegate.cc",
       "android/autofill_assistant/assistant_header_delegate.h",
       "android/autofill_assistant/client_android.cc",
@@ -4762,6 +4764,8 @@
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
+      "../android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselDelegate.java",
+      "../android/java/src/org/chromium/chrome/browser/autofill_assistant/carousel/AssistantCarouselModel.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderDelegate.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderModel.java",
       "../android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java",
diff --git a/chrome/browser/android/autofill_assistant/assistant_carousel_delegate.cc b/chrome/browser/android/autofill_assistant/assistant_carousel_delegate.cc
new file mode 100644
index 0000000..61c01244
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_carousel_delegate.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/autofill_assistant/assistant_carousel_delegate.h"
+
+#include "chrome/browser/android/autofill_assistant/ui_controller_android.h"
+#include "jni/AssistantCarouselDelegate_jni.h"
+
+using base::android::AttachCurrentThread;
+
+namespace autofill_assistant {
+
+AssistantCarouselDelegate::AssistantCarouselDelegate(
+    UiControllerAndroid* ui_controller)
+    : ui_controller_(ui_controller) {
+  java_assistant_carousel_delegate_ = Java_AssistantCarouselDelegate_create(
+      AttachCurrentThread(), reinterpret_cast<intptr_t>(this));
+}
+
+AssistantCarouselDelegate::~AssistantCarouselDelegate() {
+  Java_AssistantCarouselDelegate_clearNativePtr(
+      AttachCurrentThread(), java_assistant_carousel_delegate_);
+}
+
+void AssistantCarouselDelegate::OnChipSelected(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    jint index) {
+  ui_controller_->OnChipSelected(index);
+}
+
+base::android::ScopedJavaGlobalRef<jobject>
+AssistantCarouselDelegate::GetJavaObject() {
+  return java_assistant_carousel_delegate_;
+}
+
+}  // namespace autofill_assistant
diff --git a/chrome/browser/android/autofill_assistant/assistant_carousel_delegate.h b/chrome/browser/android/autofill_assistant/assistant_carousel_delegate.h
new file mode 100644
index 0000000..2523fb2
--- /dev/null
+++ b/chrome/browser/android/autofill_assistant/assistant_carousel_delegate.h
@@ -0,0 +1,32 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_CAROUSEL_DELEGATE_H_
+#define CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_CAROUSEL_DELEGATE_H_
+
+#include "base/android/scoped_java_ref.h"
+
+namespace autofill_assistant {
+class UiControllerAndroid;
+// Delegate class for the carousel, to react on clicks on its chips.
+class AssistantCarouselDelegate {
+ public:
+  explicit AssistantCarouselDelegate(UiControllerAndroid* ui_controller);
+  ~AssistantCarouselDelegate();
+
+  void OnChipSelected(JNIEnv* env,
+                      const base::android::JavaParamRef<jobject>& jcaller,
+                      jint index);
+
+  base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
+
+ private:
+  UiControllerAndroid* ui_controller_;
+
+  // Java-side AssistantCarouselDelegate object.
+  base::android::ScopedJavaGlobalRef<jobject> java_assistant_carousel_delegate_;
+};
+}  // namespace autofill_assistant
+
+#endif  // CHROME_BROWSER_ANDROID_AUTOFILL_ASSISTANT_ASSISTANT_CAROUSEL_DELEGATE_H_
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index ce1c5d68..4474120 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -32,6 +32,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 #include "google_apis/google_api_keys.h"
+#include "jni/AssistantCarouselModel_jni.h"
 #include "jni/AssistantHeaderModel_jni.h"
 #include "jni/AssistantModel_jni.h"
 #include "jni/AutofillAssistantUiController_jni.h"
@@ -50,6 +51,7 @@
     : client_(client),
       ui_delegate_(ui_delegate),
       header_delegate_(this),
+      carousel_delegate_(this),
       weak_ptr_factory_(this) {
   DCHECK(web_contents);
   DCHECK(client);
@@ -140,6 +142,48 @@
   return ui_delegate_->GetDebugContext();
 }
 
+// Carousel related methods.
+
+base::android::ScopedJavaLocalRef<jobject>
+UiControllerAndroid::GetCarouselModel() {
+  return Java_AssistantModel_getCarouselModel(AttachCurrentThread(),
+                                              GetModel());
+}
+
+void UiControllerAndroid::SetChips(std::unique_ptr<std::vector<Chip>> chips) {
+  DCHECK(chips);
+  current_chips_ = std::move(chips);
+
+  int types[current_chips_->size()];
+  std::vector<std::string> texts;
+  int i = 0;
+  for (const auto& chip : *current_chips_) {
+    types[i++] = chip.type;
+    texts.emplace_back(chip.text);
+  }
+
+  JNIEnv* env = AttachCurrentThread();
+  Java_AssistantCarouselModel_setChips(
+      env, GetCarouselModel(),
+      base::android::ToJavaIntArray(env, types, current_chips_->size()),
+      base::android::ToJavaArrayOfStrings(env, texts),
+      carousel_delegate_.GetJavaObject());
+}
+
+void UiControllerAndroid::ClearChips() {
+  current_chips_.reset();
+  Java_AssistantCarouselModel_clearChips(AttachCurrentThread(),
+                                         GetCarouselModel());
+}
+
+void UiControllerAndroid::OnChipSelected(int index) {
+  if (current_chips_ && index >= 0 && index < (int)current_chips_->size()) {
+    auto callback = std::move((*current_chips_)[index].callback);
+    current_chips_.reset();
+    std::move(callback).Run();
+  }
+}
+
 // Other methods.
 
 void UiControllerAndroid::ShowOnboarding(
@@ -196,16 +240,6 @@
   ui_delegate_->OnUserInteractionInsideTouchableArea();
 }
 
-void UiControllerAndroid::OnChipSelected(JNIEnv* env,
-                                         const JavaParamRef<jobject>& jcaller,
-                                         jint index) {
-  if (current_chips_ && index >= 0 && index < (int)current_chips_->size()) {
-    auto callback = std::move((*current_chips_)[index].callback);
-    current_chips_.reset();
-    std::move(callback).Run();
-  }
-}
-
 void UiControllerAndroid::OnGetPaymentInformation(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
@@ -263,31 +297,6 @@
   std::move(get_payment_information_callback_).Run(std::move(payment_info));
 }
 
-void UiControllerAndroid::SetChips(std::unique_ptr<std::vector<Chip>> chips) {
-  DCHECK(chips);
-  current_chips_ = std::move(chips);
-
-  int types[current_chips_->size()];
-  std::vector<std::string> texts;
-  int i = 0;
-  for (const auto& chip : *current_chips_) {
-    types[i++] = chip.type;
-    texts.emplace_back(chip.text);
-  }
-
-  JNIEnv* env = AttachCurrentThread();
-  Java_AutofillAssistantUiController_onSetChips(
-      env, java_autofill_assistant_ui_controller_,
-      base::android::ToJavaIntArray(env, types, current_chips_->size()),
-      base::android::ToJavaArrayOfStrings(env, texts));
-}
-
-void UiControllerAndroid::ClearChips() {
-  current_chips_.reset();
-  Java_AutofillAssistantUiController_onClearChips(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_);
-}
-
 void UiControllerAndroid::GetPaymentInformation(
     payments::mojom::PaymentOptionsPtr payment_options,
     base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 340becaf..eb9a259 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -11,6 +11,7 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
+#include "chrome/browser/android/autofill_assistant/assistant_carousel_delegate.h"
 #include "chrome/browser/android/autofill_assistant/assistant_header_delegate.h"
 #include "components/autofill_assistant/browser/client.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
@@ -66,6 +67,9 @@
   void OnFeedbackButtonClicked();
   void OnCloseButtonClicked();
 
+  // Called by AssistantCarouselDelegate:
+  void OnChipSelected(int index);
+
   // Called by Java.
   void Stop(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
   void UpdateTouchableArea(JNIEnv* env,
@@ -73,9 +77,6 @@
   void OnUserInteractionInsideTouchableArea(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller);
-  void OnChipSelected(JNIEnv* env,
-                      const base::android::JavaParamRef<jobject>& jcaller,
-                      jint index);
   void OnGetPaymentInformation(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
@@ -95,9 +96,11 @@
   Client* const client_;
   UiDelegate* const ui_delegate_;
   AssistantHeaderDelegate header_delegate_;
+  AssistantCarouselDelegate carousel_delegate_;
 
   base::android::ScopedJavaLocalRef<jobject> GetModel();
   base::android::ScopedJavaLocalRef<jobject> GetHeaderModel();
+  base::android::ScopedJavaLocalRef<jobject> GetCarouselModel();
 
   void SetProgressPulsingEnabled(bool enabled);
   void ShowDetails(const ShowDetailsProto& show_details,