diff --git a/.gitignore b/.gitignore
index 39fb081..4b3d872 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,10 @@
*.iml
.gradle
/local.properties
-/.idea/workspace.xml
-/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
+
+/.idea/
+build/
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 6c06580..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf3..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/dictionaries/AlexZandR.xml b/.idea/dictionaries/AlexZandR.xml
deleted file mode 100644
index 7b513bd..0000000
--- a/.idea/dictionaries/AlexZandR.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 97626ba..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 7ac24c7..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 3cdb271..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Java
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $USER_HOME$/.subversion
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 5f95fd3..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LICENSE.MD b/LICENSE.MD
new file mode 100644
index 0000000..031d603
--- /dev/null
+++ b/LICENSE.MD
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 [Agilie Team](https://agilie.com/en/index)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1cd2508
--- /dev/null
+++ b/README.md
@@ -0,0 +1,126 @@
+# SwipeToDelete
+
+[  ](https://bintray.com/agilie/maven/Swipe2Delete/_latestVersion)
+
+
+
+# What is SwipeToDelete?
+
+SwipeToDelete is a library you can use to simplify implementation of well-known (I believe for every Android developer :) ) swipe-to-delete behavior for your data lists. The main idea is that user has some time to undo unwanted *delete* operation. Swipe twice to remove the row immediately! Also, this library is fully customizable, feel free to use your own UI for *undo* layer, add extra buttons etc.
+
+# Features
+* Based on interface implementation;
+* Supports pending deletion with bottom layout;
+* Full customization of upper layout and bottom layout;
+* Easy way to handle deletion. All you need is to call **removeItem(key: K)** which should be overriden in your adapter and to invoke **swipeToDeleteDelegate.removeItem(key: K)** at the end of method's definition.
+
+# Usage
+
+### Gradle
+
+Add dependency in your `build.gradle` file:
+````gradle
+compile 'com.agilie:swipe2delete:1.0'
+````
+
+### Maven
+Add rependency in your `.pom` file:
+````xml
+
+ com.agilie
+ swipe2delete
+ 1.0
+ pom
+
+````
+
+### How does it work?
+
+* Implement **ISwipeToDeleteAdapter** in your own adapter or another class.
+* Make instance of **SwipeToDeleteDelegate** in your own adapter
+
+```kotlin
+SwipeToDeleteDelegate(context = context, items = mutableList, swipeToDeleteDelegate = this)
+```
+
+* Call corresponding methods in your overrided methods
+
+```kotlin
+ override fun onBindViewHolder(holder: Holder, position: Int) {
+ swipeToDeleteDelegate.onBindViewHolder(holder, mutableList[position].name, position)
+ }
+
+ override fun removeItem(key: String) {
+ swipeToDeleteDelegate.removeItem(key)
+ }
+```
+
+
+* Implement **ISwipeToDeleteHolder** in your holder. You need to have container with your regular item layout and if you need bottom container too.
+* In your holder you need to have **var pendingDelete** as false by default and you need to override **val topContainer**
+
+```kotlin
+override val topContainer: View
+ get() =
+ if (pendingDelete) undoContainer
+ else itemContainer
+
+override var pendingDelete: Boolean = false
+```
+
+
+* Also you need to make default key in holder by yourself
+
+```kotlin
+override var key: Int = -1
+```
+
+
+* If you need bottom container appearence while waiting, simply override **onBindPendingItem(holder: Holder, key: Int, item: User)** method:
+
+```kotlin
+override fun onBindPendingItem(holder: Holder, key: Int, item: User) {
+...
+}
+```
+* Also you can implement **IAnimationUpdateListener** and **IAnimatorListener** to override methods to achieve animation along the axis x with duration which equally deleting duration
+
+```
+UserAdapter(...) : ... , IAnimationUpdateListener {
+...
+ fun onAnimationUpdated(animation: android.animation.ValueAnimator?, options: ModelOptions<*>) {}
+
+ fun onAnimationEnd(animation: Animator?, options: ModelOptions<*>) {}
+
+ fun onAnimationCancel(animation: Animator?, options: ModelOptions<*>) {}
+
+ fun onAnimationStart(animation: Animator?, options: ModelOptions<*>) {}
+
+ fun onAnimationRepeat(animation: Animator, options: ModelOptions<*>) {}
+}
+```
+
+To get more information refer our [usage example](app/). Just clone the project and run this module. Examples are provided in [Java](app/src/main/java/agilie/example/swipe2delete/java/) and [Kotlin](app/src/main/java/agilie/example/swipe2delete/kotlin/) as well!
+
+## Troubleshooting
+Problems? Check the [Issues](https://github.com/agilie/SwipeToDelete/issues) block
+to find the solution or create an new issue that we will fix asap. Feel free to contribute.
+
+## Author
+
+This library is open-sourced by [Agilie Team](https://www.agilie.com)
+
+## Contributors
+
+- [Alexander Nekrasov](https://github.com/TermLog)
+- [Roman Kapshuk](https://github.com/RomanKapshuk)
+
+## Contact us
+If you have any questions, suggestions or just need a help with web or mobile development, please email us at
+You can ask us anything from basic to complex questions.
+We will continue publishing new open-source projects. Stay with us, more updates will follow!
+
+## License
+
+The [MIT](LICENSE.MD) License (MIT) Copyright © 2017 [Agilie Team](https://www.agilie.com)
+
diff --git a/app/.gitignore b/app/.gitignore
index 796b96d..42afabf 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1 +1 @@
-/build
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 558e1ac..461d16f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,4 +1,6 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 25
@@ -20,12 +22,19 @@ android {
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
+
+// compile project(':swipe2delete')
+ compile 'com.agilie:swipe2delete:1.0'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'
testCompile 'junit:junit:4.12'
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
}
diff --git a/app/src/androidTest/java/test/alexzander/swipetodelete/ExampleInstrumentedTest.java b/app/src/androidTest/java/test/alexzander/swipetodelete/ExampleInstrumentedTest.java
index 7860355..21d6dbb 100644
--- a/app/src/androidTest/java/test/alexzander/swipetodelete/ExampleInstrumentedTest.java
+++ b/app/src/androidTest/java/test/alexzander/swipetodelete/ExampleInstrumentedTest.java
@@ -7,7 +7,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
/**
* Instrumentation test, which will execute on an Android device.
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9f57f6d..b694e03 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,24 +1,27 @@
+ package="test.alexzander.swipetodelete">
+ android:theme="@style/AppTheme">
+ >
-
+
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/agilie/example/swipe2delete/User.kt b/app/src/main/java/agilie/example/swipe2delete/User.kt
new file mode 100644
index 0000000..914513f
--- /dev/null
+++ b/app/src/main/java/agilie/example/swipe2delete/User.kt
@@ -0,0 +1,4 @@
+package agilie.example.swipe2delete
+
+
+data class User(val id: Int, val name: String, val country: String = "")
\ No newline at end of file
diff --git a/app/src/main/java/agilie/example/swipe2delete/Utility.kt b/app/src/main/java/agilie/example/swipe2delete/Utility.kt
new file mode 100644
index 0000000..17feab9
--- /dev/null
+++ b/app/src/main/java/agilie/example/swipe2delete/Utility.kt
@@ -0,0 +1,37 @@
+package agilie.example.swipe2delete
+
+fun prepareContactList(count: Int): MutableList {
+ val result = ArrayList(count)
+ result.add(User(0, "Daniel Weaver", "Nauru"))
+ result.add(User(1, "Vance Stanton", "Russian Federation"))
+ result.add(User(2, "Nasim Ingram", "United Arab Emirates"))
+ result.add(User(3, "Duncan Grimes", "Libya"))
+ result.add(User(4, "Logan Mcgee","Palau"))
+ result.add(User(5, "Berk Kemp", "Albania"))
+ result.add(User(6, "Solomon Warren", "Viet Nam"))
+ result.add(User(7, "Quentin Maldonado", "Aruba"))
+ result.add(User(8, "Isaac Garza", "Malta"))
+ result.add(User(9, "Tobias Workman", "Mali"))
+ result.add(User(10, "Channing Frederick", "Congo (Brazzaville)"))
+ result.add(User(11, "Phelan Booker", "Cocos (Keeling) Islands"))
+ result.add(User(12, "Amery Lewis", "San Marino"))
+ result.add(User(13, "Valentine Nolan", "Turks and Caicos Islands"))
+ result.add(User(14, "Jamal Walls", "Christmas Island"))
+ result.add(User(15, "Jack Noel", "Nicaragua"))
+ result.add(User(16, "Alvin Peterson", "Malta"))
+ result.add(User(17, "Brody Gilmore", "Bonaire, Sint Eustatius and Saba"))
+ result.add(User(18, "Arden Rich", "Uzbekistan"))
+ result.add(User(19, "Andrew Bauer", "Indonesia"))
+ result.add(User(20, "Erich Bernard", "India"))
+ result.add(User(21, "George Hayes", "Georgia"))
+ result.add(User(22, "Carlos Slater", "France"))
+ result.add(User(23, "Talon Harrington", "Congo (Brazzaville)"))
+ result.add(User(24, "Silas Navarro", "French Southern Territories"))
+ result.add(User(25, "Ryan Drake", "Maldives"))
+ result.add(User(26, "Jesse Rojas", "Sao Tome and Principe"))
+ result.add(User(27, "Aladdin Blankenship", "Tunisia"))
+ result.add(User(28, "Raja Valdez", "Holy See (Vatican City State)"))
+ result.add(User(29, "Chadwick Watkins", "Dominican Republic"))
+ result.add(User(30, "Chaney Best", "French Polynesia"))
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/agilie/example/swipe2delete/java/UserAdapter.java b/app/src/main/java/agilie/example/swipe2delete/java/UserAdapter.java
new file mode 100644
index 0000000..671be4e
--- /dev/null
+++ b/app/src/main/java/agilie/example/swipe2delete/java/UserAdapter.java
@@ -0,0 +1,152 @@
+package agilie.example.swipe2delete.java;
+
+import android.support.v7.widget.AppCompatTextView;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import com.agilie.swipe2delete.SwipeToDeleteDelegate;
+import com.agilie.swipe2delete.interfaces.ISwipeToDeleteAdapter;
+import com.agilie.swipe2delete.interfaces.ISwipeToDeleteHolder;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import agilie.example.swipe2delete.User;
+import test.alexzander.swipetodelete.R;
+
+
+public class UserAdapter extends RecyclerView.Adapter implements ISwipeToDeleteAdapter {
+
+ private List users;
+ private SwipeToDeleteDelegate swipeToDeleteDelegate;
+
+ public UserAdapter(List users) {
+ this.users = users;
+ }
+
+ public List getUsers() {
+ return users;
+ }
+
+ public SwipeToDeleteDelegate getSwipeToDeleteDelegate() {
+ return swipeToDeleteDelegate;
+ }
+
+ public void setSwipeToDeleteDelegate(SwipeToDeleteDelegate swipeToDeleteDelegate) {
+ this.swipeToDeleteDelegate = swipeToDeleteDelegate;
+ swipeToDeleteDelegate.setDeletingDuration(5000L);
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user, parent, false);
+ return new Holder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+ if (holder instanceof ISwipeToDeleteHolder) {
+ swipeToDeleteDelegate.onBindViewHolder((ISwipeToDeleteHolder) holder, users.get(position).getName(), position);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return users.size();
+ }
+
+ @Override
+ public int findItemPositionByKey(String key) {
+ for (int i = 0; i < users.size(); ++i) {
+ if (users.get(i).getName().equals(key)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void onBindCommonItem(Holder holder, final String key, User item, int position) {
+ holder.userContainer.setVisibility(View.VISIBLE);
+ holder.undoContainer.setVisibility(View.GONE);
+ holder.userName.setText(item.getName());
+ }
+
+ @Override
+ public void onBindPendingItem(Holder holder, final String key, User item, int position) {
+ holder.undoContainer.setVisibility(View.VISIBLE);
+ holder.userContainer.setVisibility(View.GONE);
+ holder.userButtonUndo.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ swipeToDeleteDelegate.onUndo(key);
+ }
+ });
+ }
+
+ @Override
+ public void removeItem(String key) {
+ swipeToDeleteDelegate.removeItem(key);
+ }
+
+ @Override
+ public void onItemDeleted(User item) {
+
+ }
+
+ public class Holder extends RecyclerView.ViewHolder implements ISwipeToDeleteHolder {
+
+ Button userButtonUndo;
+ FrameLayout userContainer;
+
+ AppCompatTextView userName;
+ FrameLayout undoContainer;
+
+ boolean pendingDelete;
+ String key;
+
+ Holder(View view) {
+ super(view);
+ userName = (AppCompatTextView) view.findViewById(R.id.user_name);
+ userContainer = (FrameLayout) view.findViewById(R.id.user_container);
+
+ userButtonUndo = (Button) view.findViewById(R.id.user_button_undo);
+ undoContainer = (FrameLayout) view.findViewById(R.id.user_undo_container);
+ }
+
+ @Override
+ public boolean getPendingDelete() {
+ return pendingDelete;
+ }
+
+ @Override
+ public void setPendingDelete(boolean b) {
+ pendingDelete = b;
+ }
+
+ @NotNull
+ @Override
+ public View getTopContainer() {
+ if (pendingDelete) {
+ return undoContainer;
+ } else {
+ return userContainer;
+ }
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public void setKey(String s) {
+ key = s;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/agilie/example/swipe2delete/kotlin/UserAdapter.kt b/app/src/main/java/agilie/example/swipe2delete/kotlin/UserAdapter.kt
new file mode 100644
index 0000000..ea41ae2
--- /dev/null
+++ b/app/src/main/java/agilie/example/swipe2delete/kotlin/UserAdapter.kt
@@ -0,0 +1,114 @@
+package agilie.example.swipe2delete.kotlin
+
+import agilie.example.swipe2delete.User
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.agilie.swipe2delete.ModelOptions
+import com.agilie.swipe2delete.SwipeToDeleteDelegate
+import com.agilie.swipe2delete.interfaces.IAnimationUpdateListener
+import com.agilie.swipe2delete.interfaces.IAnimatorListener
+import com.agilie.swipe2delete.interfaces.ISwipeToDeleteAdapter
+import com.agilie.swipe2delete.interfaces.ISwipeToDeleteHolder
+import kotlinx.android.synthetic.main.activity_main_item.view.*
+import test.alexzander.swipetodelete.R.layout.activity_main_item
+
+
+class UserAdapter(val mutableList: MutableList, var onItemClick: (User) -> Any) :
+ RecyclerView.Adapter(), ISwipeToDeleteAdapter, IAnimationUpdateListener, IAnimatorListener {
+
+ val swipeToDeleteDelegate = SwipeToDeleteDelegate(items = mutableList, swipeToDeleteAdapter = this)
+
+ var animationEnabled = true
+ var bottomContainer = true
+
+ init {
+ swipeToDeleteDelegate.deletingDuration = 3000
+ }
+
+ override fun getItemCount() = mutableList.size
+
+ override fun onBindViewHolder(holder: MyHolder, position: Int) {
+ swipeToDeleteDelegate.onBindViewHolder(holder, mutableList[position].id, position)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int) =
+ MyHolder(LayoutInflater.from(parent?.context).inflate(activity_main_item, parent, false))
+
+ override fun removeItem(key: Int) {
+ swipeToDeleteDelegate.removeItem(key)
+ }
+
+ override fun onAnimationEnd(animation: Animator?, options: ModelOptions<*>) {
+ if (animationEnabled) {
+ swipeToDeleteDelegate.holders[options.key]?.progressBar?.animate()?.x(0.0f)?.withEndAction {
+ swipeToDeleteDelegate.holders[options.key]?.progressBar?.visibility = View.GONE
+ }
+ }
+ }
+
+ override fun onAnimationUpdated(animation: ValueAnimator?, options: ModelOptions<*>) {
+ if (animationEnabled) {
+ val posX = animation?.animatedValue as Float
+ swipeToDeleteDelegate.holders[options.key]?.progressBar?.x = posX
+ options.posX = posX
+ }
+ }
+
+ override fun onAnimationStart(animation: Animator?, options: ModelOptions<*>) {
+ if (animationEnabled) {
+ swipeToDeleteDelegate.holders[options.key]?.progressBar?.visibility = View.VISIBLE
+ }
+ }
+
+ override fun findItemPositionByKey(key: Int) = (0..mutableList.lastIndex).firstOrNull { mutableList[it].id == key } ?: -1
+
+ override fun onBindCommonItem(holder: MyHolder, key: Int, item: User, position: Int) {
+ holder.apply {
+ name.text = item.name
+ id.text = item.country
+ itemContainer.visibility = View.VISIBLE
+ undoData.visibility = View.GONE
+ progressBar.visibility = View.GONE
+ itemContainer.setOnClickListener { onItemClick.invoke(item) }
+ }
+ }
+
+ override fun onBindPendingItem(holder: MyHolder, key: Int, item: User, position: Int) {
+ holder.itemContainer.visibility = View.GONE
+ if (bottomContainer) {
+ holder.apply {
+ deletedName.text = "You have just deleted ${item.name}"
+ itemContainer.visibility = View.GONE
+ undoData.visibility = View.VISIBLE
+ if (animationEnabled) {
+ progressBar.visibility = View.VISIBLE
+ }
+ undoButton.setOnClickListener { swipeToDeleteDelegate.onUndo(key) }
+ }
+
+ }
+ }
+
+ inner class MyHolder(view: View) : RecyclerView.ViewHolder(view), ISwipeToDeleteHolder {
+
+ var deletedName = view.user_name_deleted
+ var name = view.user_name
+ var id = view.user_id
+ var itemContainer = view.contact_container
+ var undoContainer = view.undo_container
+ var undoData = view.undo_data
+ var undoButton = view.button_undo
+ var progressBar = view.progress_indicator
+
+ override val topContainer: View
+ get() =
+ if (pendingDelete && bottomContainer) undoContainer
+ else itemContainer
+ override var key: Int = -1
+ override var pendingDelete: Boolean = false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/agilie/example/swipe2delete/kotlin/UsersActivity.kt b/app/src/main/java/agilie/example/swipe2delete/kotlin/UsersActivity.kt
new file mode 100644
index 0000000..b907676
--- /dev/null
+++ b/app/src/main/java/agilie/example/swipe2delete/kotlin/UsersActivity.kt
@@ -0,0 +1,66 @@
+package agilie.example.swipe2delete.kotlin
+
+import agilie.example.swipe2delete.prepareContactList
+import android.support.v7.widget.DividerItemDecoration
+import android.support.v7.widget.helper.ItemTouchHelper
+import android.view.Menu
+import android.view.MenuItem
+import android.widget.LinearLayout.VERTICAL
+import android.widget.Toast
+import kotlinx.android.synthetic.main.activity_java.*
+import kotlinx.android.synthetic.main.recycler_view.*
+import test.alexzander.swipetodelete.R
+import test.alexzander.swipetodelete.R.layout.activity_main
+
+class UsersActivity : android.support.v7.app.AppCompatActivity() {
+
+ var adapter: UserAdapter? = null
+
+ override fun onCreate(savedInstanceState: android.os.Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(activity_main)
+ setSupportActionBar(toolbar)
+ initRecyclerView()
+ }
+
+ fun initRecyclerView() {
+ adapter = UserAdapter(prepareContactList(60)) {
+ user ->
+ Toast.makeText(this, user.toString(), Toast.LENGTH_SHORT).show()
+ }
+
+ adapter?.swipeToDeleteDelegate?.pending = true
+ recyclerView.adapter = adapter
+
+ val dividerItemDecoration = DividerItemDecoration(this, VERTICAL)
+ recyclerView.addItemDecoration(dividerItemDecoration)
+
+ val itemTouchHelper = ItemTouchHelper(adapter?.swipeToDeleteDelegate?.itemTouchCallBack)
+ itemTouchHelper.attachToRecyclerView(recyclerView)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem?) =
+ when (item?.itemId) {
+ R.id.action_undo_animation -> {
+ item.isChecked = !item.isChecked
+ adapter?.animationEnabled = item.isChecked
+ true
+ }
+ R.id.action_bottom_container -> {
+ item.isChecked = !item.isChecked
+ adapter?.bottomContainer = item.isChecked
+ true
+ }
+ R.id.action_pending -> {
+ item.isChecked = !item.isChecked
+ adapter?.swipeToDeleteDelegate?.pending = item.isChecked
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/ContactAdapter.java b/app/src/main/java/test/alexzander/swipetodelete/ContactAdapter.java
deleted file mode 100644
index bd73298..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/ContactAdapter.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package test.alexzander.swipetodelete;
-
-import android.animation.ValueAnimator;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-import java.util.List;
-
-import static test.alexzander.swipetodelete.ContactAdapterUtils.PENDING_DURATION;
-
-/**
- * Created by AlexZandR on 4/24/17
- */
-
-public class ContactAdapter extends RecyclerView.Adapter implements ItemSwipeListener,
- UndoClickListener {
-
- private Handler handler = new Handler(Looper.getMainLooper());
- private HashMap pendingRemoveActions = new HashMap<>(1);
- private HashMap animatorsMap = new HashMap<>(1);
- private List contacts;
-
- public ContactAdapter(List contacts) {
- this.contacts = contacts;
- }
-
- @Override
- public ContactHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(
- R.layout.list_item, parent, false);
- return new ContactHolder(view, this);
- }
-
- @Override
- public void onBindViewHolder(final ContactHolder holder, int position) {
- try {
- final ItemContact contact = contacts.get(position);
- if (contact == null) {
- contacts.remove(position);
- notifyItemRemoved(position);
- } else {
- holder.isPendingDelete = contact.isPendingDelete;
- if (contact.isPendingDelete) {
- onBindPendingContact(holder, contact);
- } else {
- onBindCommonContact(holder, contact);
- }
- }
- } catch (IndexOutOfBoundsException exc) {
- exc.printStackTrace();
- }
- }
-
- @Override
- public int getItemCount() {
- return contacts.size();
- }
-
- @Override
- public void onItemSwiped(final ContactHolder holder, final int swipeDir) {
- int position = holder.getAdapterPosition();
- final ItemContact contact = contacts.get(position);
- if (contact.isPendingDelete) {
- removeItem(contact);
- } else {
- contact.isPendingDelete = true;
- contact.setDirection(swipeDir);
- notifyItemChanged(position);
- }
- }
-
- @Override
- public void onUndoClick(final int position) {
- if (position != -1) {
- final String mapKey = contacts.get(position).name;
- handler.removeCallbacks(pendingRemoveActions.get(mapKey));
- contacts.get(position).isPendingDelete = false;
- notifyItemChanged(position);
- ContactAdapterUtils.clearAnimator(animatorsMap.get(mapKey));
- }
- }
-
- private void onBindCommonContact(final ContactHolder holder, final ItemContact contact) {
- clearAnimation(contact, holder.progressIndicator);
- holder.name.setText(contact.name);
- holder.phone.setText(contact.phone);
- holder.contactContainer.setVisibility(View.VISIBLE);
- holder.undoData.setVisibility(View.GONE);
- }
-
- private void onBindPendingContact(final ContactHolder holder, final ItemContact contact) {
- holder.deletedName.setText("You have just deleted " + contact.name);
- holder.contactContainer.setVisibility(View.GONE);
- holder.undoData.setVisibility(View.VISIBLE);
- holder.progressIndicator.setVisibility(View.VISIBLE);
- holder.progressIndicator.setX(contact.posX);
-
- if (pendingRemoveActions.get(contact.name) == null) {
- pendingRemoveActions.put(contact.name, new Runnable() {
- @Override
- public void run() {
- removeItem(contact);
- }
- });
- }
-
- handler.postDelayed(pendingRemoveActions.get(contact.name), PENDING_DURATION);
- final ValueAnimator animator;
- if (animatorsMap.get(contact.name) != null) {
- animator = animatorsMap.get(contact.name);
- ContactAdapterUtils.initAnimator(holder.progressIndicator, contact, animator);
- } else {
- animator = ContactAdapterUtils.createAnimator(holder.progressIndicator, contact);
- animatorsMap.put(contact.name, animator);
- }
- animator.start();
- }
-
- private void removeItem(final ItemContact contact) {
- final int pos = contacts.indexOf(contact);
- handler.removeCallbacks(pendingRemoveActions.remove(contact.name));
- pendingRemoveActions.remove(contact.name);
- contacts.remove(contact);
- notifyItemRemoved(pos);
- ContactAdapterUtils.clearContact(contact);
- ContactAdapterUtils.clearAnimator(animatorsMap.remove(contact.name));
- }
-
- private void clearAnimation(final ItemContact contact, final View progressView) {
- ContactAdapterUtils.clearAnimator(animatorsMap.get(contact.name));
- ContactAdapterUtils.clearContact(contact);
- ContactAdapterUtils.clearView(progressView);
- }
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/ContactAdapterUtils.java b/app/src/main/java/test/alexzander/swipetodelete/ContactAdapterUtils.java
deleted file mode 100644
index d9eebb7..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/ContactAdapterUtils.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package test.alexzander.swipetodelete;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Created by AlexZandR on 4/24/17
- */
-
-public abstract class ContactAdapterUtils {
-
- public static final long DELETING_DURATION = 3000;
- public static final long PENDING_DURATION = DELETING_DURATION - 150;
-
- private ContactAdapterUtils() {
- }
-
- public static List prepareContactList(int count) {
- List result = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- result.add(new ItemContact("User Name " + String.valueOf(i), "+1234567" + String.valueOf(i)));
- }
- return result;
- }
-
- public static ValueAnimator createAnimator(final View view, final ItemContact contact) {
- return initAnimator(view, contact, null);
- }
-
- public static ValueAnimator initAnimator(final View view, final ItemContact contact,
- ValueAnimator animator) {
- final float screenWidth = MainActivity.sDeviceScreenWidth;
- if (animator == null) {
- animator = ValueAnimator.ofFloat(contact.posX, screenWidth * contact.direction);
- } else {
- animator.removeAllUpdateListeners();
- animator.removeAllListeners();
- animator.setFloatValues(contact.posX, screenWidth * contact.direction);
- }
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- final float posX = (Float) animation.getAnimatedValue();
- view.setX(posX);
- contact.posX = posX;
- }
- });
- animator.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- contact.isRunningAnimation = true;
- view.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- contact.isRunningAnimation = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- clearContact(contact);
- clearView(view);
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- });
- animator.setDuration((long) (DELETING_DURATION * (screenWidth - contact.posX * contact.direction) / screenWidth));
- return animator;
- }
-
- public static void clearAnimator(final ValueAnimator animator) {
- if (animator != null) {
- animator.cancel();
- animator.removeAllUpdateListeners();
- animator.removeAllListeners();
- }
- }
-
- public static void clearContact(final ItemContact contact) {
- if (contact != null) {
- contact.isPendingDelete = false;
- contact.isRunningAnimation = false;
- contact.posX = 0;
- }
- }
-
- public static void clearView(final View view) {
- if (view != null) {
- view.setX(0);
- view.setVisibility(View.GONE);
- }
- }
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/ContactHolder.java b/app/src/main/java/test/alexzander/swipetodelete/ContactHolder.java
deleted file mode 100644
index a31e82d..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/ContactHolder.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package test.alexzander.swipetodelete;
-
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * Created by AlexZandR on 4/24/17.
- */
-
-public class ContactHolder extends RecyclerView.ViewHolder {
-
- public boolean isPendingDelete = false;
- public TextView deletedName;
- public TextView name;
- public TextView phone;
- public View progressIndicator;
- public View contactContainer;
- public View undoContainer;
- public View undoData;
-
-
- public ContactHolder(final View itemView, final UndoClickListener listener) {
- super(itemView);
- deletedName = (TextView) itemView.findViewById(R.id.user_name_deleted);
- name = (TextView) itemView.findViewById(R.id.user_name);
- phone = (TextView) itemView.findViewById(R.id.user_phone_number);
- contactContainer = itemView.findViewById(R.id.contact_container);
- undoContainer = itemView.findViewById(R.id.undo_container);
- undoData = itemView.findViewById(R.id.undo_data);
- itemView.findViewById(R.id.button_undo).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- listener.onUndoClick(getAdapterPosition());
- }
- });
- progressIndicator = itemView.findViewById(R.id.progress_indicator);
- }
-
- View getTopContainer() {
- if (isPendingDelete) {
- return undoContainer;
- } else {
- return contactContainer;
- }
- }
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/ContactItemTouchCallback.java b/app/src/main/java/test/alexzander/swipetodelete/ContactItemTouchCallback.java
deleted file mode 100644
index 654eb5e..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/ContactItemTouchCallback.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package test.alexzander.swipetodelete;
-
-import android.graphics.Canvas;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
-
-/**
- * Created by AlexZandR on 4/24/17
- */
-
-public class ContactItemTouchCallback extends ItemTouchHelper.Callback {
-
- private int swipeDirs = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.END |
- ItemTouchHelper.START;
- private ItemSwipeListener listener;
-
- public ContactItemTouchCallback(ItemSwipeListener listener) {
- this.listener = listener;
- }
-
- @Override
- public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
- return makeMovementFlags(0, swipeDirs);
- }
-
- @Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
- return false;
- }
-
- @Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
- if (listener != null) {
- listener.onItemSwiped((ContactHolder) viewHolder, swipeDir);
- }
- }
-
- @Override
- public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
- getDefaultUIUtil().clearView(((ContactHolder) viewHolder).getTopContainer());
- }
-
- @Override
- public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
- if (viewHolder != null) {
- getDefaultUIUtil().onSelected(((ContactHolder) viewHolder).getTopContainer());
- }
- }
-
- @Override
- public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder
- , float dX, float dY, int actionState, boolean isCurrentlyActive) {
- getDefaultUIUtil().onDraw(c, recyclerView,
- ((ContactHolder) viewHolder).getTopContainer(), dX, dY,
- actionState, isCurrentlyActive);
- }
-
- @Override
- public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder
- , float dX, float dY, int actionState, boolean isCurrentlyActive) {
- getDefaultUIUtil().onDrawOver(c, recyclerView,
- ((ContactHolder) viewHolder).getTopContainer(), dX, dY,
- actionState, isCurrentlyActive);
- }
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/ItemContact.java b/app/src/main/java/test/alexzander/swipetodelete/ItemContact.java
deleted file mode 100644
index 35011cf..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/ItemContact.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package test.alexzander.swipetodelete;
-
-import android.support.v7.widget.helper.ItemTouchHelper;
-
-/**
- * Created by AlexZandR on 4/24/17
- */
-
-public class ItemContact {
-
- public static final int LEFT = -1;
- public static final int RIGHT = 1;
-
- boolean isPendingDelete = false;
- boolean isRunningAnimation = false;
- int direction;
- float posX = 0;
- String name;
- String phone;
-
- public ItemContact(String name, String phone) {
- this.name = name;
- this.phone = phone;
- }
-
- void setDirection(int swipeDir) {
- if (ItemTouchHelper.LEFT == swipeDir || ItemTouchHelper.START == swipeDir) {
- direction = LEFT;
- } else {
- direction = RIGHT;
- }
- }
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/ItemSwipeListener.java b/app/src/main/java/test/alexzander/swipetodelete/ItemSwipeListener.java
deleted file mode 100644
index 3f1a6b7..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/ItemSwipeListener.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package test.alexzander.swipetodelete;
-
-/**
- * Created by AlexZandR on 4/24/17
- */
-
-public interface ItemSwipeListener {
- void onItemSwiped(ContactHolder holder, int swipeDir);
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/MainActivity.java b/app/src/main/java/test/alexzander/swipetodelete/MainActivity.java
deleted file mode 100644
index 5d3afcc..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/MainActivity.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package test.alexzander.swipetodelete;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.DividerItemDecoration;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-
-import static android.widget.LinearLayout.VERTICAL;
-
-public class MainActivity extends AppCompatActivity {
-
- public static int sDeviceScreenWidth;
- private ContactAdapter adapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- deviceWidth();
- setContentView(R.layout.activity_main);
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Snackbar.make(view, "Navigate to the Create Contact screen ", Snackbar.LENGTH_LONG)
- .setAction("Action", null).show();
- }
- });
-
- initRecyclerView();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
-
- private void initRecyclerView() {
-
- RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
- recyclerView.setLayoutManager(new LinearLayoutManager(this, VERTICAL, false));
-
- adapter = new ContactAdapter(ContactAdapterUtils.prepareContactList(50));
- recyclerView.setAdapter(adapter);
-
- DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, VERTICAL);
- recyclerView.addItemDecoration(dividerItemDecoration);
-
- ItemTouchHelper.Callback touchCallback = new ContactItemTouchCallback(adapter);
- ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchCallback);
- itemTouchHelper.attachToRecyclerView(recyclerView);
- }
-
- private void deviceWidth() {
- WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
-
- Point size = new Point();
- display.getSize(size);
- sDeviceScreenWidth = size.x;
- }
-}
diff --git a/app/src/main/java/test/alexzander/swipetodelete/UndoClickListener.java b/app/src/main/java/test/alexzander/swipetodelete/UndoClickListener.java
deleted file mode 100644
index 56e99b6..0000000
--- a/app/src/main/java/test/alexzander/swipetodelete/UndoClickListener.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package test.alexzander.swipetodelete;
-
-/**
- * Created by AlexZandR on 4/24/17
- */
-
-public interface UndoClickListener {
- void onUndoClick(int position);
-}
diff --git a/app/src/main/res/layout/activity_java.xml b/app/src/main/res/layout/activity_java.xml
new file mode 100644
index 0000000..c1cf97e
--- /dev/null
+++ b/app/src/main/res/layout/activity_java.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index d399bb2..718dab9 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,10 +1,11 @@
-
+ tools:context="agilie.example.swipe2delete.kotlin.UsersActivity">
+ app:popupTheme="@style/AppTheme.PopupOverlay"/>
-
-
-
+
diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/activity_main_item.xml
similarity index 85%
rename from app/src/main/res/layout/list_item.xml
rename to app/src/main/res/layout/activity_main_item.xml
index 4752f7c..2a033c0 100644
--- a/app/src/main/res/layout/list_item.xml
+++ b/app/src/main/res/layout/activity_main_item.xml
@@ -1,9 +1,9 @@
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:animateLayoutChanges="true"
+ android:gravity="center_vertical|start">
+ android:visibility="gone"/>
+ android:paddingStart="16dp">
+ android:textColor="@android:color/white"/>
+ android:textColor="@android:color/white"/>
@@ -66,6 +65,7 @@
android:layout_height="match_parent"
android:background="@android:color/white"
android:clickable="true"
+ android:focusable="true"
android:visibility="visible">
+ android:singleLine="true"/>
+ android:textColor="@color/black_transparent_38"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
deleted file mode 100644
index e71195b..0000000
--- a/app/src/main/res/layout/content_main.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_user.xml b/app/src/main/res/layout/item_user.xml
new file mode 100644
index 0000000..9ce16d7
--- /dev/null
+++ b/app/src/main/res/layout/item_user.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_view.xml b/app/src/main/res/layout/recycler_view.xml
new file mode 100644
index 0000000..1b9762a
--- /dev/null
+++ b/app/src/main/res/layout/recycler_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index 3f2574d..b268d18 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -1,10 +1,27 @@
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_app.png b/app/src/main/res/mipmap-mdpi/ic_launcher_app.png
new file mode 100644
index 0000000..c8f9adb
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_app.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_app.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_app.png
new file mode 100644
index 0000000..1bdab64
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_app.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_app.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_app.png
new file mode 100644
index 0000000..4dd4cbe
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_app.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_app.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_app.png
new file mode 100644
index 0000000..0b3bc16
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_app.png differ
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 545b9c6..1bd8d96 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -6,6 +6,8 @@
- @color/colorPrimary
- @color/colorPrimaryDark
- @color/colorAccent
+ - false
+ - true
-
+
-
+
diff --git a/app/src/test/java/test/alexzander/swipetodelete/ExampleUnitTest.java b/app/src/test/java/test/alexzander/swipetodelete/ExampleUnitTest.java
index c24021c..902b08d 100644
--- a/app/src/test/java/test/alexzander/swipetodelete/ExampleUnitTest.java
+++ b/app/src/test/java/test/alexzander/swipetodelete/ExampleUnitTest.java
@@ -2,7 +2,7 @@
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
/**
* Example local unit test, which will execute on the development machine (host).
diff --git a/build.gradle b/build.gradle
index b78a0b8..9997caa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,15 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.1.2-4'
repositories {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.1'
+ classpath 'com.android.tools.build:gradle:2.3.2'
+ classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
+ classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -16,8 +20,15 @@ allprojects {
repositories {
jcenter()
}
+ tasks.withType(Javadoc) {
+ // Ignores errors from mavenAndroidJavadocs task
+ //options.addStringOption('Xdoclint:none', '-quiet')
+ //options.addStringOption('encoding', 'UTF-8')
+ excludes = ['**/*.kt']
+ }
}
task clean(type: Delete) {
delete rootProject.buildDir
}
+
diff --git a/gradle.properties b/gradle.properties
index aac7c9b..743d692 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,16 +1,12 @@
# Project-wide Gradle settings.
-
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
-
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
-
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
-
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9db9b10..aa41ef2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 26 17:51:09 EEST 2017
+#Mon May 22 14:15:04 EEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zip
diff --git a/settings.gradle b/settings.gradle
index e7b4def..fab8fee 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':swipe2delete'
diff --git a/swipe2delete/.gitignore b/swipe2delete/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/swipe2delete/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/swipe2delete/bintray.gradle b/swipe2delete/bintray.gradle
new file mode 100644
index 0000000..9d5825d
--- /dev/null
+++ b/swipe2delete/bintray.gradle
@@ -0,0 +1,45 @@
+apply plugin: 'com.jfrog.bintray'
+
+version = '1.0' //YOUR LIBRARY VERSION
+
+task sourcesJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ classifier = 'sources'
+}
+
+task javadoc(type: Javadoc) {
+ source = android.sourceSets.main.java.srcDirs
+ classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+ classifier = 'javadoc'
+ from javadoc.destinationDir
+}
+artifacts {
+ archives javadocJar
+ archives sourcesJar
+}
+
+// Bintray
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+
+
+bintray {
+ user = properties.getProperty("bintray.user")
+ key = properties.getProperty("bintray.apikey")
+
+ configurations = ['archives']
+ pkg {
+ repo = 'maven'
+ name = 'Swipe2Delete' //YOUR PACKAGE NAME
+ userOrg = 'agilie'
+ desc = 'Library to achieve "swipe to delete" animation on each view in recyclerview' // YOUR LIBRARY DESCRIPTION
+ websiteUrl = 'https://github.com/agilie/SwipeToDelete' // YOUR SITE
+ vcsUrl = 'https://github.com/agilie/SwipeToDelete' // YOUR GIT REPO
+ licenses = ["Apache-2.0"] // A LIST OF YOUR LICENCES
+ publish = true
+ publicDownloadNumbers = true
+ }
+}
\ No newline at end of file
diff --git a/swipe2delete/build.gradle b/swipe2delete/build.gradle
new file mode 100644
index 0000000..1ed8874
--- /dev/null
+++ b/swipe2delete/build.gradle
@@ -0,0 +1,40 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "25.0.3"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.15'
+ compile 'com.android.support:recyclerview-v7:25.3.1'
+ compile 'com.android.support:appcompat-v7:25.3.1'
+ testCompile 'junit:junit:4.12'
+}
+repositories {
+ mavenCentral()
+}
+apply from: 'bintray.gradle'
+apply from: 'install.gradle'
\ No newline at end of file
diff --git a/swipe2delete/install.gradle b/swipe2delete/install.gradle
new file mode 100644
index 0000000..ef1c304
--- /dev/null
+++ b/swipe2delete/install.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.github.dcendents.android-maven'
+group = 'com.agilie' // CREATE A GROUP ID FOR YOUR LIBRARY
+
+install {
+ repositories.mavenInstaller {
+ pom {
+ project {
+ packaging 'Swipe2delete'
+ groupId 'com.agilie' // CREATE A GROUP ID FOR YOUR LIBRARY
+ artifactId 'swipe2delete' // THE NAME OF YOUR MODULE
+
+ name 'swipe2Deletet' // YOUR LIBRARY NAME
+ description 'Extended EditText which allows to move, rotate and resize text at the same time' // YOUR LIBRARY DESCRIPTION
+ url 'https://github.com/agilie/SwipeToDelete' // YOUR SITE
+
+ licenses {
+ license {
+ name 'The Apache Software License, Version 2.0'
+ url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ }
+ }
+ developers {
+ developer {
+
+ }
+ }
+ scm {
+ connection 'https://github.com/agilie/SwipeToDelete.git' // YOUR GIT REPO
+ developerConnection 'https://github.com/agilie/SwipeToDelete.git' // YOUR GIT REPO
+ url 'https://github.com/agilie/SwipeToDelete' // YOUR SITE
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/swipe2delete/proguard-rules.pro b/swipe2delete/proguard-rules.pro
new file mode 100644
index 0000000..943c56a
--- /dev/null
+++ b/swipe2delete/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/user/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/swipe2delete/src/androidTest/java/com/example/swipe2delete/ExampleInstrumentedTest.java b/swipe2delete/src/androidTest/java/com/example/swipe2delete/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..1285f47
--- /dev/null
+++ b/swipe2delete/src/androidTest/java/com/example/swipe2delete/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.example.swipe2delete;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.example.swipetodeletelib.test", appContext.getPackageName());
+ }
+}
diff --git a/swipe2delete/src/main/AndroidManifest.xml b/swipe2delete/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7f22ebb
--- /dev/null
+++ b/swipe2delete/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/AnimationInitiator.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/AnimationInitiator.kt
new file mode 100644
index 0000000..64bf65d
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/AnimationInitiator.kt
@@ -0,0 +1,46 @@
+package com.agilie.swipe2delete
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import com.agilie.swipe2delete.interfaces.IAnimationUpdateListener
+import com.agilie.swipe2delete.interfaces.IAnimatorListener
+
+var rowWidth: Int = 0
+
+internal fun initAnimator(options: ModelOptions<*>, animatorListener: IAnimatorListener? = null,
+ valUpdateListener: IAnimationUpdateListener? = null, valueAnimator: ValueAnimator? = null): ValueAnimator {
+ val animator: ValueAnimator
+ val screenWidth = rowWidth
+
+ if (valueAnimator == null) animator = ValueAnimator.ofFloat(options.posX, screenWidth * options.direction!!.toFloat())
+ else {
+ animator = valueAnimator
+ animator.removeAllUpdateListeners()
+ animator.removeAllListeners()
+ animator.setFloatValues(options.posX, screenWidth * options.direction!!.toFloat())
+ }
+
+ animator.addUpdateListener { animation -> valUpdateListener?.onAnimationUpdated(animation, options) }
+ animator.addListener((object : Animator.AnimatorListener {
+ override fun onAnimationStart(animation: Animator) {
+ options.runningAnimation = true
+ animatorListener?.onAnimationStart(animation, options)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ options.runningAnimation = false
+ animatorListener?.onAnimationEnd(animation, options)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ clearOptions(options)
+ animatorListener?.onAnimationCancel(animation, options)
+ }
+
+ override fun onAnimationRepeat(animation: Animator) {
+ animatorListener?.onAnimationRepeat(animation, options)
+ }
+ }))
+ animator.duration = (options.deletingDuration * (screenWidth - options.posX * options.direction!!.toFloat()) / screenWidth).toLong()
+ return animator
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/ContactItemTouchCallback.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/ContactItemTouchCallback.kt
new file mode 100644
index 0000000..5c186a5
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/ContactItemTouchCallback.kt
@@ -0,0 +1,45 @@
+package com.agilie.swipe2delete
+
+import android.graphics.Canvas
+import android.support.v7.widget.RecyclerView
+import android.support.v7.widget.helper.ItemTouchHelper
+import com.agilie.swipe2delete.interfaces.ISwipeToDeleteHolder
+import com.agilie.swipe2delete.interfaces.ItemSwipeListener
+
+class ContactItemTouchCallback(private val listener: ItemSwipeListener?) : ItemTouchHelper.Callback() {
+
+ private val swipeDirs = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT or ItemTouchHelper.END or
+ ItemTouchHelper.START
+
+ override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) =
+ ItemTouchHelper.Callback.makeMovementFlags(0, swipeDirs)
+
+ override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder) = false
+
+ override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
+ listener?.onItemSwiped(viewHolder as ISwipeToDeleteHolder, swipeDir)
+ }
+
+ override fun clearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder) {
+ ItemTouchHelper.Callback.getDefaultUIUtil().clearView((viewHolder as ISwipeToDeleteHolder).topContainer)
+ listener?.clearView(viewHolder)
+ }
+
+ override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
+ if (viewHolder != null) {
+ ItemTouchHelper.Callback.getDefaultUIUtil().onSelected((viewHolder as ISwipeToDeleteHolder<*>).topContainer)
+ }
+ }
+
+ override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
+ ItemTouchHelper.Callback.getDefaultUIUtil().onDraw(c, recyclerView,
+ (viewHolder as ISwipeToDeleteHolder<*>).topContainer, dX, dY,
+ actionState, isCurrentlyActive)
+ }
+
+ override fun onChildDrawOver(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
+ ItemTouchHelper.Callback.getDefaultUIUtil().onDrawOver(c, recyclerView,
+ (viewHolder as ISwipeToDeleteHolder<*>).topContainer, dX, dY,
+ actionState, isCurrentlyActive)
+ }
+}
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/ModelOptions.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/ModelOptions.kt
new file mode 100644
index 0000000..b0c1008
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/ModelOptions.kt
@@ -0,0 +1,25 @@
+package com.agilie.swipe2delete
+
+import android.support.v7.widget.helper.ItemTouchHelper
+
+class ModelOptions(var key: K, var deletingDuration: Long) {
+ val pendingDuration: Long
+ get() =
+ if ((deletingDuration - 150) <= 0) 150
+ else deletingDuration - 150
+ var direction: Int? = 0
+ var posX = 0f
+
+ var pendingDelete = false
+ var runningAnimation = false
+ var viewActive = false
+
+ internal fun setDirection(swipeDir: Int) =
+ if (ItemTouchHelper.LEFT == swipeDir || ItemTouchHelper.START == swipeDir) direction = LEFT
+ else direction = RIGHT
+
+ companion object {
+ val LEFT = -1
+ val RIGHT = 1
+ }
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/SwipeToDeleteDelegate.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/SwipeToDeleteDelegate.kt
new file mode 100644
index 0000000..37b1ea0
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/SwipeToDeleteDelegate.kt
@@ -0,0 +1,129 @@
+package com.agilie.swipe2delete
+
+import android.animation.ValueAnimator
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import com.agilie.swipe2delete.interfaces.*
+import java.lang.IndexOutOfBoundsException
+
+class SwipeToDeleteDelegate>(private val items: MutableList, val swipeToDeleteAdapter: ISwipeToDeleteAdapter) : ItemSwipeListener, IUndoClickListener {
+
+ var deletingDuration: Long? = null
+ val handler = Handler(Looper.getMainLooper())
+
+ val itemTouchCallBack = ContactItemTouchCallback(this)
+
+ val pendingRemoveActions = HashMap(1)
+ val holders = HashMap()
+ val animatorsMap = HashMap(1)
+ val modelOptionsMap = HashMap>()
+
+ var animationUpdateListener: IAnimationUpdateListener? = null
+ var animatorListener: IAnimatorListener? = null
+ var pending: Boolean = false
+ var knownWidth = false
+
+ init {
+ if (swipeToDeleteAdapter is IAnimatorListener) animatorListener = swipeToDeleteAdapter
+ if (swipeToDeleteAdapter is IAnimationUpdateListener) animationUpdateListener = swipeToDeleteAdapter
+ }
+
+ fun onBindViewHolder(holder: H, key: K, position: Int) {
+ try {
+ if (!knownWidth) {
+ getRowWidth(holder.topContainer)
+ knownWidth = true
+ }
+ holder.key = key
+ if (!modelOptionsMap.containsKey(key)) modelOptionsMap.put(key, ModelOptions(key, deletingDuration ?: 0))
+ val item = items[position]
+ if (item == null) {
+ items.removeAt(position)
+ swipeToDeleteAdapter.notifyItemRemoved(position)
+ } else {
+ holders[key] = holder
+ holder.pendingDelete = modelOptionsMap[key]!!.pendingDelete
+
+ if (modelOptionsMap[key]!!.pendingDelete) onBindPendingContact(holder, key, item, animatorListener, animationUpdateListener, position)
+ else onBindCommonContact(holder, key, item, position)
+ }
+ } catch (exc: IndexOutOfBoundsException) {
+ exc.printStackTrace()
+ }
+ }
+
+ override fun clearView(viewHolder: ISwipeToDeleteHolder) {
+ modelOptionsMap[viewHolder.key]?.viewActive = true
+ }
+
+ override fun onItemSwiped(viewHolder: ISwipeToDeleteHolder, swipeDir: Int) {
+ val key = viewHolder.key
+ if (pending) {
+ val modelOption = modelOptionsMap[key]
+ if (modelOption?.pendingDelete ?: false) removeItemByKey(key)
+ else {
+ modelOption?.pendingDelete = true
+ modelOption?.setDirection(swipeDir)
+ swipeToDeleteAdapter.notifyItemChanged(swipeToDeleteAdapter.findItemPositionByKey(key))
+ }
+ } else {
+ removeItem(key)
+ }
+ }
+
+ override fun onUndo(key: K) {
+ val modelOption = modelOptionsMap[key]
+ if (modelOption?.viewActive ?: false) {
+ val position = swipeToDeleteAdapter.findItemPositionByKey(key)
+ handler.removeCallbacks(pendingRemoveActions[key])
+ modelOption?.pendingDelete = false
+ swipeToDeleteAdapter.notifyItemChanged(position)
+ clearAnimator(animatorsMap[key])
+ modelOption?.viewActive = false
+ }
+ }
+
+ fun onBindCommonContact(holder: H, key: K, item: V, position: Int) =
+ swipeToDeleteAdapter.onBindCommonItem(holder, key, item, position)
+
+ fun onBindPendingContact(holder: H, key: K, item: V, IAnimatorListener: IAnimatorListener? = null, IAnimationUpdateListener: IAnimationUpdateListener? = null, position: Int) {
+ swipeToDeleteAdapter.onBindPendingItem(holder, key, item, position)
+ pendingRemoveActions[key] ?: pendingRemoveActions.put(key, Runnable { removeItemByKey(key) })
+ handler.postDelayed(pendingRemoveActions[key], modelOptionsMap[key]!!.pendingDuration)
+ val animator: ValueAnimator?
+ if (animatorsMap[key] != null) {
+ animator = animatorsMap[key]
+ initAnimator(modelOptionsMap[key]!!, IAnimatorListener, IAnimationUpdateListener, animator)
+ } else {
+ animator = initAnimator(modelOptionsMap[key]!!, IAnimatorListener, IAnimationUpdateListener)
+ animatorsMap.put(key, animator)
+ }
+ animator?.start()
+ }
+
+ fun removeItemByKey(key: K) = swipeToDeleteAdapter.removeItem(key)
+
+ fun removeItem(key: K) {
+ val position = swipeToDeleteAdapter.findItemPositionByKey(key)
+ removeItemFromList(key, items.removeAt(position), position)
+ }
+
+ fun removeItemFromList(key: K, item: V, position: Int) {
+ handler.removeCallbacks(pendingRemoveActions.remove(key))
+ pendingRemoveActions.remove(key)
+ items.remove(item)
+ holders.remove(key)
+ modelOptionsMap.remove(key)
+ swipeToDeleteAdapter.notifyItemRemoved(position)
+ clearOptions(modelOptionsMap[key])
+ clearAnimator(animatorsMap.remove(key))
+ swipeToDeleteAdapter.onItemDeleted(item)
+ }
+
+ fun clearAnimation(key: K, view: View?) {
+ clearAnimator(animatorsMap[key])
+ clearOptions(modelOptionsMap[key])
+ clearView(view)
+ }
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/Utils.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/Utils.kt
new file mode 100644
index 0000000..e6468b7
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/Utils.kt
@@ -0,0 +1,27 @@
+package com.agilie.swipe2delete
+
+import android.animation.ValueAnimator
+import android.view.View
+
+internal fun clearOptions(options: ModelOptions<*>?) {
+ options?.pendingDelete = false
+ options?.runningAnimation = false
+ options?.posX = 0f
+}
+
+internal fun clearAnimator(animator: ValueAnimator?) {
+ animator?.cancel()
+ animator?.removeAllUpdateListeners()
+ animator?.removeAllListeners()
+}
+
+internal fun clearView(view: View?) {
+ view?.x = 0f
+ view?.visibility = View.GONE
+}
+
+internal fun getRowWidth(view: View?) {
+ rowWidth = view?.measuredWidth ?: 0
+ view?.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> rowWidth = right - left }
+}
+
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IAnimationUpdateListener.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IAnimationUpdateListener.kt
new file mode 100644
index 0000000..468e26a
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IAnimationUpdateListener.kt
@@ -0,0 +1,8 @@
+package com.agilie.swipe2delete.interfaces
+
+import com.agilie.swipe2delete.ModelOptions
+
+
+interface IAnimationUpdateListener {
+ fun onAnimationUpdated(animation: android.animation.ValueAnimator?, options: ModelOptions<*>)
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IAnimatorListener.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IAnimatorListener.kt
new file mode 100644
index 0000000..3ddea1d
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IAnimatorListener.kt
@@ -0,0 +1,15 @@
+package com.agilie.swipe2delete.interfaces
+
+import android.animation.Animator
+import com.agilie.swipe2delete.ModelOptions
+
+
+interface IAnimatorListener {
+ fun onAnimationEnd(animation: Animator?, options: ModelOptions<*>) {}
+
+ fun onAnimationCancel(animation: Animator?, options: ModelOptions<*>) {}
+
+ fun onAnimationStart(animation: Animator?, options: ModelOptions<*>) {}
+
+ fun onAnimationRepeat(animation: Animator, options: ModelOptions<*>) {}
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ISwipeToDeleteAdapter.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ISwipeToDeleteAdapter.kt
new file mode 100644
index 0000000..f3dd1f2
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ISwipeToDeleteAdapter.kt
@@ -0,0 +1,19 @@
+package com.agilie.swipe2delete.interfaces
+
+
+interface ISwipeToDeleteAdapter {
+
+ fun notifyItemRemoved(position: Int)
+
+ fun notifyItemChanged(position: Int)
+
+ fun findItemPositionByKey(key: K): Int
+
+ fun onBindCommonItem(holder: H, key: K, item: V, position: Int)
+
+ fun onBindPendingItem(holder: H, key: K, item: V, position: Int) {}
+
+ fun onItemDeleted(item: V) {}
+
+ fun removeItem(key: K)
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ISwipeToDeleteHolder.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ISwipeToDeleteHolder.kt
new file mode 100644
index 0000000..213871b
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ISwipeToDeleteHolder.kt
@@ -0,0 +1,17 @@
+package com.agilie.swipe2delete.interfaces
+
+import android.view.View
+
+interface ISwipeToDeleteHolder {
+
+ var pendingDelete: Boolean
+ /**
+ * get() =
+ * if (pendingDelete!!) undoContainer
+ * else itemContainer
+ *
+ */
+ val topContainer: View
+
+ var key: K
+}
\ No newline at end of file
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IUndoClickListener.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IUndoClickListener.kt
new file mode 100644
index 0000000..13a190f
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/IUndoClickListener.kt
@@ -0,0 +1,5 @@
+package com.agilie.swipe2delete.interfaces
+
+interface IUndoClickListener {
+ fun onUndo(key: K)
+}
diff --git a/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ItemSwipeListener.kt b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ItemSwipeListener.kt
new file mode 100644
index 0000000..834c73e
--- /dev/null
+++ b/swipe2delete/src/main/java/com/agilie/swipe2delete/interfaces/ItemSwipeListener.kt
@@ -0,0 +1,7 @@
+package com.agilie.swipe2delete.interfaces
+
+interface ItemSwipeListener {
+ fun onItemSwiped(viewHolder: ISwipeToDeleteHolder, swipeDir: Int)
+
+ fun clearView(viewHolder: ISwipeToDeleteHolder)
+}
diff --git a/swipe2delete/src/main/res/values/strings.xml b/swipe2delete/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c7b0c6a
--- /dev/null
+++ b/swipe2delete/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ SwipeToDeleteLib
+
diff --git a/swipe2delete/src/test/java/com/example/swipe2delete/ExampleUnitTest.java b/swipe2delete/src/test/java/com/example/swipe2delete/ExampleUnitTest.java
new file mode 100644
index 0000000..7e4a48f
--- /dev/null
+++ b/swipe2delete/src/test/java/com/example/swipe2delete/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.example.swipe2delete;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file