Skip to content

Commit

Permalink
Merge pull request #216 from rubensousa/drag_drop
Browse files Browse the repository at this point in the history
Add DpadDragHelper for drag and drop support
  • Loading branch information
rubensousa authored Jun 15, 2024
2 parents 9b949bc + a50d76b commit 223d11a
Show file tree
Hide file tree
Showing 35 changed files with 1,792 additions and 68 deletions.
16 changes: 16 additions & 0 deletions dpadrecyclerview-compose/api/dpadrecyclerview-compose.api
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
public final class com/rubensousa/dpadrecyclerview/compose/ComposableSingletons$DpadComposeFocusViewHolderKt {
public static final field INSTANCE Lcom/rubensousa/dpadrecyclerview/compose/ComposableSingletons$DpadComposeFocusViewHolderKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
public fun <init> ()V
public final fun getLambda-1$dpadrecyclerview_compose_release ()Lkotlin/jvm/functions/Function3;
}

public final class com/rubensousa/dpadrecyclerview/compose/ComposableSingletons$DpadComposeViewHolderKt {
public static final field INSTANCE Lcom/rubensousa/dpadrecyclerview/compose/ComposableSingletons$DpadComposeViewHolderKt;
public static field lambda-1 Lkotlin/jvm/functions/Function4;
public fun <init> ()V
public final fun getLambda-1$dpadrecyclerview_compose_release ()Lkotlin/jvm/functions/Function4;
}

public final class com/rubensousa/dpadrecyclerview/compose/DpadComposeExtensionsKt {
public static final fun dpadClickable (Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;I)Landroidx/compose/ui/Modifier;
}
Expand All @@ -7,6 +21,7 @@ public final class com/rubensousa/dpadrecyclerview/compose/DpadComposeFocusViewH
public fun <init> (Landroid/view/ViewGroup;Landroidx/compose/ui/platform/ViewCompositionStrategy;ZLkotlin/jvm/functions/Function3;)V
public synthetic fun <init> (Landroid/view/ViewGroup;Landroidx/compose/ui/platform/ViewCompositionStrategy;ZLkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getItem ()Ljava/lang/Object;
public final fun setContent (Lkotlin/jvm/functions/Function3;)V
public final fun setFocusable (Z)V
public final fun setItemState (Ljava/lang/Object;)V
}
Expand All @@ -16,6 +31,7 @@ public final class com/rubensousa/dpadrecyclerview/compose/DpadComposeViewHolder
public fun <init> (Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ZLandroidx/compose/ui/platform/ViewCompositionStrategy;Lkotlin/jvm/functions/Function4;)V
public synthetic fun <init> (Landroid/view/ViewGroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ZLandroidx/compose/ui/platform/ViewCompositionStrategy;Lkotlin/jvm/functions/Function4;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getItem ()Ljava/lang/Object;
public final fun setContent (Lkotlin/jvm/functions/Function3;)V
public final fun setItemState (Ljava/lang/Object;)V
}

Expand Down
12 changes: 3 additions & 9 deletions dpadrecyclerview-compose/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
id 'org.jetbrains.dokka'
}

Expand All @@ -22,16 +23,13 @@ android {

buildTypes {
debug {
testCoverageEnabled true
enableUnitTestCoverage true
enableAndroidTestCoverage true
}
release {
}
}

buildFeatures {
compose true
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand All @@ -41,10 +39,6 @@ android {
jvmTarget = '1.8'
}

composeOptions {
kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get()
}

publishing {
singleVariant('release') {
withSourcesJar()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class DpadComposeFocusViewHolder<T>(
parent: ViewGroup,
compositionStrategy: ViewCompositionStrategy = RecyclerViewCompositionStrategy.DisposeOnRecycled,
isFocusable: Boolean = true,
private val content: @Composable (item: T) -> Unit
private val content: @Composable (item: T) -> Unit = {}
) : RecyclerView.ViewHolder(ComposeView(parent.context)) {

private val itemState = mutableStateOf<T?>(null)
Expand All @@ -57,10 +57,14 @@ class DpadComposeFocusViewHolder<T>(
composeView.apply {
this@DpadComposeFocusViewHolder.setFocusable(isFocusable)
setViewCompositionStrategy(compositionStrategy)
setContent {
itemState.value?.let { item ->
content(item)
}
setContent(content)
}
}

fun setContent(content: @Composable (item: T) -> Unit) {
composeView.setContent {
itemState.value?.let { item ->
content(item)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ class DpadComposeViewHolder<T>(
onLongClick: ((item: T) -> Boolean)? = null,
isFocusable: Boolean = true,
compositionStrategy: ViewCompositionStrategy = RecyclerViewCompositionStrategy.DisposeOnRecycled,
private val content: @Composable (item: T, isFocused: Boolean) -> Unit
private val content: @Composable (item: T, isFocused: Boolean) -> Unit = { _, _ -> }
) : RecyclerView.ViewHolder(DpadComposeView(parent.context)) {

private val focusState = mutableStateOf(false)
private val itemState = mutableStateOf<T?>(null)
private val composeView = itemView as DpadComposeView

init {
val composeView = itemView as DpadComposeView
composeView.apply {
setFocusConfiguration(
isFocusable = isFocusable,
Expand Down Expand Up @@ -96,6 +96,14 @@ class DpadComposeViewHolder<T>(
}
}

fun setContent(content: @Composable (item: T) -> Unit) {
composeView.setContent {
itemState.value?.let { item ->
content(item)
}
}
}

fun setItemState(item: T?) {
itemState.value = item
}
Expand Down
3 changes: 2 additions & 1 deletion dpadrecyclerview-testing/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ android {

buildTypes {
debug {
testCoverageEnabled true
enableUnitTestCoverage true
enableAndroidTestCoverage true
}
release {
}
Expand Down
22 changes: 21 additions & 1 deletion dpadrecyclerview/api/dpadrecyclerview.api
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ public final class com/rubensousa/dpadrecyclerview/ChildAlignment$CREATOR : andr
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/rubensousa/dpadrecyclerview/DpadDragHelper {
public fun <init> (Lcom/rubensousa/dpadrecyclerview/DpadDragHelper$DragAdapter;Lcom/rubensousa/dpadrecyclerview/DpadDragHelper$DragCallback;Ljava/util/Set;)V
public synthetic fun <init> (Lcom/rubensousa/dpadrecyclerview/DpadDragHelper$DragAdapter;Lcom/rubensousa/dpadrecyclerview/DpadDragHelper$DragCallback;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun attachToRecyclerView (Lcom/rubensousa/dpadrecyclerview/DpadRecyclerView;)V
public final fun detachFromRecyclerView ()V
public final fun isDragging ()Z
public final fun startDrag (I)Z
public final fun stopDrag ()V
}

public abstract interface class com/rubensousa/dpadrecyclerview/DpadDragHelper$DragAdapter {
public abstract fun getMutableItems ()Ljava/util/List;
}

public abstract interface class com/rubensousa/dpadrecyclerview/DpadDragHelper$DragCallback {
public abstract fun onDragStarted (Landroidx/recyclerview/widget/RecyclerView$ViewHolder;)V
public abstract fun onDragStopped ()V
}

public final class com/rubensousa/dpadrecyclerview/DpadLoopDirection : java/lang/Enum {
public static final field MAX Lcom/rubensousa/dpadrecyclerview/DpadLoopDirection;
public static final field MIN_MAX Lcom/rubensousa/dpadrecyclerview/DpadLoopDirection;
Expand Down Expand Up @@ -401,7 +420,7 @@ public final class com/rubensousa/dpadrecyclerview/layoutmanager/DpadLayoutParam
public final class com/rubensousa/dpadrecyclerview/layoutmanager/DpadLayoutParams$Companion {
}

public final class com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutManager : androidx/recyclerview/widget/RecyclerView$LayoutManager, androidx/recyclerview/widget/RecyclerView$SmoothScroller$ScrollVectorProvider {
public final class com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutManager : androidx/recyclerview/widget/RecyclerView$LayoutManager, androidx/recyclerview/widget/ItemTouchHelper$ViewDropHandler, androidx/recyclerview/widget/RecyclerView$SmoothScroller$ScrollVectorProvider {
public fun <init> (Landroidx/recyclerview/widget/RecyclerView$LayoutManager$Properties;)V
public final fun addOnLayoutCompletedListener (Lcom/rubensousa/dpadrecyclerview/DpadRecyclerView$OnLayoutCompletedListener;)V
public final fun addOnViewFocusedListener (Lcom/rubensousa/dpadrecyclerview/OnViewFocusedListener;)V
Expand Down Expand Up @@ -464,6 +483,7 @@ public final class com/rubensousa/dpadrecyclerview/layoutmanager/PivotLayoutMana
public fun onRestoreInstanceState (Landroid/os/Parcelable;)V
public fun onSaveInstanceState ()Landroid/os/Parcelable;
public fun performAccessibilityAction (Landroidx/recyclerview/widget/RecyclerView$Recycler;Landroidx/recyclerview/widget/RecyclerView$State;ILandroid/os/Bundle;)Z
public fun prepareForDrop (Landroid/view/View;Landroid/view/View;II)V
public final fun removeOnLayoutCompletedListener (Lcom/rubensousa/dpadrecyclerview/DpadRecyclerView$OnLayoutCompletedListener;)V
public final fun removeOnViewFocusedListener (Lcom/rubensousa/dpadrecyclerview/OnViewFocusedListener;)V
public final fun removeOnViewHolderSelectedListener (Lcom/rubensousa/dpadrecyclerview/OnViewHolderSelectedListener;)V
Expand Down
3 changes: 2 additions & 1 deletion dpadrecyclerview/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ android {

buildTypes {
debug {
testCoverageEnabled true
enableUnitTestCoverage true
enableAndroidTestCoverage true
}
release {

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ class RecyclerViewFragment : Fragment(R.layout.dpadrecyclerview_test_container)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<DpadRecyclerView>(R.id.recyclerView).requestFocus()
getRecyclerView().requestFocus()
}

fun getRecyclerView(): DpadRecyclerView {
return requireView().findViewById(R.id.recyclerView)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,27 @@ class TestAdapter(

}

fun assertContents(predicate: (index: Int) -> Int) {
for (i in 0 until itemCount) {
val item = getItem(i)
val expectedItem = predicate(i)
if (expectedItem != item) {
throw AssertionError("Expected item $expectedItem at position $i but got $item instead. Adapter contents: ${getAdapterContentString()}")
}
}
}

private fun getAdapterContentString(): String {
val builder = StringBuilder()
builder.append("[")
for (i in 0 until itemCount) {
builder.append(getItem(i))
if (i < itemCount - 1) {
builder.append(", ")
}
}
builder.append("]")
return builder.toString()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import android.os.Looper
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DiffUtil.DiffResult
import androidx.recyclerview.widget.RecyclerView
import com.rubensousa.dpadrecyclerview.DpadDragHelper
import com.rubensousa.dpadrecyclerview.test.TestAdapterConfiguration
import java.util.Collections
import java.util.concurrent.Executors

abstract class AbstractTestAdapter<VH : RecyclerView.ViewHolder>(
adapterConfiguration: TestAdapterConfiguration
) : RecyclerView.Adapter<VH>() {
) : RecyclerView.Adapter<VH>(), DpadDragHelper.DragAdapter<Int> {

companion object {
private val EMPTY_LIST = ArrayList<Int>(0)
Expand All @@ -43,6 +44,8 @@ abstract class AbstractTestAdapter<VH : RecyclerView.ViewHolder>(
private var currentVersion = 0
private var id = items.size

override fun getMutableItems(): MutableList<Int> = items

fun submitList(newList: MutableList<Int>, commitCallback: Runnable? = null) {
val version = ++currentVersion
if (items === newList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2024 Rúben Sousa
*
* 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.rubensousa.dpadrecyclerview.test.tests

import androidx.recyclerview.widget.RecyclerView.LayoutManager.Properties
import com.google.common.truth.Truth.assertThat
import com.rubensousa.dpadrecyclerview.layoutmanager.PivotLayoutManager
import org.junit.Test

class PivotLayoutManagerTest {

@Test
fun testDefaultSpanCountIsSetThroughConstructor() {
val properties = Properties()
properties.spanCount = 5
val pivotLayoutManager = PivotLayoutManager(properties)
assertThat(pivotLayoutManager.getSpanCount()).isEqualTo(properties.spanCount)
}

@Test
fun testDefaultFocusOutIsEnabledForAllProperties() {
val pivotLayoutManager = PivotLayoutManager(Properties())
val config = pivotLayoutManager.getConfig()
assertThat(config.focusOutFront).isTrue()
assertThat(config.focusOutBack).isTrue()
assertThat(config.focusOutSideFront).isTrue()
assertThat(config.focusOutSideBack).isTrue()
}

}
Loading

0 comments on commit 223d11a

Please sign in to comment.