diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index 25952309923e..72208f34f20c 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -78,6 +78,9 @@ public class LocalFileListAdapter extends RecyclerView.Adapter= mFiles.size()) { - return RecyclerView.NO_ID; + int headerOffset = shouldShowHeader() ? 1 : 0; + return getStableItemId(position, headerOffset, mFiles); + } + + @VisibleForTesting + static long getStableItemId(int position, int headerOffset, List files) { + if (headerOffset == 1 && position == 0) { + return HEADER_ID; + } + + int fileIndex = position - headerOffset; + if (fileIndex < 0 || fileIndex >= files.size()) { + return FOOTER_ID; } - File file = mFiles.get(position); - return file.getAbsolutePath().hashCode(); + return files.get(fileIndex).getAbsolutePath().hashCode(); } @Override diff --git a/app/src/test/java/com/owncloud/android/ui/adapter/LocalFileListAdapterTest.kt b/app/src/test/java/com/owncloud/android/ui/adapter/LocalFileListAdapterTest.kt new file mode 100644 index 000000000000..6518ca28505a --- /dev/null +++ b/app/src/test/java/com/owncloud/android/ui/adapter/LocalFileListAdapterTest.kt @@ -0,0 +1,49 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +package com.owncloud.android.ui.adapter + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import java.io.File + +class LocalFileListAdapterTest { + + private fun sampleFiles(): List = listOf(File("/sdcard/a.jpg"), File("/sdcard/b.png"), File("/sdcard/c.mp4")) + + private fun assertAllPositionsHaveUniqueIds(headerOffset: Int, files: List) { + val itemCount = files.size + 1 + headerOffset + + val ids = mutableSetOf() + for (position in 0 until itemCount) { + val id = LocalFileListAdapter.getStableItemId(position, headerOffset, files) + assertTrue("Duplicate stable ID $id at position $position", ids.add(id)) + } + assertEquals("Every position must produce a distinct stable ID", itemCount, ids.size) + } + + @Test + fun stableIdsAreUniqueWhenHeaderIsVisible() { + assertAllPositionsHaveUniqueIds(headerOffset = 1, files = sampleFiles()) + } + + @Test + fun stableIdsAreUniqueWhenHeaderIsHidden() { + assertAllPositionsHaveUniqueIds(headerOffset = 0, files = sampleFiles()) + } + + @Test + fun headerAndFooterKeepDedicatedIds() { + val files = sampleFiles() + + val headerId = LocalFileListAdapter.getStableItemId(0, 1, files) + val footerId = LocalFileListAdapter.getStableItemId(files.size + 1, 1, files) + + assertEquals(Long.MIN_VALUE, headerId) + assertEquals(Long.MIN_VALUE + 1, footerId) + } +}