[Tab Group Sync] Added separate method for move

Moving a tab results in a conflict in the tab position between the tab
being moved and the position to which it is being moved. This CL
corrects that by invoking SavedTabGroupModel::MoveTabInGroupTo that
auto-corrects position of all tabs.

Bug: b/337668132
Change-Id: I0f3e38e8ca2616bb2b9594d766c6b844478f159e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5495839
Reviewed-by: Min Qin <qinmin@chromium.org>
Commit-Queue: Shakti Sahu <shaktisahu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1293828}
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/RemoteTabGroupMutationHelper.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/RemoteTabGroupMutationHelper.java
index 4ecf742..18259df1 100644
--- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/RemoteTabGroupMutationHelper.java
+++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/RemoteTabGroupMutationHelper.java
@@ -94,6 +94,10 @@
                 tabGroupId, tab.getId(), tab.getTitle(), tab.getUrl(), position);
     }
 
+    public void moveTab(LocalTabGroupId tabGroupId, int tabId, int newPosition) {
+        mTabGroupSyncService.moveTab(tabGroupId, tabId, newPosition);
+    }
+
     public void removeTab(LocalTabGroupId tabGroupId, int tabId) {
         mTabGroupSyncService.removeTab(tabGroupId, tabId);
     }
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java
index c8a12cc0..a8c7b66 100644
--- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java
+++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java
@@ -161,7 +161,8 @@
                 LocalTabGroupId tabGroupId =
                         TabGroupSyncUtils.getLocalTabGroupId(
                                 mTabGroupModelFilter, movedTab.getRootId());
-                mRemoteTabGroupMutationHelper.updateTab(tabGroupId, movedTab, positionInGroup);
+                mRemoteTabGroupMutationHelper.moveTab(
+                        tabGroupId, movedTab.getId(), positionInGroup);
             }
 
             @Override
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java
index 01d853c..d52ae91f 100644
--- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java
+++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java
@@ -172,7 +172,7 @@
         when(mTabGroupModelFilter.getIndexOfTabInGroup(mTab1)).thenReturn(0);
         mTabGroupModelFilterObserverCaptor.getValue().didMoveWithinGroup(mTab1, 0, 1);
         verify(mTabGroupSyncService, times(1))
-                .updateTab(eq(LOCAL_TAB_GROUP_ID_1), eq(TAB_ID_1), any(), any(), anyInt());
+                .moveTab(eq(LOCAL_TAB_GROUP_ID_1), eq(TAB_ID_1), anyInt());
     }
 
     @Test
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TestTabGroupSyncService.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TestTabGroupSyncService.java
index e6895b1..15c74a2 100644
--- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TestTabGroupSyncService.java
+++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TestTabGroupSyncService.java
@@ -53,6 +53,9 @@
     public void removeTab(LocalTabGroupId tabGroupId, int tabId) {}
 
     @Override
+    public void moveTab(LocalTabGroupId tabGroupId, int tabId, int newIndexInGroup) {}
+
+    @Override
     public String[] getAllGroupIds() {
         return new String[0];
     }
diff --git a/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncService.java b/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncService.java
index e8bdac8f..07c5cf1 100644
--- a/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncService.java
+++ b/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncService.java
@@ -135,6 +135,15 @@
     void removeTab(LocalTabGroupId tabGroupId, int tabId);
 
     /**
+     * Moves a tab within a group.
+     *
+     * @param tabGroupId The local group ID of the corresponding tab group.
+     * @param tabId The local tab ID of the tab being removed.
+     * @param newIndexInGroup The new index of the tab in the group.
+     */
+    void moveTab(LocalTabGroupId tabGroupId, int tabId, int newIndexInGroup);
+
+    /**
      * Called to return all the remote tab group IDs currently existing in the system.
      *
      * @return An array of IDs of the currently known tab groups.
diff --git a/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceAndroidUnitTest.java b/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceAndroidUnitTest.java
index 19f3f956..f84d437 100644
--- a/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceAndroidUnitTest.java
+++ b/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceAndroidUnitTest.java
@@ -39,6 +39,9 @@
     private static final String TEST_TAB_TITLE = "Test Tab";
     private static final String TEST_URL = "https://google.com";
     private static final LocalTabGroupId LOCAL_TAB_GROUP_ID_1 = new LocalTabGroupId(TOKEN_1);
+    private static final int LOCAL_TAB_ID_1 = 2;
+    private static final int LOCAL_TAB_ID_2 = 4;
+    private static final int TAB_POSITION = 3;
 
     private TabGroupSyncService mService;
     private TabGroupSyncService.Observer mObserver;
@@ -129,19 +132,35 @@
 
     @CalledByNative
     public void testAddTab() {
-        mService.addTab(LOCAL_TAB_GROUP_ID_1, 2, TEST_TAB_TITLE, new GURL(TEST_URL), 3);
-        mService.addTab(LOCAL_TAB_GROUP_ID_1, 4, TEST_TAB_TITLE, new GURL(TEST_URL), -1);
+        mService.addTab(
+                LOCAL_TAB_GROUP_ID_1,
+                LOCAL_TAB_ID_1,
+                TEST_TAB_TITLE,
+                new GURL(TEST_URL),
+                TAB_POSITION);
+        mService.addTab(
+                LOCAL_TAB_GROUP_ID_1, LOCAL_TAB_ID_2, TEST_TAB_TITLE, new GURL(TEST_URL), -1);
     }
 
     @CalledByNative
     public void testUpdateTab() {
-        mService.updateTab(LOCAL_TAB_GROUP_ID_1, 2, TEST_TAB_TITLE, new GURL(TEST_URL), 3);
+        mService.updateTab(
+                LOCAL_TAB_GROUP_ID_1,
+                LOCAL_TAB_ID_1,
+                TEST_TAB_TITLE,
+                new GURL(TEST_URL),
+                TAB_POSITION);
         mService.updateTab(LOCAL_TAB_GROUP_ID_1, 4, TEST_TAB_TITLE, new GURL(TEST_URL), -1);
     }
 
     @CalledByNative
     public void testRemoveTab() {
-        mService.removeTab(LOCAL_TAB_GROUP_ID_1, 2);
+        mService.removeTab(LOCAL_TAB_GROUP_ID_1, LOCAL_TAB_ID_1);
+    }
+
+    @CalledByNative
+    public void testMoveTab() {
+        mService.moveTab(LOCAL_TAB_GROUP_ID_1, LOCAL_TAB_ID_1, TAB_POSITION);
     }
 
     @CalledByNative
diff --git a/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceImpl.java b/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceImpl.java
index ad501d3..d0bdd8b 100644
--- a/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceImpl.java
+++ b/components/saved_tab_groups/android/java/src/org/chromium/components/tab_group_sync/TabGroupSyncServiceImpl.java
@@ -65,11 +65,10 @@
     }
 
     @Override
-    public void updateVisualData(LocalTabGroupId tabGroupId, String title, int color) {
+    public void updateVisualData(LocalTabGroupId groupId, String title, int color) {
         if (mNativePtr == 0) return;
-        assert tabGroupId != null;
-        TabGroupSyncServiceImplJni.get()
-                .updateVisualData(mNativePtr, this, tabGroupId, title, color);
+        assert groupId != null;
+        TabGroupSyncServiceImplJni.get().updateVisualData(mNativePtr, this, groupId, title, color);
     }
 
     @Override
@@ -97,6 +96,13 @@
     }
 
     @Override
+    public void moveTab(LocalTabGroupId groupId, int tabId, int newIndexInGroup) {
+        if (mNativePtr == 0) return;
+        assert groupId != null;
+        TabGroupSyncServiceImplJni.get().moveTab(mNativePtr, this, groupId, tabId, newIndexInGroup);
+    }
+
+    @Override
     public String[] getAllGroupIds() {
         if (mNativePtr == 0) return new String[0];
         return TabGroupSyncServiceImplJni.get().getAllGroupIds(mNativePtr, this);
@@ -111,7 +117,7 @@
 
     @Override
     public SavedTabGroup getGroup(LocalTabGroupId localGroupId) {
-        if (mNativePtr == 0) return null;
+        assert localGroupId != null;
         return TabGroupSyncServiceImplJni.get()
                 .getGroupByLocalGroupId(mNativePtr, this, localGroupId);
     }
@@ -235,6 +241,13 @@
                 LocalTabGroupId groupId,
                 int tabId);
 
+        void moveTab(
+                long nativeTabGroupSyncServiceAndroid,
+                TabGroupSyncServiceImpl caller,
+                LocalTabGroupId groupId,
+                int tabId,
+                int newIndexInGroup);
+
         String[] getAllGroupIds(
                 long nativeTabGroupSyncServiceAndroid, TabGroupSyncServiceImpl caller);
 
diff --git a/components/saved_tab_groups/android/tab_group_sync_service_android.cc b/components/saved_tab_groups/android/tab_group_sync_service_android.cc
index ab738b9..b9e3647 100644
--- a/components/saved_tab_groups/android/tab_group_sync_service_android.cc
+++ b/components/saved_tab_groups/android/tab_group_sync_service_android.cc
@@ -182,6 +182,18 @@
   tab_group_sync_service_->RemoveTab(group_id, tab_id);
 }
 
+void TabGroupSyncServiceAndroid::MoveTab(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_caller,
+    const JavaParamRef<jobject>& j_group_id,
+    jint j_tab_id,
+    int j_new_index_in_group) {
+  auto group_id =
+      TabGroupSyncConversionsBridge::FromJavaTabGroupId(env, j_group_id);
+  auto tab_id = FromJavaTabId(j_tab_id);
+  tab_group_sync_service_->MoveTab(group_id, tab_id, j_new_index_in_group);
+}
+
 ScopedJavaLocalRef<jobjectArray> TabGroupSyncServiceAndroid::GetAllGroupIds(
     JNIEnv* env,
     const JavaParamRef<jobject>& j_caller) {
diff --git a/components/saved_tab_groups/android/tab_group_sync_service_android.h b/components/saved_tab_groups/android/tab_group_sync_service_android.h
index 8d8c4f9..82343c5 100644
--- a/components/saved_tab_groups/android/tab_group_sync_service_android.h
+++ b/components/saved_tab_groups/android/tab_group_sync_service_android.h
@@ -75,6 +75,12 @@
                  const JavaParamRef<jobject>& j_group_id,
                  jint j_tab_id);
 
+  void MoveTab(JNIEnv* env,
+               const JavaParamRef<jobject>& j_caller,
+               const JavaParamRef<jobject>& j_group_id,
+               jint j_tab_id,
+               int j_new_index_in_group);
+
   // Accessor methods.
   ScopedJavaLocalRef<jobjectArray> GetAllGroupIds(
       JNIEnv* env,
diff --git a/components/saved_tab_groups/android/tab_group_sync_service_android_unittest.cc b/components/saved_tab_groups/android/tab_group_sync_service_android_unittest.cc
index f7d7569..26b4f30 100644
--- a/components/saved_tab_groups/android/tab_group_sync_service_android_unittest.cc
+++ b/components/saved_tab_groups/android/tab_group_sync_service_android_unittest.cc
@@ -32,6 +32,9 @@
 const char16_t kTestGroupTitle[] = u"Test Group";
 const char kTestUrl[] = "https://google.com";
 const char16_t kTestTabTitle[] = u"Test Tab";
+const int kTabId1 = 2;
+const int kTabId2 = 4;
+const int kPosition = 3;
 
 class MockTabGroupSyncService : public TabGroupSyncService {
  public:
@@ -58,6 +61,7 @@
                GURL,
                std::optional<size_t>));
   MOCK_METHOD(void, RemoveTab, (const LocalTabGroupID&, const LocalTabID&));
+  MOCK_METHOD(void, MoveTab, (const LocalTabGroupID&, const LocalTabID&, int));
 
   MOCK_METHOD(std::vector<SavedTabGroup>, GetAllGroups, ());
   MOCK_METHOD(std::optional<SavedTabGroup>, GetGroup, (const base::Uuid&));
@@ -229,15 +233,13 @@
   auto* env = AttachCurrentThread();
 
   GURL url(kTestUrl);
-  std::optional<int> position = 3;
+  EXPECT_CALL(tab_group_sync_service_,
+              AddTab(Eq(test_tab_group_id_), Eq(kTabId1), Eq(kTestTabTitle),
+                     Eq(url), Eq(kPosition)));
 
   EXPECT_CALL(tab_group_sync_service_,
-              AddTab(Eq(test_tab_group_id_), Eq(2), Eq(kTestTabTitle), Eq(url),
-                     Eq(position)));
-
-  EXPECT_CALL(tab_group_sync_service_,
-              AddTab(Eq(test_tab_group_id_), Eq(4), Eq(kTestTabTitle), Eq(url),
-                     Eq(std::nullopt)));
+              AddTab(Eq(test_tab_group_id_), Eq(kTabId2), Eq(kTestTabTitle),
+                     Eq(url), Eq(std::nullopt)));
   Java_TabGroupSyncServiceAndroidUnitTest_testAddTab(env, j_test_);
 }
 
@@ -245,13 +247,11 @@
   auto* env = AttachCurrentThread();
 
   GURL url(kTestUrl);
-  std::optional<int> position = 3;
-
   EXPECT_CALL(tab_group_sync_service_,
-              UpdateTab(Eq(test_tab_group_id_), Eq(2), Eq(kTestTabTitle),
-                        Eq(url), Eq(position)));
+              UpdateTab(Eq(test_tab_group_id_), Eq(kTabId1), Eq(kTestTabTitle),
+                        Eq(url), Eq(kPosition)));
   EXPECT_CALL(tab_group_sync_service_,
-              UpdateTab(Eq(test_tab_group_id_), Eq(4), Eq(kTestTabTitle),
+              UpdateTab(Eq(test_tab_group_id_), Eq(kTabId2), Eq(kTestTabTitle),
                         Eq(url), Eq(std::nullopt)));
   Java_TabGroupSyncServiceAndroidUnitTest_testUpdateTab(env, j_test_);
 }
@@ -260,10 +260,18 @@
   auto* env = AttachCurrentThread();
 
   EXPECT_CALL(tab_group_sync_service_,
-              RemoveTab(Eq(test_tab_group_id_), Eq(2)));
+              RemoveTab(Eq(test_tab_group_id_), Eq(kTabId1)));
   Java_TabGroupSyncServiceAndroidUnitTest_testRemoveTab(env, j_test_);
 }
 
+TEST_F(TabGroupSyncServiceAndroidTest, MoveTab) {
+  auto* env = AttachCurrentThread();
+
+  EXPECT_CALL(tab_group_sync_service_,
+              MoveTab(Eq(test_tab_group_id_), Eq(kTabId1), Eq(kPosition)));
+  Java_TabGroupSyncServiceAndroidUnitTest_testMoveTab(env, j_test_);
+}
+
 TEST_F(TabGroupSyncServiceAndroidTest, GetAllGroups) {
   auto group = test::CreateTestSavedTabGroup();
   std::vector<SavedTabGroup> expectedGroups = {group};
diff --git a/components/saved_tab_groups/tab_group_sync_service.h b/components/saved_tab_groups/tab_group_sync_service.h
index f915660..0f242656 100644
--- a/components/saved_tab_groups/tab_group_sync_service.h
+++ b/components/saved_tab_groups/tab_group_sync_service.h
@@ -107,6 +107,9 @@
                          std::optional<size_t> position) = 0;
   virtual void RemoveTab(const LocalTabGroupID& group_id,
                          const LocalTabID& tab_id) = 0;
+  virtual void MoveTab(const LocalTabGroupID& group_id,
+                       const LocalTabID& tab_id,
+                       int new_group_index) = 0;
 
   // Accessor methods.
   virtual std::vector<SavedTabGroup> GetAllGroups() = 0;
diff --git a/components/saved_tab_groups/tab_group_sync_service_impl.cc b/components/saved_tab_groups/tab_group_sync_service_impl.cc
index 756a146..89282ae 100644
--- a/components/saved_tab_groups/tab_group_sync_service_impl.cc
+++ b/components/saved_tab_groups/tab_group_sync_service_impl.cc
@@ -167,6 +167,23 @@
   model_->RemoveTabFromGroupLocally(group->saved_guid(), tab->saved_tab_guid());
 }
 
+void TabGroupSyncServiceImpl::MoveTab(const LocalTabGroupID& group_id,
+                                      const LocalTabID& tab_id,
+                                      int new_group_index) {
+  auto* group = model_->Get(group_id);
+  if (!group) {
+    return;
+  }
+
+  auto* tab = group->GetTab(tab_id);
+  if (!tab) {
+    return;
+  }
+
+  model_->MoveTabInGroupTo(group->saved_guid(), tab->saved_tab_guid(),
+                           new_group_index);
+}
+
 std::vector<SavedTabGroup> TabGroupSyncServiceImpl::GetAllGroups() {
   VLOG(2) << __func__;
   return model_->saved_tab_groups();
diff --git a/components/saved_tab_groups/tab_group_sync_service_impl.h b/components/saved_tab_groups/tab_group_sync_service_impl.h
index fe3d1bd2..1eee8764 100644
--- a/components/saved_tab_groups/tab_group_sync_service_impl.h
+++ b/components/saved_tab_groups/tab_group_sync_service_impl.h
@@ -69,6 +69,10 @@
                  std::optional<size_t> position) override;
   void RemoveTab(const LocalTabGroupID& group_id,
                  const LocalTabID& tab_id) override;
+  void MoveTab(const LocalTabGroupID& group_id,
+               const LocalTabID& tab_id,
+               int new_group_index) override;
+
   std::vector<SavedTabGroup> GetAllGroups() override;
   std::optional<SavedTabGroup> GetGroup(const base::Uuid& guid) override;
   std::optional<SavedTabGroup> GetGroup(LocalTabGroupID& local_id) override;